Consuming a service client from a header filter

176 views
Skip to first unread message

Asanka Sanjaya Herath

unread,
Jun 11, 2017, 7:38:10 AM6/11/17
to Lagom Framework Users
Hi,

I'm building several micro services with following architecture using lagom with Maven.
  • Micro service 1: auth-api and auth-api-impl - which handles the authentication for all service calls coming to all services 
  • Micro service 2: api-1 and api-1-impl - A generic micro service
  • Micro service 2: api-2 and api-2-impl - Another generic micro service

I'm using JWT based authentication in auth-api. auth-api has two API calls (Class AuthService).


ServiceCall<AuthRequest, AuthResponse> login(); //returns a jwt token
ServiceCall<RequestHeader, NotUsed> auth(); //analyzes the request header and validates the jwt token


All the other APIs are called from outside and the caller will add the JWT token to the header. So all the api calls which coming to api-1 and api-2 should go through auth() api call  and authenticate first.

I wrote a HeaderFilter for this purpose as this documentation says. The custom HeaderFilter is placed in the auth-api. Now api-1 and api-2 can use that header filter, so all the API-calls will route through that HeaderFilter. Now i'm trying to trigger auth() api call from inside the HeaderFilter. That is where I fail. I tried using @Inject too. But it does not work. Authservice object is always null. 


@Inject
private AuthService authService;

@Override
public RequestHeader transformClientRequest(RequestHeader request) {
authService.auth().invoke(request);
return request;
}


Then I moved HeaderFilter from auth-api to api-1 and used bindClient method as this doc says. Still the result is same.


@Override
protected void configure() {
bindClient(AuthService.class);
}


Am I missing anything here? Can't we utilize other micro services from HeaderFilters?

Carlos Feliz

unread,
Jun 11, 2017, 10:37:34 AM6/11/17
to Lagom Framework Users
I think the docs are little confusing here. At the top of https://www.lagomframework.com/documentation/1.3.x/java/HeaderFilters.html it reads 
In a HeaderFilter you will usually handle protocol negotiation or authentication.

Then a little further down it reads: 
Keep in mind that a header filter should only be used to deal with cross cutting protocol concerns, and nothing more. For example, you may have a header filter that describes how the current authenticated user is communicated over HTTP (by adding a user header, for example). Cross cutting domain concerns, such as authentication and validation, should not be handled in a header filter, rather they should be handled using service call composition.

I think what you mean to do is combine service-call-composition as outlined here (specifically you'll want to use composeAsync): https://www.lagomframework.com/documentation/1.3.x/java/ServiceImplementation.html#Service-call-composition

Note that the `invoke()` on the service client will return a CompletionStage<Result>. In pseudo code I think it will look something like this: 

class MyServiceImpl extends Service {
   @Inject
   AuthService authService;
   
   public ServerServiceCall<MyRequest, MyResponse
> doSomething() {
     return authenticated(user -> 
                 myRequest -> completedFuture(new MyResponse())
           );
   }

   public <Request, Response> ServerServiceCall<Request, Response> authenticated(Function<User, ServerServiceCall<Request, Response>> serviceCall) {
     return HeaderServiceCall.composeAsync(requestHeader -> {
        AuthRequest authRequest = AuthRequestBuilder.buildFromHeader(requestHeader);
        
CompletionStage<User> authAttempt = authService.getAuthenticatedUserOrThrowException().invoke(authRequest);
                  authAttempt.thenApply(user -> {
           
return serviceCall.apply(user);
        });
        
     });
   }
}

Forgive me for the pseudo code. I'm a Scala programmer. Just threw this together based on what I see in the java docs and what I know of the scala implementation. In any case I think thats the general idea. Perhaps you can also make the `authenticated` method take the AuthService client as a parameter so that you can move `authenticated` into the AuthService api project and use the `authenticated` method in any service that needs to authenticate. Haven't tried this but I think it would work. Let me know how it goes :)
 

Asanka Sanjaya Herath

unread,
Aug 16, 2017, 8:07:42 AM8/16/17
to Lagom Framework Users
Hi Carlos,

Sorry for the late reply (forgot to reply actually). I followed the service call composition for all the API calls as you said, and solved the problem. But one disadvantage I see here is, if another developer comes in and adds a new API call to the project, he should not forget to add the service call composition as well :). Thanks for the help!.

dheerajk

unread,
May 11, 2018, 3:42:30 AM5/11/18
to Lagom Framework Users [deprecated]
Hi All,

I agree with Carlos that if a developer writes some new API then he should not forget to add the service call composition in it.
Like is there any way to define secure and unsecured routes in service descriptor instead of composing a service call every time?
Any thoughts.

Regards,
DheerajK
Reply all
Reply to author
Forward
0 new messages