How to get the request header in a Dart Aqueduct server - dart

I want to get a token from the Authorization header of the request.
import 'dart:async';
import 'package:aqueduct/aqueduct.dart';
class SecretController extends ResourceController {
#Operation.get()
Future<Response> signin() async {
request.headers // ??? not available
}
}
How do I access that header in Aqueduct?

A request is a wrapper around a raw HttpRequest object from the Dart standard library, so one way is to access it like this:
request.raw.headers["authorization"]
You can also bind the value of a header to a method parameter; this is nice when you want to parse the string header value into another type and/or validate the header. (You can do some really convenient stuff with binding.)
#Operation.get()
Future<Response> signIn(
#Bind.header("authorization") String authorization) async {
...
}
Finally, specific to the Authorization header, you can use Authorizer middleware. This validates the authorization header and creates an Authorization object with the details of authorized resource owner ('the user') that you can access from your method.
router.route("/secret")
.link(() => Authorizer.bearer(authServer))
.link(() => SecretController());
...
#Operation.get()
Future<Response> signIn() async {
final userIDForRequest = request.authorization.ownerID;
}

Related

Spring security saml2 provider: InResponseTo validation for saml2 executed even if saved request is not found

With spring security saml2 provider version 5.7.x mandatory validation of InResponseTo was introduced if it is provided in the authentication response.
Validation logic expects to find saved Saml2AuthenticationRequest in HttpSession. However that is only possible if SameSite attribute is not set.
According security requirements of current project I'm working on it is set to Lax or Strict. This configuration is done outside of the application. This causes loss of the session and request data.
Maybe someone already have dealed with an issue and knows how to deal with it? I don't see any way to disable validation or alternative way of saving request available in the library.
While upgrading spring security from 5.x to 6, I ran into the same issue with spring session cookie. There doesn't seem to be any standard solution for this. For now, I have provided an implementation of Saml2AuthenticationRequestRepository which saves the SAML request in database based on RelayState parameter. Following is the sample implementation using Mongo. Any backend (In-memory cache, Redis, MySQL etc.) can be used -
import java.util.Optional;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.security.saml2.core.Saml2ParameterNames;
import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest;
import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository;
import org.springframework.stereotype.Repository;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
#Repository
#RequiredArgsConstructor
#Slf4j
public class MongoSaml2AuthenticationRequestRepository implements Saml2AuthenticationRequestRepository<AbstractSaml2AuthenticationRequest> {
public static final String SAML2_REQUEST_COLLECTION = "saml2RequestsRepository";
private final #NonNull MongoTemplate mongoTemplate;
#Override
public AbstractSaml2AuthenticationRequest loadAuthenticationRequest(HttpServletRequest request) {
String relayState = request.getParameter(Saml2ParameterNames.RELAY_STATE);
if (relayState == null) {
return null;
}
log.debug("Fetching SAML2 Authentication Request by relay state : {}", relayState.get());
Query query = Query.query(Criteria.where("relayState").is(relayState.get()));
AbstractSaml2AuthenticationRequest authenticationRequest = mongoTemplate.findOne(query, AbstractSaml2AuthenticationRequest.class, SAML2_REQUEST_COLLECTION);
if (!authenticationRequest.getRelayState().equals(relayState.get())) {
log.error("Relay State received from request '{}' is different from saved request '{}'.", relayState.get(), authenticationRequest.getRelayState());
return null;
}
log.debug("SAML2 Request retrieved : {}", authenticationRequest);
return authenticationRequest;
}
#Override
public void saveAuthenticationRequest(AbstractSaml2AuthenticationRequest authenticationRequest,
HttpServletRequest request, HttpServletResponse response) {
//As per OpenSamlAuthenticationRequestResolver, it will always have value. However, one validation can be added to check for null and regenerate.
String relayState = authenticationRequest.getRelayState();
log.debug("Relay State Received: {}", relayState);
mongoTemplate.save(authenticationRequest, SAML2_REQUEST_COLLECTION);
}
#Override
public AbstractSaml2AuthenticationRequest removeAuthenticationRequest(HttpServletRequest request,
HttpServletResponse response) {
AbstractSaml2AuthenticationRequest authenticationRequest = loadAuthenticationRequest(request);
if (authenticationRequest == null) {
return null;
}
mongoTemplate.remove(authenticationRequest, SAML2_REQUEST_COLLECTION);
return authenticationRequest;
}
}
Post adding this, all the SAML validations including InResponseTo validation are passed successfully. Since RelayState is not supposed to change between SAML requests as per specifications, this seemed like reasonable alternative (in absence of any standard solution so far). I am running this through standard security testing and will update the solution with my findings if any.

Smallrye open api interceptor

I am developing a rest application.
Some endpoints require a custom header parameter, not related to authorisation. I created a custom annotation using jax-rs NameBinding. Here is an usage example:
#GET
#RequiresBankHeader
public int get(
#HeaderParam("bank")
#Parameter(ref = "#/components/parameters/banks")
String bank) {
return someService.getSomeInformation();
}
There is a provider that intercepts this call and do some routine using the information in the header parameter.
The problem is that I have to repeat '#HeaderParam("bank") #Parameter(ref = "#/components/parameters/banks") String bank' everywhere, just so it appears in Swagger, even though the service classes do not need it. I was able to at least reuse the parameter definition with ref = "#/components/parameters/banks", and declaring it in the OpenAPI.yml file, that Quarkus merges with generated code very nicely.
But I also want to create and interceptor to dynamically add this do the OpenApi definition whenever RequiresBankHeader annotation is present.
Is there a way to do it?
I dont think you can use interceptors to modify the generated Openapi schema output.
If all methods on a given endpoint require some parameter, you can specify it on class level like so:
#Path("/someendpoint")
public class MyEndpoint {
#HeaderParam("bank")
#Parameter(name = "bank")
String bank;
#GET
public Response getAll() {return Response.ok().build()}
#GET
#Path("{id}")
public Response someMethod(#PathParam("id") String id) {return Response.ok().build();}
}
As mentioned by Roberto Cortez, the MP OpenAPI spec provides a programmatic way to contribute metadata to the openapi.yml file.
It is not possible to detect an annotation in the JAX-RS endpoint definition, but it was good enough to automate what I needed. Since all methods that had the RequiresBankHeader return the same Schema, I was able to hack it like this:
public class OpenApiConfigurator implements OASFilter {
#Override
public Operation filterOperation(Operation operation) {
operation.getResponses().getAPIResponses().values().stream().
map(APIResponse::getContent).
filter(Objects::nonNull).
map(Content::getMediaTypes).
flatMap(mediaTypes -> mediaTypes.values().stream()).
map(MediaType::getSchema).
filter(Objects::nonNull).
map(Schema::getRef).
filter(Objects::nonNull).
filter(ref -> ref.contains("the common response schema")).
findAny().
ifPresent(schema -> {
ParameterImpl parameter = new ParameterImpl();
parameter.setRef("#/components/parameters/banks");
operation.addParameter(parameter);
});
return operation;
}
OpenApiConfigurator should be configure in the application properties, using mp.openapi.filter=com.yourcompany.OpenApiConfigurator

Is there a way to change http request method in netflix zuul routing filter?

I'm trying to trasform http GET method call from legacy api server built with MVC1 pattern to new restful api server without any change of front-end source code using netflix zuul and eureka.
I added zuul pre filter transforming legacy url to restful convention url working after PreDecorationFilter and it works fine.
But now I'm facing problem converting the GET method to proper method like POST, PUT, DELETE by distinguising url so that the requests are properly mapped in spring controller via #GetMapping/#PostMapping/#PutMapping/#DeleteMapping.
I looked into SimpleRoutingFilter that handles HttpClient but
Because of environmental constraint, I have to use eureka service id to route to the new api server and that means I should use RibbonRoutingFilter which is quite complicated to find out a right place to this operation in.
So, is this possible to change http method or make new http request before RibbonRoutingFilter?
If possible can you please suggest where is the right place to do that or some reference?
Many thanks!
======================================================================
Milenko Jevremovic,
Would you please tell me more detail about using Feign?
I defiend #FeignClient like below
#PostMapping(value = "{url"}, consumes = "application/json")
ResponseEntity<?> postMethod(#PathVariable("url") String url);
and to get query parameters to request body for POST In zuul pre filter,
after transform logic from GET request url to POST new restful url ...
byte[] bytes = objectMapper.writeValueAsBytes(ctx.get("requestQueryParams"));
ctx.setRequests(new HttpServletRequestWrapper(request) {
#Override ..getMethod
#Override ..getContentLength
#Override ..getConentLengthLong
#Override
public ServletInputStream getInputStream() {
return new ServletInputStreamWrapper(bytes);
}
}
ResponseEntity<?> response feignClient.post(transformedNewApiUri);
and set RequestContext code that you suggested ....
and controller of new api server is like,
#PostMapping
ResponseEntity<model> post(#RequestBody req..)
It comes to controller fine but when I see the http request in post method of controller,
There is no request body for parameters.
(HttpServleterRequest getInputStream shows empty)
The request data set in zuul pre filter by HttpServletRequestWrapper is
not used in Feign maybe...?
Would you please get me more idea setting request body when changing GET query
to POST constructor for using Feign?
It is not possible to change method of HttpServletRequest, but it's possible to replace request in RequestContext. HttpServletRequestWrapper appears to be very helpful:
static class PostHttpServletRequest extends HttpServletRequestWrapper {
public PostHttpServletRequest(HttpServletRequest request) {
super(request);
}
#Override
public String getMethod() {
return "POST";
}
}
So method run can be rewritten as following:
#Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
HttpServletRequest requestWrapper = new PostHttpServletRequest(request);
ctx.setRequest(requestWrapper);
return null;
}
After doing some research did not find any built in solution.
But what comes in my mind you can use Feign client in your Pre filter, get the response, set the response and return it immediately to client from your Pre filter.
You can set Feign client url or your service id, like it is explained in the docs, it uses ribbon as well .
Change response in your run method like:
...
RequestContext ctx = RequestContext.getCurrentContext();
ctx.setResponseStatusCode(your_code);
ctx.setResponseBody(new_body);
ctx.setSendZuulResponse(false);
return null

Dropwizard ExceptionMapper: Validate original header fields

Does anyone know how I can get the original header fields from request? I would like to validate, if client will receive html or just plain/text response. Can I get this fields inside 'toResponse' method of exceptionMapper?
I created exceptionMapper like in this post:
http://gary-rowe.com/agilestack/2012/10/23/how-to-implement-a-runtimeexceptionmapper-for-dropwizard/
If you did want to get information from the original request object, you can add the following to your controller.
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.core.Context;
#Path("/my")
#Produces(["application/json", "application/hal+json"])
class MyController {
#Context
protected HttpServletRequest httpRequest
#Timed
#GET
public Response getOne(){
httpRequest.getHeaders();
... //do something with headers
return Response.ok(new Person(id:1), httpRequest.getContentType());
}

Passing Authentication Token with Breeze query

My server side breeze api calls require me to validate the token value provided with each call before returning any data. To achieve this, i am passing TokenId with each Entity Query using withParameters function of breeze Entity Query and specify the parameter on my server side controller action as illustrated below.
Following is how i am doing it right now:
Client Side
function GetCustomers(){
return breeze.EntityQuery.from('Customers')
.withParameters({ TokenId: 'token value' })
.using(entityManager).execute()
}
Server Side
[HttpGet]
public IQueryable<Customer> Customers(string TokenId)
{
//server side logic
}
This for some reason looks to me can be simplified using some configuration on Breeze Entity Manager which automatically adds the parameter value to every query sent from client. And also, on server side, i don't think its necessary to have tokenId parameter defined on each controller method. This should be easy to solve with Action Filters on the server side.
Can someone point me in right direction if it is possible and how to do this?
Thanks
You should always pass custom authentication information, such as tokens, in the headers.
If you can pass it as a custom header you can search for 'define custom headers breeze.js' to see how that can be done.
A good example -
http://breeze.github.io/doc-js/server-ajaxadapter.html
Also keep in mind that if you put the token in the query string that is persisted by most of the servers that your call is crossing over, making it much easier to find tokens for your application if an evil-doer were to download the logs from that server.
Also keep in mind that if you are using cross-domain requests (CORS) you will need to enable the custom header on the server side that is receiving the call.
To give an example of the answer above:
// get the current default Breeze AJAX adapter
var ajaxAdapter: any = breeze.config.getAdapterInstance('ajax');
// set fixed headers
ajaxAdapter.defaultSettings = {
headers: {
"Bearer": this.bearerToken
}
};
Note also that if you are using ASP.net Identity (which includes a cookie based authentication), you must include this code so that the asp.net pipeline does NOT use that cookie:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new Ask.Filters.RequireHttpsAttribute());
//This forces http header authentication which is required for web api calls.
config.SuppressDefaultHostAuthentication();
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Note also that you should use only https, with this filter:
public class RequireHttpsAttribute : AuthorizationFilterAttribute
{
public int Port { get; set; }
public RequireHttpsAttribute()
{
Port = 443;
}
public override void OnAuthorization(HttpActionContext actionContext)
{
var request = actionContext.Request;
if (request.RequestUri.Scheme != Uri.UriSchemeHttps)
{
var response = new HttpResponseMessage();
if (request.Method == HttpMethod.Get || request.Method == HttpMethod.Head)
{
var uri = new UriBuilder(request.RequestUri);
uri.Scheme = Uri.UriSchemeHttps;
uri.Port = this.Port;
response.StatusCode = HttpStatusCode.Found;
response.Headers.Location = uri.Uri;
}
else
{
response.StatusCode = HttpStatusCode.Forbidden;
}
actionContext.Response = response;
}
else
{
base.OnAuthorization(actionContext);
}
}
}
And finally, make sure you use the [Authorize] and [HostAuthentication(DefaultAuthenticationTypes.ExternalBearer)] attribute on your controllers.
See the Full Sample and article on asp.net
I suggest using oAuth tokens in the header. I am currently using Auth0, but there are many others including Google Firebase. BreezeJS has been expanded such that it will accept AuthHttp as it's HTTP client for all calls. AuthHttp will automatically add a bearer token to the header of every call to the server.
One the server side it's a matter of adding the proper oAuth client lib (via Nuget) which automatically parses out the bearer token, validates it against the oAuth server, and makes claims available for use within your API call.
For example, in my implementation my api code will receive a claim that contains the users id, which I can then validate against my db and use to filter all actions. This keeps user B from loading user A's data by directly calling the api in a browser.
For example, all API calls are first validated by the oAuth subsystem. Once my api code is executed I know the caller has been validated and I use the passed claims (user id) to access only the calling users data - REGARDLESS what was actually requested by the API call parameters (which can be forged as easily as editing a URL string).
A simple example of this would be exposing your UserSet in the API. If you don't filter by the user claim id you must realize that every user in your system will be accessible by constructing a simple URL in a browser.
Here I filter out and return only the user record identified by the user id in the auth claim. Note I use #if AUTH simply for testing as PROD will always have AUTH turned on.
#if AUTH
[Authorize]
#endif
[HttpGet]
[EnableBreezeQuery(MaxExpansionDepth = 5)]
public IQueryable<User> UserSet()
{
#if AUTH
Guid guid = userGuid();
return _efContext.Context.UserSet.Where(x => x.active && x.guid == guid);
#else
return _efContext.Context.UserSet;
#endif
}
All of this sounds complicated, but it really is quite easy to implement.
Thanks,
Mike

Resources