All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.c4_soft.springaddons.security.oidc.starter.properties.SpringAddonsOidcClientProperties Maven / Gradle / Ivy

The newest version!
package com.c4_soft.springaddons.security.oidc.starter.properties;

import java.net.URI;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.springframework.http.HttpStatus;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.server.ServerAuthenticationEntryPoint;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponentsBuilder;
import lombok.Data;

/**
 * Auto-configuration for an OAuth2 client (secured with session, not access token)
 * Security(Web)FilterChain with @Order(Ordered.LOWEST_PRECEDENCE - 1). Typical use-cases are
 * spring-cloud-gateway used as BFF and applications with Thymeleaf or another server-side rendering
 * framework. Default configuration includes: enabled sessions, CSRF protection, "oauth2Login",
 * "logout". securityMatchers must be set for this filter-chain @Bean and its dependencies to be
 * defined. Properties defined here are a complement for spring.security.oauth2.client.*
 * (which are required when enabling spring-addons client filter-chain).
 *
 * @author Jerome Wacongne ch4mp@c4-soft.com
 */
@Data
public class SpringAddonsOidcClientProperties {
  public static final String RESPONSE_STATUS_HEADER = "X-RESPONSE-STATUS";

  public static final String POST_AUTHENTICATION_SUCCESS_URI_HEADER = "X-POST-LOGIN-SUCCESS-URI";
  public static final String POST_AUTHENTICATION_SUCCESS_URI_PARAM = "post_login_success_uri";
  public static final String POST_AUTHENTICATION_SUCCESS_URI_SESSION_ATTRIBUTE =
      POST_AUTHENTICATION_SUCCESS_URI_PARAM;

  public static final String POST_AUTHENTICATION_FAILURE_URI_HEADER = "X-POST-LOGIN-FAILURE-URI";
  public static final String POST_AUTHENTICATION_FAILURE_URI_PARAM = "post_login_failure_uri";
  public static final String POST_AUTHENTICATION_FAILURE_URI_SESSION_ATTRIBUTE =
      POST_AUTHENTICATION_FAILURE_URI_PARAM;
  public static final String POST_AUTHENTICATION_FAILURE_CAUSE_ATTRIBUTE = "error";

  public static final String POST_LOGOUT_SUCCESS_URI_HEADER = "X-POST-LOGOUT-SUCCESS-URI";
  public static final String POST_LOGOUT_SUCCESS_URI_PARAM = "post_logout_success_uri";

  /**
   * Path matchers for the routes secured with the auto-configured client filter-chain. If left
   * empty, OAuth2 client auto-configuration is disabled. It should include "/login/**" and
   * "/oauth2/**" for login process. Can be set to "/**" to intercept all requests (OAuth2 client
   * only application, no REST API secured with access tokens).
   */
  private List securityMatchers = List.of();

  /**
   * Fully qualified URI of the configured OAuth2 client.
   */
  private URI clientUri = URI.create("/");

  /**
   * URI at which a login can be performed. If left empty, ${client-uri}/login is used. Can be
   * changed to the URI on a SPA or a mobile application deep-link
   */
  private Optional loginUri = Optional.empty();

  /**
   * URI containing scheme, host and port where the user should be redirected after a successful
   * login (defaults to the client URI)
   */
  private Optional postLoginRedirectHost = Optional.empty();

  /**
   * Where to redirect the user after successful login
   */
  private Optional postLoginRedirectPath = Optional.empty();

  /**
   * Where to redirect the user after login failure
   */
  private Optional loginErrorRedirectPath = Optional.empty();

  /**
   * HTTP status for redirections in OAuth2 login and logout. You might set this to something in 2xx
   * range (like OK, ACCEPTED, NO_CONTENT, ...) for single page and mobile applications to handle
   * this redirection as it wishes (change the user-agent, clear some headers, ...).
   */
  private OAuth2RedirectionProperties oauth2Redirections = new OAuth2RedirectionProperties();

  public URI getPostLoginRedirectHost() {
    return postLoginRedirectHost.orElse(clientUri);
  }

  public Optional getPostLoginRedirectUri() {
    if (postLoginRedirectHost.isEmpty() && postLoginRedirectPath.isEmpty()) {
      return Optional.empty();
    }
    final var uri = UriComponentsBuilder.fromUri(getPostLoginRedirectHost());
    postLoginRedirectPath.ifPresent(uri::path);

    return Optional.of(uri.build(Map.of()));
  }

  /**
   * URI containing scheme, host and port where the user should be redirected after a successful
   * logout (defaults to the client URI)
   */
  private Optional postLogoutRedirectHost = Optional.empty();

  /**
   * Path (relative to clientUri) where the user should be redirected after being logged out from
   * authorization server(s)
   */
  private Optional postLogoutRedirectPath = Optional.empty();

  public URI getPostLogoutRedirectHost() {
    return postLogoutRedirectHost.orElse(clientUri);
  }

  public URI getPostLogoutRedirectUri() {
    var uri = UriComponentsBuilder.fromUri(getPostLogoutRedirectHost());
    postLogoutRedirectPath.ifPresent(uri::path);

    return uri.build(Map.of());
  }

  /**
   * Map of logout properties indexed by client registration ID (must match a registration in Spring
   * Boot OAuth2 client configuration). {@link OAuth2LogoutProperties} are configuration for
   * authorization server not strictly following the
   * RP-Initiated Logout
   * standard, but exposing a logout end-point expecting an authorized GET request with following
   * request params:
   * 
    *
  • "client-id" (required)
  • *
  • post-logout redirect URI (optional)
  • *
*/ private Map oauth2Logout = new HashMap<>(); /** *

* If true, AOP is used to instrument authorized client repository and keep the principalName * current user has for each issuer he authenticates on. *

*

* This is useful only if you allow a user to authenticate on more than one OpenID Provider at a * time. For instance, user logs in on Google and on an authorization server of your own and your * client sends direct queries to Google APIs (with an access token issued by Google) and resource * servers of your own (with an access token from your authorization server). *

*/ private boolean multiTenancyEnabled = false; /** * Path matchers for the routes accessible to anonymous requests */ private List permitAll = List.of("/login/**", "/oauth2/**"); /** * CSRF protection configuration for the auto-configured client filter-chain */ private Csrf csrf = Csrf.DEFAULT; /** * When true, PKCE is enabled (by default, Spring enables it only for "public" clients) */ private boolean pkceForced = false; /** * Fine grained CORS configuration * * @deprecated use com.c4-soft.springaddons.oidc.cors instead */ @Deprecated(forRemoval = true) private List cors = List.of(); /** * Additional parameters to send with authorization request, mapped by client registration IDs * * @deprecated use the more concise authorization-params syntax */ @Deprecated private Map> authorizationRequestParams = new HashMap<>(); /** *

* Additional parameters to send with authorization request, mapped by client registration IDs. *

*

* {@link OAuth2AuthorizationRequest#getAdditionalParameters()} return a Map<String, * Object>, when it should probably be Map<String, List<String>>. Also the * serializer does not handle collections correctly (serializes using {@link Object#toString()} * instead of repeating the parameter with each value toString()). What spring-addons does is * joining the String values with a comma. *

*/ private Map>> authorizationParams = new HashMap<>(); public MultiValueMap getExtraAuthorizationParameters(String registrationId) { return getExtraParameters(registrationId, authorizationRequestParams, authorizationParams); } /** * Additional parameters to send with token request, mapped by client registration IDs * * @deprecated use the more concise token-params syntax */ @Deprecated private Map> tokenRequestParams = new HashMap<>(); /** * Additional parameters to send with authorization request, mapped by client registration IDs */ private Map>> tokenParams = new HashMap<>(); public MultiValueMap getExtraTokenParameters(String registrationId) { return getExtraParameters(registrationId, tokenRequestParams, tokenParams); } private static MultiValueMap getExtraParameters(String registrationId, Map> requestParams, Map>> requestParamsMap) { final var extraParameters = Optional.ofNullable(requestParamsMap.get(registrationId)) .map(LinkedMultiValueMap::new).orElse(new LinkedMultiValueMap<>()); for (final var param : requestParams.getOrDefault(registrationId, List.of())) { if (StringUtils.hasText(param.getName())) { extraParameters.add(param.getName(), param.getValue()); } } return extraParameters; } /** * Logout properties for OpenID Providers which do not implement the RP-Initiated Logout spec * * @author Jerome Wacongne ch4mp@c4-soft.com */ @Data public static class OAuth2LogoutProperties { /** * URI on the authorization server where to redirect the user for logout */ private URI uri; /** * request param name for client-id */ private Optional clientIdRequestParam = Optional.empty(); /** * request param name for post-logout redirect URI (where the user should be redirected after * his session is closed on the authorization server) */ private Optional postLogoutUriRequestParam = Optional.empty(); /** * request param name for setting an ID-Token hint */ private Optional idTokenHintRequestParam = Optional.empty(); /** * RP-Initiated Logout is enabled by default. Setting this to false disables it. */ private boolean enabled = true; } private BackChannelLogoutProperties backChannelLogout = new BackChannelLogoutProperties(); @Data public static class BackChannelLogoutProperties { private boolean enabled = false; /** * The URI for a loop of the Spring client to itself in which it actually ends the user session. * Overriding this can be useful to force the scheme and port in the case where the client is * behind a reverse proxy with different scheme and port (default URI uses the original * Back-Channel Logout request scheme and ports). */ private Optional internalLogoutUri = Optional.empty(); private Optional cookieName = Optional.empty(); } /** * Request parameter * * @author Jerome Wacongne ch4mp@c4-soft.com */ @Data public static class RequestParam { /** * request parameter name */ private String name; /** * request parameter value */ private String value; } @Data public static class OAuth2RedirectionProperties { /** * Defines {@link AuthenticationEntryPoint} or {@link ServerAuthenticationEntryPoint} behavior */ private HttpStatus authenticationEntryPoint = HttpStatus.FOUND; /** * Status for the 1st response in authorization code flow, with location to get authorization * code from authorization server */ private HttpStatus preAuthorizationCode = HttpStatus.FOUND; /** * Status for the response after authorization code, with location to the UI */ private HttpStatus postAuthorizationCode = HttpStatus.FOUND; /** * Status for the response after an authorization failure */ private HttpStatus postAuthorizationFailure = HttpStatus.FOUND; /** * Status for the response after BFF logout, with location to authorization server logout * endpoint */ private HttpStatus rpInitiatedLogout = HttpStatus.FOUND; /** * Used only in servlet applications */ private HttpStatus invalidSessionStrategy = HttpStatus.FOUND; } public Optional getLogoutProperties(String clientRegistrationId) { return Optional.ofNullable(oauth2Logout.get(clientRegistrationId)); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy