Skip to content

JWT Authentication

Overview

JSON Web Tokens (JWT) are an open, industry standard (RFC 7519) method for representing claims securely between two parties.

JSON Web Token defines a compact and self-contained way for securely transmitting information between parties as a JSON object. With JWT Auth you can integrate security features such as single sign on into your Helidon MP applications.

Maven Coordinates

To enable JWT Authentication, either add a dependency on the helidon-microprofile bundle or add the following dependency to your project’s pom.xml (see Managing Dependencies).

xml
<dependency>
    <groupId>io.helidon.microprofile.jwt</groupId>
    <artifactId>helidon-microprofile-jwt-auth</artifactId>
</dependency>

Usage

The main configuration point for JWT Auth is a JAX-RS Application class. As this class is discovered using CDI, it must have a bean defining annotation.

Minimal required setup is done using @LoginConfig(authMethod = "MP-JWT"):

java
@LoginConfig(authMethod = "MP-JWT")
@ApplicationScoped
public class ProtectedApplication extends Application {
}

API

The following interfaces and annotations are used to work with JWT in Helidon MP:

  • JsonWebToken - an interface used in CDI beans (@RequestScoped) dependency injection to obtain the JWT of the currently executing caller.
  • @Claim - an annotation used by CDI bean (@RequestScoped) dependency injection to obtain individual claims from the caller’s JWT.
  • ClaimValue - a proxy interface used with @Claim annotation to оbtain the value of a claim by calling getValue().

Configuration

Configuration options

KeyKindTypeDefault ValueDescription
mp.jwt.decrypt.key.algorithmVALUEi.h.m.j.a.J.j.d.k.algorithmExpected key management algorithm supported by the MP JWT endpoint
mp.jwt.decrypt.key.locationVALUEStringPrivate key for decryption of encrypted claims
mp.jwt.token.cookieVALUEStringBearerSpecific cookie property name where we should search for JWT property
mp.jwt.token.headerVALUEStringAuthorizationName of the header expected to contain the token
mp.jwt.verify.audiencesLISTStringExpected audiences of incoming tokens
mp.jwt.verify.clock.skewVALUEInteger5Clock skew to be accounted for in token expiration and max age validations in seconds
mp.jwt.verify.issuerVALUEStringExpected issuer in incoming requests
mp.jwt.verify.publickeyVALUEStringString representation of the public key
mp.jwt.verify.publickey.locationVALUEStringPath to public key
mp.jwt.verify.token.ageVALUEIntegerMaximal expected token age in seconds
security.providers.mp-jwt-auth.allow-impersonationVALUEBooleanfalseWhether to allow impersonation by explicitly overriding username from outbound requests using io.helidon.security.EndpointConfig#PROPERTY_OUTBOUND_ID property
security.providers.mp-jwt-auth.atn-token.default-key-idVALUEStringDefault JWT key ID which should be used
security.providers.mp-jwt-auth.atn-token.handlerVALUEi.h.s.u.TokenHandlerToken handler to extract username from request
security.providers.mp-jwt-auth.atn-token.jwk.resourceVALUEi.h.c.c.ResourceJWK resource for authenticating the request
security.providers.mp-jwt-auth.atn-token.jwt-audienceVALUEStringAudience expected in inbound JWTs
security.providers.mp-jwt-auth.atn-token.verify-keyVALUEStringPath to public key
security.providers.mp-jwt-auth.authenticateVALUEBooleantrueWhether to authenticate requests
security.providers.mp-jwt-auth.load-on-startupVALUEBooleanfalseWhether to load JWK verification keys on server startup Default value is false
security.providers.mp-jwt-auth.optionalVALUEBooleanfalseWhether authentication is required
security.providers.mp-jwt-auth.principal-typeVALUEi.h.s.SubjectTypeUSERPrincipal type this provider extracts (and also propagates)
security.providers.mp-jwt-auth.jwt-groups-pathVALUEStringgroupsPath to the JWT payload claim containing groups to add as role grants. Nested object claims can be configured with slash-separated path segments, such as realm/groups.
security.providers.mp-jwt-auth.jwt-groups-separatorVALUEStringSeparator used to split a string claim value into multiple groups. This is used only when jwt-groups-path is configured to a custom path other than groups; setting only jwt-groups-separator has no effect.
security.providers.mp-jwt-auth.propagateVALUEBooleantrueWhether to propagate identity
security.providers.mp-jwt-auth.sign-tokenVALUEi.h.s.p.c.OutboundConfigConfiguration of outbound rules

A configuration example in microprofile-config.properties:

properties
mp.jwt.verify.issuer=https://{PublicIssuerDomain}/oauth2/default
mp.jwt.verify.publickey.location=${mp.jwt.verify.issuer}/v1/keys

Examples

java
@Path("/hello")
public class HelloResource {

    @GET
    @Produces(TEXT_PLAIN)
    public String hello(@Context SecurityContext context) {
        Optional<Principal> userPrincipal = context.userPrincipal();
        return "Hello, " + userPrincipal.get().getName() + "!";
    }
}

Do not forget to annotate the HelloApplication class to enable JWT:

java
@LoginConfig(authMethod = "MP-JWT")
@ApplicationScoped
public class HelloApplication extends Application {

    @Override
    public Set<Class<?>> getClasses() {
        return Set.of(HelloResource.class);
    }
}

Add the following configuration in microprofile-config.properties:

properties
mp.jwt.verify.issuer=https://{IssuerPublicDomain}/oauth2/default
mp.jwt.verify.publickey.location=${mp.jwt.verify.issuer}/v1/keys

Obtain the Security Token from external issuer:

bash
TOKEN=sdf4dDSWFcswdsffDSasEgv...

Run the application and execute an http request against it:

bash
curl -X GET -I -H "Authorization: Bearer $TOKEN" http://localhost:8080/hello

Curl output

bash
HTTP/1.1 200 OK
Date: 08.06.2022 10:33:47 EEST
connection: keep-alive
content-length: 28

Hello, secure@helidon.io!

which means that the request successfully passed authentication.

Additional Information

Learn more about JWT authentication at:
Eclipse MicroProfile Interoperable JWT RBAC

Reference