-
Notifications
You must be signed in to change notification settings - Fork 460
Spring Security 5.1.x OIDC Integration (For spring boot 2.1.x)
Almost all web applications are sensitive to security, they may leverage spring security framework to customizable authentication and access-control. Assume there is one web application with spring security. One use sign-in process may take the process in steps:
- A user is prompted to log in with a username and password.
- The web application (successfully) verifies that the password is correct for the username.
- The context information for that user is obtained.
- A security context is established for the user.
- The user proceeds, potentially to perform some operation which is potentially protected by an access control mechanism which checks the required permissions for the operation against the current security context information.
- The web application can leverage AAD B2C as the Id management service and delegate the authentication to AAD B2C. Then the web application can get rid of the Id/User management like DB based authentication and focus on its own logic.
The OAuth 2.0 Login feature provides an application with the capability to have users log in to the application by using their existing account at an OAuth 2.0 Provider or OpenID Connect 1.0 Provider.
When users would like to configure custom provider properties, Spring Boot 2.x provides
the following base property for configuring custom provider properties:
spring.security.oauth2.client.provider.[providerId]
. For example:
spring:
security:
oauth2:
client:
registration:
okta:
client-id: okta-client-id
client-secret: okta-client-secret
provider:
okta:
authorization-uri: https://your-subdomain.oktapreview.com/oauth2/v1/authorize
token-uri: https://your-subdomain.oktapreview.com/oauth2/v1/token
user-info-uri: https://your-subdomain.oktapreview.com/oauth2/v1/userinfo
user-name-attribute: sub
jwk-set-uri: https://your-subdomain.oktapreview.com/oauth2/v1/keys
And the WebSecurityConfigurationAdapter
configuration:
@EnableWebSecurity
public class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login();
}
}
Azure AD B2C extends the standard OpenID Connect protocol to do more than simple authentication and authorization. It introduces the user flow parameter, which enables you to use OpenID Connect to add user experiences--such as sign-up, sign-in, and profile management--to your app.
When your web app needs to authenticate the user and execute a user flow, it can direct the user to the /authorize
endpoint. This is the interactive portion of the flow, where the user takes action, depending on the user flow. In
this request, the client indicates the permissions that it needs to acquire from the user in the scope parameter
and the user flow to execute in the p
parameter.
As stated above B2C involved p
parameters for each application, we need overriding default spring boot
auto-configuration for Oauth Client in OAuth2ClientAutoConfiguration
. We may need to do at least following things:
- Register one
ClientRegistrationRepository
as bean. - Provide one custom
WebSecurityConfigurerAdapter
.
The code may looks like following:
@EnableWebSecurity
public static class OAuth2LoginSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private AuthorizationRequestResolver b2cRequestResolver;
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.loginProcessingUrl("${your-b2c-policies-redirect-url-path}")
.authorizationEndpoint()
.authorizationRequestResolver(b2cRequestResolver);
}
}
@Configuration
public class ClientRegistrationConfig {
@Bean
public ClientRegistrationRepository clientRegistrationRepository() {
return new InMemoryClientRegistrationRepository(this.b2COauth2ClientRegistion());
}
private ClientRegistration b2COauth2ClientRegistration() {
return ClientRegistration.withRegistrationId("${your-client-name}")
.clientId("${your-client-id}")
.clientSecret("${your-client-secret}")
.clientAuthenticationMethod(ClientAuthenticationMethod.POST)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUriTemplate("${your-redirect-uri}")
.scope("${your-client-id}", "openid")
.authorizationUri("https://${your-b2c-domain-name}.b2clogin.com/${your-b2c-domain-name}.onmicrosoft.com/oauth2/v2.0/authorize")
.tokenUri("https://${your-b2c-domain-name}.b2clogin.com/${your-b2c-domain-name}.onmicrosoft.com/oauth2/v2.0/token?p=${your-b2c-policy-name}")
.userNameAttributeName(IdTokenClaimNames.SUB)
.jwkSetUri("https://{your-b2c-domain-name}.b2clogin.com/${your-b2c-domain-name}.onmicrosoft.com/discovery/v2.0/keys?p=${your-b2c-policy-name")
.clientName("aad-b2c")
.build();
}
}
With this configuration, the spring security will start the oauth2Login
standard process and store the
Oauth2AuthenticationToken
to session.
As the p
parameter need to provide when process login, the integration may also need to customize the
OAuth2AuthorizationRequestResolver
and make the AAD B2C service side locating the p
as policy.
There are at lease 2 things need to do from AAD B2C spring boot sdk, as following:
- Provide one bean as AAD B2C
ClientRegistrationRepository
, prepare theClientRegistration
for user. - Customize the
OAuth2AuthorizationRequestResolver
for parameterp
.
For security consideration, after user logout from browser, the sdk should tell the AAD B2C that the session or token is invalid. We leverage spring security LogoutSuccessHandler to archive this. The LogoutSuccessHandler is called after a successful logout by the LogoutFilter, to handle e.g. redirection or forwarding to the appropriate destination. So there is only one thing need to do here.
- Provide one bean
LogoutSuccessHandler
to invalid the token from AAD B2C.
Azure AD B2C treats policies as one OAuth2 provider. The policies password-reset
and profile-edit
can also be
the OAuth2 ClientRegistration
in spring security. Just like want we do for policy sign-up-or-in
, we should add
both password-reset
and profile-edit
polices to ClientRegistrationRepository
, and the Oauth2AuthorizationXXX
will take good care of the rest part.
- Registry the
password-reset
andprofile-edit
policies to aboveClientRegistrationRepository
.
The impact to end user is in the scope of WebSecurityConfigurationAdapter
. The end user need to leverage
our sdk provided bean to enable the feature of AAD B2C. It may include but not limited to:
- Specific the
loginProcessingUrl
, which should be the same as b2c policy redirect url.
.oauth2Login()
.loginProcessingUrl("${your-b2c-policies-redirect-url-path}")
- Customize the
authorizationEndpoint
with sdk providedOAuth2AuthorizationRequestResolver
.
.authorizationEndpoint()
.authorizationRequestResolver(b2cRequestResolver);
The end user need to specific the LogoutSuccessHandler
when configure WebSecurityConfigurationAdapter
.
.logoutSuccessHandler(b2cLogoutSuccessHandler)
By default, the OAuth2AuthorizationRequestRedirectFilter
will use /oauth2/authorization/{registrationId}
as
the processing url. We register the registrationId
with policy value from portal User flow
. So the user must
be aware of the processing url for each policy is /oauth2/authorization/{policy-name}
if you would like to do
some customize work(Like customize the log in page).
- Configure
WebSecurityConfigureAdaptor
.
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private final AADB2CProperties properties;
private final AADB2CLogoutSuccessHandler logoutSuccessHandler;
private final AADB2CAuthorizationRequestResolver requestResolver;
public WebSecurityConfiguration(AADB2CProperties properties,
AADB2CLogoutSuccessHandler logoutSuccessHandler,
AADB2CAuthorizationRequestResolver requestResolver) {
this.properties = properties;
this.logoutSuccessHandler = logoutSuccessHandler;
this.requestResolver = requestResolver;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.logout().logoutSuccessHandler(logoutSuccessHandler)
.and()
.oauth2Login()
.loginProcessingUrl(properties.getLoginProcessingUrl())
.authorizationEndpoint().authorizationRequestResolver(requestResolver)
;
}
}
- Configure
application.yml
to enable the auto-configuration.
server:
port: 8080
azure:
activedirectory:
b2c:
tenant: ${your-tenant-name}
client-id: ${your-client-id}
client-secret: ${your-client-secret}
reply-url: http://localhost:8080/home # should be absolute url.
logout-success-url: http://localhost:8080/login
policies:
sign-up-or-sign-in: ${your-sign-up-or-in-policy-value}
profile-edit: ${your-profile-edit-policy-value}
password-reset: ${your-password-reset-policy-value}