-
Notifications
You must be signed in to change notification settings - Fork 460
Spring Security 5.0.x OIDC integration (for spring boot 2.0.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.
Typically web security WebSecurityConfigurerAdapter
configuration may looks like below code. Assume that
you have one web application deployed in Azure. It may be one simple web site like Photo gallery
.
The web site may allow users to upload photos, as well as something like CRUD
. Like most web site,
there is one home page for everyone from internet, as well as one button named Sign up or in
.
@Slf4j
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
private AADB2CEntryPoint aadb2CEntryPoint;
private AADB2CLogoutSuccessHandler aadb2CLogoutSuccessHandler;
private AADB2CFilter filter;
public WebSecurityConfiguration(AADB2CEntryPoint entryPoint, AADB2CLogoutSuccessHandler successHandler,
AADB2CFilter filter) {
this.aadb2CEntryPoint = entryPoint;
this.aadb2CLogoutSuccessHandler = successHandler;
this.filter = filter;
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/favicon.ico")
.permitAll()
.anyRequest()
.authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(aadb2CEntryPoint)
.and()
.logout()
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.logoutSuccessHandler(aadb2CLogoutSuccessHandler).deleteCookies("JSESSIONID")
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);
}
}
Leveraging AAD B2C as identify management service is simplify configured by the application.yml
.
Only tenan
, client-id
, sign-up-or-sign-in
and logout-success-url
are required to configure.
server:
port: 8080
azure:
activedirectory:
b2c:
tenant: ${your-tenant-id}
client-id: ${your-client-id}
policies:
sign-up-or-sign-in: # Required
name: ${your-policy-name}
reply-url: http://localhost:8080/
password-reset: # Optional
name: ${your-policy-name}
reply-url: http://localhost:8080/
profile-edit: # Optional
name: ${your-policy-name}
reply-url: http://localhost:8080/
# Required
logout-success-url: http://localhost:8080/ # Only absolute URL is supported
# Optional
password-reset-url: http://localhost:8080/password-reset # Only absolute URL is supported
# Optional
profile-edit-url: http://localhost:8080/profile-edit # Only absolute URL is supported
Before we start to learn how above code works, we would like to introduce the basic workflow of AAD B2C directly.
- First the user try to access protected resource from customer application.
- Then the customer application will redirect to AAD B2C login page.
- After login success, AAD B2C will redirect to reply URL (registry in AAD application) with id_token and code.
- Customer application get the id_token and(or) code and mark the user authenticated.
Integration AAD B2C will involve one more layer between web application and AAD B2C service. It will accept the request from web site, do some filter work and redirect to AAD B2C endpoint which is transparent to web applications.
The filter of AAD B2C contains many different kinds of scenarios, for now we support Policy reply
,
Password reset
, Profile edit
and Default sceaniro
in filter.
AAD B2C provides the Policy
for such kind of Sign up or in
scenario for web site. Fortunately our
package make the things easier when redirecting users to AAD B2C Sign in
endpoint, as well as the
scenario that accessing resource from web site without login. We provide one bean of
authenticationEntryPoint
for security configuration. According to the concept of spring security, it
works with httpBasic
as web site entry point, and redirect to AAD B2C endpoint for Sign in
process.
After success, the AAD B2C will redirect the users back to one registered Reply URL
. Only need to
provide the AAD B2C policy name and Reply URL
from your application.yml
.
The Sign up
almost the same process as Sign In
, we can skip this here.
For Sign out
scenario with security consideration, there is one extra thing we would like to do beside
spring security standard logout process. We should tell the AAD B2C that the token of the logout user
is invalid after logout from web site successfully. One LogoutSuccessHandler
bean is also provided to
archive this. It will redirect to AAD B2C endpoint for invalidating current user token transparently, and
then redirect user to the Reply URL
like Sign in
process.
The most important part is about how we obtain JWT from AAD B2C endpoint. Actually it may contains 2
steps. First the AAD B2C redirect to the Reply URL
with query state
, code
and id_token
. Second
it redirects users to the Referer
url before redirect to AAD B2C Sign in
endpoint. Following the
convention of spring security, we provide one Filter
to archive this. It will process some important
processes include but not limit to:
- Validate the result from AAD B2C.
- Obtain the query from
Reply URL
. - Parse and validate the
id_token
. - Create
UserPrincipal
from JWTClaim and code. - Create
PreAuthenticatedAuthenticationToken
withUserPrincipal
and updateSecurityContext
.
For security consideration, we recommend users to reset your password every 3-6 months. In previous
section, the filter only take care of the Reply URL
scenario from AAD B2C. It is simple to support
the Password Reset
scenario within that filter.
Straightforwardly, Reply URL
is the redirect URL after password reset success. As Password reset
is
not the standard scenario of spring security, the web site need to specify the URL for Password reset
.
That means if there is one request to the URL of Password reset url
, the filter will consider that
the authenticated users would like to change password, and then redirect to AAD B2C endpoint to start
the Password reset
process. After the process end successfully, the AAD B2C will redirect to the
Reply URL
, just like what we do from Sign in
scenario.
The scenario of Profile edit
is the same as Password reset
. The web site need to specify the URL
for Profile edit
, and then start the Profile reeset
process.
This scenario take care of the Reply URL
redirecting from AAD B2C endpoint, includes but not limits to policies
Sign up or in
, Password rest
and Profile edit
. The Reply URL
will contains query code
, state
and
id_token
when success, or error
and error_description
. The scenario handler will parse these information and
start the process respectively.
This scenario only acts when users request Password reset
with the Password reset url
configured by web site.
It will filter out the request to Password reset url
and redirect users to AAD B2C endpoint to start the process.
After the AAD B2C redirect users to Reply URL
, it will start the policy reply scenario
and update the
authentication.
This scenario interests Profile edit
requests from users. Like password reset scenario, the web site need to
configure Profile edit url
. Then it will filter out the request to Profile edit url
and redirect users to AAD
B2C endpoint to start the process. The same as password reset scenario, it will start the policy reply scenario
and update authentication after redirecting users to Reply URL
from AAD B2C endpoint.
This is the default scenario in most cases for authenticated users. No surprise here and just update the users authentication as time passing.
Creating filter chain: any request, [
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@14e2e1c3,
org.springframework.security.web.context.SecurityContextPersistenceFilter@109d724c,
org.springframework.security.web.header.HeaderWriterFilter@51b1a8f6,
org.springframework.security.web.csrf.CsrfFilter@7103ab0,
org.springframework.security.web.authentication.logout.LogoutFilter@48c3205a,
com.microsoft.azure.spring.autoconfigure.b2c.AADB2CFilter@e93f3d5,
org.springframework.security.web.savedrequest.RequestCacheAwareFilter@e4d2696,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@70242f38,
org.springframework.security.web.authentication.AnonymousAuthenticationFilter@2b4786dd,
org.springframework.security.web.session.SessionManagementFilter@1b005a0b,
org.springframework.security.web.access.ExceptionTranslationFilter@cb0f763,
org.springframework.security.web.access.intercept.FilterSecurityInterceptor@562457e1
]