io.quarkus.oidc.OidcTenantConfig Maven / Gradle / Ivy
package io.quarkus.oidc;
import java.nio.file.Path;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.OptionalInt;
import io.quarkus.oidc.common.runtime.OidcCommonConfig;
import io.quarkus.oidc.common.runtime.OidcConstants;
import io.quarkus.oidc.runtime.OidcConfig;
import io.quarkus.runtime.annotations.ConfigDocMapKey;
import io.quarkus.runtime.annotations.ConfigGroup;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConvertWith;
import io.quarkus.runtime.configuration.TrimmedStringConverter;
import io.quarkus.security.identity.SecurityIdentityAugmentor;
@ConfigGroup
public class OidcTenantConfig extends OidcCommonConfig {
/**
* A unique tenant identifier. It can be set by {@code TenantConfigResolver} providers, which
* resolve the tenant configuration dynamically.
*/
@ConfigItem
public Optional tenantId = Optional.empty();
/**
* If this tenant configuration is enabled.
*
* The default tenant is disabled if it is not configured but
* a {@link TenantConfigResolver} that resolves tenant configurations is registered,
* or named tenants are configured.
* In this case, you do not need to disable the default tenant.
*/
@ConfigItem(defaultValue = "true")
public boolean tenantEnabled = true;
/**
* The application type, which can be one of the following {@link ApplicationType} values.
*/
@ConfigItem(defaultValueDocumentation = "service")
public Optional applicationType = Optional.empty();
/**
* The relative path or absolute URL of the OpenID Connect (OIDC) authorization endpoint, which authenticates
* users.
* You must set this property for `web-app` applications if OIDC discovery is disabled.
* This property is ignored if OIDC discovery is enabled.
*/
@ConfigItem
public Optional authorizationPath = Optional.empty();
/**
* The relative path or absolute URL of the OIDC UserInfo endpoint.
* You must set this property for `web-app` applications if OIDC discovery is disabled
* and the `authentication.user-info-required` property is enabled.
* This property is ignored if OIDC discovery is enabled.
*/
@ConfigItem
public Optional userInfoPath = Optional.empty();
/**
* Relative path or absolute URL of the OIDC RFC7662 introspection endpoint which can introspect both opaque and
* JSON Web Token (JWT) tokens.
* This property must be set if OIDC discovery is disabled and 1) the opaque bearer access tokens must be verified
* or 2) JWT tokens must be verified while the cached JWK verification set with no matching JWK is being refreshed.
* This property is ignored if the discovery is enabled.
*/
@ConfigItem
public Optional introspectionPath = Optional.empty();
/**
* Relative path or absolute URL of the OIDC JSON Web Key Set (JWKS) endpoint which returns a JSON Web Key
* Verification Set.
* This property should be set if OIDC discovery is disabled and the local JWT verification is required.
* This property is ignored if the discovery is enabled.
*/
@ConfigItem
public Optional jwksPath = Optional.empty();
/**
* Relative path or absolute URL of the OIDC end_session_endpoint.
* This property must be set if OIDC discovery is disabled and RP Initiated Logout support for the `web-app` applications is
* required.
* This property is ignored if the discovery is enabled.
*/
@ConfigItem
public Optional endSessionPath = Optional.empty();
/**
* The public key for the local JWT token verification.
* OIDC server connection is not created when this property is set.
*/
@ConfigItem
public Optional publicKey = Optional.empty();
/**
* Introspection Basic Authentication which must be configured only if the introspection is required
* and OpenId Connect Provider does not support the OIDC client authentication configured with
* {@link OidcCommonConfig#credentials} for its introspection endpoint.
*/
@ConfigItem
public IntrospectionCredentials introspectionCredentials = new IntrospectionCredentials();
/**
* Introspection Basic Authentication configuration
*/
@ConfigGroup
public static class IntrospectionCredentials {
/**
* Name
*/
@ConfigItem
public Optional name = Optional.empty();
/**
* Secret
*/
@ConfigItem
public Optional secret = Optional.empty();
/**
* Include OpenId Connect Client ID configured with `quarkus.oidc.client-id`.
*/
@ConfigItem(defaultValue = "true")
public boolean includeClientId = true;
public Optional getName() {
return name;
}
public void setName(String name) {
this.name = Optional.of(name);
}
public Optional getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = Optional.of(secret);
}
public boolean isIncludeClientId() {
return includeClientId;
}
public void setIncludeClientId(boolean includeClientId) {
this.includeClientId = includeClientId;
}
}
/**
* Configuration to find and parse a custom claim containing the roles information.
*/
@ConfigItem
public Roles roles = new Roles();
/**
* Configuration how to validate the token claims.
*/
@ConfigItem
public Token token = new Token();
/**
* RP Initiated, BackChannel and FrontChannel Logout configuration
*/
@ConfigItem
public Logout logout = new Logout();
/**
* Configuration of the certificate chain which can be used to verify tokens.
* If the certificate chain truststore is configured, the tokens can be verified using the certificate
* chain inlined in the Base64-encoded format as an `x5c` header in the token itself.
*
* The certificate chain inlined in the token is verified.
* Signature of every certificate in the chain but the root certificate is verified by the next certificate in the chain.
* Thumbprint of the root certificate in the chain must match a thumbprint of one of the certificates in the truststore.
*
* Additionally, a direct trust in the leaf chain certificate which will be used to verify the token signature must
* be established.
* By default, the leaf certificate's thumbprint must match a thumbprint of one of the certificates in the truststore.
* If the truststore does not have the leaf certificate imported, then the leaf certificate must be identified by its Common
* Name.
*/
@ConfigItem
public CertificateChain certificateChain = new CertificateChain();
@ConfigGroup
public static class CertificateChain {
/**
* Common name of the leaf certificate. It must be set if the {@link #trustStoreFile} does not have
* this certificate imported.
*
*/
@ConfigItem
public Optional leafCertificateName = Optional.empty();
/**
* Truststore file which keeps thumbprints of the trusted certificates.
*/
@ConfigItem
public Optional trustStoreFile = Optional.empty();
/**
* A parameter to specify the password of the truststore file if it is configured with {@link #trustStoreFile}.
*/
@ConfigItem
public Optional trustStorePassword = Optional.empty();
/**
* A parameter to specify the alias of the truststore certificate.
*/
@ConfigItem
public Optional trustStoreCertAlias = Optional.empty();
/**
* An optional parameter to specify type of the truststore file. If not given, the type is automatically
* detected
* based on the file name.
*/
@ConfigItem
public Optional trustStoreFileType = Optional.empty();
public Optional getTrustStoreFile() {
return trustStoreFile;
}
public void setTrustStoreFile(Path trustStoreFile) {
this.trustStoreFile = Optional.of(trustStoreFile);
}
public Optional getTrustStoreCertAlias() {
return trustStoreCertAlias;
}
public void setTrustStoreCertAlias(String trustStoreCertAlias) {
this.trustStoreCertAlias = Optional.of(trustStoreCertAlias);
}
public Optional getTrustStoreFileType() {
return trustStoreFileType;
}
public void setTrustStoreFileType(Optional trustStoreFileType) {
this.trustStoreFileType = trustStoreFileType;
}
public Optional getLeafCertificateName() {
return leafCertificateName;
}
public void setLeafCertificateName(String leafCertificateName) {
this.leafCertificateName = Optional.of(leafCertificateName);
}
}
/**
* Different options to configure authorization requests
*/
public Authentication authentication = new Authentication();
/**
* Authorization code grant configuration
*/
public CodeGrant codeGrant = new CodeGrant();
/**
* Default token state manager configuration
*/
@ConfigItem
public TokenStateManager tokenStateManager = new TokenStateManager();
/**
* Allow caching the token introspection data.
* Note enabling this property does not enable the cache itself but only permits to cache the token introspection
* for a given tenant. If the default token cache can be used, see {@link OidcConfig.TokenCache} to enable
* it.
*/
@ConfigItem(defaultValue = "true")
public boolean allowTokenIntrospectionCache = true;
/**
* Allow caching the user info data.
* Note enabling this property does not enable the cache itself but only permits to cache the user info data
* for a given tenant. If the default token cache can be used, see {@link OidcConfig.TokenCache} to enable
* it.
*/
@ConfigItem(defaultValue = "true")
public boolean allowUserInfoCache = true;
/**
* Allow inlining UserInfo in IdToken instead of caching it in the token cache.
* This property is only checked when an internal IdToken is generated when Oauth2 providers do not return IdToken.
* Inlining UserInfo in the generated IdToken allows to store it in the session cookie and avoids introducing a cached
* state.
*/
@ConfigItem(defaultValue = "false")
public boolean cacheUserInfoInIdtoken = false;
@ConfigGroup
public static class Logout {
/**
* The relative path of the logout endpoint at the application. If provided, the application is able to
* initiate the
* logout through this endpoint in conformance with the OpenID Connect RP-Initiated Logout specification.
*/
@ConfigItem
public Optional path = Optional.empty();
/**
* Relative path of the application endpoint where the user should be redirected to after logging out from the
* OpenID
* Connect Provider.
* This endpoint URI must be properly registered at the OpenID Connect Provider as a valid redirect URI.
*/
@ConfigItem
public Optional postLogoutPath = Optional.empty();
/**
* Name of the post logout URI parameter which is added as a query parameter to the logout redirect URI.
*/
@ConfigItem(defaultValue = OidcConstants.POST_LOGOUT_REDIRECT_URI)
public String postLogoutUriParam;
/**
* Additional properties which is added as the query parameters to the logout redirect URI.
*/
@ConfigItem
public Map extraParams;
/**
* Back-Channel Logout configuration
*/
@ConfigItem
public Backchannel backchannel = new Backchannel();
/**
* Front-Channel Logout configuration
*/
@ConfigItem
public Frontchannel frontchannel = new Frontchannel();
public void setPath(Optional path) {
this.path = path;
}
public Optional getPath() {
return path;
}
public void setPostLogoutPath(Optional postLogoutPath) {
this.postLogoutPath = postLogoutPath;
}
public Optional getPostLogoutPath() {
return postLogoutPath;
}
public Map getExtraParams() {
return extraParams;
}
public void setExtraParams(Map extraParams) {
this.extraParams = extraParams;
}
public String getPostLogoutUriParam() {
return postLogoutUriParam;
}
public void setPostLogoutUriParam(String postLogoutUriParam) {
this.postLogoutUriParam = postLogoutUriParam;
}
public Backchannel getBackchannel() {
return backchannel;
}
public void setBackchannel(Backchannel backchannel) {
this.backchannel = backchannel;
}
public Frontchannel getFrontchannel() {
return frontchannel;
}
public void setFrontchannel(Frontchannel frontchannel) {
this.frontchannel = frontchannel;
}
}
@ConfigGroup
public static class Backchannel {
/**
* The relative path of the Back-Channel Logout endpoint at the application.
*/
@ConfigItem
public Optional path = Optional.empty();
/**
* Maximum number of logout tokens that can be cached before they are matched against ID tokens stored in session
* cookies.
*/
@ConfigItem(defaultValue = "10")
public int tokenCacheSize = 10;
/**
* Number of minutes a logout token can be cached for.
*/
@ConfigItem(defaultValue = "10M")
public Duration tokenCacheTimeToLive = Duration.ofMinutes(10);
/**
* Token cache timer interval.
* If this property is set, a timer checks and removes the stale entries periodically.
*/
@ConfigItem
public Optional cleanUpTimerInterval = Optional.empty();
/**
* Logout token claim whose value is used as a key for caching the tokens.
* Only `sub` (subject) and `sid` (session id) claims can be used as keys.
* Set it to `sid` only if ID tokens issued by the OIDC provider have no `sub` but have `sid` claim.
*/
@ConfigItem(defaultValue = "sub")
public String logoutTokenKey = "sub";
public void setPath(Optional path) {
this.path = path;
}
public Optional getPath() {
return path;
}
public String getLogoutTokenKey() {
return logoutTokenKey;
}
public void setLogoutTokenKey(String logoutTokenKey) {
this.logoutTokenKey = logoutTokenKey;
}
public int getTokenCacheSize() {
return tokenCacheSize;
}
public void setTokenCacheSize(int tokenCacheSize) {
this.tokenCacheSize = tokenCacheSize;
}
public Duration getTokenCacheTimeToLive() {
return tokenCacheTimeToLive;
}
public void setTokenCacheTimeToLive(Duration tokenCacheTimeToLive) {
this.tokenCacheTimeToLive = tokenCacheTimeToLive;
}
public Optional getCleanUpTimerInterval() {
return cleanUpTimerInterval;
}
public void setCleanUpTimerInterval(Duration cleanUpTimerInterval) {
this.cleanUpTimerInterval = Optional.of(cleanUpTimerInterval);
}
}
/**
* Configuration for controlling how JsonWebKeySet containing verification keys should be acquired and managed.
*/
@ConfigItem
public Jwks jwks = new Jwks();
@ConfigGroup
public static class Jwks {
/**
* If JWK verification keys should be fetched at the moment a connection to the OIDC provider
* is initialized.
*
* Disabling this property delays the key acquisition until the moment the current token
* has to be verified. Typically it can only be necessary if the token or other telated request properties
* provide an additional context which is required to resolve the keys correctly.
*/
@ConfigItem(defaultValue = "true")
public boolean resolveEarly = true;
/**
* Maximum number of JWK keys that can be cached.
* This property is ignored if the {@link #resolveEarly} property is set to true.
*/
@ConfigItem(defaultValue = "10")
public int cacheSize = 10;
/**
* Number of minutes a JWK key can be cached for.
* This property is ignored if the {@link #resolveEarly} property is set to true.
*/
@ConfigItem(defaultValue = "10M")
public Duration cacheTimeToLive = Duration.ofMinutes(10);
/**
* Cache timer interval.
* If this property is set, a timer checks and removes the stale entries periodically.
* This property is ignored if the {@link #resolveEarly} property is set to true.
*/
@ConfigItem
public Optional cleanUpTimerInterval = Optional.empty();
public int getCacheSize() {
return cacheSize;
}
public void setCacheSize(int cacheSize) {
this.cacheSize = cacheSize;
}
public Duration getCacheTimeToLive() {
return cacheTimeToLive;
}
public void setCacheTimeToLive(Duration cacheTimeToLive) {
this.cacheTimeToLive = cacheTimeToLive;
}
public Optional getCleanUpTimerInterval() {
return cleanUpTimerInterval;
}
public void setCleanUpTimerInterval(Duration cleanUpTimerInterval) {
this.cleanUpTimerInterval = Optional.of(cleanUpTimerInterval);
}
public boolean isResolveEarly() {
return resolveEarly;
}
public void setResolveEarly(boolean resolveEarly) {
this.resolveEarly = resolveEarly;
}
}
@ConfigGroup
public static class Frontchannel {
/**
* The relative path of the Front-Channel Logout endpoint at the application.
*/
@ConfigItem
public Optional path = Optional.empty();
public void setPath(Optional path) {
this.path = path;
}
public Optional getPath() {
return path;
}
}
/**
* Default Authorization Code token state manager configuration
*/
@ConfigGroup
public static class TokenStateManager {
public enum Strategy {
/**
* Keep ID, access and refresh tokens.
*/
KEEP_ALL_TOKENS,
/**
* Keep ID token only
*/
ID_TOKEN,
/**
* Keep ID and refresh tokens only
*/
ID_REFRESH_TOKENS
}
/**
* Default TokenStateManager strategy.
*/
@ConfigItem(defaultValue = "keep_all_tokens")
public Strategy strategy = Strategy.KEEP_ALL_TOKENS;
/**
* Default TokenStateManager keeps all tokens (ID, access and refresh)
* returned in the authorization code grant response in a single session cookie by default.
*
* Enable this property to minimize a session cookie size
*/
@ConfigItem(defaultValue = "false")
public boolean splitTokens;
/**
* Mandates that the Default TokenStateManager encrypt the session cookie that stores the tokens.
*/
@ConfigItem(defaultValue = "true")
public boolean encryptionRequired = true;
/**
* The secret used by the Default TokenStateManager to encrypt the session cookie
* storing the tokens when {@link #encryptionRequired} property is enabled.
*
* If this secret is not set, the client secret configured with
* either `quarkus.oidc.credentials.secret` or `quarkus.oidc.credentials.client-secret.value` is checked.
* Finally, `quarkus.oidc.credentials.jwt.secret` which can be used for `client_jwt_secret` authentication is
* checked.
* The secret is auto-generated if it remains uninitialized after checking all of these properties.
*
* The length of the secret used to encrypt the tokens should be at least 32 characters long.
* A warning is logged if the secret length is less than 16 characters.
*/
@ConfigItem
public Optional encryptionSecret = Optional.empty();
public boolean isEncryptionRequired() {
return encryptionRequired;
}
public void setEncryptionRequired(boolean encryptionRequired) {
this.encryptionRequired = encryptionRequired;
}
public Optional getEncryptionSecret() {
return encryptionSecret;
}
public void setEncryptionSecret(String encryptionSecret) {
this.encryptionSecret = Optional.of(encryptionSecret);
}
public boolean isSplitTokens() {
return splitTokens;
}
public void setSplitTokens(boolean splitTokens) {
this.splitTokens = splitTokens;
}
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
}
public Optional getAuthorizationPath() {
return authorizationPath;
}
public void setAuthorizationPath(String authorizationPath) {
this.authorizationPath = Optional.of(authorizationPath);
}
public Optional getUserInfoPath() {
return userInfoPath;
}
public void setUserInfoPath(String userInfoPath) {
this.userInfoPath = Optional.of(userInfoPath);
}
public Optional getIntrospectionPath() {
return introspectionPath;
}
public void setIntrospectionPath(String introspectionPath) {
this.introspectionPath = Optional.of(introspectionPath);
}
public Optional getJwksPath() {
return jwksPath;
}
public void setJwksPath(String jwksPath) {
this.jwksPath = Optional.of(jwksPath);
}
public Optional getEndSessionPath() {
return endSessionPath;
}
public void setEndSessionPath(String endSessionPath) {
this.endSessionPath = Optional.of(endSessionPath);
}
public Optional getPublicKey() {
return publicKey;
}
public void setPublicKey(String publicKey) {
this.publicKey = Optional.of(publicKey);
}
public Roles getRoles() {
return roles;
}
public void setRoles(Roles roles) {
this.roles = roles;
}
public Token getToken() {
return token;
}
public void setToken(Token token) {
this.token = token;
}
public Authentication getAuthentication() {
return authentication;
}
public void setAuthentication(Authentication authentication) {
this.authentication = authentication;
}
public Optional getTenantId() {
return tenantId;
}
public void setTenantId(String tenantId) {
this.tenantId = Optional.of(tenantId);
}
public boolean isTenantEnabled() {
return tenantEnabled;
}
public void setTenantEnabled(boolean enabled) {
this.tenantEnabled = enabled;
}
public void setLogout(Logout logout) {
this.logout = logout;
}
public Logout getLogout() {
return logout;
}
@ConfigGroup
public static class Roles {
public static Roles fromClaimPath(List path) {
return fromClaimPathAndSeparator(path, null);
}
public static Roles fromClaimPathAndSeparator(List path, String sep) {
Roles roles = new Roles();
roles.roleClaimPath = Optional.ofNullable(path);
roles.roleClaimSeparator = Optional.ofNullable(sep);
return roles;
}
/**
* A list of paths to claims containing an array of groups.
* Each path starts from the top level JWT JSON object
* and can contain multiple segments.
* Each segment represents a JSON object name only; for example: "realm/groups".
* Use double quotes with the namespace-qualified claim names.
* This property can be used if a token has no `groups` claim but has the groups set in one or more different claims.
*/
@ConfigItem
public Optional> roleClaimPath = Optional.empty();
/**
* The separator for splitting strings that contain multiple group values.
* It is only used if the "role-claim-path" property points to one or more custom claims whose values are strings.
* A single space is used by default because the standard `scope` claim can contain a space-separated sequence.
*/
@ConfigItem
public Optional roleClaimSeparator = Optional.empty();
/**
* Source of the principal roles.
*/
@ConfigItem
public Optional
* The secret is auto-generated if it remains uninitialized after checking all of these properties.
*
* Error is reported if the secret length is less than 16 characters.
*/
@ConfigItem
public Optional stateSecret = Optional.empty();
public Optional getInternalIdTokenLifespan() {
return internalIdTokenLifespan;
}
public void setInternalIdTokenLifespan(Duration internalIdTokenLifespan) {
this.internalIdTokenLifespan = Optional.of(internalIdTokenLifespan);
}
public Optional isPkceRequired() {
return pkceRequired;
}
public void setPkceRequired(boolean pkceRequired) {
this.pkceRequired = Optional.of(pkceRequired);
}
@Deprecated(forRemoval = true)
public Optional getPkceSecret() {
return pkceSecret;
}
@Deprecated(forRemoval = true)
public void setPkceSecret(String pkceSecret) {
this.pkceSecret = Optional.of(pkceSecret);
}
public Optional getErrorPath() {
return errorPath;
}
public void setErrorPath(String errorPath) {
this.errorPath = Optional.of(errorPath);
}
public boolean isJavaScriptAutoRedirect() {
return javaScriptAutoRedirect;
}
public void setJavaScriptAutoredirect(boolean autoRedirect) {
this.javaScriptAutoRedirect = autoRedirect;
}
public Optional getRedirectPath() {
return redirectPath;
}
public void setRedirectPath(String redirectPath) {
this.redirectPath = Optional.of(redirectPath);
}
public Optional> getScopes() {
return scopes;
}
public void setScopes(List scopes) {
this.scopes = Optional.of(scopes);
}
public Map getExtraParams() {
return extraParams;
}
public void setExtraParams(Map extraParams) {
this.extraParams = extraParams;
}
public void setAddOpenidScope(boolean addOpenidScope) {
this.addOpenidScope = Optional.of(addOpenidScope);
}
public Optional isAddOpenidScope() {
return addOpenidScope;
}
public Optional isForceRedirectHttpsScheme() {
return forceRedirectHttpsScheme;
}
public void setForceRedirectHttpsScheme(boolean forceRedirectHttpsScheme) {
this.forceRedirectHttpsScheme = Optional.of(forceRedirectHttpsScheme);
}
public boolean isRestorePathAfterRedirect() {
return restorePathAfterRedirect;
}
public void setRestorePathAfterRedirect(boolean restorePathAfterRedirect) {
this.restorePathAfterRedirect = restorePathAfterRedirect;
}
public boolean isCookieForceSecure() {
return cookieForceSecure;
}
public void setCookieForceSecure(boolean cookieForceSecure) {
this.cookieForceSecure = cookieForceSecure;
}
public String getCookiePath() {
return cookiePath;
}
public void setCookiePath(String cookiePath) {
this.cookiePath = cookiePath;
}
public Optional getCookieDomain() {
return cookieDomain;
}
public void setCookieDomain(String cookieDomain) {
this.cookieDomain = Optional.of(cookieDomain);
}
public Optional isUserInfoRequired() {
return userInfoRequired;
}
public void setUserInfoRequired(boolean userInfoRequired) {
this.userInfoRequired = Optional.of(userInfoRequired);
}
public boolean isRemoveRedirectParameters() {
return removeRedirectParameters;
}
public void setRemoveRedirectParameters(boolean removeRedirectParameters) {
this.removeRedirectParameters = removeRedirectParameters;
}
public boolean isVerifyAccessToken() {
return verifyAccessToken;
}
public void setVerifyAccessToken(boolean verifyAccessToken) {
this.verifyAccessToken = verifyAccessToken;
}
public Duration getSessionAgeExtension() {
return sessionAgeExtension;
}
public void setSessionAgeExtension(Duration sessionAgeExtension) {
this.sessionAgeExtension = sessionAgeExtension;
}
public Optional getCookiePathHeader() {
return cookiePathHeader;
}
public void setCookiePathHeader(String cookiePathHeader) {
this.cookiePathHeader = Optional.of(cookiePathHeader);
}
public Optional isIdTokenRequired() {
return idTokenRequired;
}
public void setIdTokenRequired(boolean idTokenRequired) {
this.idTokenRequired = Optional.of(idTokenRequired);
}
public Optional getCookieSuffix() {
return cookieSuffix;
}
public void setCookieSuffix(String cookieSuffix) {
this.cookieSuffix = Optional.of(cookieSuffix);
}
public Optional getResponseMode() {
return responseMode;
}
public void setResponseMode(ResponseMode responseMode) {
this.responseMode = Optional.of(responseMode);
}
public Optional> getForwardParams() {
return forwardParams;
}
public void setForwardParams(List forwardParams) {
this.forwardParams = Optional.of(forwardParams);
}
public CookieSameSite getCookieSameSite() {
return cookieSameSite;
}
public void setCookieSameSite(CookieSameSite cookieSameSite) {
this.cookieSameSite = cookieSameSite;
}
public boolean isAllowMultipleCodeFlows() {
return allowMultipleCodeFlows;
}
public void setAllowMultipleCodeFlows(boolean allowMultipleCodeFlows) {
this.allowMultipleCodeFlows = allowMultipleCodeFlows;
}
public boolean isNonceRequired() {
return nonceRequired;
}
public void setNonceRequired(boolean nonceRequired) {
this.nonceRequired = nonceRequired;
}
public Optional getStateSecret() {
return stateSecret;
}
public void setStateSecret(Optional stateSecret) {
this.stateSecret = stateSecret;
}
}
/**
* Authorization Code grant configuration
*/
@ConfigGroup
public static class CodeGrant {
/**
* Additional parameters, in addition to the required `code` and `redirect-uri` parameters,
* which must be included to complete the authorization code grant request.
*/
@ConfigItem
public Map extraParams = new HashMap<>();
/**
* Custom HTTP headers which must be sent to complete the authorization code grant request.
*/
@ConfigItem
public Map headers = new HashMap<>();
public Map getExtraParams() {
return extraParams;
}
public void setExtraParams(Map extraParams) {
this.extraParams = extraParams;
}
public Map getHeaders() {
return headers;
}
public void setHeaders(Map headers) {
this.headers = headers;
}
}
/**
* Supported asymmetric signature algorithms
*/
public static enum SignatureAlgorithm {
RS256,
RS384,
RS512,
PS256,
PS384,
PS512,
ES256,
ES384,
ES512,
EDDSA;
private static String EDDSA_ALG = "EDDSA";
private static String REQUIRED_EDDSA_ALG = "EdDSA";
public String getAlgorithm() {
String name = name();
return EDDSA_ALG.equals(name) ? REQUIRED_EDDSA_ALG : name;
}
}
@ConfigGroup
public static class Token {
public static Token fromIssuer(String issuer) {
Token tokenClaims = new Token();
tokenClaims.issuer = Optional.of(issuer);
tokenClaims.audience = Optional.ofNullable(null);
return tokenClaims;
}
public static Token fromAudience(String... audience) {
Token tokenClaims = new Token();
tokenClaims.issuer = Optional.ofNullable(null);
tokenClaims.audience = Optional.of(Arrays.asList(audience));
return tokenClaims;
}
/**
* The expected issuer `iss` claim value.
* This property overrides the `issuer` property, which might be set in OpenId Connect provider's well-known
* configuration.
* If the `iss` claim value varies depending on the host, IP address, or tenant id of the provider, you can skip the
* issuer verification by setting this property to `any`, but it should be done only when other options (such as
* configuring
* the provider to use the fixed `iss` claim value) are not possible.
*/
@ConfigItem
public Optional issuer = Optional.empty();
/**
* The expected audience `aud` claim value, which can be a string or an array of strings.
*
* Note the audience claim is verified for ID tokens by default.
* ID token audience must be equal to the value of `quarkus.oidc.client-id` property.
* Use this property to override the expected value if your OpenID Connect provider
* sets a different audience claim value in ID tokens. Set it to `any` if your provider
* does not set ID token audience` claim.
*
* Audience verification for access tokens is only done if this property is configured.
*/
@ConfigItem
public Optional> audience = Optional.empty();
/**
* Require that the token includes a `sub` (subject) claim which is a unique
* and never reassigned identifier for the current user.
* Note that if you enable this property and if UserInfo is also required,
* both the token and UserInfo `sub` claims must be present and match each other.
*/
@ConfigItem(defaultValue = "false")
public boolean subjectRequired = false;
/**
* A map of required claims and their expected values.
* For example, `quarkus.oidc.token.required-claims.org_id = org_xyz` would require tokens to have the `org_id` claim to
* be present and set to `org_xyz`.
* Strings are the only supported types. Use {@linkplain SecurityIdentityAugmentor} to verify claims of other types or
* complex claims.
*/
@ConfigItem
@ConfigDocMapKey("claim-name")
public Map requiredClaims = new HashMap<>();
/**
* Expected token type
*/
@ConfigItem
public Optional tokenType = Optional.empty();
/**
* Life span grace period in seconds.
* When checking token expiry, current time is allowed to be later than token expiration time by at most the configured
* number of seconds.
* When checking token issuance, current time is allowed to be sooner than token issue time by at most the configured
* number of seconds.
*/
@ConfigItem
public OptionalInt lifespanGrace = OptionalInt.empty();
/**
* Token age.
*
* It allows for the number of seconds to be specified that must not elapse since the `iat` (issued at) time.
* A small leeway to account for clock skew which can be configured with `quarkus.oidc.token.lifespan-grace` to verify
* the token expiry time
* can also be used to verify the token age property.
*
* Note that setting this property does not relax the requirement that Bearer and Code Flow JWT tokens
* must have a valid (`exp`) expiry claim value. The only exception where setting this property relaxes the requirement
* is when a logout token is sent with a back-channel logout request since the current
* OpenId Connect Back-Channel specification does not explicitly require the logout tokens to contain an `exp` claim.
* However, even if the current logout token is allowed to have no `exp` claim, the `exp` claim is still verified
* if the logout token contains it.
*/
@ConfigItem
public Optional age = Optional.empty();
/**
* Name of the claim which contains a principal name. By default, the `upn`, `preferred_username` and `sub`
* claims are
* checked.
*/
@ConfigItem
public Optional principalClaim = Optional.empty();
/**
* Refresh expired authorization code flow ID or access tokens.
* If this property is enabled, a refresh token request is performed if the authorization code
* ID or access token has expired and, if successful, the local session is updated with the new set of tokens.
* Otherwise, the local session is invalidated and the user redirected to the OpenID Provider to re-authenticate.
* In this case, the user might not be challenged again if the OIDC provider session is still active.
*
* For this option be effective the `authentication.session-age-extension` property should also be set to a nonzero
* value since the refresh token is currently kept in the user session.
*
* This option is valid only when the application is of type {@link ApplicationType#WEB_APP}}.
*
* This property is enabled if `quarkus.oidc.token.refresh-token-time-skew` is configured,
* you do not need to enable this property manually in this case.
*/
@ConfigItem
public boolean refreshExpired;
/**
* The refresh token time skew, in seconds.
* If this property is enabled, the configured number of seconds is added to the current time
* when checking if the authorization code ID or access token should be refreshed.
* If the sum is greater than the authorization code ID or access token's expiration time, a refresh is going to
* happen.
*/
@ConfigItem
public Optional refreshTokenTimeSkew = Optional.empty();
/**
* The forced JWK set refresh interval in minutes.
*/
@ConfigItem(defaultValue = "10M")
public Duration forcedJwkRefreshInterval = Duration.ofMinutes(10);
/**
* Custom HTTP header that contains a bearer token.
* This option is valid only when the application is of type {@link ApplicationType#SERVICE}}.
*/
@ConfigItem
public Optional header = Optional.empty();
/**
* HTTP Authorization header scheme.
*/
@ConfigItem(defaultValue = OidcConstants.BEARER_SCHEME)
public String authorizationScheme = OidcConstants.BEARER_SCHEME;
/**
* Required signature algorithm.
* OIDC providers support many signature algorithms but if necessary you can restrict
* Quarkus application to accept tokens signed only using an algorithm configured with this property.
*/
@ConfigItem
public Optional signatureAlgorithm = Optional.empty();
/**
* Decryption key location.
* JWT tokens can be inner-signed and encrypted by OpenId Connect providers.
* However, it is not always possible to remotely introspect such tokens because
* the providers might not control the private decryption keys.
* In such cases set this property to point to the file containing the decryption private key in
* PEM or JSON Web Key (JWK) format.
* If this property is not set and the `private_key_jwt` client authentication method is used, the private key
* used to sign the client authentication JWT tokens are also used to decrypt the encrypted ID tokens.
*/
@ConfigItem
public Optional decryptionKeyLocation = Optional.empty();
/**
* Allow the remote introspection of JWT tokens when no matching JWK key is available.
*
* This property is set to `true` by default for backward-compatibility reasons. It is planned that this default value
* will be changed to `false` in an upcoming release.
*
* Also note this property is ignored if JWK endpoint URI is not available and introspecting the tokens is
* the only verification option.
*/
@ConfigItem(defaultValue = "true")
public boolean allowJwtIntrospection = true;
/**
* Require that JWT tokens are only introspected remotely.
*
*/
@ConfigItem(defaultValue = "false")
public boolean requireJwtIntrospectionOnly = false;
/**
* Allow the remote introspection of the opaque tokens.
*
* Set this property to `false` if only JWT tokens are expected.
*/
@ConfigItem(defaultValue = "true")
public boolean allowOpaqueTokenIntrospection = true;
/**
* Token customizer name.
*
* Allows to select a tenant specific token customizer as a named bean.
* Prefer using {@link TenantFeature} qualifier when registering custom {@link TokenCustomizer}.
* Use this property only to refer to `TokenCustomizer` implementations provided by this extension.
*/
@ConfigItem
public Optional customizerName = Optional.empty();
/**
* Indirectly verify that the opaque (binary) access token is valid by using it to request UserInfo.
* Opaque access token is considered valid if the provider accepted this token and returned a valid UserInfo.
* You should only enable this option if the opaque access tokens must be accepted but OpenId Connect
* provider does not have a token introspection endpoint.
* This property has no effect when JWT tokens must be verified.
*/
@ConfigItem(defaultValueDocumentation = "false")
public Optional verifyAccessTokenWithUserInfo = Optional.empty();
public Optional isVerifyAccessTokenWithUserInfo() {
return verifyAccessTokenWithUserInfo;
}
public void setVerifyAccessTokenWithUserInfo(boolean verify) {
this.verifyAccessTokenWithUserInfo = Optional.of(verify);
}
public Optional getIssuer() {
return issuer;
}
public void setIssuer(String issuer) {
this.issuer = Optional.of(issuer);
}
public Optional getHeader() {
return header;
}
public void setHeader(String header) {
this.header = Optional.of(header);
}
public Optional> getAudience() {
return audience;
}
public void setAudience(List audience) {
this.audience = Optional.of(audience);
}
public OptionalInt getLifespanGrace() {
return lifespanGrace;
}
public void setLifespanGrace(int lifespanGrace) {
this.lifespanGrace = OptionalInt.of(lifespanGrace);
}
public Optional getPrincipalClaim() {
return principalClaim;
}
public void setPrincipalClaim(String principalClaim) {
this.principalClaim = Optional.of(principalClaim);
}
public boolean isRefreshExpired() {
return refreshExpired;
}
public void setRefreshExpired(boolean refreshExpired) {
this.refreshExpired = refreshExpired;
}
public Duration getForcedJwkRefreshInterval() {
return forcedJwkRefreshInterval;
}
public void setForcedJwkRefreshInterval(Duration forcedJwkRefreshInterval) {
this.forcedJwkRefreshInterval = forcedJwkRefreshInterval;
}
public Optional getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = Optional.of(tokenType);
}
public Optional getRefreshTokenTimeSkew() {
return refreshTokenTimeSkew;
}
public void setRefreshTokenTimeSkew(Duration refreshTokenTimeSkew) {
this.refreshTokenTimeSkew = Optional.of(refreshTokenTimeSkew);
}
public boolean isAllowJwtIntrospection() {
return allowJwtIntrospection;
}
public void setAllowJwtIntrospection(boolean allowJwtIntrospection) {
this.allowJwtIntrospection = allowJwtIntrospection;
}
public boolean isAllowOpaqueTokenIntrospection() {
return allowOpaqueTokenIntrospection;
}
public void setAllowOpaqueTokenIntrospection(boolean allowOpaqueTokenIntrospection) {
this.allowOpaqueTokenIntrospection = allowOpaqueTokenIntrospection;
}
public Optional getAge() {
return age;
}
public void setAge(Duration age) {
this.age = Optional.of(age);
}
public Optional getDecryptionKeyLocation() {
return decryptionKeyLocation;
}
public void setDecryptionKeyLocation(String decryptionKeyLocation) {
this.decryptionKeyLocation = Optional.of(decryptionKeyLocation);
}
public Map getRequiredClaims() {
return requiredClaims;
}
public void setRequiredClaims(Map requiredClaims) {
this.requiredClaims = requiredClaims;
}
public boolean isRequireJwtIntrospectionOnly() {
return requireJwtIntrospectionOnly;
}
public void setRequireJwtIntrospectionOnly(boolean requireJwtIntrospectionOnly) {
this.requireJwtIntrospectionOnly = requireJwtIntrospectionOnly;
}
public Optional getSignatureAlgorithm() {
return signatureAlgorithm;
}
public void setSignatureAlgorithm(SignatureAlgorithm signatureAlgorithm) {
this.signatureAlgorithm = Optional.of(signatureAlgorithm);
}
public Optional getCustomizerName() {
return customizerName;
}
public void setCustomizerName(String customizerName) {
this.customizerName = Optional.of(customizerName);
}
public boolean isSubjectRequired() {
return subjectRequired;
}
public void setSubjectRequired(boolean subjectRequired) {
this.subjectRequired = subjectRequired;
}
public String getAuthorizationScheme() {
return authorizationScheme;
}
public void setAuthorizationScheme(String authorizationScheme) {
this.authorizationScheme = authorizationScheme;
}
}
public static enum ApplicationType {
/**
* A {@code WEB_APP} is a client that serves pages, usually a front-end application. For this type of client the
* Authorization Code Flow is defined as the preferred method for authenticating users.
*/
WEB_APP,
/**
* A {@code SERVICE} is a client that has a set of protected HTTP resources, usually a backend application following the
* RESTful Architectural Design. For this type of client, the Bearer Authorization method is defined as the preferred
* method for authenticating and authorizing users.
*/
SERVICE,
/**
* A combined {@code SERVICE} and {@code WEB_APP} client.
* For this type of client, the Bearer Authorization method is used if the Authorization header is set
* and Authorization Code Flow - if not.
*/
HYBRID
}
/**
* Well known OpenId Connect provider identifier
*/
@ConfigItem
public Optional provider = Optional.empty();
public static enum Provider {
APPLE,
DISCORD,
FACEBOOK,
GITHUB,
GOOGLE,
LINKEDIN,
MASTODON,
MICROSOFT,
SPOTIFY,
STRAVA,
TWITCH,
TWITTER,
// New name for Twitter
X
}
public Optional getProvider() {
return provider;
}
public void setProvider(Provider provider) {
this.provider = Optional.of(provider);
}
public Optional getApplicationType() {
return applicationType;
}
public void setApplicationType(ApplicationType type) {
this.applicationType = Optional.of(type);
}
public boolean isAllowTokenIntrospectionCache() {
return allowTokenIntrospectionCache;
}
public void setAllowTokenIntrospectionCache(boolean allowTokenIntrospectionCache) {
this.allowTokenIntrospectionCache = allowTokenIntrospectionCache;
}
public boolean isAllowUserInfoCache() {
return allowUserInfoCache;
}
public void setAllowUserInfoCache(boolean allowUserInfoCache) {
this.allowUserInfoCache = allowUserInfoCache;
}
public boolean isCacheUserInfoInIdtoken() {
return cacheUserInfoInIdtoken;
}
public void setCacheUserInfoInIdtoken(boolean cacheUserInfoInIdtoken) {
this.cacheUserInfoInIdtoken = cacheUserInfoInIdtoken;
}
public IntrospectionCredentials getIntrospectionCredentials() {
return introspectionCredentials;
}
public void setIntrospectionCredentials(IntrospectionCredentials introspectionCredentials) {
this.introspectionCredentials = introspectionCredentials;
}
public CodeGrant getCodeGrant() {
return codeGrant;
}
public void setCodeGrant(CodeGrant codeGrant) {
this.codeGrant = codeGrant;
}
public CertificateChain getCertificateChain() {
return certificateChain;
}
public void setCertificateChain(CertificateChain certificateChain) {
this.certificateChain = certificateChain;
}
}