Skip to content
This repository has been archived by the owner on Aug 28, 2024. It is now read-only.

Spring Security 5.1.x OIDC Integration (For spring boot 2.1.x)

Pan Li edited this page Feb 11, 2019 · 1 revision

B2C Integration with Spring Security 5.1.x OIDC

Overview

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.

Spring Security Oauth2 Login

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();
    }
}

AAD B2C OpenId Connect Extension

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.

Integration with Spring Security

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.

What We Need to Do for Integration

OAuth2 Login

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 the ClientRegistration for user.
  • Customize the OAuth2AuthorizationRequestResolver for parameter p.

Logout from AAD B2C

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.

Password Reset and Profile Edit

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 and profile-edit policies to above ClientRegistrationRepository.

What's the Impact to User

OAuth2 Login

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 provided OAuth2AuthorizationRequestResolver.
.authorizationEndpoint()
.authorizationRequestResolver(b2cRequestResolver);

Logout from AAD B2C

The end user need to specific the LogoutSuccessHandler when configure WebSecurityConfigurationAdapter.

.logoutSuccessHandler(b2cLogoutSuccessHandler)

Password Reset and Profile Edit

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).

Overall Impact to User Code

  • 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}