Application Security (Jakarta Security )
6.0
5.0
4.0
3.0
2.0
1.0

This feature enables support for securing the server runtime environment and applications using Jakarta Security 4.0.

If you are updating your application from using the appSecurity-5.0 feature to using the appSecurity-6.0 feature, the removal of a single, previously deprecated Jakarta class might require you to update your application code. For more information, see Differences between Jakarta Security 4.0 and 3.0.

Enabling this feature

To enable the Application Security 6.0 (Jakarta Security 4.0) feature, add the following element declaration into your server.xml file, inside the featureManager element:

<feature>appSecurity-6.0</feature>

Examples

Configure a basic user registry

You can configure Open Liberty to authenticate and authorize users by using a basic user registry. The basic user registry contains user credentials that applications need for security-related tasks. To configure a basic user registry, the Application Security feature must be enabled in the server.xml file. The following example shows the configuration of a basic user registry in the server.xml file:

<basicRegistry id="basic" realm="BasicRealm">
   <user name="Bob" password="bobpwd" />
   <user name="John" password="johnpwd" />
</basicRegistry>

To configure a basic user registry with multiple users, you can create groups for users with unique group names as shown in the following example:

<basicRegistry id="basic" realm="BasicRealm">
  <user name="Bob" password="bobpwd" />
  <user name="John" password="johnpwd" />
  <user name="user1" password="user1pwd"/>
  <user name="user2" password="user2pwd" />

  <group name="myAdmins">
    <member name="Bob" />
    <member name="user1" />
  </group>

  <group name="users">
    <member name="user1" />
    <member name="user2" />
  </group>
</basicRegistry>

User and group names must be unique and cannot contain any trailing or leading spaces. If a user ID or password contains characters other than US-ASCII, make sure that the server.xml file is saved by using UTF-8 character encoding. You can use the securityUtility encode command to encode the password for each user. For more information, see securityUtility encode. You can also specify administrative roles for users and groups to govern access to Open Liberty administrative REST APIs. For more information, see the Admin REST Connector feature.

Configure a basic user registry with QuickStart security

When you want to configure a basic user registry for test purposes, you can use the quickStartSecurity element to automatically configure a registry that grants the administrator role to a user. The administrator role gives the user the authority to manage applications. The following example shows the server.xml file configuration to define the username and password for a user that is granted the administrator role with the quickStartSecurity element:

<quickStartSecurity userName="Bob" userPassword="bobpwd" />

You can use QuickStart security configuration for test purposes. The registry that is configured by this option is not intended for production environments. However, it is useful in test scenarios, particularly for testing secured JMX connections that require administrator access.

Specify LTPA keys

When security features are enabled, Lightweight Third-Party Authentication (LTPA) is enabled by default.

The LTPA keys are encrypted using a password and stored in the ltpa.keys file. If a keysPassword is configured in the ltpa element, then it will be used as the password to encrypt the LTPA keys. If it is not configured, the LTPA keys are encrypted by using a randomly generated password created during server creation (unless the --no-password option is specified with the server create command) and stored as ltpa_keys_password in the server.env file.

On existing servers where the ltpa_keys_password does not exist, and the keysPassword is not specified in the ltpa element, the LTPA keys are encrypted by using the keystore_password in the server.env file.

If a keyPassword is not configured in the ltpa element in the server.xml file, and if the ltpa_keys_password and keystore_password environment variables are not set in the server.env file, such as when the --no-password option is specified with the server create command, then the LTPA service will fail.

You can override the default settings for the ltpa element by configuring the ltpa element in the server.xml file. The following example shows how to configure the ltpa element:

<ltpa keysFileName="yourLTPAKeysFileName.keys" keysPassword="changeIt" expiration="120" />

For more information on LTPA configuration attributes, see the LTPA configuration element.

For more information about LTPA best practices, including how to configure secure cookies that must use TLS, see LTPA best practices.

Configure LTPA authentication for a subset of requests

You can configure an authentication filter to specify whether certain requests for protected resources are authenticated with LTPA. If the request meets the criteria that are specified in the authentication filter, then the request can authenticate with LTPA to access the protected resource. Conversely, if the request does not meet the criteria that are configured in the LTPA authentication filter, then the user is prompted to provide login credentials. For more information, see Authentication filters.

<ltpa keysFileName="yourLTPAKeysFileName.keys" keysPassword="keysPassword" expiration="120" authFilterRef="myAuthFilter"/>

<authFilter id="myAuthFilter">
         <requestUrl id="myRequestUrl" urlPattern="/SimpleServlet" matchType="contains"/>
</authFilter>

In the example, request URLs that contain the /SimpleServlet pattern are authenticated by using LTPA SSO authentication. If the ltpa element does not specify the authFilterRef attribute, all requests that include an LTPA cookie are processed by LTPA SSO authentication.

Disable LTPA cookies for TAI

LTPA cookies contain secure tokens that are used to verify user credentials and enable SSO. When you don’t want to rely on LTPA tokens for SSO, you can use other methods, such as a Trust Association Interceptor (TAI), for authentication. A TAI is used to validate HTTP requests between a third-party security server and an Open Liberty server to complete authentication. The following example shows how to disable LTPA cookies for TAI by specifying the disableLtpaCookie attribute with a value of true in the server.xml file:

<trustAssociation id="sample" disableLtpaCookie="true" />

Rotate LTPA keys without requiring users to reauthenticate

Open Liberty can use either primary keys or validation keys to validate LTPA tokens. You can rotate LTPA keys without requiring reauthentication by using validation keys to validate existing LTPA tokens whenever you generate new primary keys.

Primary Keys are LTPA keys in the specified LTPA keys file, which is ltpa.keys by default but can be configured with the keysFileName attribute. Primary keys are used both for generating new LTPA tokens and for validating LTPA tokens. Only one primary keys file can exist for each Liberty runtime.

Validation keys are LTPA keys in any *.keys files other than the primary keys file. The validation keys are used only for validating LTPA tokens. They are not used for generating new LTPA tokens. All validation keys files must be located in the same directory as the primary keys file.

To rotate LTPA keys without requiring reauthentication, copy the primary keys to a validation keys file and then delete the primary keys file. Open Liberty automatically generates a new primary LTPA keys file to validate any new LTPA tokens while it continues to use validation keys files to validate existing LTPA tokens.

  1. Configure Open Liberty to use validation keys.

    To enable Open Liberty to use both primary keys and validation keys, specify the monitorValidationKeysDir and monitorInterval attributes for the ltpa element in your server.xml file, as shown in the following example:

    <ltpa monitorValidationKeysDir="true" monitorInterval="5m"/>

    The directory monitor looks for any LTPA keys files with the *.keys extension in the ${server.config.dir}/resources/security/ directory. Open Liberty reads the LTPA keys in these files and uses them to validate LTPA tokens.

    The monitorValidationKeysDir attribute monitors the ${server.config.dir}/resources/security/ directory by default, but can monitor any directory the primary keys file is specified in. Monitoring is enabled only when the updateTrigger attribute is set to polled (the default value) and the monitorInterval attribute is set to a duration greater than 0. The default value of the monitorInterval attribute is 0.

    Alternatively, you can specify the validationKeys subelement to specify a particular validation keys file. You can also remove the validation keys in this file from use at a particular date and time with the optional validUntilDate attribute. In the following example, a validation keys file is specified with an expiration date after which the keys in the file are removed from use automatically:

    <ltpa>
        <validationKeys fileName="validation1.keys" password="{xor}Lz4sLCgwLTs=" validUntilDate="2024-01-02T12:30:00Z"/>
    </ltpa>

    The fileName and password attributes are required in the validationKeys element, but the validUntilDate attribute is optional.

    When you specify the validationKeys subelement, the monitorValidationKeysDir element is not required. However, you can use both elements in combination so that any *.keys files in the primary keys file directory are used to validate LTPA tokens and not just the file that is specified by the validationKeys fileName attribute.

    <ltpa monitorValidationKeysDir="true" monitorInterval="5m">
        <validationKeys fileName="validation1.keys" password="{xor}Lz4sLCgwLTs=" validUntilDate="2024-01-02T12:30:00Z"/>
    </ltpa>

    In this case, any validation keys in files other than the file that is specified by the validationKeys subelement remain in use until you delete the corresponding .keys file or set the monitorValidationKeysDir attribute to false.

  2. Copy the primary keys to a validation keys file.

    If you copy the primary keys to a validation keys file in the same directory, or to a file that is specified by the validationKeys subelement, the runtime can continue to use these keys to validate LTPA tokens when the primary keys file is removed.

  3. Delete the primary keys file.

    Open Liberty automatically generates a new primary keys file to create and validate new LTPA tokens, while it continues to use the validation keys files to validate existing LTPA tokens. In this way, you can rotate the LTPA keys without requiring existing users to reauthenticate.

  4. Optionally, when you no longer need the validation keys, remove them by deleting the validation keys file or by setting the monitorValidationKeysDir attribute to false.

    Removing unused validation keys can improve performance.


Enable in-memory identity stores

By default, the in-memory identity store is disabled for security reasons. To use the @InMemoryIdentityStoreDefinition annotation within your application for testing or development, you must explicitly set the allowInMemoryIdentityStores attribute to true on the webAppSecurity element in the server.xml file:

<webAppSecurity allowInMemoryIdentityStores="true"/>

Once enabled, you can define users, passwords, and groups directly in your application code. The following example shows an in-memory identity store definition with multiple credentials:

@InMemoryIdentityStoreDefinition(
    priority = 10, useFor = {VALIDATE, PROVIDE_GROUPS}, value = {
        @Credentials(callerName = "jasmine", password = "secret1", groups = { "caller", "user" } ),
        @Credentials(callerName = "bill", password = "{xor}LDo8LTorbg==", groups = { "foo", "bar" }),
        @Credentials(callerName = "frank", groups = { "user" }, password = "{hash}ARAAAAAUUEJLREYyV...x2w=="),
        @Credentials(callerName = "sally", groups = { "user" }, password = "{aes}ARAFCrWIYJCL7ZBNjN+...EFA==")
    }
)

The password specification supports the same encryption and encoding as the basic user registry: hash, aes, and xor (along with plain text passwords, which are supported but not encouraged).

If this annotation is found within an application, a clear warning about its use in a production environment is output in the main log files upon application start-up.

Use multiple built-in authentication mechanisms

You can specify multiple built-in mechanisms, such as Basic and OpenID Connect, within the same application code. The container provides a default prioritization algorithm to select the mechanism if a custom handler is not provided.

The following example shows an application class configured with three different authentication mechanism types:

@BasicAuthenticationMechanismDefinition(realmName = "basicAuth")

@OpenIdAuthenticationMechanismDefinition(
    providerURI = "https://samples.auth0.com/authorize",
    clientId="your_client_id",
    redirectURI= "https://openidconnect.net/callback",
    scope="openid profile email",
    responseType="code")

@FormAuthenticationMechanismDefinition(
    loginToContinue = @LoginToContinue(errorPage = "/login-error.html",
    loginPage = "/login.html"))

public class MySecureApplication extends Application { }

The internal prioritization algorithm selects mechanisms in the following order: 1. Developer-provided custom mechanisms (using @Priority annotation values if multiple are found) 2. Built-in mechanism types in order: OpenID, Custom Form, Form, Basic

Use qualifiers with multiple mechanisms of the same type

When you need to define multiple authentication mechanisms of the same type (for example, multiple Basic authentication mechanisms), you can use qualifiers to differentiate them. First, define custom qualifiers:

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface Admin {
}

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface User {
}

Then use these qualifiers in your mechanism definitions:

// No explicit qualifier, so "BasicAuthenticationMechanism" (the default) is assigned
@BasicAuthenticationMechanismDefinition(realmName="my-realm")

// Explicit qualifiers Admin and User
@BasicAuthenticationMechanismDefinition(realmName="admin-realm", qualifiers={Admin.class})
@BasicAuthenticationMechanismDefinition(realmName="user-realm", qualifiers={User.class})

The following table shows the default qualifiers for built-in HTTP authentication mechanisms:

HttpAuthenticationMechanism definitionDefault Qualifier

BasicAuthenticationMechanismDefinition

BasicAuthenticationMechanism

FormAuthenticationMechanismDefinition

FormAuthenticationMechanism

CustomFormAuthenticationMechanismDefinition

CustomFormAuthenticationMechanism

OpenIdAuthenticationMechanismDefinition

OpenIdAuthenticationMechanism

If you use qualifiers, you must provide a custom HttpAuthenticationMechanismHandler implementation. Otherwise, an error is thrown at application start-up.

Configure a custom authentication mechanism handler

Jakarta Security 4.0 allows you to specify multiple HTTP authentication mechanisms within a single application. When you use custom qualifiers to differentiate between multiple mechanisms of the same type, or when you need custom routing logic, you must provide a custom HttpAuthenticationMechanismHandler implementation.

The HttpAuthenticationMechanismHandler interface abstracts the authentication mechanism from the authentication module, providing a bridge between them. Its main purpose is to select and invoke the appropriate authentication mechanism based on your custom logic.

The following example shows a custom handler that routes requests to different authentication mechanisms based on the request URI:

@Default
@ApplicationScoped
public class CustomHttpAuthenticationMechanismHandler implements HttpAuthenticationMechanismHandler {

    @Inject @Admin
    private HttpAuthenticationMechanism adminHAM;

    @Inject @User
    private HttpAuthenticationMechanism userHAM;

    @Inject @BasicAuthenticationMechanism
    private HttpAuthenticationMechanism defaultHAM;

    @Override
    public AuthenticationStatus validateRequest(HttpServletRequest request,
                                               HttpServletResponse response,
                                               HttpMessageContext httpMessageContext)
                                               throws AuthenticationException {
        String path = request.getRequestURI();

        // Route to appropriate mechanism based on path
        if (path.startsWith("/admin")) {
            return adminHAM.validateRequest(request, response, httpMessageContext);
        } else if (path.startsWith("/user")) {
            return userHAM.validateRequest(request, response, httpMessageContext);
        }
        // Default behavior
        return defaultHAM.validateRequest(request, response, httpMessageContext);
    }

    @Override
    public AuthenticationStatus secureResponse(HttpServletRequest request,
                                              HttpServletResponse response,
                                              HttpMessageContext httpMessageContext)
                                              throws AuthenticationException {
        // Custom implementation (optional - interface has default)
        return HttpAuthenticationMechanismHandler.super.secureResponse(request, response, httpMessageContext);
    }

    @Override
    public void cleanSubject(HttpServletRequest request,
                            HttpServletResponse response,
                            HttpMessageContext httpMessageContext) {
        // Custom implementation (optional - interface has default)
        HttpAuthenticationMechanismHandler.super.cleanSubject(request, response, httpMessageContext);
    }
}

The HttpAuthenticationMechanismHandler interface provides default implementations for secureResponse() and cleanSubject() methods, so you only need to override them if custom behavior is required.

Use custom authentication mechanisms

You can also create custom authentication mechanisms by implementing the HttpAuthenticationMechanism interface. Custom mechanisms can be combined with built-in mechanisms and can use qualifiers for differentiation:

@ApplicationScoped
@Priority(100)
@User
public class CustomHttpAuthenticationMechanism implements HttpAuthenticationMechanism {

    @Override
    public AuthenticationStatus validateRequest(HttpServletRequest request,
                                               HttpServletResponse response,
                                               HttpMessageContext httpMessageContext)
                                               throws AuthenticationException {
        // Custom authentication logic
        return httpMessageContext.notifyContainerAboutLogin(
            new CustomPrincipal("user"),
            new HashSet<>(Arrays.asList("role1", "role2"))
        );
    }
}

If you don’t specify @Priority, a default value is assigned that is higher than all built-in authentication mechanisms. If you specify multiple custom mechanisms without @Priority, you must set unique @Priority values to avoid application start-up errors.

Common errors and troubleshooting

When working with multiple authentication mechanisms, you might encounter the following errors.

Ambiguous CDI bean resolution:

CWWKS2605E: Unable to determine which HttpAuthenticationMechanism to handle request.
The following HttpAuthenticationMechanisms have the same priority or Http Authentication
Mechanism Type: CustomHAMOne Priority = 100, CustomHAMTwo Priority = 100.

Solution: If you define multiple mechanisms with the same qualifier, CDI will throw an ambiguous resolution error at start-up. Ensure each HttpAuthenticationMechanism implementation (either annotation built-ins or developer defined) have unique qualifiers or use a custom handler to manage them.

Missing custom handler with qualified mechanisms:

CWWKS2610E: Missing a custom handler that uses qualified HttpAuthenticationMechanisms.

Solution: When using qualifiers, you must provide a custom HttpAuthenticationMechanismHandler implementation.

Logging and debugging

The container logs helpful information to assist with debugging multiple authentication mechanisms.

  • The order and prioritization of all authentication mechanisms found during the authentication flow (logged once):

Order of HttpAuthenticationMechanisms found (the first one will be used if its
prioritization is unique): CustomHAMThree Priority = 300, CustomHAMTwo Priority = 200,
CustomHAMOne Priority = 100, OidcHttpAuthenticationMechanism,
FormAuthenticationMechanism, BasicHttpAuthenticationMechanism
  • The name of the authentication mechanism handler being used:

HttpAuthenticationMechanismHandler is [CustomAuthenticationMechanismHandler]

These log entries help you verify which mechanism is being selected and whether your custom handler is being used.

Standard API packages provided by this feature

  • jakarta.security.auth.message

  • jakarta.security.auth.message.callback

  • jakarta.security.auth.message.config

  • jakarta.security.auth.message.module

  • jakarta.security.enterprise

  • jakarta.security.enterprise.authentication.mechanism.http

  • jakarta.security.enterprise.authentication.mechanism.http.openid

  • jakarta.security.enterprise.credential

  • jakarta.security.enterprise.identitystore

  • jakarta.security.enterprise.identitystore.openid

Supported Java versions

  • JavaSE-17.0

  • JavaSE-21.0

  • JavaSE-25.0

  • JavaSE-26.0

Developing a feature that depends on this feature

If you are developing a feature that depends on this feature, include the following item in the Subsystem-Content header in your feature manifest file.

io.openliberty.appSecurity-6.0; type="osgi.subsystem.feature"