Enable an OpenID Connect client for an application

You can use the OpenIdAuthenticationMechanismDefinition annotation in a web or RESTful Web Services (JAX-RS) application to enable an OpenID Connect client for that application. Annotations can simplify the process of enabling OpenID Connect authentication in a web or JAX-RS application by eliminating the need for manual protocol implementation.

The OpenIdAuthenticationMechanismDefinition annotation is a part of the Jakarta Security 3.0 specification and is supported in Open Liberty by the Application Security 5.0 feature.

Configure an OpenID Connect Client and callback servlet

To use the OpenIdAuthenticationMechanismDefinition annotation in your application, you must enable the appSecurity-5.0 feature in the server.xml file. The following example shows a servlet with a minimal OpenIdAuthenticationMechanismDefinition annotation.

@WebServlet("/OidcAnnotatedServlet")
@OpenIdAuthenticationMechanismDefinition(
                    providerURI = "https://localhost:8920/oidc/endpoint/OP",
                    clientId = "clientId",
                    clientSecret = "clientSecret",
                    redirectURI = "https://localhost:8940/MyApp/Callback")
@DeclareRoles("all")
@ServletSecurity(@HttpConstraint(rolesAllowed = "all"))
public class OidcAnnotatedServlet extends HttpServlet {
…
}

This example demonstrates how to enable a basic OpenID Connect client for protected resources within an application that is named MyApp by using the OpenIdAuthenticationMechanismDefinition annotation. This client uses an OpenID Connect Provider (OP) at the https://localhost:8920/oidc/endpoint/OP URL. When you attempt to access a protected resource in MyApp application, you are directed to the OP authorization endpoint, which is obtained from the OP discovery metadata. After you authenticate, the OP redirects you to the https://localhost:8940/MyApp/Callback URL with the authorization code.

The OpenID Connect client receives the authentication response, verifies it and retrieves the access, identity, and userinfo tokens by using the authorization code. These tokens are then verified and used to establish an authenticated subject before the request is dispatched to the callback servlet. The default TLS configuration that is specified by the server is used by the OpenID Connect client when it sends requests to the OP token and userinfo endpoints. As a result, the signer certificates of the OP must be imported into the default truststore that is configured for the default TLS configuration.

To use the minimal annotation, the application needs to provide an implementation for the callback servlet. This callback servlet must use the OpenIdContext API to retrieve the original request URL and then redirect to the original resource, as shown in the following example.

…
import jakarta.security.enterprise.identitystore.openid.OpenIdContext;
…

@WebServlet("/Callback")
public class CallbackServlet extends HttpServlet {

    @Inject
    private OpenIdContext context;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        …
        if (context != null) {
            Optional<String> originalRequest = context.getStoredValue(request, response, OpenIdConstant.ORIGINAL_REQUEST);
            String originalRequestString = originalRequest.get();
            response.sendRedirect(originalRequestString);
        }
    }
}

You can avoid creating a callback servlet by setting the redirectToOriginalResource attribute to true in the annotation, as shown in the following example. The OpenID Connect client automatically redirects to the original resource.

@OpenIdAuthenticationMechanismDefinition(
                    redirectToOriginalResource = true,
                    …

Configure custom metadata

You can also override the metadata that is obtained from the OP discovery endpoint by adding a providerMetadata attribute to the annotation. The following example overrides the authorization and token endpoints.

@OpenIdAuthenticationMechanismDefinition(
                    providerMetadata = @OpenIdProviderMetadata(
                                        authorizationEndpoint = "https://localhost:8920/oidc/endpoint/OP/authorize",
                                        tokenEndpoint = "https://localhost:8920/oidc/endpoint/OP/token"),
                    …

By default, the OpenID Connect client uses the jwks_uri value from the OP discovery metadata to acquire the signing key that is required for validating token signatures. However, you can specify a different JWK endpoint URL by using the jwksURI attribute within the @OpenIdProviderMetadata annotation for the providerMetadata attribute.

@OpenIdAuthenticationMechanismDefinition(
                    providerMetadata = @OpenIdProviderMetadata(
                                        jwksURI = "https://localhost:8920/oidc/endpoint/OP/jwk",
                    …

Specifiy Expression Language expressions

All attributes support Jakarta Expression Language 5.0 (EL), and EL expressions can be specified directly in a String attribute. For example, if you have a bean that supplies the secret and you prefer not to include it in the annotation, you can use the following syntax.

@OpenIdAuthenticationMechanismDefinition(
                    clientSecret = "${openIdConfig.clientSecret}",
                    …

You can use alternative attributes that are designed for EL expressions for attributes that are not string. The boolean result for the EL expression in the useSessionExpression attribute in the following example overrides the value of the useSession boolean attribute.

@OpenIdAuthenticationMechanismDefinition(
                    useSession = true,
                    useSessionExpression = "#{openIdConfig.useSessionExpression}",
                    …

Specify missing claims

If the OP generates tokens that lack a preferred_username claim for the usernames or a groups claim for the groups, the claimsDefinition attribute can be used. Liberty OP generates an identity token with sub and groupIds claims. The claimsDefinition attribute must be used to specify the name and groups claim, especially when the providerURI attribute points to a Liberty OP.

The following example shows how to specify the sub claim for the caller name and the groupIds claim for groups.

@OpenIdAuthenticationMechanismDefinition(
                    providerURI = "https://localhost:8920/oidc/endpoint/LibertyOP",
                    claimsDefinition = @ClaimsDefinition(callerNameClaim = "sub", callerGroupsClaim = "groupIds"),
                    …

In the absence of any role mappings for the application, role protection of the resource is automatically mapped to the associated group from the claim specified by the callerGroupsClaim attribute. For example, an application that is protected by the all role would necessitate an all group in the groupIds claim.

Check token expirations

With the use of the LogoutDefinition annotation and the logout attribute, you can examine the token expirations and their corresponding outcomes. The following example shows you how to enable checks for access and identity token expiration. It also demonstrates how to activate a Relying Party (RP)-Initiated Logout with the OP end_session_endpoint when either token expires.

@OpenIdAuthenticationMechanismDefinition(
                    logout = @LogoutDefinition(
                                        notifyProvider = true,
                                        accessTokenExpiry = true,
                                        identityTokenExpiry = true),
                    …

For more information about the OpenIdAuthenticationMechanismDefinition annotation and its attributes, see section 2.4.4. OpenID Connect Annotation.

The Jakarta Security 3.0 specification also introduces the OpenIdContext bean and various APIs for handling the access, identity, and refresh tokens.

The Javadoc for the Jakarta Security 3.0 APIs are available from Jakarta Security.