io.helidon.security.providers.oidc.common.OidcConfig Maven / Gradle / Ivy
/*
* Copyright (c) 2018, 2024 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.helidon.security.providers.oidc.common;
import java.lang.System.Logger.Level;
import java.net.URI;
import java.time.Duration;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.function.Supplier;
import io.helidon.common.Errors;
import io.helidon.common.LazyValue;
import io.helidon.common.config.Config;
import io.helidon.common.configurable.Resource;
import io.helidon.config.metadata.Configured;
import io.helidon.config.metadata.ConfiguredOption;
import io.helidon.cors.CrossOriginConfig;
import io.helidon.http.SetCookie;
import io.helidon.security.Security;
import io.helidon.security.SecurityException;
import io.helidon.security.jwt.jwk.JwkKeys;
import io.helidon.security.providers.oidc.common.spi.TenantConfigFinder;
import io.helidon.security.util.TokenHandler;
import io.helidon.webclient.api.WebClient;
import io.helidon.webclient.api.WebClientConfig;
/**
* Configuration of OIDC usable from all resources that utilize OIDC specification, such as security provider, web server
* extension and IDCS connectivity.
*
* Some of the configuration options below use "resource" type. The following configuration
* can be used for a resource (example for oidc-metadata key):
* {@code
* oidc-metadata-path: "path/on/filesystem"
* oidc-metadata-resource-path: "class-path/resource"
* oidc-metadata-url: "URI on the net"
* oidc-metadata-content-plain: "Value of the resource in plain text"
* oidc-metadata-content: "Value in base64 encoded bytes"
* }
*
* Configuration options required (under security.providers[].${name}):
*
* Mandatory configuration parameters
*
* key
* description
*
*
* client-id
* Client ID as generated by OIDC server
*
*
* client-secret
* Client secret as generated by OIDC server
*
*
* identity-uri
* URI of the identity server, base used to retrieve OIDC metadata
*
*
* frontend-uri
* Full URI of the frontend for redirects back from OIDC server (e.g. http://myserver/myApp)
*
*
*
*
* Optional configuration parameters
*
* key
* default value
* description
*
*
* proxy-protocol
* http
* Proxy protocol to use when proxy is used.
*
*
* proxy-host
* null
* Proxy host to use. When defined, triggers usage of proxy for HTTP requests.
*
*
* proxy-port
* 80
* Port of the proxy server to use
*
*
* relative-uris
* false
* Flag to force the use of relative URIs in all requests. By default,
* requests that use the Proxy will have absolute URIs. Set this flag to
* true if the host is unable to accept absolute URIs.
*
*
* redirect-uri
* /oidc/redirect
* URI to register web server component on, used by the OIDC server to
* redirect authorization requests to after a user logs in or approves scopes. Note that usually the redirect URI
* configured here must be the same one as configured on OIDC server.
*
*
* scope-audience
* empty string
* Audience of the scope required by this application. This is prefixed to
* the scope name when requesting scopes from the identity server.
*
*
* cookie-use
* true
* Whether to use cookie to store JWT. If used, redirects happen only in case the user
* is not authenticated or has insufficient scopes
*
*
* cookie-name
* JSESSIONID
* Name of the cookie
*
*
* cookie-domain
* null
* Domain the cookie is valid for. Not used by default
*
*
* cookie-path
* /
* Path the cookie is valid for.
*
*
* cookie-max-age-seconds
* null
* When using cookie, used to set MaxAge attribute of the cookie, defining
* how long the cookie is valid.
*
*
* cookie-http-only
* true
* When using cookie, if set to true, the HttpOnly attribute will be configured.
*
*
* cookie-secure
* false
* When using cookie, if set to true, the Secure attribute will be configured.
*
*
* cookie-same-site
* Lax
* When using cookie, used to set the SameSite cookie value. Can be "Strict" or "Lax".
* Setting this to "Strict" will result in infinite redirects when calling OIDC on a different host.
*
*
*
* query-param-use
* false
* Whether to expect JWT in a query parameter
*
*
* query-param-name
* accessToken
* Name of a query parameter that contains the JWT token when parameter is used.
*
*
* header-use
* true
* Whether to expect JWT in a header field.
*
*
* header-token
* "Authorization" header with prefix "bearer "
* A {@link TokenHandler} configuration to process header containing a JWT
*
*
* oidc-metadata-well-known
* true
* If set to true, metadata will be loaded from default (well known)
* location, unless it is explicitly defined using oidc-metadata-resource. If set to false, it would not be loaded even
* if oidc-metadata-resource is not defined. In such a case all URIs must be explicitly
* defined (e.g. token-endpoint-uri).
*
*
* oidc-metadata.resource
* identity-uri/.well-known/openid-configuration
* Resource configuration for OIDC Metadata containing endpoints to various identity services, as well as information
* about the identity server. See {@link Resource#create(io.helidon.common.config.Config)}
*
*
* token-endpoint-uri
* token_endpoint in OIDC metadata, or identity-url/oauth2/v1/token if not available
* URI of a token endpoint used to obtain a JWT based on the authentication code.
*
*
* authorization-endpoint-uri
* "authorization_endpoint" in OIDC metadata, or identity-uri/oauth2/v1/authorize if not available
* URI of an authorization endpoint used to redirect users to for logging-in.
*
*
* validate-jwt-with-jwk
* true
* When true - validate against jwk defined by "sign-jwk", when false
* validate JWT through OIDC Server endpoint "validation-endpoint-uri"
*
*
* sign-jwk.resource
* "jwks-uri" in OIDC metadata, or identity-uri/admin/v1/SigningCert/jwk if not available, only needed
* when jwt validation is done by us
* A resource pointing to JWK with public keys of signing certificates used to validate JWT.
* See {@link Resource#create(io.helidon.common.config.Config)}
*
*
* introspect-endpoint-uri
* "introspection_endpoint" in OIDC metadata, or identity-uri/oauth2/v1/introspect
* When validate-jwt-with-jwk is set to "false", this is the endpoint used
*
*
* base-scopes
* {@value Builder#DEFAULT_BASE_SCOPES}
* Configure scopes to be requested by default. If the scope has a qualifier, it must be included here
*
*
* redirect
* false
* Whether to redirect to identity server when authentication failed.
*
*
* realm
* helidon
* Realm returned in HTTP response if redirect is not enabled or possible.
*
*
* redirect-attempt-param
* {@value DEFAULT_ATTEMPT_PARAM}
* Query parameter holding the number of times we redirected to an identity server. Customizable to prevent
* conflicts with application parameters
*
*
* max-redirects
* {@value DEFAULT_MAX_REDIRECTS}
* Maximal number of times we can redirect to an identity server. When the number is reached, no further redirects
* happen and the request finishes with an error (status {@code 401})
*
*
* server-type
*
* Type of identity server. Currently supported is {@code idcs} or not configured (for default).
*
*
* {@code client-timeout-millis}
* 30 seconds
* Timeout on HTTP client calls
*
*
* {@code cookie-encryption-enabled}
* Depends on other configuration
* Whether cookies should be encrypted. Will be enabled if logout is enabled.
*
*
* {@code cookie-encryption-password}
* Generated for this service (as a file)
* Encryption password to be used for symmetric cipher. Must be the same for all services that are intended
* to share a cookie as a form of authentication
*
*
* {@code cookie-encryption-name}
*
* Name of encryption configuration in {@link io.helidon.security.Security}. If used, security must be registered
* in curent context or in global context (this is done automatically in Helidon MP).
*
*
* {@code logout-endpoint-uri}
* From well known metadata endpoint
* Endpoint to redirect user to log out from OIDC server.
*
*
* {@code post-logout-uri}
*
* Required if logout is enabled. Endpoint the OIDC server redirects back to after logging user out.
*
*
* {@code logout-enabled}
* {@code false}
* Whether logout support should be enabled. Requires encryption of cookies (and cookies must be used).
*
*
* {@code cors}
*
* Cross-origin resource sharing settings. See {@link io.helidon.cors.CrossOriginConfig}.
*
*
* {@code force-https-redirects}
*
* Force https for redirects to identity provider.
* This is helpful if you have a frontend SSL or cloud load balancer in front and Helidon is serving plain http.
*
*
* {@code optional-audience}
* {@code false}
* Allow audience claim to be optional.
*
*
* {@code check-audience}
* {@code true}
* Turn audience claim check on when {@code true} or off when {@code false}.
*
*
*/
public final class OidcConfig extends TenantConfigImpl {
/**
* Default name of the header we expect JWT in.
*/
public static final String PARAM_HEADER_NAME = "X_OIDC_TOKEN_HEADER";
/**
* Default name of the header we expect JWT in.
*/
public static final String PARAM_ID_HEADER_NAME = "X_OIDC_ID_TOKEN_HEADER";
/**
* Default tenant query param name.
*/
public static final String DEFAULT_TENANT_PARAM_NAME = "h_tenant";
/**
* Default access token cookie name.
*/
public static final String DEFAULT_COOKIE_NAME = "JSESSIONID";
/**
* Default id token cookie name.
*/
public static final String DEFAULT_ID_COOKIE_NAME = DEFAULT_COOKIE_NAME + "_2";
/**
* Default refresh token cookie name.
*/
public static final String DEFAULT_REFRESH_COOKIE_NAME = DEFAULT_COOKIE_NAME + "_3";
/**
* Default tenant cookie name.
*/
public static final String DEFAULT_TENANT_COOKIE_NAME = "HELIDON_TENANT";
/**
* Default state cookie name.
*/
public static final String DEFAULT_STATE_COOKIE_NAME = "OIDC_STATE";
static final String DEFAULT_REDIRECT_URI = "/oidc/redirect";
static final String DEFAULT_LOGOUT_URI = "/oidc/logout";
static final boolean DEFAULT_REDIRECT = true;
static final String DEFAULT_ATTEMPT_PARAM = "h_ra";
static final int DEFAULT_MAX_REDIRECTS = 5;
static final boolean DEFAULT_FORCE_HTTPS_REDIRECTS = false;
static final Duration DEFAULT_TOKEN_REFRESH_SKEW = Duration.ofSeconds(5);
static final boolean DEFAULT_RELATIVE_URIS = false;
static final int DEFAULT_PROXY_PORT = 80;
static final String DEFAULT_PROXY_PROTOCOL = "http";
static final String TENANT_IDENT = "name";
static final String DEFAULT_PARAM_NAME = "accessToken";
static final String DEFAULT_ID_TOKEN_PARAM_NAME = "id_token";
static final boolean DEFAULT_PARAM_USE = false;
static final boolean DEFAULT_HEADER_USE = false;
static final boolean DEFAULT_COOKIE_USE = true;
private static final System.Logger LOGGER = System.getLogger(OidcConfig.class.getName());
private final Map tenantConfigurations;
private final String redirectUri;
private final String logoutUri;
private final boolean logoutEnabled;
private final String frontendUri;
private final boolean redirect;
private final String redirectAttemptParam;
private final int maxRedirects;
private final URI postLogoutUri;
private final CrossOriginConfig crossOriginConfig;
private final boolean forceHttpsRedirects;
private final Duration tokenRefreshSkew;
private final boolean relativeUris;
private final WebClient webClient;
private final Supplier webClientBuilderSupplier;
private final LazyValue defaultTenant;
private final boolean useParam;
private final String paramName;
private final String idTokenParamName;
private final String tenantParamName;
private final boolean useHeader;
private final TokenHandler headerHandler;
private final boolean useCookie;
private final OidcCookieHandler tokenCookieHandler;
private final OidcCookieHandler idTokenCookieHandler;
private final OidcCookieHandler refreshTokenCookieHandler;
private final OidcCookieHandler tenantCookieHandler;
private final OidcCookieHandler stateCookieHandler;
private final boolean tokenSignatureValidation;
private final boolean idTokenSignatureValidation;
private final boolean accessTokenIpCheck;
private OidcConfig(Builder builder) {
super(builder);
this.frontendUri = builder.frontendUri;
this.redirectUri = builder.redirectUri;
this.logoutUri = builder.logoutUri;
this.logoutEnabled = builder.logoutEnabled;
this.postLogoutUri = builder.postLogoutUri;
this.redirect = builder.redirect;
this.redirectAttemptParam = builder.redirectAttemptParam;
this.maxRedirects = builder.maxRedirects;
this.forceHttpsRedirects = builder.forceHttpsRedirects;
this.crossOriginConfig = builder.crossOriginConfig;
this.tokenRefreshSkew = builder.tokenRefreshSkew;
this.tenantConfigurations = Map.copyOf(builder.tenantConfigurations);
this.webClient = builder.webClient;
this.relativeUris = builder.relativeUris;
this.useParam = builder.useParam;
this.paramName = builder.paramName;
this.idTokenParamName = builder.idTokenParamName;
this.tenantParamName = builder.tenantParamName;
this.useHeader = builder.useHeader;
this.headerHandler = builder.headerHandler;
this.useCookie = builder.useCookie;
this.tokenCookieHandler = builder.tokenCookieBuilder.build();
this.idTokenCookieHandler = builder.idTokenCookieBuilder.build();
this.tenantCookieHandler = builder.tenantCookieBuilder.build();
this.refreshTokenCookieHandler = builder.refreshTokenCookieBuilder.build();
this.stateCookieHandler = builder.stateCookieBuilder.build();
this.tokenSignatureValidation = builder.tokenSignatureValidation;
this.idTokenSignatureValidation = builder.idTokenSignatureValidation;
this.accessTokenIpCheck = builder.accessTokenIpCheck;
this.webClientBuilderSupplier = builder.webClientBuilderSupplier;
this.defaultTenant = LazyValue.create(() -> Tenant.create(this, this));
LOGGER.log(Level.TRACE, () -> "Redirect URI with host: " + frontendUri + redirectUri);
}
/**
* Create a builder to programmatically construct OIDC configuration.
*
* @return a new builder instance usable for fluent API
*/
public static Builder builder() {
return new Builder();
}
/**
* Create a new instance from {@link io.helidon.common.config.Config}.
* The config instance has to be on the node containing keys used by this class (e.g. client-id).
*
* @param config configuration used to obtain OIDC integration values
* @return a new instance of this class configured from provided config
*/
public static OidcConfig create(Config config) {
return OidcConfig.builder()
.config(config)
.build();
}
/**
* Whether to use query parameter to get the information from request.
*
* @return if query parameter should be used
* @see Builder#useParam(Boolean)
*/
public boolean useParam() {
return useParam;
}
/**
* Query parameter name.
*
* @return name of the query parameter to use
* @see Builder#paramName(String)
*/
public String paramName() {
return paramName;
}
/**
* Query id token parameter name.
*
* @return name of the query parameter to use
* @see Builder#idTokenParamName(String)
*/
public String idTokenParamName() {
return idTokenParamName;
}
/**
* Tenant query parameter name.
*
* @return name of the tenant query parameter to use
* @see Builder#paramTenantName(String)
*/
public String tenantParamName() {
return tenantParamName;
}
/**
* Whether to use HTTP header to get the information from request.
*
* @return if header should be used
* @see Builder#useHeader(Boolean)
*/
public boolean useHeader() {
return useHeader;
}
/**
* {@link TokenHandler} to extract header information from request.
*
* @return handler to extract header
* @see Builder#headerTokenHandler(TokenHandler)
*/
public TokenHandler headerHandler() {
return headerHandler;
}
/**
* Whether to use cooke to get the information from request.
*
* @return if cookie should be used
* @see Builder#useCookie(Boolean)
*/
public boolean useCookie() {
return useCookie;
}
/**
* Cookie handler to create cookies or unset cookies for token.
*
* @return a new cookie handler
*/
public OidcCookieHandler tokenCookieHandler() {
return tokenCookieHandler;
}
/**
* Cookie handler to create cookies or unset cookies for id token.
*
* @return a new cookie handler
*/
public OidcCookieHandler idTokenCookieHandler() {
return idTokenCookieHandler;
}
/**
* Cookie handler to create cookies or unset cookies for tenant name.
*
* @return a new cookie handler
*/
public OidcCookieHandler tenantCookieHandler() {
return tenantCookieHandler;
}
/**
* Cookie handler to create cookies or unset cookies for refresh token.
*
* @return a new cookie handler
*/
public OidcCookieHandler refreshTokenCookieHandler() {
return refreshTokenCookieHandler;
}
/**
* Cookie handler to create cookies or unset cookies for state value.
*
* @return a new cookie handler
*/
public OidcCookieHandler stateCookieHandler() {
return stateCookieHandler;
}
/**
* Redirection URI.
*
* @return uri the OIDC server redirects back to
* @see Builder#redirectUri(String)
*/
public String redirectUri() {
return redirectUri;
}
/**
* Whether to force https when redirecting to identity provider.
*
* @return {@code true} to force use of https
*/
public boolean forceHttpsRedirects() {
return forceHttpsRedirects;
}
/**
* Whether logout is enabled.
*
* @return {@code true} if logout is enabled
*/
public boolean logoutEnabled() {
return logoutEnabled;
}
/**
* Logout URI.
*
* @return uri that processes logout in Helidon and redirects to OIDC server logout
* @see Builder#logoutUri(String)
*/
public String logoutUri() {
return logoutUri;
}
/**
* Post logout redirect URI.
*
* @return uri that OIDC server redirects to once logout is finished
* @see Builder#postLogoutUri(java.net.URI)
*/
public URI postLogoutUri() {
return postLogoutUri;
}
/**
* Redirect URI with host information.
*
* @return redirect URI
* @see Builder#redirectUri(String)
*/
public String redirectUriWithHost() {
if (frontendUri == null) {
throw new SecurityException("Frontend URI is not defined");
}
return frontendUri + redirectUri;
}
/**
* Redirect URI with host information taken from request,
* unless an explicit frontend uri is defined in configuration.
*
* @param frontendUri the frontend uri
* @return redirect URI
*/
public String redirectUriWithHost(String frontendUri) {
if (this.frontendUri != null) {
return redirectUriWithHost();
}
return frontendUri + this.redirectUri;
}
/**
* Whether to redirect to identity server if user is not authenticated.
*
* @return whether to redirect, defaults to true
*/
public boolean shouldRedirect() {
return redirect;
}
/**
* Name of the parameter used in state passed to OIDC to store the number of attempted redirects.
* This is to prevent infinite redirects.
*
* @return name of the query parameter
*/
public String redirectAttemptParam() {
return redirectAttemptParam;
}
/**
* Maximal number of redirects allowed between Helidon and OIDC provider.
*
* @return maximal number of redirects
*/
public int maxRedirects() {
return maxRedirects;
}
/**
* Cross-origin resource sharing settings.
*
* @return CORS settings
*/
public CrossOriginConfig crossOriginConfig() {
return crossOriginConfig;
}
/**
* Amount of time access token should be refreshed before its expiration time.
*
* @return refresh time skew
*/
public Duration tokenRefreshSkew() {
return tokenRefreshSkew;
}
/**
* Determines whether to force the use of relative URIs in all requests,
* regardless of the presence or absence of proxies or no-proxy lists.
*
* @return {@code true} if we should use relative URIs
*/
public boolean relativeUris() {
return relativeUris;
}
/**
* Client with configured proxy with no security.
*
* @return client for general use.
*/
public WebClient generalWebClient() {
return webClient;
}
/**
* Client with configured proxy and security.
*
* @return client for communicating with OIDC identity server
*/
public WebClient appWebClient() {
return defaultTenant.get().appWebClient();
}
/**
* Return {@link TenantConfig} bound to the provided tenant id.
* If no {@link TenantConfig} found, default OIDC configuration should be returned.
*
* @param tenantId tenant id of the configuration
* @return configuration bound to the tenant id, or default oidc configuration if not found
*/
public TenantConfig tenantConfig(String tenantId) {
TenantConfig tenantConfig = tenantConfigurations.get(tenantId);
if (tenantConfig == null) {
return tenantConfigurations.getOrDefault(TenantConfigFinder.DEFAULT_TENANT_ID, this);
}
return tenantConfig;
}
/**
* Token endpoint URI.
*
* @return endpoint URI
*/
public URI tokenEndpointUri() {
return defaultTenant.get().tokenEndpointUri();
}
/**
* Authorization endpoint.
*
* @return authorization endpoint uri as a string
*/
public String authorizationEndpointUri() {
return defaultTenant.get().authorizationEndpointUri();
}
/**
* Logout endpoint on OIDC server.
*
* @return URI of the logout endpoint
* @see OidcConfig.Builder#logoutEndpointUri(java.net.URI)
*/
public URI logoutEndpointUri() {
return defaultTenant.get().logoutEndpointUri();
}
/**
* Token issuer.
*
* @return token issuer
* @see OidcConfig.Builder#issuer(String)
*/
public String issuer() {
return defaultTenant.get().issuer();
}
/**
* JWK used for signature validation.
*
* @return set of keys used use to verify tokens
*/
public JwkKeys signJwk() {
return defaultTenant.get().signJwk();
}
/**
* Introspection endpoint URI.
*
* @return introspection endpoint URI
* @see OidcConfig.Builder#introspectEndpointUri(java.net.URI)
*/
public URI introspectUri() {
return defaultTenant.get().introspectUri();
}
/**
* Whether access token signature should be validated.
*
* @return validate access token signature
*/
public boolean tokenSignatureValidation() {
return tokenSignatureValidation;
}
/**
* Whether id token signature should be validated.
*
* @return validate id token signature
*/
public boolean idTokenSignatureValidation() {
return idTokenSignatureValidation;
}
/**
* Whether to check IP address access token was issued for.
*
* @return whether to check IP address access token was issued for
*/
public boolean accessTokenIpCheck() {
return accessTokenIpCheck;
}
Supplier webClientBuilderSupplier() {
return webClientBuilderSupplier;
}
/**
* Client Authentication methods that are used by Clients to authenticate to the Authorization
* Server when using the Token Endpoint.
*/
public enum ClientAuthentication {
/**
* Clients that have received a client_secret value from the Authorization Server authenticate with the Authorization
* Server in accordance with Section 2.3.1 of OAuth 2.0 [RFC6749] using the HTTP Basic authentication scheme.
* This is the default client authentication.
*/
CLIENT_SECRET_BASIC,
/**
* Clients that have received a client_secret value from the Authorization Server, authenticate with the Authorization
* Server in accordance with Section 2.3.1 of OAuth 2.0 [RFC6749] by including the Client Credentials in the request body.
*/
CLIENT_SECRET_POST,
/**
* Clients that have received a client_secret value from the Authorization Server create a JWT using an HMAC SHA
* algorithm, such as HMAC SHA-256. The HMAC (Hash-based Message Authentication Code) is calculated using the octets of
* the UTF-8 representation of the client_secret as the shared key.
* The Client authenticates in accordance with JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and
* Authorization Grants [OAuth.JWT] and Assertion Framework for OAuth 2.0 Client Authentication and Authorization
* Grants [OAuth.Assertions].
*
* The JWT MUST contain the following REQUIRED Claim Values and MAY contain the following
* OPTIONAL Claim Values.
*
* Required:
* {@code iss, sub, aud, jti, exp}
*
* Optional:
* {@code iat}
*/
CLIENT_SECRET_JWT,
/**
* Clients that have registered a public key sign a JWT using that key. The Client authenticates in accordance with
* JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants [OAuth.JWT] and Assertion
* Framework for OAuth 2.0 Client Authentication and Authorization Grants [OAuth.Assertions].
*
* The JWT MUST contain the following REQUIRED Claim Values and MAY contain the following
* OPTIONAL Claim Values.
*
* Required:
* {@code iss, sub, aud, jti, exp}
*
* Optional:
* {@code iat}
*/
PRIVATE_KEY_JWT,
/**
* The Client does not authenticate itself at the Token Endpoint, either because it uses only the Implicit Flow (and so
* does not use the Token Endpoint) or because it is a Public Client with no Client Secret or other authentication
* mechanism.
*/
NONE
}
/**
* Types of requests to identity provider.
*/
public enum RequestType {
/**
* Request to exchange code for a token issued against the token endpoint.
*/
CODE_TO_TOKEN,
/**
* Request to validate a JWT against an introspection endpoint.
*/
INTROSPECT_JWT;
}
/**
* A fluent API {@link io.helidon.common.Builder} to build instances of {@link OidcConfig}.
*/
@Configured(description = "Open ID Connect configuration")
public static class Builder extends BaseBuilder {
private final Map tenantConfigurations = new HashMap<>();
// mandatory properties
private String redirectUri = DEFAULT_REDIRECT_URI;
private String logoutUri = DEFAULT_LOGOUT_URI;
private boolean logoutEnabled = false;
// optional properties
private String frontendUri;
private boolean redirect = DEFAULT_REDIRECT;
private String redirectAttemptParam = DEFAULT_ATTEMPT_PARAM;
private int maxRedirects = DEFAULT_MAX_REDIRECTS;
private URI postLogoutUri;
private CrossOriginConfig crossOriginConfig;
private boolean forceHttpsRedirects = DEFAULT_FORCE_HTTPS_REDIRECTS;
private Duration tokenRefreshSkew = DEFAULT_TOKEN_REFRESH_SKEW;
private String proxyHost;
private String proxyProtocol = DEFAULT_PROXY_PROTOCOL;
private int proxyPort = DEFAULT_PROXY_PORT;
private WebClient webClient;
private Supplier webClientBuilderSupplier;
private String paramName = DEFAULT_PARAM_NAME;
private String idTokenParamName = DEFAULT_ID_TOKEN_PARAM_NAME;
private String tenantParamName = DEFAULT_TENANT_PARAM_NAME;
private boolean useHeader = DEFAULT_HEADER_USE;
private boolean useParam = DEFAULT_PARAM_USE;
private final OidcCookieHandler.Builder tenantCookieBuilder = OidcCookieHandler.builder()
.encryptionEnabled(true)
.cookieName(DEFAULT_TENANT_COOKIE_NAME);
private final OidcCookieHandler.Builder tokenCookieBuilder = OidcCookieHandler.builder()
.cookieName(DEFAULT_COOKIE_NAME);
private final OidcCookieHandler.Builder idTokenCookieBuilder = OidcCookieHandler.builder()
.encryptionEnabled(true)
.cookieName(DEFAULT_ID_COOKIE_NAME);
private final OidcCookieHandler.Builder refreshTokenCookieBuilder = OidcCookieHandler.builder()
.encryptionEnabled(true)
.cookieName(DEFAULT_REFRESH_COOKIE_NAME);
private final OidcCookieHandler.Builder stateCookieBuilder = OidcCookieHandler.builder()
.encryptionEnabled(true)
.cookieName(DEFAULT_STATE_COOKIE_NAME);
private TokenHandler headerHandler = TokenHandler.builder()
.tokenHeader("Authorization")
.tokenPrefix("bearer ")
.build();
private boolean useCookie = DEFAULT_COOKIE_USE;
private boolean cookieSameSiteDefault = true;
private boolean relativeUris = DEFAULT_RELATIVE_URIS;
private boolean tokenSignatureValidation = true;
private boolean idTokenSignatureValidation = true;
private boolean accessTokenIpCheck = true;
protected Builder() {
}
@Override
public OidcConfig build() {
buildConfiguration();
Errors.Collector collector = Errors.collector();
if (useCookie && logoutEnabled) {
if (postLogoutUri == null) {
collector.fatal("post-logout-uri must be defined when logout is enabled.");
}
}
// second set of validations
collector.collect().checkValid();
if (cookieSameSiteDefault && useCookie) {
// compare frontend and oidc endpoints to see if
// we should use lax or strict by default
if (identityUri() != null) {
String identityHost = identityUri().getHost();
if (frontendUri != null) {
String frontendHost = URI.create(frontendUri).getHost();
if (identityHost.equals(frontendHost)) {
LOGGER.log(Level.INFO, "As frontend host and identity host are equal, setting Same-Site policy"
+ " to Strict this can be overridden using configuration option of OIDC: "
+ "\"cookie-same-site\"");
this.tenantCookieBuilder.sameSite(SetCookie.SameSite.STRICT);
this.tokenCookieBuilder.sameSite(SetCookie.SameSite.STRICT);
this.idTokenCookieBuilder.sameSite(SetCookie.SameSite.STRICT);
}
}
}
}
this.webClientBuilderSupplier = () -> OidcUtil.webClientBaseBuilder(proxyProtocol,
proxyHost,
proxyPort,
relativeUris,
clientTimeout());
this.webClient = webClientBuilderSupplier.get().build();
if (!tokenSignatureValidation) {
LOGGER.log(Level.WARNING, "You have disabled access token signature validation. "
+ "This option should never be disabled for production environment "
+ "since it could cause security issues");
}
if (!idTokenSignatureValidation) {
LOGGER.log(Level.WARNING, "You have disabled id token signature validation. "
+ "This option should never be disabled for production environment "
+ "since it could cause security issues");
}
return new OidcConfig(this);
}
/**
* Update this builder with values from configuration.
*
* @param config configuration located on node with OIDC configuration keys (e.g. client-id)
* @return updated builder instance
*/
public Builder config(Config config) {
super.config(config);
// mandatory configuration
config.get("frontend-uri").asString().ifPresent(this::frontendUri);
// environment
config.get("proxy-protocol")
.asString()
.ifPresent(this::proxyProtocol);
config.get("proxy-host").asString().ifPresent(this::proxyHost);
config.get("proxy-port").asInt().ifPresent(this::proxyPort);
config.get("relative-uris").asBoolean().ifPresent(this::relativeUris);
// token handling
config.get("query-param-use").asBoolean().ifPresent(this::useParam);
config.get("query-param-name").asString().ifPresent(this::paramName);
config.get("query-param-tenant-name").asString().ifPresent(this::paramTenantName);
config.get("header-use").asBoolean().ifPresent(this::useHeader);
config.get("header-token").as(TokenHandler.class).ifPresent(this::headerTokenHandler);
config.get("cookie-use").asBoolean().ifPresent(this::useCookie);
config.get("cookie-name").asString().ifPresent(this::cookieName);
config.get("cookie-name-id-token").asString().ifPresent(this::cookieNameIdToken);
config.get("cookie-name-tenant").asString().ifPresent(this::cookieTenantName);
config.get("cookie-name-refresh-token").asString().ifPresent(this::cookieNameRefreshToken);
config.get("cookie-name-state").asString().ifPresent(this::cookieNameState);
config.get("cookie-domain").asString().ifPresent(this::cookieDomain);
config.get("cookie-path").asString().ifPresent(this::cookiePath);
config.get("cookie-max-age-seconds").asLong().ifPresent(this::cookieMaxAgeSeconds);
config.get("cookie-http-only").asBoolean().ifPresent(this::cookieHttpOnly);
config.get("cookie-secure").asBoolean().ifPresent(this::cookieSecure);
config.get("cookie-same-site").asString().ifPresent(this::cookieSameSite);
// encryption of cookies
config.get("cookie-encryption-enabled").asBoolean().ifPresent(this::cookieEncryptionEnabled);
config.get("cookie-encryption-id-enabled").asBoolean().ifPresent(this::cookieEncryptionEnabledIdToken);
config.get("cookie-encryption-tenant-enabled").asBoolean().ifPresent(this::cookieEncryptionEnabledTenantName);
config.get("cookie-encryption-refresh-enabled").asBoolean().ifPresent(this::cookieEncryptionEnabledRefreshToken);
config.get("cookie-encryption-state-enabled").asBoolean().ifPresent(this::cookieEncryptionEnabledState);
config.get("cookie-encryption-password").as(String.class)
.map(String::toCharArray)
.ifPresent(this::cookieEncryptionPassword);
config.get("cookie-encryption-name").asString().ifPresent(this::cookieEncryptionName);
// our application
config.get("redirect-uri").asString().ifPresent(this::redirectUri);
config.get("logout-uri").asString().ifPresent(this::logoutUri);
config.get("post-logout-uri").as(URI.class).ifPresent(this::postLogoutUri);
config.get("logout-enabled").asBoolean().ifPresent(this::logoutEnabled);
config.get("redirect").asBoolean().ifPresent(this::redirect);
config.get("redirect-attempt-param").asString().ifPresent(this::redirectAttemptParam);
config.get("max-redirects").asInt().ifPresent(this::maxRedirects);
config.get("force-https-redirects").asBoolean().ifPresent(this::forceHttpsRedirects);
config.get("cors").map(CrossOriginConfig::create).ifPresent(this::crossOriginConfig);
config.get("token-refresh-before-expiration").as(Duration.class).ifPresent(this::tokenRefreshSkew);
config.get("token-signature-validation").asBoolean().ifPresent(this::tokenSignatureValidation);
config.get("id-token-signature-validation").asBoolean().ifPresent(this::idTokenSignatureValidation);
config.get("access-token-ip-check").asBoolean().ifPresent(this::accessTokenIpCheck);
config.get("tenants").asList(Config.class)
.ifPresent(confList -> confList.forEach(tenantConfig -> tenantFromConfig(config, tenantConfig)));
return this;
}
private void tenantFromConfig(Config defaultConfig, Config tenantConfig) {
addTenantConfig(TenantConfig.tenantBuilder().config(defaultConfig).config(tenantConfig).build());
}
/**
* Amount of time access token should be refreshed before its expiration time.
* Default is 5 seconds.
*
* @param tokenRefreshSkew time to refresh token before expiration
* @return updated builder
*/
public Builder tokenRefreshSkew(Duration tokenRefreshSkew) {
this.tokenRefreshSkew = tokenRefreshSkew;
return this;
}
/**
* Assign cross-origin resource sharing settings.
*
* @param crossOriginConfig cross-origin settings to apply to the redirect endpoint
* @return updated builder instance
*/
@ConfiguredOption(key = "cors")
public Builder crossOriginConfig(CrossOriginConfig crossOriginConfig) {
this.crossOriginConfig = crossOriginConfig;
return this;
}
/**
* Whether to enable logout support.
* When logout is enabled, we use two cookies (User token and user ID token) and we expose
* an endpoint {@link #logoutUri(String)} that can be used to log the user out from Helidon session
* and also from OIDC session (uses {@link #logoutEndpointUri(java.net.URI)} on OIDC server).
* Logout support is disabled by default.
*
* @param logoutEnabled whether to enable logout
* @return updated builder instance
*/
public Builder logoutEnabled(Boolean logoutEnabled) {
this.logoutEnabled = logoutEnabled;
return this;
}
/**
* By default, the client should redirect to the identity server for the user to log in.
* This behavior can be overridden by setting redirect to false. When token is not present in the request, the client
* will not redirect and just return appropriate error response code.
*
* @param redirect Whether to redirect to OIDC server in case the request does not contain sufficient information to
* authenticate the user, defaults to true
* @return updated builder instance
*/
@ConfiguredOption("false")
public Builder redirect(boolean redirect) {
this.redirect = redirect;
return this;
}
/**
* Full URI of this application that is visible from user browser.
* Used to redirect request back from identity server after successful login.
*
* @param uri the frontend URI, such as "http://my.server.com/myApp
* @return updated builder instance
*/
@ConfiguredOption
public Builder frontendUri(String uri) {
this.frontendUri = uri;
return this;
}
/**
* Force HTTPS for redirects to identity provider.
* Defaults to {@code false}.
*
* @param forceHttpsRedirects flag to redirect with https
* @return updated builder instance
*/
@ConfiguredOption("false")
public Builder forceHttpsRedirects(boolean forceHttpsRedirects) {
this.forceHttpsRedirects = forceHttpsRedirects;
return this;
}
/**
* Can be set to {@code true} to force the use of relative URIs in all requests,
* regardless of the presence or absence of proxies or no-proxy lists. By default,
* requests that use the Proxy will have absolute URIs. Set this flag to {@code true}
* if the host is unable to accept absolute URIs.
* Defaults to {@value #DEFAULT_RELATIVE_URIS}.
*
* @param relativeUris relative URIs flag
* @return updated builder instance
*/
@ConfiguredOption("false")
public Builder relativeUris(boolean relativeUris) {
this.relativeUris = relativeUris;
return this;
}
/**
* URI to register web server component on, used by the OIDC server to
* redirect authorization requests to after a user logs in or approves
* scopes.
* Note that usually the redirect URI configured here must be the
* same one as configured on OIDC server.
*
*
* Defaults to {@value #DEFAULT_REDIRECT_URI}
*
* @param redirectUri the URI (path without protocol, host and port) used to redirect requests back to us
* @return updated builder instance
*/
@ConfiguredOption(value = DEFAULT_REDIRECT_URI)
public Builder redirectUri(String redirectUri) {
this.redirectUri = redirectUri;
return this;
}
/**
* Path to register web server for logout link.
* This should be used by application to redirect user to logout the current user
* from Helidon based session (when using cookies and redirection).
* This endpoint will logout user from Helidon session (remove Helidon cookies) and redirect user to
* logout screen of the OIDC server.
*
* @param logoutUri URI path for logout component
* @return updated builder instance
*/
public Builder logoutUri(String logoutUri) {
this.logoutUri = logoutUri;
return this;
}
/**
* URI to redirect to once the logout process is done.
* The endpoint should not be protected by OIDC (as this would serve no purpose, just to log the user in again).
* This endpoint usually must be registered with the application as the allowed post-logout redirect URI.
* Note that the URI should not contain any query parameters. You can obtain state using the
* state query parameter that must be provided to {@link #logoutUri(String)}.
*
* @param uri this will be used by the OIDC server to redirect user to once logout is done, can define just path,
* in which case the scheme, host and port will be taken from request.
* @return updated builder instance
*/
public Builder postLogoutUri(URI uri) {
this.postLogoutUri = uri;
return this;
}
/**
* Configure the parameter used to store the number of attempts in redirect.
*
* Defaults to {@value #DEFAULT_ATTEMPT_PARAM}
*
* @param paramName name of the parameter used in the state parameter
* @return updated builder instance
*/
@ConfiguredOption(value = DEFAULT_ATTEMPT_PARAM)
public Builder redirectAttemptParam(String paramName) {
this.redirectAttemptParam = paramName;
return this;
}
/**
* Configure maximal number of redirects when redirecting to an OIDC provider within a single authentication
* attempt.
*
* Defaults to {@value #DEFAULT_MAX_REDIRECTS}
*
* @param maxRedirects maximal number of redirects from Helidon to OIDC provider
* @return updated builder instance
*/
@ConfiguredOption("5")
public Builder maxRedirects(int maxRedirects) {
this.maxRedirects = maxRedirects;
return this;
}
/**
* Proxy protocol to use when proxy is used.
* Defaults to {@value DEFAULT_PROXY_PROTOCOL}.
*
* @param protocol protocol to use (such as https)
* @return updated builder instance
*/
@ConfiguredOption(value = DEFAULT_PROXY_PROTOCOL)
public Builder proxyProtocol(String protocol) {
this.proxyProtocol = protocol;
return this;
}
/**
* Proxy host to use. When defined, triggers usage of proxy for HTTP requests.
* Setting to empty String has the same meaning as setting to null - disables proxy.
*
* @param proxyHost host of the proxy
* @return updated builder instance
* @see #proxyProtocol(String)
* @see #proxyPort(int)
*/
@ConfiguredOption
public Builder proxyHost(String proxyHost) {
if ((proxyHost == null) || proxyHost.isEmpty()) {
this.proxyHost = null;
} else {
this.proxyHost = proxyHost;
}
return this;
}
/**
* Proxy port.
* Defaults to {@value DEFAULT_PROXY_PORT}
*
* @param proxyPort port of the proxy server to use
* @return updated builder instance
*/
@ConfiguredOption("80")
public Builder proxyPort(int proxyPort) {
this.proxyPort = proxyPort;
return this;
}
/**
* A {@link TokenHandler} to
* process header containing a JWT.
* Default is "Authorization" header with a prefix "bearer ".
*
* @param tokenHandler token handler to use
* @return updated builder instance
*/
@ConfiguredOption(key = "header-token")
public Builder headerTokenHandler(TokenHandler tokenHandler) {
this.headerHandler = tokenHandler;
return this;
}
/**
* Whether to expect JWT in a header field.
*
* @param useHeader set to true to use a header extracted with {@link #headerTokenHandler(TokenHandler)}
* @return updated builder instance
*/
@ConfiguredOption(key = "header-use", value = "true")
public Builder useHeader(Boolean useHeader) {
this.useHeader = useHeader;
return this;
}
/**
* Name of a query parameter that contains the JWT access token when parameter is used.
*
* @param paramName name of the query parameter to expect
* @return updated builder instance
*/
@ConfiguredOption(key = "query-param-name", value = DEFAULT_PARAM_NAME)
public Builder paramName(String paramName) {
this.paramName = paramName;
return this;
}
/**
* Name of a query parameter that contains the JWT id token when parameter is used.
*
* @param idTokenParamName name of the query parameter to expect
* @return updated builder instance
*/
@ConfiguredOption(key = "query-id-token-param-name", value = DEFAULT_ID_TOKEN_PARAM_NAME)
public Builder idTokenParamName(String idTokenParamName) {
this.idTokenParamName = idTokenParamName;
return this;
}
/**
* Name of a query parameter that contains the tenant name when the parameter is used.
* Defaults to {@link #DEFAULT_TENANT_PARAM_NAME}.
*
* @param paramName name of the query parameter to expect
* @return updated builder instance
*/
@ConfiguredOption(key = "query-param-tenant-name", value = DEFAULT_TENANT_PARAM_NAME)
public Builder paramTenantName(String paramName) {
this.tenantParamName = paramName;
return this;
}
/**
* Whether to use a query parameter to send JWT token from application to this
* server.
*
* @param useParam whether to use a query parameter (true) or not (false)
* @return updated builder instance
* @see #paramName(String)
*/
@ConfiguredOption(key = "query-param-use", value = "false")
public Builder useParam(Boolean useParam) {
this.useParam = useParam;
return this;
}
/**
* Name of the encryption configuration available through {@link Security#encrypt(String, byte[])} and
* {@link Security#decrypt(String, String)}.
* If configured and encryption is enabled for any cookie,
* Security MUST be configured in global or current {@code io.helidon.common.context.Context} (this
* is done automatically in Helidon MP).
*
* @param cookieEncryptionName name of the encryption configuration in security used to encrypt/decrypt cookies
* @return updated builder
*/
@ConfiguredOption
public Builder cookieEncryptionName(String cookieEncryptionName) {
this.tokenCookieBuilder.encryptionName(cookieEncryptionName);
this.idTokenCookieBuilder.encryptionName(cookieEncryptionName);
this.tenantCookieBuilder.encryptionName(cookieEncryptionName);
this.refreshTokenCookieBuilder.encryptionName(cookieEncryptionName);
this.stateCookieBuilder.encryptionName(cookieEncryptionName);
return this;
}
/**
* Master password for encryption/decryption of cookies. This must be configured to the same value on each microservice
* using the cookie.
*
* @param cookieEncryptionPassword encryption password
* @return updated builder
*/
@ConfiguredOption
public Builder cookieEncryptionPassword(char[] cookieEncryptionPassword) {
this.tokenCookieBuilder.encryptionPassword(cookieEncryptionPassword);
this.idTokenCookieBuilder.encryptionPassword(cookieEncryptionPassword);
this.tenantCookieBuilder.encryptionPassword(cookieEncryptionPassword);
this.refreshTokenCookieBuilder.encryptionPassword(cookieEncryptionPassword);
this.stateCookieBuilder.encryptionPassword(cookieEncryptionPassword);
return this;
}
/**
* Whether to encrypt token cookie created by this microservice.
* Defaults to {@code false}.
*
* @param cookieEncryptionEnabled whether cookie should be encrypted {@code true}, or as obtained from
* OIDC server {@code false}
* @return updated builder instance
*/
@ConfiguredOption(value = "false")
public Builder cookieEncryptionEnabled(boolean cookieEncryptionEnabled) {
this.tokenCookieBuilder.encryptionEnabled(cookieEncryptionEnabled);
return this;
}
/**
* Whether to encrypt id token cookie created by this microservice.
* Defaults to {@code true}.
*
* @param cookieEncryptionEnabled whether cookie should be encrypted {@code true}, or as obtained from
* OIDC server {@code false}
* @return updated builder instance
*/
@ConfiguredOption(key = "cookie-encryption-id-enabled", value = "true")
public Builder cookieEncryptionEnabledIdToken(boolean cookieEncryptionEnabled) {
this.idTokenCookieBuilder.encryptionEnabled(cookieEncryptionEnabled);
return this;
}
/**
* Whether to encrypt tenant name cookie created by this microservice.
* Defaults to {@code true}.
*
* @param cookieEncryptionEnabled whether cookie should be encrypted {@code true}, or as plain text name {@code false}
* @return updated builder instance
*/
@ConfiguredOption(key = "cookie-encryption-tenant-enabled", value = "true")
public Builder cookieEncryptionEnabledTenantName(boolean cookieEncryptionEnabled) {
this.tenantCookieBuilder.encryptionEnabled(cookieEncryptionEnabled);
return this;
}
/**
* Whether to encrypt refresh token cookie created by this microservice.
* Defaults to {@code true}.
*
* @param cookieEncryptionEnabled whether cookie should be encrypted {@code true}, or as obtained from
* OIDC server {@code false}
* @return updated builder instance
*/
@ConfiguredOption(key = "cookie-encryption-refresh-enabled", value = "true")
public Builder cookieEncryptionEnabledRefreshToken(boolean cookieEncryptionEnabled) {
this.refreshTokenCookieBuilder.encryptionEnabled(cookieEncryptionEnabled);
return this;
}
/**
* Whether to encrypt state cookie created by this microservice.
* Defaults to {@code true}.
*
* @param cookieEncryptionEnabled whether cookie should be encrypted {@code true}, or as sent to
* OIDC server {@code false}
* @return updated builder instance
*/
@ConfiguredOption(key = "cookie-encryption-state-enabled", value = "true")
public Builder cookieEncryptionEnabledState(boolean cookieEncryptionEnabled) {
this.stateCookieBuilder.encryptionEnabled(cookieEncryptionEnabled);
return this;
}
/**
* When using cookie, used to set the SameSite cookie value. Can be
* "Strict" or "Lax"
*
* @param sameSite SameSite cookie attribute value
* @return updated builder instance
*/
public Builder cookieSameSite(String sameSite) {
return cookieSameSite(SetCookie.SameSite.valueOf(sameSite.toUpperCase(Locale.ROOT)));
}
/**
* When using cookie, used to set the SameSite cookie value. Can be
* "Strict" or "Lax".
*
* @param sameSite SameSite cookie attribute
* @return updated builder instance
*/
@ConfiguredOption(value = "LAX")
public Builder cookieSameSite(SetCookie.SameSite sameSite) {
this.tokenCookieBuilder.sameSite(sameSite);
this.idTokenCookieBuilder.sameSite(sameSite);
this.tenantCookieBuilder.sameSite(sameSite);
this.refreshTokenCookieBuilder.sameSite(sameSite);
this.stateCookieBuilder.sameSite(sameSite);
this.cookieSameSiteDefault = false;
return this;
}
/**
* When using cookie, if set to true, the Secure attribute will be configured.
* Defaults to false.
*
* @param secure whether the cookie should be secure (true) or not (false)
* @return updated builder instance
*/
@ConfiguredOption("false")
public Builder cookieSecure(Boolean secure) {
this.tokenCookieBuilder.secure(secure);
this.idTokenCookieBuilder.secure(secure);
this.tenantCookieBuilder.secure(secure);
this.refreshTokenCookieBuilder.secure(secure);
this.stateCookieBuilder.secure(secure);
return this;
}
/**
* When using cookie, if set to true, the HttpOnly attribute will be configured.
* Defaults to {@value OidcCookieHandler.Builder#DEFAULT_HTTP_ONLY}.
*
* @param httpOnly whether the cookie should be HttpOnly (true) or not (false)
* @return updated builder instance
*/
@ConfiguredOption("true")
public Builder cookieHttpOnly(Boolean httpOnly) {
this.tokenCookieBuilder.httpOnly(httpOnly);
this.idTokenCookieBuilder.httpOnly(httpOnly);
this.tenantCookieBuilder.httpOnly(httpOnly);
this.refreshTokenCookieBuilder.httpOnly(httpOnly);
this.stateCookieBuilder.httpOnly(httpOnly);
return this;
}
/**
* When using cookie, used to set MaxAge attribute of the cookie, defining how long
* the cookie is valid.
* Not used by default.
*
* @param age age in seconds
* @return updated builder instance
*/
@ConfiguredOption
public Builder cookieMaxAgeSeconds(long age) {
this.tokenCookieBuilder.maxAge(age);
this.idTokenCookieBuilder.maxAge(age);
this.tenantCookieBuilder.maxAge(age);
this.refreshTokenCookieBuilder.maxAge(age);
this.stateCookieBuilder.maxAge(age);
return this;
}
/**
* Path the cookie is valid for.
* Defaults to "/".
*
* @param path the path to use as value of cookie "Path" attribute
* @return updated builder instance
*/
@ConfiguredOption(value = OidcCookieHandler.Builder.DEFAULT_PATH)
public Builder cookiePath(String path) {
this.tokenCookieBuilder.path(path);
this.idTokenCookieBuilder.path(path);
this.tenantCookieBuilder.path(path);
this.refreshTokenCookieBuilder.path(path);
this.stateCookieBuilder.path(path);
return this;
}
/**
* Domain the cookie is valid for.
* Not used by default.
*
* @param domain domain to use as value of cookie "Domain" attribute
* @return updated builder instance
*/
@ConfiguredOption
public Builder cookieDomain(String domain) {
this.tokenCookieBuilder.domain(domain);
this.idTokenCookieBuilder.domain(domain);
this.tenantCookieBuilder.domain(domain);
this.refreshTokenCookieBuilder.domain(domain);
this.stateCookieBuilder.domain(domain);
return this;
}
/**
* Name of the cookie to use.
* Defaults to {@value #DEFAULT_COOKIE_NAME}.
*
* @param cookieName name of a cookie
* @return updated builder instance
*/
@ConfiguredOption(value = DEFAULT_COOKIE_NAME)
public Builder cookieName(String cookieName) {
this.tokenCookieBuilder.cookieName(cookieName);
return this;
}
/**
* Name of the cookie to use for id token.
* Defaults to {@value #DEFAULT_COOKIE_NAME}_2.
*
* This cookie is only used when logout is enabled, as otherwise it is not needed.
* Content of this cookie is encrypted.
*
* @param cookieName name of a cookie
* @return updated builder instance
*/
@ConfiguredOption(DEFAULT_ID_COOKIE_NAME)
public Builder cookieNameIdToken(String cookieName) {
this.idTokenCookieBuilder.cookieName(cookieName);
return this;
}
/**
* The name of the cookie to use for the tenant name.
* Defaults to {@value #DEFAULT_TENANT_COOKIE_NAME}.
*
* @param cookieName name of a cookie
* @return updated builder instance
*/
@ConfiguredOption(key = "cookie-name-tenant", value = DEFAULT_TENANT_COOKIE_NAME)
public Builder cookieTenantName(String cookieName) {
this.tenantCookieBuilder.cookieName(cookieName);
return this;
}
/**
* The name of the cookie to use for the refresh token.
* Defaults to {@value #DEFAULT_REFRESH_COOKIE_NAME}.
*
* @param cookieName name of a cookie
* @return updated builder instance
*/
@ConfiguredOption(DEFAULT_REFRESH_COOKIE_NAME)
public Builder cookieNameRefreshToken(String cookieName) {
this.refreshTokenCookieBuilder.cookieName(cookieName);
return this;
}
/**
* The name of the cookie to use for the state storage.
* Defaults to {@value #DEFAULT_STATE_COOKIE_NAME}.
*
* @param cookieName name of a cookie
* @return updated builder instance
*/
@ConfiguredOption(DEFAULT_REFRESH_COOKIE_NAME)
public Builder cookieNameState(String cookieName) {
this.stateCookieBuilder.cookieName(cookieName);
return this;
}
/**
* Whether to use cookie to store JWT between requests.
* Defaults to {@value #DEFAULT_COOKIE_USE}.
*
* @param useCookie whether to use cookie to store JWT (true) or not (false))
* @return updated builder instance
*/
@ConfiguredOption(key = "cookie-use", value = "true")
public Builder useCookie(Boolean useCookie) {
this.useCookie = useCookie;
return this;
}
/**
* Add specific {@link TenantConfig} instance.
*
* @param tenantConfig tenant configuration
* @return updated builder instance
*/
@ConfiguredOption(key = "tenants", type = TenantConfig.class, description = "Configurations of the tenants")
public Builder addTenantConfig(TenantConfig tenantConfig) {
tenantConfigurations.put(tenantConfig.name(), tenantConfig);
return this;
}
/**
* Whether access token signature check should be enabled.
* Signature check is enabled by default, and it is highly recommended to not change that.
* Change this setting only when you really know what you are doing, otherwise it could case security issues.
*
* @param enabled whether access token signature check is enabled
* @return updated builder instance
*/
@ConfiguredOption("true")
public Builder tokenSignatureValidation(boolean enabled) {
tokenSignatureValidation = enabled;
return this;
}
/**
* Whether id token signature check should be enabled.
* Signature check is enabled by default, and it is highly recommended to not change that.
* Change this setting only when you really know what you are doing, otherwise it could case security issues.
*
* @param enabled whether id token signature check is enabled
* @return updated builder instance
*/
@ConfiguredOption("true")
public Builder idTokenSignatureValidation(boolean enabled) {
idTokenSignatureValidation = enabled;
return this;
}
/**
* Whether to check if current IP address matches the one access token was issued for.
* This check helps with cookie replay attack prevention.
*
* @param enabled whether to check if current IP address matches the one access token was issued for
* @return updated builder instance
*/
@ConfiguredOption("true")
public Builder accessTokenIpCheck(boolean enabled) {
accessTokenIpCheck = enabled;
return this;
}
}
}