Categories
Jakarta EE ORM-Persistence

Why ORM and Data frameworks are not your best option

Subscribe to continue reading

Subscribe to get access to the rest of this post and other subscriber-only content.

Categories
Jakarta EE Ktor Security

Comparing JWT Token Usage in Spring Boot, Quarkus, Jakarta, and Kotlin Ktor: A Framework Exploration – Part 4

Since this topic became very extensive, I decided to split up the blog into 4 parts. To keep blog lengths manageable. Here is the split up

Part 1: Introduction
Part 2: Payara, Spring Boot and Quarkus
Part 3: Ktor and Atbash Runtime
Part 4: Discussion and conclusion (this one)

For an introduction around JWT Tokens, you can have a look at the first part of this blog. It also contains a description how the Keycloak service is created for the example programs described in this part.
Part 2 and 3 contains the description of the example application for each runtime.

Discussion

In parts 2 and 3, I showed the most important aspects of using a JWT token with Payara Micro (Jakarta EE), Spring Boot, Quarkus, Kotlin, and Atbash Runtime. The JWT tokens themselves are standardised but how you must use them in the different runtimes is not defined and thus different. Although there exists the MicroProfile JWT Auth specification, even those runtimes that follow it, have differences in how it should be activated and how roles should be verified, especially when you don’t want to check a role. The specification, besides duplicating a few things from the JWT specification itself like how validation needs to be done, only defines how a MicroProfile application should retrieve claim values.

It is obvious that for each runtime we need to add some dependency that brings in the code to handle the JWT tokens. But for several of these runtimes, you also need to activate the functionality. This is the case for Payara Micro through the @LoginConfig and also for Atbash Runtime since the functionality is provided there by a non-core module.

Another configuration aspect is the definition of the location of the certificates. Spring Boot is the only one that makes use of the OAuth2 / OpenId Connect well know endpoint for this. The other runtimes require you to specify the URL where the keys can be retrieved in a certain format. This allows for more flexibility of course and potential support for providers that do not follow the standard in all its extends. But since we are talking about security, it would probably be better that only those certified, properly tested providers would be used as is the case with the Spring Boot implementation.

The main difference in using a JWT token on runtime is how the roles are verified. Not only is not specified which claim should hold the role names, nor is it defined how the authorization should be performed. This leads to important differences between the runtimes.

Within Kotlin Ktor, We should define a security protocol for each different role we want to check and assign it a name. Or you create a custom extension function that allows you to specify the role at the endpoint as I have done in the example. But important to note is that we need to be explicit in each case. Which role or if no role at all is required, we need to indicate this.

This is not the case for the other runtimes, except the Atbash Runtime.

When you don’t use any annotation on the JAX-RS method with Payara Micro and Spring Boot, no role is required, only a valid JWT token. But with Quarkus, when not specifying anything, the endpoint becomes publicly accessible. This is not a good practice because when you as a developer forget to put an annotation, the endpoint becomes available for everyone, or at least any authenticated user for certain runtime. This violates the “principle of least privilege” that by default, a user has no rights and you explicitly need to define who is allowed to call that action. That is the reason why Atbash Runtime treats the omission of an annotation to check on roles as an error and hides the endpoint and shows a warning in the log.

If you do not want to check for a role when using Atbash Runtime, you can annotate the JAX-RS method with @PermitAll. The JavaDoc says “Specifies that all security roles are allowed to invoke the specified method(s)” and thus it is clearly about the authorization on the endpoint. But if you use @PermitAll in Payara Micro, the endpoint becomes publicly accessible, dropping also authentication. That is not the intention of the annotation if you ask me. Although the Javadoc might be to blame for this as it mentions “that the specified method(s) are ‘unchecked'” which might be interpreted as no check at all.

Conclusion

All major frameworks and runtimes have support for using JWT Tokens within your application to authenticate and authorise a client call to a JAX-RS endpoint. When adding the necessary dependency to have the code available and adding some minimal configuration like defining where the keys can be retrieved to verify the signature, you are ready to go. The only exception here might be Kotlin Ktor where you are confronted with a few manual statements about the verification and validation of the token. It is not completely hidden away.

The most important difference lies in how the check for the roles is done. And especially in the case that we don’t require any role, just a valid JWT token. Only Atbash Runtime applies the “principle of least privilege”. On the other runtimes, forgetting to define a check for a role leads to the fact that the endpoint becomes accessible to any authenticated user or even worse, publicly accessible.

There is also confusion around @PermitAll which according to the java doc is about authorization, but in Jakarta EE runtime like Payara Micro, the endpoint also suddenly becomes publicly accessible.

Interested in running an example on the mentioned runtimes, check out the directories in the https://github.com/rdebusscher/Project_FF/tree/main/jwt repo which work with KeyCloak as the provider.

Training and Support

Do you need a specific training session on Jakarta EE, Quarkus, Kotlin or MicroProfile? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.

Categories
Jakarta EE Ktor Security

Comparing JWT Token Usage in Spring Boot, Quarkus, Jakarta, and Kotlin Ktor: A Framework Exploration – Part 3

Since this topic became very extensive, I decided to split up the blog into 4 parts. To keep blog lengths manageable. Here is the split up

Part 1: Introduction
Part 2: Payara, Spring Boot and Quarkus
Part 3: Ktor and Atbash Runtime (this one)
Part 4: Discussion and conclusion

For an introduction around JWT Tokens, you can have a look at the first part of this blog. It also contains a description how the Keycloak service is created for the example programs described in this part.
Part 2 contains the description for Payara Micro, Spring Boot and Quarkus.

Ktor

Also within Ktor there is some excellent support for using JWT tokens although we need to code a little bit more if we want to have support for rotating public keys and easy checks on the roles within the tokens.

But first, Let us start again with the dependencies you need within your application.

        <!-- Ktor authentication -->
        <dependency>
            <groupId>io.ktor</groupId>
            <artifactId>ktor-server-auth-jvm</artifactId>
            <version>${ktor_version}</version>
        </dependency>
        <!-- Ktor support for JWT -->
        <dependency>
            <groupId>io.ktor</groupId>
            <artifactId>ktor-server-auth-jwt-jvm</artifactId>
            <version>${ktor_version}</version>
        </dependency>

We need a dependency to add the authentication support and another one for having the JWT token as the source for authentication and authorisation.

Just as with the Payara and Quarkus case, we need to define the location to retrieve the public key, expected issuer, and audience through the configuration of our application. In our example application, this is provided in the application.yml file.

jwt:
  issuer: "http://localhost:8888/auth/realms/atbash_project_ff"
  audience: "account"

We programmatically read these values in our own code, so the keys can be whatever you like, they are not predetermined as with the other runtimes. In the example, you see that we also don’t define the location of the public key endpoint as we can derive that from the issuer value in the case of KeyCloak. But you are free to specify a specific URL for this value of course.

Configuration of the modules in Ktor is commonly done by creating an extension function on Application object, as I have also done in this example. This is the general structure of this function

fun Application.configureSecurity() {

    authentication {
        jwt("jwt-auth") {
            realm = "Atbash project FF"
            // this@configureSecurity refers to Application.configureSecurity()
            val issuer = this@configureSecurity.environment.config.property("jwt.issuer").getString()
            val expectedAudience = this@configureSecurity.environment.config.property("jwt.audience").getString()
            val jwkUrl = URL("$issuer/protocol/openid-connect/certs")
            val jwkProvider = UrlJwkProvider(jwkUrl)

            verifier {
        // not shown for brevity              

            }

            validate { credential ->
                // If we need validation of the roles, use authorizeWithRoles
                // We cannot define the roles that we need to be able to check this here.
                JWTPrincipal(credential.payload)
            }

            challenge { defaultScheme, realm ->
                // Response when verification fails
                // Ideally should be a JSON payload that we sent back
                call.respond(HttpStatusCode.Unauthorized, "$realm: Token is not valid or has expired")
            }
        }
    }

}

The function jwt("jwt-auth") { indicates that we define an authentication protocol based on the JWT tokens and we name it jwt-auth. We can name it differently and can have even multiple protocols in the same application as long ask we correctly indicate which protocol name we want at the endpoint.

The JWT protocol in Ktor requires 3 parts, a verification part, a validation one, and lastly how the challenge is handled.

The verification part defines how the verification of the token is performed and will be discussed in more detail in a moment. We can do further validation on the token by looking at the roles that are in the token. If you have many different roles, this leads to many different named JWT protocols. Therefore I opted in this example to write another extension function on the Route object that handles this requirement more generically. And the challenge part is executed to formulate a response for the client in case the validation of the token failed.

The verifier method defines how the verification of the token is performed. We make use of the UrlJwkProvider which can read the keys in the JWKS format which contains keys in a JSON format. But it doesn’t try to reread the endpoint in case the key is not found. This also means we cannot apply rotating keys for signing the JWT tokens which is recommended in production. Therefore, we make use of a small helper which caches the keys but read the endpoint again when the key is not found. This functionality could be improved to avoid a DOS attack by calling your endpoint with some random key ids which would put Keycloak or the JWT Token provider under stress.

            val jwkProvider = UrlJwkProvider(jwkUrl)

            verifier {
                val publicKey = PublicKeyCache.getPublicKey(jwkProvider, it)

                JWT.require(Algorithm.RSA256(publicKey, null))
                    .withAudience(expectedAudience)
                    .withIssuer(issuer)
                    .build()

            }

The other improvement that you can find in the example is the validation part. Since you only have the credential as input for this validation, you can check if the token has a certain role, but you can’t make this check dynamic based on the endpoint. As mentioned, this would mean that for each role that you want to check, you should make a different JWT check.

The example contains an extension function on the Route object so that you can define the role that you expect. This is how you can use this new authorizeWithRoles function

        authorizeWithRoles("jwt-auth", listOf("administrator")) {
            get("/protected/admin") {
                call.respondText("Protected Resource; Administrator Only ")
            }
        }

So besides the name for the protocol we like to use, you can also define a set of roles that you expect to be in the token. The function itself is not that long but a little complex because we add a new interceptor in the pipeline used by Ktor to handle the request. If you want to look at the details, have a look at the example code.

If you just need a valid token, without any check on the roles, you can make use of the standard Ktor functionality

        authenticate("jwt-auth") {
            get("/protected/user") {
                val principal = call.authentication.principal<JWTPrincipal>()
                //val username = principal?.payload?.getClaim("username")?.asString()
                val username = principal?.payload?.getClaim("preferred_username")?.asString()
                call.respondText("Hello, $username!")
            }
        }

This last snippet also shows how you can get access to the claims within the token. You can access the principal associated with- the request by requesting call.authentication.principal<JWTPrincipal>() where you immediately make the cast to the JWTPrincipal class. This contains the entire token content easily accessible from within your Kotlin code as you can see in the example where I retrieve the preferred_username.

You can review all code presented here in the example https://github.com/rdebusscher/Project_FF/tree/main/jwt/ktor.

Atbash Runtime

Atbash Runtime is a small modular Jakarta EE Core profile runtime. So by default, it doesn’t has support for using JWT tokens. But since these tokens are the de facto standard, there is an Atbash Runtime module that supports them so that you can use it for your application.

As a dependency, you can add this JWT supporting module to your project

        <dependency>
            <!-- Adds JWT Support in the case we are using the Jakarta Runner, no addition of the MP JWT Auth API required -->
            <!-- Otherwise, when not using Jakarta Runner, the addition of JWT Auth API as provided is enough if you are using Atbash Runner Jar executable -->
            <groupId>be.atbash.runtime</groupId>
            <artifactId>jwt-auth-module</artifactId>
            <version>1.0.0-SNAPSHOT</version>
        </dependency>

Since we use the Jakarta Runner feature of the Atbash runtime, which allows you to execute your web application through a simple main method, we need to add the module itself. If you run your application as a war file, make sure you activate the JWT module within the configuration so that the module is active.

The JWT support within Atbash runtime is also based on the Microprofile JWT Auth specification, so you will see many similarities with the Payara and Quarkus examples we have discussed in part 2 of this blog.

Configuration requires the 3 values for public key location, expected issuer, and audience.

mp.jwt.verify.publickey.location=http://localhost:8888/auth/realms/atbash_project_ff/protocol/openid-connect/certs
mp.jwt.verify.issuer=http://localhost:8888/auth/realms/atbash_project_ff
mp.jwt.verify.audiences=account

You are also required to indicate the @LoginConfig (in case you are executing your application as a WAR file) so that the JWT Module is active for the application. But there is no need to define @DeclareRoles as Atbash Runtime takes the value of the individual @RolesAllowed as valid roles.

A difference with Payara, for example, is that you need to add @PermitAll to a method when you don’t want to check on any roles. Within Atbash Runtime there is the “principle of least privilege” implemented. If you don’t specify anything on a JAX_RS method no client can call it. This is to avoid that you forget to define some security requirements and expose the endpoint without any checks. The JavaDoc says “Specifies that all security roles are allowed to invoke the specified method(s)” and thus it is clearly what we need. Although, some runtimes, including Payara, interpret this differently and I’ll go deeper on this topic in part 4.

The example code is located at https://github.com/rdebusscher/Project_FF/tree/main/jwt/atbash.

Discussion

In the last part of the blog, I’ll have a discussion about similarities and differences. These differences are especially important when you don’t want to have a check on a role within the token.

Part 1: Introduction
Part 2: Payara, Spring Boot and Quarkus
Part 3: Ktor and Atbash Runtime (this one)
Part 4: Discussion and conclusion

Training and Support

Do you need a specific training session on Jakarta EE, Quarkus, Kotlin or MicroProfile? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.

Categories
Jakarta EE Ktor Security

Comparing JWT Token Usage in Spring Boot, Quarkus, Jakarta, and Kotlin Ktor: A Framework Exploration – Part 2

Since this topic became very extensive, I decided to split up the blog into 4 parts. To keep blog lengths manageable. Here is the split up

Part 1: Introduction
Part 2: Payara, Spring Boot and Quarkus (this one)
Part 3: Ktor and Atbash Runtime
Part 4: Discussion and conclusion

For an introduction around JWT Tokens, you can have a look at the first part of this blog. It also contains a description how the Keycloak service is created for the example programs described in this part.

Payara

As an example of how you can work with JWT tokens with Jakarta EE and MicroProfile, we make use of Payara Micro.

The JWT Token support is provided by MicroProfile, so add the dependency to your project.

        <dependency>
            <groupId>org.eclipse.microprofile</groupId>
            <artifactId>microprofile</artifactId>
            <version>6.0</version>
            <type>pom</type>
            <scope>provided</scope>
        </dependency>

We are using MicroProfile 6, which requires Jakarta EE 10 runtime as this is the version that is supported by the Payara Micro community edition.

As configuration, we need to provide the endpoint where the MicroProfile JWT Auth implementation can retrieve the public key that is required to validate the content of the token against the provided signature. This can be done by specifying mp.jwt.verify.publickey.location configuration key. Two other configuration keys are required, one that verifies if the issuer of the token is as expected and the audience claim is the other one.

Other configuration aspects are the indication that a JWT token will be used as authentication and authorization for the endpoints through the @LoginConfig annotation. The @DeclareRoles annotation is a Jakarta EE annotation that indicates which roles are recognised and can be used. These annotations can be placed on any CDI bean.

@LoginConfig(authMethod = "MP-JWT")
@DeclareRoles({"administrator"})

On the JAX-RS method, we can add the @RolesAllowed annotation to indicate the role that must be present in the token before the client is allowed to call the endpoint.

    @GET
    @Path("/admin")
    @RolesAllowed("administrator")
    public String getAdminMessage() {

When there is no annotation placed on the method, only a valid JWT token is required to call the endpoint. Also, have a look at the part 4 of this blog for some important info and differences between runtimes.

Through the MicroProfile JWT Auth specification, we can also access one or all the claims that are present in the token. The following snippet shows how you can access a single claim or the entire token in a CDI bean or JAX-RS resource class.

    @Inject
    @Claim("preferred_username")
    private String name;

    @Inject
    private JsonWebToken jsonWebToken;
    // When you need access to every aspect of the JWT token.

The entire example can be found in the project https://github.com/rdebusscher/Project_FF/tree/main/jwt/payara.

Spring Boot

Also, Spring Boot has excellent support for using JWT tokens for the authentication and authorization of rest endpoints. Besides the Spring Boot Security starter, the Oauth2 Resource Server dependency is required within your application. So you don’t need to handle the JWT token yourself in a programmatic way as some resources on the internet claim.

In our example, we use Spring Boot 3 and JDK 17.

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
    </dependency>

In contrast to MicroProfile where you need to provide several configuration keys, Spring boot makes use of the OpenId Connect specification where it is defined that the endpoint .well-known/openid-configuration provides all info. This includes the location of the public key required for the validation of the token against the signature and the value of the issuer. The location can be specified through a Spring Configuration resource.

spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:8888/auth/realms/atbash_project_ff
spring.security.oauth2.resourceserver.jwt.audiences=account

The audience value is not required to be defined, Spring Boot works without it. But it is a recommended configuration aspect to make sure that tokens are correctly used, especially when you use tokens for multiple applications.

You can either define the requirements for the roles that should be present in the token using a Spring Bean that extends the WebSecurityConfigurerAdapter class and the HttpSecurity builder, but I prefer the method-based approach.
With this approach, you can define the required role using the @PreAuthorize annotation

    @GetMapping("/admin")
    @PreAuthorize("hasAuthority('administrator')")
    public String getAdminMessage() {

It makes it easier to find out which role is required before a client can call the endpoint and also easier to verify if you didn’t make any error in the security configuration of your application. This method-based approach requires a small activation and mapping between the roles within the token and the authority we check in the annotation.

@Configuration
@EnableMethodSecurity
public class MethodSecurityConfig {
}

The configuration for the JWT token roles is provided by a JwtAuthenticationConverter bean.

    @Bean
    public JwtAuthenticationConverter jwtAuthenticationConverter() {
        JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        grantedAuthoritiesConverter.setAuthorityPrefix("");
        grantedAuthoritiesConverter.setAuthoritiesClaimName("groups");

        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
        return jwtAuthenticationConverter;
    }

Within the REST methods, we can have access to the JWT token claims, just as with the Jakarta EE and MicroProfile example. We need to add a JwtAuthenticationToken parameter to the method which allows access to claims through the getTokenAttributes() method.

    @GetMapping("/user")
    public String getUser(JwtAuthenticationToken authentication) {
        Object username = authentication.getTokenAttributes().get("preferred_username");

The entire example can be found in the project https://github.com/rdebusscher/Project_FF/tree/main/jwt/spring.

Quarkus

The Quarkus support is also based on MicroProfile, so you will see several similarities with the Payara case I described earlier. The Quarkus example is based on the recent Quarkus 3.x version. As a dependency, we need two artifacts related to the JWT support provided by the SmallRye project. Although it seems you do not need the build one at first sight, as it is about creating JWT tokens within your application, the example did not work without it.

        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-smallrye-jwt</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-smallrye-jwt-build</artifactId>
        </dependency>

Since the SmallRye JWT implementation is also using the SMicroProfile JWT auth specification, the configuration through key-value pairs is identical to the Payara one. We need to define the location of the publicKey, and the expected values for the issuer and audience. In the example, I have defined them in the application.properties file, a Quarkus-specific configuration source. But as long as they can be retrieved through any of the supported configuration sources, it is ok.

Since Quarkus is not a Jakarta-compliant runtime, it doesn’t require any indication that the application will make use of the JWT tokens for authentication and authorisation. The existence of the two dependencies we added earlier to the project is enough. In this case, it is similar to the Spring Boot case where we also did not do this.

On the JAX-RS resource methods, we can indicate if we need a certain role within the token, or that just the token itself is required and no specific role is required. If a role is required, we can make use of the same @RolesAllowed annotation we encountered in the Payara example or we need to add the @Authenticated annotation if we just need a valid token.

    @GET
    @Path("/admin")
    @RolesAllowed("administrator")
    public String getAdminMessage() {
        return "Protected Resource; Administrator Only ";
    }

    @GET
    @Path("/user")
    @Authenticated
    // No roles specified, so only valid JWT is required
    public String getUser() {
        return "Protected Resource; user : " + name;
    }

This @Authenticated annotation is defined in the Quarkus Security artifact, brought in transitively, and indicates that an authenticated user is required. Without this annotation, the endpoint would become publicly accessible, without the need for any token or authentication method.

More on that in a part 4 of this blog.

The retrieval of the claims is again identical to the Payara case. The example project can be found at https://github.com/rdebusscher/Project_FF/tree/main/jwt/quarkus.

Runtimes

The Ktor and Atbash Runtime versions of the example application are described in part 3.

Part 1: Introduction
Part 2: Payara, Spring Boot and Quarkus (this one)
Part 3: Ktor and Atbash Runtime
Part 4: Discussion and conclusion

Training and Support

Do you need a specific training session on Jakarta EE, Quarkus, Kotlin or MicroProfile? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.

Categories
Jakarta EE Ktor Security

Comparing JWT Token Usage in Spring Boot, Quarkus, Jakarta, and Kotlin Ktor: A Framework Exploration – Part 1

Since this topic became very extensive, I decided to split up the blog into 4 parts. To keep blog lengths manageable. Here is the split up

Part 1: Introduction (this one)
Part 2: Payara, Spring Boot and Quarkus
Part 3: Ktor and Atbash Runtime
Part 4: Discussion and conclusion

But don’t worry, all these 4 parts will be released within the same week so that those people that are eager to process it in one go, do not need to wait a long time before the series is published.

Introduction

As the demand for secure and efficient authentication and authorization mechanisms grows, JSON Web Tokens (JWT) have emerged as a favored choice for developers. JWT tokens provide a modern approach to verifying user identity and defining access privileges within web applications. In this blog post, we will delve into the usage of JWT tokens across various frameworks, namely Spring Boot, Quarkus, Jakarta, and Kotlin Ktor. By comparing their implementation approaches, we aim to provide insights into how JWT tokens are utilized within each framework and help you make a transition from one to another easier.

Understanding the Basics of JWT Tokens

At the core of JWT tokens lies a simple yet powerful structure that encompasses all the necessary information for secure authentication and authorization. Let’s dive into the basics of JWT tokens and explore their three essential components: the header, the body, and the signature.

1. Header

The header of a JWT token contains metadata about the token itself and the algorithms used to secure it. It typically consists of two parts: the token type, which is always “JWT,” and the signing algorithm employed, such as HMAC, RSA, or ECDSA. This header is Base64Url encoded and forms the first part of the JWT token.

2. Body (Payload):

The body, also known as the payload, carries the actual data within the JWT token. It contains the claims, which are statements about the user and additional metadata. Claims can include information like the user’s ID, name, email, or any other relevant data. The payload is also Base64Url encoded and forms the second part of the JWT token.

3. Signature

The signature is the crucial component that ensures the integrity and authenticity of the JWT token. It is created by combining the encoded header, the encoded payload, and a secret key known only to the server. The signature is used to verify that the token has not been tampered with during transmission or storage. It acts as a digital signature and prevents unauthorized modifications to the token. The signature is appended as the third part of the JWT token.

Self-Contained and Secure

One of the significant advantages of JWT tokens is their self-contained nature. Since all the necessary information is embedded within the token itself, there is no need for additional database queries or session lookups during authentication and authorization processes. This inherent characteristic contributes to improved performance and scalability.

To verify the authenticity and integrity of a JWT token, the recipient needs access to the public key or shared secret used to generate the signature. By retrieving the public key or shared secret, the recipient can verify the token’s signature and ensure that no tampering or unauthorized modifications have occurred. This mechanism provides a robust security layer, assuring that the token’s contents can be trusted.

User Roles in JWT Tokens

JWT tokens can also include user roles as part of their payload. User roles define the permissions and privileges associated with a particular user. By including this information in the JWT token, applications can determine the user’s authorization level and grant or restrict access to specific resources or functionalities accordingly. This granular approach to authorization allows for fine-grained control over user permissions within the application.

In the upcoming sections, we will explore how different frameworks incorporate these fundamental JWT token concepts into their authentication and authorization workflows. Understanding the core principles behind JWT tokens sets the stage for a comprehensive comparison, enabling us to evaluate the strengths and nuances of each framework’s implementation.

Example application

The same example application is made with different runtimes. It contains a couple of endpoints, they all require a valid token before they should be executed. One of the endpoints requires that the token contains the role of administrator.

GET /protected/user -> Hello username
GET /protected/admin -> Protected Resource; Administrator Only

The tokens utilised in our example are sourced from Keycloak, a reliable and widely adopted Authorization provider. Keycloak offers various standard flows for obtaining these tokens, catering to diverse authentication scenarios.

One of the commonly employed flows is the authorization code flow, which involves user interaction through dedicated screens provided by the Authorization provider. Users are prompted to log in and provide their credentials, following which Keycloak generates the necessary tokens for authentication and authorization purposes.

Alternatively, Keycloak supports a username and password-based approach where users can submit their credentials to a designated endpoint. This method allows Keycloak to validate the provided information and issue the relevant tokens required for subsequent authentication and authorization processes.

For our example, a custom realm with a configuration that is suitable for all our runtimes is created by setup_jwt_example.py and can be found in the directory https://github.com/rdebusscher/Project_FF/tree/main/jwt/keycloak. The script prepares the realm and a OpenId Connect client so that in response to a valid user name and password combination, a JWT token with the roles of the user is returned. It creates also two users, one of them having the admin role.

The Python script test_jwt_example.py can be used to test out the solution in each of the runtimes. It calls both endpoints with the two users that are defined. And so, one of the calls will result in an error since the non-administrator user is not allowed to call the administrator endpoint.

Runtimes

The different runtimes are discussed in part 2 and part 3 of this series.

Part 1: Introduction (this one)
Part 2: Payara, Spring Boot and Quarkus
Part 3: Ktor and Atbash Runtime
Part 4: Discussion and conclusion

Training and Support

Do you need a specific training session on Jakarta EE, Quarkus, Kotlin or MicroProfile? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.

Categories
Jakarta EE Observability

Using OpenTelemetry with Jakarta EE

OpenTelemetry (informally called OTEL or OTel) is a powerful observability framework that assists in generating and capturing telemetry data from the software. With the rise of now-distributed systems, it has become increasingly difficult for developers to see how their services depend on or affect other services, especially in critical situations such as deployments or outages. OpenTelemetry provides a solution to this problem by making it possible for both developers and operators to gain visibility into their systems.

It is the merger of two other standards, OpenTracing provided a vendor-neutral API for sending telemetry data over to an Observability back-end, and OpenCensus provided a set of language-specific libraries that developers could use to instrument their code and send to any one of their supported back-ends.

As a CNCF incubating project, it is providing a set of standardized, vendor-agnostic SDKs, APIs, and tools for ingesting, transforming, and sending data to an Observability back-end. There is an implementation of the API for Java available, so integration with any Jakarta EE runtime is very easy and doesn’t require any additional implementation.

What does it do?

If you are not familiar with Otel or Observability, then this little introduction is for you. Especially in a micro-services world, but also in other scenarios, it can be very helpful to trace back individual requests and where they spent their time on. Not that it is the main goal, it already captures how much time was needed before a response was sent back to the client. But it can also collect information about database calls, calls to other services, etc … You get information about each request and you can use it to analyse your environment and find the bottlenecks for example. As the saying goes “To measure is to know.” (Although that sounds way much better in dutch “meten is weten”).

You can analyse the information as all the different pieces of information are linked together and stored by a collector. A kind of data storage where you can retrieve and analyse the information.

Observability is much more than just tracking a request throughout your environment. It is also about metrics and processing logs. In this blog, I’m concentrating on request tracing.

Out of the box

The Java implementation of OTel provides integration with Jakarta REST out of the box through a Java Agent. You can find all the supported frameworks and libraries on this page. Some of them require a specific dependency, other frameworks, like JAX-RS require only the Java agent itself.

Personally, I’m not a fan of Java agents. It allows of course to add functionality to your project without the need to change anything in your project. But I don’t like fiddling with the command line to add the Java agent and large sections to define the configuration. And in most cases, you will require the OpenTelemetry API in your application anyway as you want to interact with the system and define for example which CDI methods you like to be included in the trace.

Manual integration

But also integrating the OTel functionality in the application yourself is very straightforward. As an example, I created an integration of the OpenTelemetry Java API for Jakarta EE 10 with the following features.

  • Traces the JAX-RS requests that are processed.
  • Starts a new Tracing or starts a new child span if the header indicates that tracing is already in progress.
  • You can include information about CDI method calls by using an annotation on the method.
  • A Filter for JAX-RS clients is available to propagate the OpenTelemetry information on the header
  • Automatically registers this filter when you have a MicroProfile Rest Client available.

And all this with around 10 classes.

What do you need to do?

If you are interested in creating this integration for your project yourself, have a look at the GitHub repository. It should give you all the information needed to create a specific integration yourself very rapidly.

If you want to use my version in your Jakarta EE 10 project, follow the following steps.

  • Add the maven artefact to your project
    <dependency>
    <groupId>be.atbash.jakarta</groupId>
    <artifactId>opentelemetry</artifactId>
    <version>0.9.0</version>
    </dependency>
  • Define the name of your service, and how it shows up in the OpenTelemetry collector, through a MicroProfile Config source with the key atbash.otel.service.name.
  • Configure, if needed to OTel connector connection through environment variables as required by the OpenTelemetry Specification.
  • Use @WithSpan on a CDI method, or register RestClientPropagationFilter on your JAX-RS client if you want to make use of it.

And that is it.

You can access the current Span by injecting it into your CDI bean.

@Inject
private Span currentSpan;

You can add attributes and events to the span using this instance. These attributes end up in your collector and events mark specific moments in the span.

You can also make use of Baggage Items to carry information to other parts of the requests. Although, you should not see this as a replacement for passing parameters to methods and other services that are called. For that reason, the Baggage Items are not propagated externally. However, by adding the W3CBaggagePropagator provided by the Otel Java implementation to the OpenTelemetry instance, this is possible.

To add something to a baggage item.

    Baggage.current()
            .toBuilder()
            .put("baggageItem", name)
            .build()
            .makeCurrent();

And to access it later on

Baggage.current().asMap();

Conclusion

The OpenTelemetry specification goal is to get insight into what your process is doing when processing requests from the users. Besides metrics and logging, an important part is tracing the requests through your entire environment. It is a language-agnostic specification so that information is compatible regardless of what language the processes are programmed in. But there are also implementations available for many languages, including Java.

This means that vendors or framework maintainers don’t need to do anything to get it integrated. And developers can just use the Java implementation if they need the functionality in their environment. There exists a Java Agent but also manual integration can be done very easily as showcased by the Jakarta EE 10 integration code available in the Github repo.

Atbash Training and Support

Do you need a specific training session on Jakarta or MicroProfile? Or do you need some help in getting up and running with your next project? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.

Enjoy

Categories
Atbash Core Profile Jakarta EE

CDI 4.0 Lite and Potential Pitfalls

The CDI Lite specification is specifically created for the new Jakarta EE 10 Core Profile. This lite version is created to allow the discovery of CDI bans and preparation of the CDI container at build time.

This blog goes a bit deeper into a potential problem that you can encounter when building Core profile applications and running them on a Jakarta runtime concerning this CDI 4.0 Lite.

CDI Lite

To populate the CDI container with all the beans and the metadata to create the beans on demand like for the RequestScoped context, the CDI code needs to perform some initialisation.

Until now, the only available option is to perform a scan of the classpath and find the classes eligible as a CDI bean. Another source for beans is the CDI Portable Extensions that can register additional beans programmatically by the runtime or the application itself.

In the last few years, there are many initiatives to reduce the startup time of applications in various ways. The scanning of the classpath is a process that can be moved to the build phase if you have all classes available at build time. As you know, the JVM allows additional classes and libraries are added at runtime that was not available at build time. So, in case you move the metadata and bean discovery to build time, you need to make sure all classes are known and available or you need to provide the info.

The CDI Lite specification introduced the BuildCompatibleExtension interface to have the ability to move the scanning to the build phase of your application. This mechanism works similarly to the CDI Portable Extensions, where you have the opportunity to execute some methods during one of the 5 phases of the process.

At the same time, some classes are marked as not relevant for CDI Lite, as they are not really useful for the applications that are built with the Core Profile. Examples are the SessionScope and ConversationScope since REST applications are typically stateless. Keeping session information is in this case not required.

One single API artefact

The underlying narrative for creating the CDI Lite solution makes sense, but the single API solution is less obvious.

CDI lite contains most of the classes of the previous version of CDI and a few additional ones, like the BuildCompatibleExtension interface.
CDI full comprises CDI lite and a few additional concepts like the SessionScoped annotation that were available from the previous version of CDI but which didn’t fit into the CDI lite concept.

But the CDI 4.0 API artefact contains all classes, those of CDI Lite and CDI full.

Your project has only the option to add this single CDI API dependency to your project, as provided since the runtime has already these classes. And when your application is compiled and built against this API, it might fail at runtime.

You as a developer could have used or referenced a class from the CDI Full specification but you use a Core Profile certified runtime. This runtime only needs to support the CDI Lite classes and is not required to implement the other ones of CDI Full. So the application you successfully build fails at runtime. And you didn’t get notified of this problem during build time.

Testing for CDI Full classes usage

For this reason, I have created a small library that can inform you if you made use of one of the CDI Full classes in your code. You can write a test based on this code that fails when you make use of such a class. This way you get warned upfront that it might not run in production.

To make use of this library, add the following dependency to your project

    <dependency>
        <groupId>be.atbash.jakarta</groupId>
        <artifactId>core-profile-tester</artifactId>
        <version>1.0.0</version>
        <scope>test</scope>
    </dependency>

With this dependency, you can write the following test method

@Test
void testCompliance() {
    TestCoreProfile tester = new TestCoreProfile("be.atbash");
    Assertions.assertThat(tester.violations()).isEmpty();
}

The TestCoreProfile can determine those classes in the package you specify as a parameter, and in sub-packages, where you make use of a CDI Full class. If the list is empty, you are safe and good to go running it on a Core profile implementation.

Instead of using your top-level package, you can also specify a package from one of the CDI libraries that you have added to your project. This will tell you if that CDI library is safe to use on a Core Profile product.

Conclusion

CDI Lite groups all concepts of the CDI specification that is supported on the new Jakarta EE Core Profile. It also allows gathering information for the CDI container at build time.
But there is only 1 single artefact, containing all CDI Lite and Full classes. This means that even if your application compiles and builds successfully, it might fail on a Core Profile product. This is because you used some CDI Full-only classes that are not supported in Core Profile.
With the Jakarta Core Tester, you can find out if you referenced one of the CDI Full classes and it will run safely on your runtime.

Want to lean more about Jakarta EE 10 Core Profile?

This topic was discussed during my JakartaOne LiveStream event of 2022. Have a look at the presentation if you want to know more about the Core Profile “Explore the new Jakarta EE Core Profile“.

Atbash Training and Support

Do you need a specific training session on Jakarta or MicroProfile? Or do you need some help in getting up and running with your next project? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.

Enjoy

Categories
Jakarta EE release Testing

Atbash Jakarta EE Integration Testing version 1.1 Released.

The first version of the integration testing framework was released in July of this year. It allowed you to deploy your application using the Jakarta Runtime of your choice to a Docker environment using the TestContainer framework.

Selecting the runtime is as easy as setting a member value of the test annotation, the runtime version can be specified, endpoints can be easily called based on a MicroProfile Rest Client interface definition and it allows to have remote debugging by just setting a flag.

In this version1.1, you have the option to define the Docker build file that will be used for the test, and features for testing microservices are added.

Custom builds

In the initial version, the official docker images of the Jakarta runtimes are used to run your application within the docker container. But in many cases, your application needs a specific image as it requires some additional content.

This can now be achieved by specifying a custom build file for Docker.

@ContainerIntegrationTest(runtime = SupportedRuntime.PAYARA_MICRO)
@CustomBuildFile(location = "custom/payara")
public class CustomPayaraIT extends AbstractContainerIntegrationTest {
}

The annotation defines that the content of the directory src/docker/custom/payara will be used to generate the Docker image that will be used to test your application. The build file is expected as a file called Dockerfile (if this is not present, the runtime default will be used) and all other files in that directory and subdirectories will be part of the build step. This allows you to have a fully customized Docker image for testing.

Of course, by defining the runtime like Payara Micro in the above example, the port assignment checks to see if the application is ready and debug setup is still taken from the indicated runtime. So make sure you specify the correct runtime in the @ContainerIntegrationTest annotation which corresponds to the system used within the custom Docker build file.

This functionality is especially useful when you are working with Glassfish since they don’t have any official Docker image and support was limited in the first release for this runtime.

Microservices Testing

In most cases, your application depends on other services. They are called to retrieve data for or process parts of the functionality required for the user request. There are 2 additions available in this version that makes it easier to test your application in this situation.

Each public static variable that is a startle test container and added with @Container is also started just before the execution of your test code. It is now easier in this version 1.1 when this additional container is running an application using one of the supported Jakarta runtimes. You only need to specify the docker image name and the setup like health checks and port mappings is taken care of for you automatically.

  @Container
  public static GenericContainer<?> remote = new PayaraMicroContainer(DockerImageName.parse("test-remote:1.0"));

Also, networking configuration is taken care of, as it will share the same network as your application and this container will also have the name of the variable ‘remote’ in this case, as a hostname alias.

This makes it easy to add an external service needed for your application.

But instead of having an entire application providing data for your application, you can also configure the responses for URLs. In other words, you can mock the responses for certain requests for external services. For this purpose, Wiremock functionality is integrated into the framework.

First of all, you define a container that will be running the WireMock application where you also define the hostname which will be used.

  @Container
  public static final WireMockContainer wireMockContainer = WireMockContainer.forHost("wire");

And within each test method, you can define the responses using a builder pattern

  MappingBuilder mappingBuilder = new MappingBuilder()
   .forURL("/path")
   .withBody(foo);

  String mappingId = wireMockContainer.configureResponse(mappingBuilder);

The variable foo can be a String and in that case, the response is a plain text response with the content of the variable as the body. Otherwise, the variable is serialised to JSON and a JSON-type response is prepared.

The builder also has some other methods to control the response like the HTTP status, media type, etc …

Integration testing

With the initial release, you had all the features required to test your application with the Jakarta runtime of your choice using Testcontainers and a docker environment.

In this update, the container running your application under test can now be fully customised using a build file and resources that you specify by indicating the directory where they can be found.

Also, testing applications that are part of a microservices environment got easier. When the eternal service is also a Jakarta application, you only need to indicate its docker image name, and all other configuration is taken care of for you by default.

You can also use Wiremock to define predefined responses for certain requests. Again, the network setup is greatly simplified, and defining responses is easy. JSON responses can be specified by providing the data as a Java Object that will be serialised so that you don’t need to write complex JSON strings.

Atbash Training and Support

Do you need a specific training session on Jakarta or MicroProfile? Or do you need some help in getting up and running with your next project? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.

Enjoy

Categories
Atbash Jakarta EE Testing

Jakarta EE Integration Testing

Testing your application is very important but also difficult to do. Unit tests are relatively easy to create but it can be difficult to run them with realistic values. So developers sometimes use unrealistic values and constructs just to have the code coverage above the company-required threshold.

Integration testing is even more difficult as you need additional systems, like a database, with a predefined set of data to have repeatable tests. So some test frameworks solve this problem by creating specific artefacts for the test, containing parts of your application and fake and mocked other parts. But you are no longer testing your end product anymore and merely are testing that specific test artefact.

With the availability of the Testcontainers framework, developers can again test the real application by deploying it in a container using the runtime that will be used in production. External dependencies like databases can also be set up easily within a container and initialised with the required dataset for each test. At last, we can actually perform some true integration testing.

Now that we have used it for some time, we see that we always use certain code snippets over and over again in our tests.

– Starting the container with the runtime and your application

– Calling the endpoints of our web application and verifying the structure and response codes

– Setting up remote debugging of our application to find out what is going wrong in our tests.

And so on.

The idea of the Atbash Integration testing framework for Jakarta EE is to provide all those snippets in a reusable dependency so that we can quickly and in a uniform way create integration tests for our Jakarta EE applications. As the focus these days of applications are micro services or at least applications that provide REST endpoints, the focus of the testing framework is also for these applications.

Pieces of the Puzzle

The framework is built as a JUnit 5 extension that brings together all the pieces so that you as a developer can very easily start testing your application. Testing a ‘Hello World’ application is as simple as the Hello World application itself.

@ContainerIntegrationTest(runtime = SupportedRuntime.PAYARA_MICRO)
public class HelloPayaraIT extends AbstractContainerIntegrationTest {


  @RestClient
  public HelloService helloService;

  @Test
  void sayHello() {
    String value = helloService.sayHello("JUnit");
    Assertions.assertThat(value).isEqualTo("Hello JUnit");
  }
}

The test starts the container with Payara Micro, deploys your application, and calls the ‘Hello World’ endpoint to verify if the response is as expected. As already mentioned, the Testcontainers framework is used and with the help of Apache CXF, Jackson, and the MicroProfile Rest Client specification, endpoints can be called very easily by defining an interface with the contract of your endpoint(s).

Deploy the Application

You don’t need to do anything. The extension looks for the war file in the target directory and makes sure it ends up in the Docker image that is created on the fly.

In future versions, the search logic will be made more flexible so that it can be used when you don’t use the default structure or Gradle for example.

Define the Runtime

The framework supports Payara Micro, OpenLiberty, and WildFly as Jakarta EE runtime. You can also use Glassfish but there are several limitations with it. See more on that later on.

The runtime that the framework will use, is determined as the value of the ‘runtime’ member of the @ContainerIntegrationTest annotation. You can also live this value to its default and specify the runtime through the JVM System Properties be.atbash.test.runtime. This is especially handy if you are developing a Jakarta EE library or framework and want to test it out on multiple runtimes easily.

The JUnit5 extension creates a specific Docker image for each runtime based on the official Docker image. The tag number of that base image is taken from a default (look at the documentation to find out which number it is) or you can specify it also as a JVM System property.

When this property specifies not only the tag number, but the entire Docker Image identification like payara/micro:5.2022.2 it uses it as the base image. In the example, we use the JDK8 version of Payara Micro 5.2022.2 instead of the default JDK11 version that is chosen by the framework.

The System property can also be used to select a Jakarta EE 9.x or even Jakarta EE 10 based version of the runtime and not the default Jakarta EE 8 ones. This is possible because for the integration tests we only call the application endpoints and there is no direct dependency of the test framework on the Jakarta version that is used within your application.

Also, Glassfish is supported but with a few limitations since there is no official Docker image available. This means I had to use the Docklands image from Adam Bien but it only has an image that is based on JDK8. This image lacks the possibility to have a remote debugging session. See more on that later on in the text.

Call the Endpoints

The second major point of the framework is the simple way how endpoints of your application can be called. The JVM itself can be used for this with the URL class and the openConnection() method for example. But it is rather cumbersome, especially if you do need to create the JSON and read the response, and especially can be tricky since the host and port where the application is running are determined by Testcontainers and the Docker Client.

Although Testcontainers is used under the hood, the developer that writes the test is, as little as possible, confronted with it. For that reason, I choose to make use of the MicroProfile Rest Client to call the application endpoints. The only thing we need to do is to define the ‘contract’ of the endpoint as an interface.

@Path("/hello")
public interface HelloService {

  @GET
  @Path("{name}")
  String sayHello(@PathParam("name") String name);
}

Based on this definition, Apache CXF can create a proxy that actually calls the endpoint of your application. The JUnit5 extension knows or can find out from Testcontainers the URL on which your application is accessible. And with the help of Jackson, you as a developer are also not confronted with creating and reading JSON content. Automatic conversion to Java instances is performed.

The proxy can be ‘injected’ into your test class by using the @RestClient annotation of MicroProfile.

Behind the scenes, some low-level handling and reflection are going on. To avoid the usage of setAccessible() method, the test class and the field where the rest Client is injected must be declared with the scope ‘public’. So that we can avoid warnings and problems by the JVM when performing this low-level stuff.

Some Other Features

Besides the main features described above to automatically deploy your application in a specially created Docker container using the runtime of your choice and making it easy to call endpoints of the application, there are a few other useful features.

Remote debugging

When your test fails, it is very handy that you can start a debugging session to follow the code execution to the point where it goes wrong. When performing integration tests, a remote debugging session will be the fastest and easiest way to find the problem. The integration testing framework has support for remote debugging with a simple option. Just set the debug member of the annotation to true.

@ContainerIntegrationTest(runtime = SupportedRuntime.PAYARA_MICRO, debug = true)

When the container starts, the JVM will wait due to the suspend=y option until the debugger is attached. So when you see the message that Testcontainers is waiting until the container is started, connect the debugger to localhost:5005 (or the host the container is accessible on, you see already some messages in the log where the container is). The start-up continues and comes to the point where you have placed a breakpoint in your application code.

Logging

First of all, make sure you configure a logging output framework like Logback as described in the Testcontainers documentation. The framework has two additional features to show the container log for a specific test.

When a test fails due to a java.lang.AssertionError error, an URL that could not be found (proxy received a status 404) or there was an internal server error detected, the entire container log is shown as part of the test output.

You also can specify the value liveLogging = true resulting in showing the container logs during the execution of the test. It appears as the runtime log is written out ‘immediately’ when generated by the runtime but there is a very small delay.

@ContainerIntegrationTest(runtime = SupportedRuntime.PAYARA_MICRO, liveLogging = true)

Planned Features

The current version of the framework (see Github and Maven Central) already allows to perform an integration test with the Jakarta runtimes but there are already some improvements in the pipeline to cover more use case scenarios.

– Support for database containers and defining the initial data set easily

– Support for integration with other (micro)service(s) that also start as a Docker container during the test.

Atbash Training and Support

Do you need a specific training session on integration testing with a Jakarta runtime or want some help in setting up integration tests? Have a look at the training support that I provide on the page https://www.atbash.be/training/ and contact me for more information.

Enjoy

Categories
Jakarta EE Security

Where is the Security Specification in Jakarta EE Core profile?

The Jakarta EE Core profile targets the smaller runtimes, those that focus on delivering support for applications with REST endpoints only. For that reason, the main specifications included in the profile are the JAX-RS specifications and the specifications around JSON, JSON-P and JSON-B.

Another very important specification was targeted for Jakarta EE 10 and this new core profile, Jakarta config. The discussion about it is still going on. It is thus not included yet. That is the reason why the Atbash Runtime includes an implementation of the MicroProfile Config specification for the moment.

But there is another very important aspect that seems to be forgotten for this core profile, security.

Jakarta Security specification

For the moment, there are no plans to include a security-related specification in this core profile and this is a missed opportunity. Security is a very important aspect of each application but as usual, it doesn’t receive enough attention. Although there were several major security issue events in the last 6 months, we all remember the log4shell vulnerability.

Within the Jakarta family, we have the Jakarta Security API specification which combines the more low-level specifications Authentication and Authorisation. It also makes it easier for the developer to define the requirements without relying on the runtime to perform the setup. Within cloud environments, we should be able to start our application without extensive setup of the server or runtime.

But this Security API specification is not really a good candidate to be included in the Core profile. First of all, it depends on many other specifications and some of them are archaic and not really adapted to be used in these small runtimes targeted to the cloud.

But most of all, there is no support for the token-based security that is typically used in microservices and the applications that stands as a model for these Core profile runtimes.

The JSON Web Tokens (JWS token specification) are an ideal means to carry the information about the user on the requests. Within the header, a cryptographically signed JSON carries the information about the identity and the permissions of the user who made the (initial) call to the environment. With the signature, we can be sure the content is not tampered with and we can make use of the groups and role information available in the token to determine if the user is allowed to perform the action.

And we only need to use the public key of the key pair to verify the signature. This public key can be kept cached and only occasionally refreshed from the process that creates those JWT tokens. The verification and the usage of the JWT tokens do not require calls to external systems and are ideal to implement performant systems.

MicroProfile Security specification

This is a major gap in the support of technologies for Jakarta EE that calls itself ‘Cloud-native Enterprise Java’. But also within MicroProfile, there is no adequate support for JWT tokens. You have the MicroProfile JWT Auth specification that defines functionality for the MicroProfile runtimes that are using JWT tokens.

But there are several restrictions on top of the JOSE (Javascript Object Signing and Encryption) specification that make the MicroProfile specification less useful. The configuration is defined so that an application can only be used by one token emitter. You can define only one issuer, only one signature algorithm, you can indicate that a JWS or JWE token is used but not both at the same time, etc.

This makes that applications that make use of the MicroProfile JWT Auth specification are in fact part of a distributed monolith. You cannot use it in a real microservices situation as it will be called by many different applications, including external systems using different token providers.

Side note, since the majority of the companies that claim to do microservices are actually just building a distributed monolith, the problem with the MicroProfile JWT Auth specification is not impacting the majority I guess.

Security in Atbash Runtime

Since security is an important and vital aspect of any application, the upcoming version of the Atbash runtime will have support for JWT tokens. It is based on the JOSE specifications and for convenience, is using the MicroProfile JWT Auth API but not having the many restrictions of the MicroProfile Specification.

The configuration parameters are interpreted slightly differently, like the key defining the issues is interpreted as a list. This still allows the developer to only allow tokens from one source, but also allows the application to be used in real microservices situations and accept multiple issuers.

A first draft of the implementation is included in the main branch of the GitHub repository and will be refined in the coming weeks before the release is made.

Conclusion

Although security is a very important aspect of each application, specifications, and products should help the developers to implement the security requirements they have. There is no Security specification included, nor is there one planned in the new upcoming Core profile of Jakarta EE.

In fact, Jakarta EE does not have a specification that is useful in cloud-native environments since there is no support for JWT tokens.

But also the MicroProfile security specification is not a solution in all cases since it restricts the tokens to one issuer. Making it only useable in a distributed monolith situation and not in a real large-scale microservices environment.

The JWT token implementation within Atbash Runtime is based on the API of MicroProfile JWT Auth but overcomes the limitations of the MicroProfile specification by supporting the configuration of multiple sources, multiple algorithms, and using JWT and JWE tokens at the same time.
Sounds good? Give it a try when the next version will be available in a few weeks.

This website uses cookies. By continuing to use this site, you accept our use of cookies.  Learn more