Chapter 2. Authentication mechanisms in Quarkus
Chapter 2. Authentication mechanisms in Quarkus The Quarkus Security framework supports multiple authentication mechanisms, which you can use to secure your applications. You can also combine authentication mechanisms. Tip Before you choose an authentication mechanism for securing your Quarkus applications, review the information provided. 2.1. Overview of supported authentication mechanisms Some supported authentication mechanisms are built into Quarkus, while others require you to add an extension. All of these mechanisms are detailed in the following sections: Built-in authentication mechanisms Other supported authentication mechanisms The following table maps specific authentication requirements to a supported mechanism that you can use in Quarkus: Table 2.1. Authentication requirements and mechanisms Authentication requirement Authentication mechanism Username and password Basic , Form-based authentication Bearer access token OIDC Bearer token authentication , JWT Single sign-on (SSO) OIDC Code Flow , Form-based authentication Client certificate Mutual TLS authentication For more information, see the following Token authentication mechanism comparison table. 2.2. Built-in authentication mechanisms Quarkus Security provides the following built-in authentication support: Basic authentication Form-based authentication Mutual TLS authentication 2.2.1. Basic authentication You can secure your Quarkus application endpoints with the built-in HTTP Basic authentication mechanism. For more information, see the following documentation: Basic authentication Enable Basic authentication Quarkus Security with Jakarta Persistence Getting started with Security by using Basic authentication and Jakarta Persistence Identity providers 2.2.2. Form-based authentication Quarkus provides form-based authentication that works similarly to traditional Servlet form-based authentication. Unlike traditional form authentication, the authenticated user is not stored in an HTTP session because Quarkus does not support clustered HTTP sessions. Instead, the authentication information is stored in an encrypted cookie, which can be read by all cluster members who share the same encryption key. To apply encryption, add the quarkus.http.auth.session.encryption-key property, and ensure the value you set is at least 16 characters long. The encryption key is hashed by using SHA-256. The resulting digest is used as a key for AES-256 encryption of the cookie value. The cookie contains an expiry time as part of the encrypted value, so all nodes in the cluster must have their clocks synchronized. At one-minute intervals, a new cookie gets generated with an updated expiry time if the session is in use. To get started with form authentication, you should have similar settings as described in Enable Basic authentication and property quarkus.http.auth.form.enabled must be set to true . Simple application.properties with form-base authentication can look similar to this: quarkus.http.auth.form.enabled=true quarkus.http.auth.form.login-page=login.html quarkus.http.auth.form.landing-page=hello quarkus.http.auth.form.error-page= # Define testing user quarkus.security.users.embedded.enabled=true quarkus.security.users.embedded.plain-text=true quarkus.security.users.embedded.users.alice=alice quarkus.security.users.embedded.roles.alice=user Important Configuring user names, secrets, and roles in the application.properties file is appropriate only for testing scenarios. For securing a production application, it is crucial to use a database or LDAP to store this information. For more information you can take a look at Quarkus Security with Jakarta Persistence or other mentioned in Enable Basic authentication . and application login page will contain HTML form similar to this: <form action="/j_security_check" method="post"> <label>Username</label> <input type="text" placeholder="Username" name="j_username" required> <label>Password</label> <input type="password" placeholder="Password" name="j_password" required> <button type="submit">Login</button> </form> With applications (SPA), you typically want to avoid redirects by removing default page paths, as shown in the following example: # do not redirect, respond with HTTP 200 OK quarkus.http.auth.form.landing-page= # do not redirect, respond with HTTP 401 Unauthorized quarkus.http.auth.form.login-page= quarkus.http.auth.form.error-page= # HttpOnly must be false if you want to log out on the client; it can be true if logging out from the server quarkus.http.auth.form.http-only-cookie=false Now that you have disabled redirects for the SPA, you must log in and log out programmatically from your client. Below are examples of JavaScript methods for logging into the j_security_check endpoint and logging out of the application by destroying the cookie. const login = () => { // Create an object to represent the form data const formData = new URLSearchParams(); formData.append("j_username", username); formData.append("j_password", password); // Make an HTTP POST request using fetch against j_security_check endpoint fetch("j_security_check", { method: "POST", body: formData, headers: { "Content-Type": "application/x-www-form-urlencoded", }, }) .then((response) => { if (response.status === 200) { // Authentication was successful console.log("Authentication successful"); } else { // Authentication failed console.error("Invalid credentials"); } }) .catch((error) => { console.error(error); }); }; To log out of the SPA from the client, the cookie must be set to quarkus.http.auth.form.http-only-cookie=false so you can destroy the cookie and possibly redirect back to your main page. const logout= () => { // delete the credential cookie, essentially killing the session const removeCookie = `quarkus-credential=; Max-Age=0;path=/`; document.cookie = removeCookie; // perform post-logout actions here, such as redirecting back to your login page }; To log out of the SPA from the server, the cookie can be set to quarkus.http.auth.form.http-only-cookie=true and use this example code to destroy the cookie. @ConfigProperty(name = "quarkus.http.auth.form.cookie-name") String cookieName; @Inject CurrentIdentityAssociation identity; @POST public Response logout() { if (identity.getIdentity().isAnonymous()) { throw new UnauthorizedException("Not authenticated"); } final NewCookie removeCookie = new NewCookie.Builder(cookieName) .maxAge(0) .expiry(Date.from(Instant.EPOCH)) .path("/") .build(); return Response.noContent().cookie(removeCookie).build(); } The following properties can be used to configure form-based authentication: Configuration property fixed at build time - All other configuration properties are overridable at runtime Configuration property Type Default quarkus.http.auth.permission."permissions".enabled Determines whether the entire permission set is enabled, or not. By default, if the permission set is defined, it is enabled. Environment variable: QUARKUS_HTTP_AUTH_PERMISSION__PERMISSIONS__ENABLED boolean quarkus.http.auth.permission."permissions".policy The HTTP policy that this permission set is linked to. There are three built-in policies: permit, deny and authenticated. Role based policies can be defined, and extensions can add their own policies. Environment variable: QUARKUS_HTTP_AUTH_PERMISSION__PERMISSIONS__POLICY string required quarkus.http.auth.permission."permissions".methods The methods that this permission set applies to. If this is not set then they apply to all methods. Note that if a request matches any path from any permission set, but does not match the constraint due to the method not being listed then the request will be denied. Method specific permissions take precedence over matches that do not have any methods set. This means that for example if Quarkus is configured to allow GET and POST requests to /admin to and no other permissions are configured PUT requests to /admin will be denied. Environment variable: QUARKUS_HTTP_AUTH_PERMISSION__PERMISSIONS__METHODS list of string quarkus.http.auth.permission."permissions".paths The paths that this permission check applies to. If the path ends in /* then this is treated as a path prefix, otherwise it is treated as an exact match. Matches are done on a length basis, so the most specific path match takes precedence. If multiple permission sets match the same path then explicit methods matches take precedence over matches without methods set, otherwise the most restrictive permissions are applied. Environment variable: QUARKUS_HTTP_AUTH_PERMISSION__PERMISSIONS__PATHS list of string quarkus.http.auth.permission."permissions".auth-mechanism Path specific authentication mechanism which must be used to authenticate a user. It needs to match HttpCredentialTransport authentication scheme such as 'basic', 'bearer', 'form', etc. Environment variable: QUARKUS_HTTP_AUTH_PERMISSION__PERMISSIONS__AUTH_MECHANISM string quarkus.http.auth.permission."permissions".shared Indicates that this policy always applies to the matched paths in addition to the policy with a winning path. Avoid creating more than one shared policy to minimize the performance impact. Environment variable: QUARKUS_HTTP_AUTH_PERMISSION__PERMISSIONS__SHARED boolean false quarkus.http.auth.permission."permissions".applies-to Whether permission check should be applied on all matching paths, or paths specific for the Jakarta REST resources. Environment variable: QUARKUS_HTTP_AUTH_PERMISSION__PERMISSIONS__APPLIES_TO all : Apply on all matching paths. jaxrs : Declares that a permission check must only be applied on the Jakarta REST request paths. Use this option to delay the permission check if an authentication mechanism is chosen with an annotation on the matching Jakarta REST endpoint. This option must be set if the following REST endpoint annotations are used: - io.quarkus.oidc.Tenant annotation which selects an OIDC authentication mechanism with a tenant identifier - io.quarkus.vertx.http.runtime.security.annotation.BasicAuthentication which selects the Basic authentication mechanism - io.quarkus.vertx.http.runtime.security.annotation.FormAuthentication which selects the Form-based authentication mechanism - io.quarkus.vertx.http.runtime.security.annotation.MTLSAuthentication which selects the mTLS authentication mechanism - io.quarkus.security.webauthn.WebAuthn which selects the WebAuth authentication mechanism - io.quarkus.oidc.BearerTokenAuthentication which selects the OpenID Connect Bearer token authentication mechanism - io.quarkus.oidc.AuthorizationCodeFlow which selects the OpenID Connect Code authentication mechanism all quarkus.http.auth.policy."role-policy".roles-allowed The roles that are allowed to access resources protected by this policy. By default, access is allowed to any authenticated user. Environment variable: QUARKUS_HTTP_AUTH_POLICY__ROLE_POLICY__ROLES_ALLOWED list of string ** quarkus.http.auth.policy."role-policy".roles."role-name" Add roles granted to the SecurityIdentity based on the roles that the SecurityIdentity already have. For example, the Quarkus OIDC extension can map roles from the verified JWT access token, and you may want to remap them to a deployment specific roles. Environment variable: QUARKUS_HTTP_AUTH_POLICY__ROLE_POLICY__ROLES__ROLE_NAME_ Map<String,List<String>> quarkus.http.auth.policy."role-policy".permissions."role-name" Permissions granted to the SecurityIdentity if this policy is applied successfully (the policy allows request to proceed) and the authenticated request has required role. For example, you can map permission perm1 with actions action1 and action2 to role admin by setting quarkus.http.auth.policy.role-policy1.permissions.admin=perm1:action1,perm1:action2 configuration property. Granted permissions are used for authorization with the @PermissionsAllowed annotation. Environment variable: QUARKUS_HTTP_AUTH_POLICY__ROLE_POLICY__PERMISSIONS__ROLE_NAME_ Map<String,List<String>> quarkus.http.auth.policy."role-policy".permission-class Permissions granted by this policy will be created with a java.security.Permission implementation specified by this configuration property. The permission class must declare exactly one constructor that accepts permission name ( String ) or permission name and actions ( String , String[] ). Permission class must be registered for reflection if you run your application in a native mode. Environment variable: QUARKUS_HTTP_AUTH_POLICY__ROLE_POLICY__PERMISSION_CLASS string io.quarkus.security.StringPermission quarkus.http.auth.roles-mapping."role-name" Map the SecurityIdentity roles to deployment specific roles and add the matching roles to SecurityIdentity . For example, if SecurityIdentity has a user role and the endpoint is secured with a 'UserRole' role, use this property to map the user role to the UserRole role, and have SecurityIdentity to have both user and UserRole roles. Environment variable: QUARKUS_HTTP_AUTH_ROLES_MAPPING__ROLE_NAME_ Map<String,List<String>> quarkus.http.auth.certificate-role-attribute Client certificate attribute whose values are going to be mapped to the 'SecurityIdentity' roles according to the roles mapping specified in the certificate properties file. The attribute must be either one of the Relative Distinguished Names (RDNs) or Subject Alternative Names (SANs). By default, the Common Name (CN) attribute value is used for roles mapping. Supported values are: RDN type - Distinguished Name field. For example 'CN' represents Common Name field. Multivalued RNDs and multiple instances of the same attributes are currently not supported. 'SAN_RFC822' - Subject Alternative Name field RFC 822 Name. 'SAN_URI' - Subject Alternative Name field Uniform Resource Identifier (URI). 'SAN_ANY' - Subject Alternative Name field Other Name. Please note that only simple case of UTF8 identifier mapping is supported. For example, you can map 'other-identifier' to the SecurityIdentity roles. If you use 'openssl' tool, supported Other name definition would look like this: subjectAltName=otherName:1.2.3.4;UTF8:other-identifier Environment variable: QUARKUS_HTTP_AUTH_CERTIFICATE_ROLE_ATTRIBUTE string CN quarkus.http.auth.certificate-role-properties Properties file containing the client certificate attribute value to role mappings. Use it only if the mTLS authentication mechanism is enabled with either quarkus.http.ssl.client-auth=required or quarkus.http.ssl.client-auth=request . Properties file is expected to have the CN_VALUE=role1,role,... ,roleN format and should be encoded using UTF-8. Environment variable: QUARKUS_HTTP_AUTH_CERTIFICATE_ROLE_PROPERTIES path quarkus.http.auth.realm The authentication realm Environment variable: QUARKUS_HTTP_AUTH_REALM string quarkus.http.auth.form.login-page The login page. Redirect to login page can be disabled by setting quarkus.http.auth.form.login-page= . Environment variable: QUARKUS_HTTP_AUTH_FORM_LOGIN_PAGE string /login.html quarkus.http.auth.form.username-parameter The username field name. Environment variable: QUARKUS_HTTP_AUTH_FORM_USERNAME_PARAMETER string j_username quarkus.http.auth.form.password-parameter The password field name. Environment variable: QUARKUS_HTTP_AUTH_FORM_PASSWORD_PARAMETER string j_password quarkus.http.auth.form.error-page The error page. Redirect to error page can be disabled by setting quarkus.http.auth.form.error-page= . Environment variable: QUARKUS_HTTP_AUTH_FORM_ERROR_PAGE string /error.html quarkus.http.auth.form.landing-page The landing page to redirect to if there is no saved page to redirect back to. Redirect to landing page can be disabled by setting quarkus.http.auth.form.landing-page= . Environment variable: QUARKUS_HTTP_AUTH_FORM_LANDING_PAGE string /index.html quarkus.http.auth.form.location-cookie Option to control the name of the cookie used to redirect the user back to the location they want to access. Environment variable: QUARKUS_HTTP_AUTH_FORM_LOCATION_COOKIE string quarkus-redirect-location quarkus.http.auth.form.timeout The inactivity (idle) timeout When inactivity timeout is reached, cookie is not renewed and a new login is enforced. Environment variable: QUARKUS_HTTP_AUTH_FORM_TIMEOUT Duration PT30M quarkus.http.auth.form.new-cookie-interval How old a cookie can get before it will be replaced with a new cookie with an updated timeout, also referred to as "renewal-timeout". Note that smaller values will result in slightly more server load (as new encrypted cookies will be generated more often); however, larger values affect the inactivity timeout because the timeout is set when a cookie is generated. For example if this is set to 10 minutes, and the inactivity timeout is 30m, if a user's last request is when the cookie is 9m old then the actual timeout will happen 21m after the last request because the timeout is only refreshed when a new cookie is generated. That is, no timeout is tracked on the server side; the timestamp is encoded and encrypted in the cookie itself, and it is decrypted and parsed with each request. Environment variable: QUARKUS_HTTP_AUTH_FORM_NEW_COOKIE_INTERVAL Duration PT1M quarkus.http.auth.form.cookie-name The cookie that is used to store the persistent session Environment variable: QUARKUS_HTTP_AUTH_FORM_COOKIE_NAME string quarkus-credential quarkus.http.auth.form.cookie-path The cookie path for the session and location cookies. Environment variable: QUARKUS_HTTP_AUTH_FORM_COOKIE_PATH string / quarkus.http.auth.form.http-only-cookie Set the HttpOnly attribute to prevent access to the cookie via JavaScript. Environment variable: QUARKUS_HTTP_AUTH_FORM_HTTP_ONLY_COOKIE boolean false quarkus.http.auth.form.cookie-same-site SameSite attribute for the session and location cookies. Environment variable: QUARKUS_HTTP_AUTH_FORM_COOKIE_SAME_SITE strict , lax , none strict About the Duration format To write duration values, use the standard java.time.Duration format. See the Duration#parse() Java API documentation for more information. You can also use a simplified format, starting with a number: If the value is only a number, it represents time in seconds. If the value is a number followed by ms , it represents time in milliseconds. In other cases, the simplified format is translated to the java.time.Duration format for parsing: If the value is a number followed by h , m , or s , it is prefixed with PT . If the value is a number followed by d , it is prefixed with P . 2.2.3. Mutual TLS authentication Quarkus provides mutual TLS (mTLS) authentication so that you can authenticate users based on their X.509 certificates. To use this authentication method, you must first enable SSL/TLS for your application. For more information, see the Supporting secure connections with SSL/TLS section of the Quarkus "HTTP reference" guide. After your application accepts secure connections, the step is to configure the quarkus.http.ssl.certificate.trust-store-file property with the name of the file that holds all the certificates your application trusts. This file also includes information about how your application requests certificates when a client, such as a browser or another service, tries to access one of its protected resources. Because JKS is no longer the default keystore and truststore format in Quarkus, the framework makes an educated guess based on the file extension: .pem , .crt , and .key are read as PEM certificates and keys. .jks , .keystore , and .truststore are read as JKS keystores and truststores. .p12 , .pkcs12 , and .pfx are read as PKCS12 keystores and truststores. If your file does not use one of these extensions, you must set the format by using the following properties: quarkus.http.ssl.certificate.key-store-file-type=JKS # or P12 or PEM quarkus.http.ssl.certificate.trust-store-file-type=JKS # or P12 or PEM JKS is becoming less commonly used. Since Java 9, the default keystore format in Java is PKCS12. The most significant difference between JKS and PKCS12 is that JKS is a format specific to Java. In contrast, PKCS12 is a standardized, language-neutral way of storing encrypted private keys and certificates. Here is an example configuration for enabling mTLS: quarkus.http.ssl.certificate.key-store-file=server-keystore.jks 1 quarkus.http.ssl.certificate.key-store-password=the_key_store_secret quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks 2 quarkus.http.ssl.certificate.trust-store-password=the_trust_store_secret quarkus.http.ssl.client-auth=required 3 quarkus.http.auth.permission.default.paths=/* 4 quarkus.http.auth.permission.default.policy=authenticated quarkus.http.insecure-requests=disabled 5 1 The keystore where the server's private key is located. 2 The truststore from which the trusted certificates are loaded. 3 Setting quarkus.http.ssl.client-auth to required makes the server demand client certificates. You can set it to REQUEST if the server should accept requests without a certificate. This setting is useful when supporting multiple authentication methods besides mTLS. 4 Defines a policy where only authenticated users can access resources from your application. 5 Disables the plain HTTP protocol, requiring all requests to use HTTPS. When you set quarkus.http.ssl.client-auth to required , quarkus.http.insecure-requests is automatically disabled. When an incoming request matches a valid certificate in the truststore, your application can obtain the subject by injecting a SecurityIdentity as follows: Obtaining the subject @Inject SecurityIdentity identity; @GET @Produces(MediaType.TEXT_PLAIN) public String hello() { return String.format("Hello, %s", identity.getPrincipal().getName()); } You can also get the certificate by using the code outlined in the following example: Obtaining the certificate import java.security.cert.X509Certificate; import io.quarkus.security.credential.CertificateCredential; CertificateCredential credential = identity.getCredential(CertificateCredential.class); X509Certificate certificate = credential.getCertificate(); 2.2.3.1. Mapping certificate attributes to roles The information from the client certificate can be used to add roles to Quarkus SecurityIdentity . You can add new roles to SecurityIdentity after checking a client certificate's common name (CN) attribute. The easiest way to add new roles is to use a certificate attribute to role mapping feature. For example, you can update the properties shown in the section which introduces Mutual TLS authentication as follows: quarkus.http.ssl.certificate.key-store-file=server-keystore.jks quarkus.http.ssl.certificate.key-store-password=the_key_store_secret quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks quarkus.http.ssl.certificate.trust-store-password=the_trust_store_secret quarkus.http.ssl.client-auth=required quarkus.http.insecure-requests=disabled quarkus.http.auth.certificate-role-properties=cert-role-mappings.properties 1 quarkus.http.auth.permission.certauthenticated.paths=/* 2 quarkus.http.auth.permission.certauthenticated.policy=role-policy-cert 3 quarkus.http.auth.policy.role-policy-cert.roles-allowed=user,admin 4 1 The cert-role-mappings.properties classpath resource contains a map of certificate's CN values to roles in the form CN=role or CN=role1,role2 , etc. Let us assume it contains three entries: alice=user,admin , bob=user and jdoe=tester . 2 3 4 Use HTTP security policy to require that SecurityIdentity must have either user or admin roles for the requests to be authorized. Given the preceding configuration, the request is authorized if the client certificate's CN attribute is equal to alice or bob and forbidden if it is equal to jdoe . 2.2.3.2. Using certificate attributes to augment SecurityIdentity You can always register SecurityIdentityAugmentor if the automatic Mapping certificate attributes to roles option does not suit. Custom SecurityIdentityAugmentor can check the values of different client certificate attributes and augment the SecurityIdentity accordingly. For more information about customizing SecurityIdentity , see the Security identity customization section in the Quarkus "Security tips and tricks" guide. 2.3. Other supported authentication mechanisms Quarkus Security also supports the following authentication mechanisms through extensions: OpenID Connect authentication SmallRye JWT authentication 2.3.1. OpenID Connect authentication OpenID Connect (OIDC) is an identity layer that works on top of the OAuth 2.0 protocol. OIDC enables client applications to verify the identity of a user based on the authentication performed by the OIDC provider and retrieve basic information about that user. The Quarkus quarkus-oidc extension provides a reactive, interoperable, multitenant-enabled OIDC adapter that supports Bearer token and Authorization Code Flow authentication mechanisms. The Bearer token authentication mechanism extracts the token from the HTTP Authorization header. The Authorization Code Flow mechanism redirects the user to an OIDC provider to authenticate the user's identity. After the user is redirected back to Quarkus, the mechanism completes the authentication process by exchanging the provided code that was granted for the ID, access, and refresh tokens. You can verify ID and access JSON Web Token (JWT) tokens by using the refreshable JSON Web Key (JWK) set or introspect them remotely. However, opaque, also known as binary tokens, can only be introspected remotely. Note Using the Quarkus OIDC extension, both the Bearer token and Authorization Code Flow authentication mechanisms use SmallRye JWT authentication to represent JWT tokens as MicroProfile JWT org.eclipse.microprofile.jwt.JsonWebToken . 2.3.1.1. Additional Quarkus resources for OIDC authentication For more information about OIDC authentication and authorization methods that you can use to secure your Quarkus applications, see the following resources: OIDC topic Quarkus information resource Bearer token authentication mechanism OIDC Bearer token authentication Authorization Code Flow authentication mechanism OpenID Connect (OIDC) Authorization Code Flow mechanism OIDC and SAML Identity broker OpenID Connect (OIDC) Authorization Code Flow and SAML Identity broker Multiple tenants that can support the Bearer token authentication or Authorization Code Flow mechanisms Using OpenID Connect (OIDC) multi-tenancy Securing Quarkus with commonly used OpenID Connect providers Configuring well-known OpenID Connect providers Using Keycloak to centralize authorization Using OpenID Connect (OIDC) and Keycloak to centralize authorization Note To enable the Quarkus OIDC extension at runtime, set quarkus.oidc.tenant-enabled=false at build time. Then, re-enable it at runtime by using a system property. For more information about managing the individual tenant configurations in multitenant OIDC deployments, see the Disabling tenant configurations section in the "Using OpenID Connect (OIDC) multi-tenancy" guide. 2.3.1.2. OpenID Connect client and filters The quarkus-oidc-client extension provides OidcClient for acquiring and refreshing access tokens from OpenID Connect and OAuth2 providers that support the following token grants: client-credentials password refresh_token The quarkus-resteasy-client-oidc-filter extension requires the quarkus-oidc-client extension. It provides JAX-RS RESTful Web Services OidcClientRequestFilter , which sets the access token acquired by OidcClient as the Bearer scheme value of the HTTP Authorization header. This filter can be registered with MicroProfile REST client implementations injected into the current Quarkus endpoint, but it is not related to the authentication requirements of this service endpoint. For example, it can be a public endpoint or be protected with mTLS. Important In this scenario, you do not need to protect your Quarkus endpoint by using the Quarkus OpenID Connect adapter. The quarkus-resteasy-client-oidc-token-propagation extension requires the quarkus-oidc extension. It provides Jakarta REST TokenCredentialRequestFilter , which sets the OpenID Connect Bearer token or Authorization Code Flow access token as the Bearer scheme value of the HTTP Authorization header. This filter can be registered with MicroProfile REST client implementations injected into the current Quarkus endpoint, which must be protected by using the Quarkus OIDC adapter. This filter can propagate the access token to the downstream services. For more information, see the OpenID Connect client and token propagation quickstart and OpenID Connect (OIDC) and OAuth2 client and filters reference guides. 2.3.2. SmallRye JWT authentication The quarkus-smallrye-jwt extension provides a MicroProfile JSON Web Token (JWT) 2.1 implementation and multiple options to verify signed and encrypted JWT tokens. It represents them as org.eclipse.microprofile.jwt.JsonWebToken . quarkus-smallrye-jwt is an alternative to the quarkus-oidc Bearer token authentication mechanism and verifies only JWT tokens by using either Privacy Enhanced Mail (PEM) keys or the refreshable JWK key set. quarkus-smallrye-jwt also provides the JWT generation API, which you can use to easily create signed , inner-signed , and encrypted JWT tokens. For more information, see the Using JWT RBAC guide. 2.4. Choosing between OpenID Connect, SmallRye JWT, and OAuth2 authentication mechanisms Use the following information to select the appropriate token authentication mechanism to secure your Quarkus applications. List of authentication mechanism use cases quarkus-oidc requires an OpenID Connect provider such as Keycloak, which can verify the bearer tokens or authenticate the end users with the Authorization Code flow. In both cases, quarkus-oidc requires a connection to the specified OpenID Connect provider. If the user authentication requires Authorization Code flow, or you need to support multiple tenants, use quarkus-oidc . quarkus-oidc can also request user information by using both Authorization Code Flow and Bearer access tokens. If your bearer tokens must be verified, use quarkus-oidc or quarkus-smallrye-jwt . If your bearer tokens are in a JSON web token (JWT) format, you can use any extensions in the preceding list. Both quarkus-oidc and quarkus-smallrye-jwt support refreshing the JsonWebKey (JWK) set when the OpenID Connect provider rotates the keys. Therefore, if remote token introspection must be avoided or is unsupported by the providers, use quarkus-oidc or quarkus-smallrye-jwt to verify JWT tokens. To introspect the JWT tokens remotely, you can use quarkus-oidc for verifying the opaque or binary tokens by using remote introspection. quarkus-smallrye-jwt does not support the remote introspection of both opaque or JWT tokens but instead relies on the locally available keys that are usually retrieved from the OpenID Connect provider. quarkus-oidc and quarkus-smallrye-jwt support the JWT and opaque token injection into the endpoint code. Injected JWT tokens provide more information about the user. All extensions can have the tokens injected as Principal . quarkus-smallrye-jwt supports more key formats than quarkus-oidc . quarkus-oidc uses only the JWK-formatted keys that are part of a JWK set, whereas quarkus-smallrye-jwt supports PEM keys. quarkus-smallrye-jwt handles locally signed, inner-signed-and-encrypted, and encrypted tokens. In contrast, although quarkus-oidc can also verify such tokens, it treats them as opaque tokens and verifies them through remote introspection. Note Architectural considerations drive your decision to use opaque or JSON web token (JWT) token format. Opaque tokens tend to be much shorter than JWT tokens but need most of the token-associated state to be maintained in the provider database. Opaque tokens are effectively database pointers. JWT tokens are significantly longer than opaque tokens. Nonetheless, the providers effectively delegate most of the token-associated state to the client by storing it as the token claims and either signing or encrypting them. Table 2.2. Token authentication mechanism comparison Feature required Authentication mechanism quarkus-oidc quarkus-smallrye-jwt Bearer JWT verification Local verification or introspection Local verification Bearer opaque token verification Introspection No Refreshing JsonWebKey set to verify JWT tokens Yes Yes Represent token as Principal Yes Yes Inject JWT as MP JWT Yes Yes Authorization code flow Yes No Multi-tenancy Yes No User information support Yes No PEM key format support No Yes SecretKey support No In JSON Web Key (JWK) format Inner-signed and encrypted or encrypted tokens Introspection Local verification Custom token verification No With injected JWT parser JWT as a cookie support No Yes 2.5. Combining authentication mechanisms If different sources provide the user credentials, you can combine authentication mechanisms. For example, you can combine the built-in Basic and the Quarkus quarkus-oidc Bearer token authentication mechanisms. Important You cannot combine the Quarkus quarkus-oidc Bearer token and smallrye-jwt authentication mechanisms because both mechanisms attempt to verify the token extracted from the HTTP Bearer token authentication scheme. 2.5.1. Use HTTP Security Policy to enable path-based authentication The following configuration example demonstrates how you can enforce a single selectable authentication mechanism for a given request path: quarkus.http.auth.permission.basic-or-bearer.paths=/service quarkus.http.auth.permission.basic-or-bearer.policy=authenticated quarkus.http.auth.permission.basic.paths=/basic-only quarkus.http.auth.permission.basic.policy=authenticated quarkus.http.auth.permission.basic.auth-mechanism=basic quarkus.http.auth.permission.bearer.paths=/bearer-only quarkus.http.auth.permission.bearer.policy=authenticated quarkus.http.auth.permission.bearer.auth-mechanism=bearer Ensure that the value of the auth-mechanism property matches the authentication scheme supported by HttpAuthenticationMechanism , for example, basic , bearer , or form . 2.5.2. Use annotations to enable path-based authentication for Jakarta REST endpoints It is possible to use annotations to select an authentication mechanism specific to each Jakarta REST endpoint. This feature is only enabled when Proactive authentication is disabled due to the fact that the annotations can only be used to select authentication mechanisms after a REST endpoint has been matched. Here is how you can select an authentication mechanism per a REST endpoint basis: quarkus.http.auth.proactive=false import io.quarkus.oidc.AuthorizationCodeFlow; import io.quarkus.vertx.http.runtime.security.annotation.BasicAuthentication; import jakarta.annotation.security.RolesAllowed; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @Path("hello") public class HelloResource { @GET @BasicAuthentication 1 2 @Path("basic") public String basicAuthMechanism() { return "basic"; } @GET @RolesAllowed("admin") 3 @AuthorizationCodeFlow 4 @Path("code-flow") public String codeFlowAuthMechanism() { return "code-flow"; } } 1 The REST endpoint /hello/basic can only ever be accessed by using the Basic authentication . 2 This endpoint requires authentication, because when no standard security annotation accompanies the @BasicAuthentication annotation, the @Authenticated annotation is added by default. 3 The @AuthorizationCodeFlow annotation can be combined with any other standard security annotation like @RolesAllowed , @PermissionsAllowed and others. 4 The REST endpoint /hello/code-flow can only ever be accessed by using the OIDC authorization code flow mechanism . Table 2.3. Supported authentication mechanism annotations Authentication mechanism^ Annotation Basic authentication mechanism io.quarkus.vertx.http.runtime.security.annotation.BasicAuthentication Form-based authentication mechanism io.quarkus.vertx.http.runtime.security.annotation.FormAuthentication Mutual TLS authentication mechanism io.quarkus.vertx.http.runtime.security.annotation.MTLSAuthentication Bearer token authentication mechanism io.quarkus.oidc.BearerTokenAuthentication OIDC authorization code flow mechanism io.quarkus.oidc.AuthorizationCodeFlow SmallRye JWT authentication mechanism io.quarkus.smallrye.jwt.runtime.auth.BearerTokenAuthentication Tip Quarkus automatically secures endpoints annotated with the authentication mechanism annotation. When no standard security annotation is present on the REST endpoint and resource, the io.quarkus.security.Authenticated annotation is added for you. It is also possible to use the io.quarkus.vertx.http.runtime.security.annotation.HttpAuthenticationMechanism annotation to select any authentication mechanism based on its scheme. Annotation-based analogy to the quarkus.http.auth.permission.basic.auth-mechanism=custom configuration property is the @HttpAuthenticationMechanism("custom") annotation. Note For consistency with various Jakarta EE specifications, it is recommended to always repeat annotations instead of relying on annotation inheritance. 2.5.2.1. How to combine it with HTTP Security Policy The easiest way to define roles that are allowed to access individual resources is the @RolesAllowed annotation. Nevertheless, it is also possible to use the HTTP Security Policy like in the example below: quarkus.http.auth.policy.roles1.roles-allowed=user quarkus.http.auth.permission.roles1.paths=/hello/code-flow quarkus.http.auth.permission.roles1.applies-to=JAXRS 1 quarkus.http.auth.permission.roles1.policy=roles1 quarkus.http.auth.permission.roles1.methods=GET 2 1 Delay this policy's permission check after the endpoint-specific authentication mechanism has been selected. 2 Make the roles1 permission match only the endpoint annotated with the @AuthorizationCodeFlow annotation. Unannotated endpoints must avoid the delay caused by the applies-to=JAXRS option. 2.6. Proactive authentication Proactive authentication is enabled in Quarkus by default. This means that if an incoming request has a credential, the request will always be authenticated, even if the target page does not require authentication. For more information, see the Quarkus Proactive authentication guide. 2.7. References Quarkus Security overview Quarkus Security architecture Identity providers Authorization of web endpoints
quarkus.http.auth.form.enabled=true quarkus.http.auth.form.login-page=login.html quarkus.http.auth.form.landing-page=hello quarkus.http.auth.form.error-page= Define testing user quarkus.security.users.embedded.enabled=true quarkus.security.users.embedded.plain-text=true quarkus.security.users.embedded.users.alice=alice quarkus.security.users.embedded.roles.alice=user <form action="/j_security_check" method="post"> <label>Username</label> <input type="text" placeholder="Username" name="j_username" required> <label>Password</label> <input type="password" placeholder="Password" name="j_password" required> <button type="submit">Login</button> </form> do not redirect, respond with HTTP 200 OK quarkus.http.auth.form.landing-page= do not redirect, respond with HTTP 401 Unauthorized quarkus.http.auth.form.login-page= quarkus.http.auth.form.error-page= HttpOnly must be false if you want to log out on the client; it can be true if logging out from the server quarkus.http.auth.form.http-only-cookie=false const login = () => { // Create an object to represent the form data const formData = new URLSearchParams(); formData.append("j_username", username); formData.append("j_password", password); // Make an HTTP POST request using fetch against j_security_check endpoint fetch("j_security_check", { method: "POST", body: formData, headers: { "Content-Type": "application/x-www-form-urlencoded", }, }) .then((response) => { if (response.status === 200) { // Authentication was successful console.log("Authentication successful"); } else { // Authentication failed console.error("Invalid credentials"); } }) .catch((error) => { console.error(error); }); }; const logout= () => { // delete the credential cookie, essentially killing the session const removeCookie = `quarkus-credential=; Max-Age=0;path=/`; document.cookie = removeCookie; // perform post-logout actions here, such as redirecting back to your login page }; @ConfigProperty(name = "quarkus.http.auth.form.cookie-name") String cookieName; @Inject CurrentIdentityAssociation identity; @POST public Response logout() { if (identity.getIdentity().isAnonymous()) { throw new UnauthorizedException("Not authenticated"); } final NewCookie removeCookie = new NewCookie.Builder(cookieName) .maxAge(0) .expiry(Date.from(Instant.EPOCH)) .path("/") .build(); return Response.noContent().cookie(removeCookie).build(); } quarkus.http.ssl.certificate.key-store-file-type=JKS # or P12 or PEM quarkus.http.ssl.certificate.trust-store-file-type=JKS # or P12 or PEM quarkus.http.ssl.certificate.key-store-file=server-keystore.jks 1 quarkus.http.ssl.certificate.key-store-password=the_key_store_secret quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks 2 quarkus.http.ssl.certificate.trust-store-password=the_trust_store_secret quarkus.http.ssl.client-auth=required 3 quarkus.http.auth.permission.default.paths=/* 4 quarkus.http.auth.permission.default.policy=authenticated quarkus.http.insecure-requests=disabled 5 @Inject SecurityIdentity identity; @GET @Produces(MediaType.TEXT_PLAIN) public String hello() { return String.format("Hello, %s", identity.getPrincipal().getName()); } import java.security.cert.X509Certificate; import io.quarkus.security.credential.CertificateCredential; CertificateCredential credential = identity.getCredential(CertificateCredential.class); X509Certificate certificate = credential.getCertificate(); quarkus.http.ssl.certificate.key-store-file=server-keystore.jks quarkus.http.ssl.certificate.key-store-password=the_key_store_secret quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks quarkus.http.ssl.certificate.trust-store-password=the_trust_store_secret quarkus.http.ssl.client-auth=required quarkus.http.insecure-requests=disabled quarkus.http.auth.certificate-role-properties=cert-role-mappings.properties 1 quarkus.http.auth.permission.certauthenticated.paths=/* 2 quarkus.http.auth.permission.certauthenticated.policy=role-policy-cert 3 quarkus.http.auth.policy.role-policy-cert.roles-allowed=user,admin 4 quarkus.http.auth.permission.basic-or-bearer.paths=/service quarkus.http.auth.permission.basic-or-bearer.policy=authenticated quarkus.http.auth.permission.basic.paths=/basic-only quarkus.http.auth.permission.basic.policy=authenticated quarkus.http.auth.permission.basic.auth-mechanism=basic quarkus.http.auth.permission.bearer.paths=/bearer-only quarkus.http.auth.permission.bearer.policy=authenticated quarkus.http.auth.permission.bearer.auth-mechanism=bearer quarkus.http.auth.proactive=false import io.quarkus.oidc.AuthorizationCodeFlow; import io.quarkus.vertx.http.runtime.security.annotation.BasicAuthentication; import jakarta.annotation.security.RolesAllowed; import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; @Path("hello") public class HelloResource { @GET @BasicAuthentication 1 2 @Path("basic") public String basicAuthMechanism() { return "basic"; } @GET @RolesAllowed("admin") 3 @AuthorizationCodeFlow 4 @Path("code-flow") public String codeFlowAuthMechanism() { return "code-flow"; } } quarkus.http.auth.policy.roles1.roles-allowed=user quarkus.http.auth.permission.roles1.paths=/hello/code-flow quarkus.http.auth.permission.roles1.applies-to=JAXRS 1 quarkus.http.auth.permission.roles1.policy=roles1 quarkus.http.auth.permission.roles1.methods=GET 2