diff --git a/README.MD b/README.MD index 73f9514ea..99c598240 100644 --- a/README.MD +++ b/README.MD @@ -482,7 +482,7 @@ These starters are designed to push auto-configuration one step further. In most I could forget to update README before releasing, so please refer to [maven central](https://repo1.maven.org/maven2/com/c4-soft/springaddons/spring-addons/) to pick latest available release ```xml - 7.3.3 + 7.3.4 @@ -518,9 +518,12 @@ I could forget to update README before releasing, so please refer to [maven cent ### 5.1. `7.x` Branch +### `7.3.4` +- [gh-178](https://github.com/ch4mpy/spring-addons/issues/178) `authorization-request-params` ignored in reactive applications + ### `7.3.3` -- [gh-176](https://github.com/ch4mpy/spring-addons/issues/176) Exception thrown when post-logout-redirect-path is null -- [gh-177](https://github.com/ch4mpy/spring-addons/issues/177) post-login-success-uri params and headers on authentication request are ignored +- [gh-176](https://github.com/ch4mpy/spring-addons/issues/176) Exception thrown when `post-logout-redirect-path` is null +- [gh-177](https://github.com/ch4mpy/spring-addons/issues/177) Post-login success & failure URI params and headers on authentication request are ignored in reactive applications ### `7.3.2` - [gh-174](https://github.com/ch4mpy/spring-addons/issues/174) Fix a regression on request to exchange authorization-code for tokens in servlet applications diff --git a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/SpringAddonsOidcClientProperties.java b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/SpringAddonsOidcClientProperties.java index 785303d0f..a3059813a 100644 --- a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/SpringAddonsOidcClientProperties.java +++ b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/properties/SpringAddonsOidcClientProperties.java @@ -12,224 +12,226 @@ 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). + * 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 @ConfigurationProperties("com.c4-soft.springaddons.oidc.client") public class SpringAddonsOidcClientProperties { - 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_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 String[] securityMatchers = {}; - - /** - * Fully qualified URI of the configured OAuth2 client. - */ - private URI clientUri = URI.create("/"); - - /** - * Path to the login page. Provide one only in the following cases: - * - * If left empty, the default Spring Boot configuration for OAuth2 login is applied - */ - private Optional loginPath = 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() { - final 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: - * - */ - 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; - - /** - * Whether to enable a security filter-chain and a controller (intercepting POST requests to "/backchannel_logout") to implement the client side of a - * Back-Channel Logout - */ - // private boolean backChannelLogoutEnabled = false; - - /** - * Path matchers for the routes accessible to anonymous requests - */ - private String[] permitAll = { "/login/**", "/oauth2/**" }; - - /** - * CSRF protection configuration for the auto-configured client filter-chain - */ - private Csrf csrf = Csrf.DEFAULT; - - /** - * Fine grained CORS configuration - */ - private CorsProperties[] cors = {}; - - /** - * Additional parameters to send with authorization-code request, mapped by client registration IDs - */ - private Map authorizationRequestParams = new HashMap<>(); - - /** - * 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(); - } - - /** - * 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 - @ConfigurationProperties("com.c4-soft.springaddons.oidc.client.oauth2-redirections") - public static class OAuth2RedirectionProperties { - - /** - * 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 BFF logout, with location to authorization server logout endpoint - */ - private HttpStatus rpInitiatedLogout = HttpStatus.FOUND; - } - - public Optional getLogoutProperties(String clientRegistrationId) { - return Optional.ofNullable(oauth2Logout.get(clientRegistrationId)); - } + 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_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 String[] securityMatchers = {}; + + /** + * Fully qualified URI of the configured OAuth2 client. + */ + private URI clientUri = URI.create("/"); + + /** + * Path to the login page. Provide one only in the following cases: + *
    + *
  • you want to provide your own login @Controller
  • + *
  • you want to use port 80 or 8080 with SSL enabled (this will require you to provide with the login @Controller above)
  • + *
+ * If left empty, the default Spring Boot configuration for OAuth2 login is applied + */ + private Optional loginPath = 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() { + final 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; + + /** + * Whether to enable a security filter-chain and a controller (intercepting POST requests to "/backchannel_logout") to implement the client + * side of a Back-Channel Logout + */ + // private boolean backChannelLogoutEnabled = false; + + /** + * Path matchers for the routes accessible to anonymous requests + */ + private String[] permitAll = { "/login/**", "/oauth2/**" }; + + /** + * CSRF protection configuration for the auto-configured client filter-chain + */ + private Csrf csrf = Csrf.DEFAULT; + + /** + * Fine grained CORS configuration + */ + private CorsProperties[] cors = {}; + + /** + * Additional parameters to send with authorization request, mapped by client registration IDs + */ + private Map authorizationRequestParams = new HashMap<>(); + + /** + * 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(); + } + + /** + * 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 + @ConfigurationProperties("com.c4-soft.springaddons.oidc.client.oauth2-redirections") + public static class OAuth2RedirectionProperties { + + /** + * 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 BFF logout, with location to authorization server logout endpoint + */ + private HttpStatus rpInitiatedLogout = HttpStatus.FOUND; + } + + public Optional getLogoutProperties(String clientRegistrationId) { + return Optional.ofNullable(oauth2Logout.get(clientRegistrationId)); + } } diff --git a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/SpringAddonsServerOAuth2AuthorizationRequestResolver.java b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/SpringAddonsServerOAuth2AuthorizationRequestResolver.java index 644f5b6f0..95342019a 100644 --- a/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/SpringAddonsServerOAuth2AuthorizationRequestResolver.java +++ b/spring-addons-starter-oidc/src/main/java/com/c4_soft/springaddons/security/oidc/starter/reactive/client/SpringAddonsServerOAuth2AuthorizationRequestResolver.java @@ -133,11 +133,10 @@ static String resolveRegistrationId(String requestPath) { } private static Consumer requestParamAuthorizationRequestCustomizer(RequestParam[] additionalParams) { - return customizer -> customizer.authorizationRequestUri(authorizationRequestUri -> { + return customizer -> customizer.additionalParameters(params -> { for (var reqParam : additionalParams) { - authorizationRequestUri.queryParam(reqParam.getName(), reqParam.getValue()); + params.put(reqParam.getName(), reqParam.getValue()); } - return authorizationRequestUri.build(); }); }