JWT and HttpApi

SDK: 9.7.115.1441

I want to use JWT to authenticate access to my services using HttpApi.

I’ve looked at the examples posted to this forum, but I feel I need some advice how to implement this.

My goals.

  • The tokens will potentially have a long expiration time and I want to be able to revoke them at any time.
  • As long as the token is valid the user should not have to login again
  • In some cases the token will be pre-generated and as long as it’s valid user will not have to login

Thus:

  • I want to persist all tokens in a database
  • I want to validate the token before a request is accepted
  • I want to verify the token against the database, e.g I want to check the expiration time of tokens before the request is accepted
  • Session must be valid as long as the token is valid

It seems the ApiSimpleAuthenticationManager will just create a session and the timeout of the sessionmanager will decide how long the session will be valid before the user will need to login again.

My initial reaction is that I need to implement my own AuthenticationManager which looking at the sources should be quite simple.
In ReadAuthenticationInfo I will implement my custom logic for validating tokens.

But how can I access the sessionmanager from my custom AuthenticationManager?

1 Like

This task is both easier and harder than it seems.

I will use https://github.com/jwt-dotnet/jwt as a 3rd part JWT serializer / deserializer. Not that I do NOT ‘recommend’ or ‘not recommend’ it. I use it only for sample purposes. There might be better JWT libraries out there or maybe you’ll decide to implement your own.

Also I’ll assume that you do not need to store any values in the server session.

So your custom AuthenticationManager needs to implement 2 interfaces: IApiAuthenticationManager and IApiSecurityDefinition

As for the DescribeServerSecurity' andDescribeMethodSecurity’ methods - just reuse the ApiSimpleAuthenticationManager implementation of these methods.

The ReadAuthenticationInfo method is the one that has to be implemented.

So the steps are:
1.Take the Authorization header value
1.1.If the header is not present at all then return new ApiSimpleSession(Guid.NewGuid());
1.2.If the header is present but its value doesn’t start with 'Bearer ’ then raise SessionNotFoundException
2.Remove 'Bearer ’ from the header value
3.Deserialize header value using JWT deserializer.
If the JWT value has been tampered with, is not correct or is already expired the serializer will raise corresponding exception. Reraise it as a ApiMethodException with corresponding Http error code
4.Extract JWT payload values.
4.1.Take jti value - this is the standard name for the unique ID of a JWT token
4.2.Verify token ID value against your list of revoked tokens (consider adding some kind of cache here as poking database on every incoming request could be really slow)
4.3.Take sub value - this is the standard name for the subject ID of a given JWT token. Convert this value to a Guid and use as sessionId below
4.4.Call

   var session = RemObjects.SDK.Server.SessionManager.GlobalSessionManager.GetSession(sessionId);
   RemObjects.SDK.Server.SessionManager.GlobalSessionManager.ReleaseSession(sessionId);

4.5.Return new ApiSimpleSession(sessionId);

The ‘WriteAuthenticationInfo’ method implementation should be an empty method. This method is used to write back to the response any security-related information.

During login you’ll need to generate a correct JWT token and set there token Id and session Id.

1 Like

Hello Anton,

Perfect, I appreciate the detailed reply, and tips.

In step 4.4 Could you explain why you release the session immediately after creating it?

I assume it will also work to call GetSession on every request and store values in the session object for use as sort of “temporary” storage. E.g. to pass values to business processor?

Currently in my service when you login via normal means (call LoginEx), some information is stored in the session object.

image

These are in turn used in business processor to filter the DataTable.

It’s not critical for me to have the server side session.
I will be able to do the filtering without it, but in this case I could use most of the exisiting functionality and just build my new HttpApi methods on top.

Anyway, thank you for your time, you provided the missing pieces to my puzzle.

Release here is not like free all memory and destroy the object. It is more like I do not this object anymore, so now you are to handle it.
The idea is that code there creates a session in the Session Manager (the Get method will create the session if it doesn’t still exist or will return an existing session) and then will tell the session manager that this particular code section won’t do anything with this session instance anymore.
It is not that important for Memory session manager, but out-of-process session managers like Olympia might want to do something with the session instance once the code that earlier requested it doesn’t need it anymore.
It is a good behavior to release session instance once it is not needed anymore.

Yes. The cool thing about JWT is that it is actually a self-contained session. So during login you can put there some information and then retrieve it back on every request. However please remember that this JWT token is being sent with every request, so it is better not to put there way too much data. Another concern is that JWT can be easily read client-side (ofc it cannot be modified by client), so it is better not to put there any sensitive info like passwords.

At step 4.4. between Get and Release calls you can ask the JWT deserializer to get you all values stored in the JWT token and then you can put these values in the session object. So all your service code will have access to them like earlier:

image

Thank you Anton, your support is much appreciated.

I’ll log an issue to convert this conversation into a code portion and an article on the RO SDK docs page. Unfortunately we cannot add this directly into the core package due to external references.

Regards

Thanks, logged as bugs://83275