Search code examples
javamicronaut

How to get Authorization header from an MethodInterpcetor on micronaut?


Before everything I tried this two solution but didn't work for me
Equivalent of javax.ws.rs NameBinding in Micronaut?
https://blogs.ashrithgn.com/custom-annotation-to-handle-authorisation-in-micronaut-aop-tutorial/

In my application I have to get a string in the Authorization header and then decode it from base64 and the json transform it into a POJO. Certainly the string is a jwt and I need to decode the public part of the json to get a data from a field.
Technically speaking a client will forward the header to me to take it, decode it and extract the data. (It's very bad practice but that's what I have to do).

For this I am using micronaut 2.4.1 and this is my code:

Interceptor:

public class HeadInterceptor implements MethodInterceptor<Object, Object> {
    @SneakyThrows
    @Override
    public Object intercept(MethodInvocationContext<Object, Object> context) {

        Request request = (Request) context.getParameterValueMap().get("request");
        // Where do i get Authorization header?
        // i.e String token = (String) context.getParameterValueMap().get("Authorization");
        String token = "eyJhdWQiOiJ0ZXN0IiwiaXNzIjoidGVzdCIsInN1YiI6InRlc3QiLCJleHAiOjExMTExMTEsImlhdCI6MTExMTExMTEsImRhdGEiOiJ0ZXN0In0=";

        ObjectMapper mapper = new ObjectMapper();
        Info info = mapper.readValue(new String(Base64.getDecoder().decode(token)), Info.class);
        request.setData(info.getSub().toUpperCase());

        return context.proceed();
    }
}

Controller:

@Controller("/main")
public class MainController {

    @Post
    @Head
    public Single<Response> index(@Body @Valid Request request) {
        return Single.just(
                Response.builder()
                        .message(String.format("%s-%s", request.getData(), request.getInfo()))
                        .build()
        );
    }
}

Here's a sample app https://github.com/j1cs/micronaut-jacksonxml-error
(ignore the name is for other issue)


Solution

  • In your implementation, the header cannot be shown in the interceptor because your index method doesn't receive it as a parameter.

    So, if you add it as a parameter as below:

    ...
    @Post
    @Head
    public Single<Response> index(@Body @Valid Request request, @Header("Authorization") String authorizationHeader) {
        return Single.just(
                Response.builder()
                        .message(String.format("%s-%s", request.getData(), request.getInfo()))
                        .build()
        );
    }
    ...
    

    Then, you can retrieve it in the intercept method via getParameterValues(). Basically, it will be the second argument.

    ...
    @SneakyThrows
    @Override
    public Object intercept(MethodInvocationContext<Object, Object> context) {
        ...
        String token = (String) context.getParameterValues()[1];
        ...
    }
    ...
    

    Update

    Since you want your Request to contain both body and header, I edited the solution a bit. Basically, the header is added as a member variable to Request as below:

    public class Request {
        @NotNull
        @NotBlank
        private String info;
        private String data;
    
        @Header("Authorization")
        String authorizationHeader;
    } 
    

    Then, use @RequestBean rather than a @Body annotation on your Request parameter:

    ...
    @Post
    @Head
    public Single<Response> index(@RequestBean @Valid Request request) {
        return Single.just(
                Response.builder()
                        .message(String.format("%s-%s", request.getData(), request.getInfo()))
                        .build()
        );
    }
    ...
    

    Finally, you can access the header easily in your intercept() method as follows:

    @SneakyThrows
    @Override
    public Object intercept(MethodInvocationContext<Object, Object> context) {
        ...   
        Request request = (Request) context.getParameterValueMap().get("request");
        String token = request.authorizationHeader;
        ...
    }
    

    I created a pull request for this change here, so you can check how it works.