Java EE Security API (JSR 375/Soteria) with JWT tokens

Uncategorized

Introduction : 

Java EE Security API (JSR 375) :

The Java EE Security API 1.0 is a new spec for Java EE 8 that aims to bridge some of the gaps that have traditionally been left unspecified and provides the new way to define or configure identity stores and authentication mechanisms.  

 

{{cta(‘c11b699a-704e-4144-8f8a-6d473cb091c6’)}}

 

Identity stores :

The Java EE Security API standardize identity store e.g DataBase, Embedded, LDAP etc and introduced a new standard interface for the custom identity store that is responsible for providing access to a storage system where caller data and credentials are stored.

 

@RequestScoped

public class AuthenticationIdentityStore implements IdentityStore {

    @Override

    public CredentialValidationResult            validate( Credential  credential       ) {         return result;

    }

}

 

Authentication mechanisms :

The authentication mechanism is responsible for interacting with the caller to obtain credentials. Just like with an identity store, an application can install an authentication mechanism by simply having an implementation of HttpAuthenticationMechanism interface.

 

@RequestScoped

public class JWTAuthenticationMechanism implements HttpAuthenticationMechanism { 

    @Inject      

    private IdentityStoreHandler identityStoreHandler;

 

    @Override

    public AuthenticationStatus validateRequest(HttpServletRequese, HttpServletResponse,

HttpMessageContext) throws AuthException {

        Status = identityStoreHandler.validate(credential);         return status;

    }

}

 

JWT :

A JSON Web Token (JWT) is a JSON object composed of a header, a payload, and a signature with the following format:

 

header.payload.signature
eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJkdWtlIiwiYXV0aCI6IlJPTEVfVVNFUiIsImV4cCI6MTQ5NzU5NzUwMn0.o47IZdvtl5qI91iSH9AcAs9-mirWQtM9ssXH-Edby-dfLOp9HNXLSQV-F4ll20nsC0E6mfjWyECTKSlUXaVLYg

 

 

HEADER:

The header component of the JWT contains information about how the JWT signature should be computed. The header is a JSON object in the following format:

 

{

 "typ": "JWT",

 "alg": "HS512"

} 

 

In this JSON, the value of the “typ” key specifies that the object is a JWT, and the value of the “alg” key specifies which hashing algorithm is being used to create the JWT signature component. In our example, we’re using the HS512 algorithm, a hashing algorithm that uses a secret key, to compute the signature

 

PAYLOAD (Claims):

The payload component of the JWT is the data that’s stored inside the JWT (this data is also referred to as the “claims” of the JWT). In our example, the server creates a JWT with the user information stored inside of it.

 

{

 "sub": "duke",

 "auth": "ROLE_USER",

 "exp": 1497597502

}

 

In our example, we are putting caller name in “sub” (subject), roles in “auth” (custom defined) and expiration time in “exp” claim into the payload. You can put as many claims as you like. There are several different standard claims for the JWT payload, such as “iss” the issuer, “iat” the issuedAt, “aud” the audience etc. These fields can be useful when creating JWT, but they are optional.

 

Note : The size of the data will affect the overall size of the JWT that may negatively affect performance and cause latency.

 

SIGNATURE :

The signature is computed using the following pseudo code:

 

data = base64urlEncode( header) + . + base64urlEncode( payload );           
signature = Hash( data, secret );      

 

To get the JWT signature, the data string is hashed with the secret key using the hashing algorithm specified in the JWT header.

 

Authentication flow :

During authentication, Initially Client logged in by sending their credentials to the server. And the server verifies the credentials using authentication mechanism which invokes an identity store to match the given credentials with a known user detail from the database.

  • If a match is found, the Authentication Mechanism retrieves the user data, generates a JWT that will be used to access the services. JWT containing user details(username as the subject) and roles(authorities) so the service provider does not need to go into the database to verify user roles and permissions for each request and it also sets the expiration on the JWT (which is the date and time to show when this JWT will expire). Server signs (and if needed, encrypts) the JWT by the HS512 algorithm where only the server know the secret key and sends it to the client as a response through Authorization header.
  • If a match is not found, the Authentication Mechanism reports a failed authentication(401 status code), the caller is not logged in, and is unable to be given authorization.
4-3

 

RememberMe Authentication flow:

 

In case of rememberme, RememberMeIdentityStore generates the JWT and server sends it to the client as a response through cookies “JREMEMBERMEID“.

 

3-2

 

Authorization flow :

During Authorization, Whenever the user wants to access a protected resource, the user agent(browser) send the JWT in the Authorization header using the bearer schema to the server. For every request, The server takes the JWT from the Authorization header (and decrypts it, if needed), validates the signature using the same signature algorithm HS512. The application verify that the signature obtained from its own hashing operation matches the signature on the

JWT itself. 

  • If signature is valid which indicates that the API call is coming from an authentic source, extracts the user data and roles. Based on this data solely, and again without looking up further details in the database, it can accept(HTTP code 200) or deny(HTTP code 403) the client request.
  • If the signatures is invalid, which may be an indicator of a potential attack on the application, sends an unauthorized(HTTP code 401) response.  So by verifying the JWT, the application adds a layer of trust between itself and the user. 
2-2

 

Rememberme Authorization flow :

Whenever the user wants to access a protected resource, the user agent would automatically include the JWT in the cookie with JREMEMBERMEID key.  It does not require state to be stored on the server because the JWT encapsulates everything the server needs to serve the request. 

 

1-2

 

Wrap-up

 

This approach allows for great flexibility while still keeping things secure and easy to develop. By using this approach, it is easy to add new server nodes to the service provider cluster, initializing them with only the ability to verify the signature by providing them a shared secret key. No session replication, database synchronization or inter-node communication is required. 

 

Hopefully this article has given some sort of an insight into the power of JWT as well as the new Java EE Security API. You can find the example code of this article in our Payara Examples repository, under Java-EE/security-jwt-example, Just fork it and play with it as you like.

 

 

{{cta(‘4c7626f5-1e53-418e-90c8-add6e4af19c9′,’justifycenter’)}} 

 

 

Comments (16)

Post a comment

Your email address will not be published. Required fields are marked *

Payara needs the contact information you provide to us to contact you about our products and services. You may unsubscribe from these communications at any time. For information on how to unsubscribe, as well as our privacy practices and commitment to protecting your privacy, please review our Legal & Privacy Policy.

  1. Stephen Kay

    Your article was exactly what I was looking for since I grabbed your JWT auth example on GitHub 🙂 Thank your for detailing the theory with beautiful sequence diagrams !
    Now I released a war of 3 mb because of embeeding some Jackson / sounds like unuseful (?)
    io.jsonwebtoken
    jjwt
    imports also Jackson – doesnt that mean that it duplicates JSON-B ?

    Question – do you think turining your sample with an other JWT lib ? or does it plan that jjwt swith to JSON-B ?
    Regards

    1. Gaurav Gupta

      Hi Stephen,

      Payara bundles both nimbus-jose-jwt and jackson so either you can use
      com.nimbusds:nimbus-jose-jwt lib with provided scope or you may use io.jsonwebtoken:jjwt with excluded Jackson lib using maven .

      Thanks for pointing out, In future we will update this sample with nimbus-jose-jwt api.

  2. Vano

    With HttpSession you set the session timeout and server automatically extends the expiration date if the request is within the range. How can you achieve that with JWT?

    1. Gaurav Gupta

      Hi Vano,

      There is no specific in-built support in EE Security atm to renew the token.

      But you may add the renew/refresh token functionality in your app during the token validation by replacing the expiresIn & iat value and coping all other attributes values which is considered to have the same token returned but with a extended expiry time.

  3. Hector Gatica

    Hi. I am new developing backend with Java EE and payara. I tried the example you mentioned and I can authenticate and it effectively gives me the token, but why is the response canceled when it should show me the protected content? Am I missing something? I think it’s the SSL certificate but I still do not know. I would greatly appreciate your help.

    1. Gaurav Gupta

      Hi Hector,

      May be you are trying to access protected resource (e.g ‘/delete’) with less accessibility attributes user (e.g duke). May you share more details about the HTTP error-code, protected endpoint and user name so I can help further.

  4. Kamlendus Pandey

    While calling /login resource as payara , it returns correct username and password as response but subsequently when I try to access /read in SampleController It returns an anonymous user . /write and /delete return unauthorized access. Same is happening when I login with username duke and password secret. What can perhaps go wrong?

  5. kamlendu Pandey

    I used your code from GitHub for making a sample web application in netbeans. Everything works fine as long as I supply username, password in calling all resources like /read /write /delete. Actually I should have passed them in login only and the credentials must persist there on in SecurityContext. This is not happening. Although delete is protected resource for USER but this too is accessed when I pass admin credentials.

    1. Gaurav Gupta

      Hi Kamlendu,

      ‘/login’ endpoint returns the JWT token which further can be used to call other resources.
      For example to access ‘/delete’ resource, user should have ADMIN_ROLE group access and pass the JWT in request header.

      You may find the details about the groups and users for the sample application, here : https://github.com/payara/Payara-Examples/blob/master/javaee/security-jwt-example/README.md

  6. kamlendu Pandey

    I understood and went through ReaMe.md file in the project which tells me to put token into authentication header. This is already done createToken Method of AuthenticationMechanism class. When it call /login with username,password and rememberme=true in query string then it must remember the token in succeeding calls to /read. Shall I have a write a filter to put token in REMEMBERID cookie or shall I put the created token in Session. HttpMessageContext does not mentain the state to store token in next call of session.
    Maybe I haven’t got the concept well. Pl. help

    1. Gaurav Gupta

      Hi kamlendu,

      Don’t confuse with remember-me flag, JWT does not require state to be stored on the server because It encapsulates all the info server needs to serve the request. It actually defines that JWT token will be presented via a cookie. You may configure the cookie maxAge and other properties using @RememberMe annotation.

      After successful authentication, server sends a RememberMe cookie (with JREMEMBERMEID key and JWT token value) which user-agent(browser) automatically sends back to server when requesting another resource (e.g ‘/read’).

  7. Maarten van Leunen

    I am looking at the example. I notice a complete absence of security realms. So I take it this Java EE Security principle leaves security realms behind?

    Let us say I wish to use the same authentication/authorization principle for several wars. I would have to create a jar of the example above and include it in each war?

    1. Gaurav Gupta

      Java EE Security API provides an alternative way to configure authentication mechanisms and identity stores but it does not mean existing security mechanism ( e.g declaration via web.xml) support are replaced.

      Yes, The example.jar can be include in several war (WEB-INF/lib) but make sure to place the beans.xml on the META-INF folder inside the jar root folder .

  8. Maarten van Leunen

    Please mention in the article, that the JREMEMBERMEID cookie bij default is “Secure”, meaning it won’t be set/sent by your browser, if your browser is accessing Payara over common HTTP instead of HTTPS.

    That threw me for a loop.

  9. Krzysztof Rams

    Hello,
    i’m trying to implement HttpAuthenticationMechanism on Payara 5 and i get such error when i try to authenticate the user with my custom implementation using token and I get an error (below the code). I tried with @ApplicationScope and @RequestScope. From time to time it works but in 99% cases it throws UnsatisfiedResolutionException.

    Code:

    @RequestScoped
    public class CustomAuthentication implements HttpAuthenticationMechanism {

    @Override
    public AuthenticationStatus validateRequest(HttpServletRequest request, HttpServletResponse response, HttpMessageContext context) throws AuthenticationException {
    String grantType = request.getParameter(“grant_type”);
    try {
    switch (grantType) {
    case “password”:
    return loginUsingPassword(request, response, context);
    case “refresh_token”:
    return loginUsingToken(request, response, context);
    default:
    return context.responseUnauthorized();
    }
    } catch (OauthAuthorizationFailure e) {
    return context.responseUnauthorized();
    }
    }

    private AuthenticationStatus loginUsingPassword(HttpServletRequest request, HttpServletResponse response, HttpMessageContext context) throws OauthAuthorizationFailure {
    ….
    }

    private AuthenticationStatus loginUsingToken(HttpServletRequest request, HttpServletResponse response, HttpMessageContext context) {

    }

    }

    Error from Payara:

    [2019-06-10T15:21:48.131+0200] [Payara 5.192] [WARNING] [] [javax.enterprise.system.container.web.com.sun.web.security] [tid: _ThreadID=42 _ThreadName=http-thread-pool::http-listener-1(2)] [timeMillis: 1560172908131] [levelValue: 900] [[
    JASPIC: http msg authentication fail
    org.jboss.weld.exceptions.UnsatisfiedResolutionException: WELD-001334: Unsatisfied dependencies for type HttpAuthenticationMechanism with qualifiers
    at org.jboss.weld.bean.builtin.InstanceImpl.checkBeanResolved(InstanceImpl.java:241)
    at org.jboss.weld.bean.builtin.InstanceImpl.get(InstanceImpl.java:113)
    at org.glassfish.soteria.mechanisms.jaspic.HttpBridgeServerAuthModule.validateRequest(HttpBridgeServerAuthModule.java:113)
    at org.glassfish.soteria.mechanisms.jaspic.DefaultServerAuthContext.validateRequest(DefaultServerAuthContext.java:76)
    at com.sun.web.security.realmadapter.JaspicRealm.validateRequest(JaspicRealm.java:391)
    at com.sun.web.security.realmadapter.JaspicRealm.validateRequest(JaspicRealm.java:358)
    at com.sun.web.security.realmadapter.JaspicRealm.validateRequest(JaspicRealm.java:181)
    at com.sun.web.security.RealmAdapter.invokeAuthenticateDelegate(RealmAdapter.java:489)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:470)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:724)
    at org.apache.catalina.core.StandardPipeline.doChainInvoke(StandardPipeline.java:579)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:159)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:371)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:238)
    at com.sun.enterprise.v3.services.impl.ContainerMapper$HttpHandlerCallable.call(ContainerMapper.java:520)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:217)
    at org.glassfish.grizzly.http.server.HttpHandler.runService(HttpHandler.java:182)
    at org.glassfish.grizzly.http.server.HttpHandler.doHandle(HttpHandler.java:156)
    at org.glassfish.grizzly.http.server.HttpServerFilter.handleRead(HttpServerFilter.java:218)
    at org.glassfish.grizzly.filterchain.ExecutorResolver$9.execute(ExecutorResolver.java:95)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeFilter(DefaultFilterChain.java:260)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.executeChainPart(DefaultFilterChain.java:177)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.execute(DefaultFilterChain.java:109)
    at org.glassfish.grizzly.filterchain.DefaultFilterChain.process(DefaultFilterChain.java:88)
    at org.glassfish.grizzly.ProcessorExecutor.execute(ProcessorExecutor.java:53)
    at org.glassfish.grizzly.nio.transport.TCPNIOTransport.fireIOEvent(TCPNIOTransport.java:524)
    at org.glassfish.grizzly.strategies.AbstractIOStrategy.fireIOEvent(AbstractIOStrategy.java:89)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.run0(WorkerThreadIOStrategy.java:94)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy.access$100(WorkerThreadIOStrategy.java:33)
    at org.glassfish.grizzly.strategies.WorkerThreadIOStrategy$WorkerThreadRunnable.run(WorkerThreadIOStrategy.java:114)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:569)
    at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:549)
    at java.lang.Thread.run(Thread.java:748)

    1. Gaurav Gupta

      Hi Krzysztof,

      Can you create the Github issue with the reproducer: https://github.com/payara/payara/issues

Related Posts

Payara Qube-Cloud Light banner 4 minutes
Security

Zero Trust Security in Enterprise Java: What it is and How to Implement it

Cybersecurity isn’t just about building walls, fortresses, moats or any other external barrier anymore. Nowadays, it’s important to check […]

4 minutes
Uncategorized

Leading the Way: Payara Platform Community 7 Beta Now Fully Jakarta EE 11 Certified

We’re excited to announce that Payara Platform Community 7 Beta application server is now fully certified as Jakarta EE 11 […]

JBoss ELS Decoded 5 minutes
Migration

JBoss ELS Decoded: What Extended Lifecycle Support Really Means for Your Java Applications​

If your Java EE 8 applications run on Red Hat JBoss Enterprise Application Platform (EAP) 7, you can’t afford […]