Secure REST API with OAuth 2.0 Client Credentials Flow using Azure AD. Part 2
Part 2. Spring REST API configuration
Introduction
The second part of the post will cover Spring Boot/Spring Security setup and configuration details. It will rely on the configuration of Azure AD from Part 1.
Spring Boot REST API Example
Define a dummy Spring Boot REST API with 2 endpoints ‘hi’ and ‘hello’.
OAuth 2.0 Resource Server
Configuration steps
-
Create custom
ResourceServerConfig
class which extendsResourceServerConfigurerAdapter
and is annotated with@Configuration
. -
Enable resource server by adding
@EnableResourceServer
annotation to. -
Enable Web Security by adding
@EnableWebSecurity
annotation toResourceServerConfig
class -
Enable method level authorization by adding
@EnableGlobalMethodSecurity(prePostEnabled = true)
... @Configuration @EnableResourceServer @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) public class ResourceServerConfig extends ResourceServerConfigurerAdapter { ...
-
Explicitly set Resource ID for our resource server - it must be the same as our API URI which was defined in Part 1 and used as
scope
in access token request (without ‘.default’).... @Override public void configure(final ResourceServerSecurityConfigurer resources) throws Exception { resources.resourceId("api://<api_application_id>").stateless(true); } ...
-
Update Security Configuration and require authentication for all API endpoints:
... @Override public void configure(final HttpSecurity http) throws Exception { http.authorizeRequests(authorize -> authorize .antMatchers("/api/v1/**").authenticated() .antMatchers("/").permitAll()); } ...
-
By default Spring Security expects
authorities
claim inAccess Token
. However Azure AD populatesroles
claim with the Application Roles. Will need to customize the default behaviour by defining a custom implementation ofJwtAuthenticationConverter
:... @Override public void configure(final HttpSecurity http) throws Exception { http .authorizeRequests(authorize -> authorize .antMatchers("/api/v1/**").authenticated() .antMatchers("/").permitAll()) .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .jwtAuthenticationConverter(jwtAuthenticationConverter()))); } private JwtAuthenticationConverter jwtAuthenticationConverter() { JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); grantedAuthoritiesConverter.setAuthoritiesClaimName("roles"); JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter(); jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter); return jwtAuthenticationConverter; } ...
-
Spring Security needs to know the location of the public key which was used to sign the token so token signature valication can be performed. Azure AD has several active private keys and uses one of them to sign the token. The public keys are available at https://login.microsoftonline.com/common/discovery/v2.0/keys. The actual key id is defined in
kid
claim in theaccess token
. Will need to provide JWK URI to Spring Security so it knows where to grab the keys.... @Override public void configure(final HttpSecurity http) throws Exception { http .authorizeRequests(authorize -> authorize .antMatchers("/api/v1/**").authenticated() .antMatchers("/").permitAll()) // ; .oauth2ResourceServer(oauth2 -> oauth2 .jwt(jwt -> jwt .jwtAuthenticationConverter(jwtAuthenticationConverter()) .jwkSetUri("https://login.microsoftonline.com/common/discovery/v2.0/keys"))); } ...
-
Will protect the endpoints of REST API with different roles/authorities by applying method level authorization configuration using
@PreAuthorize
annotation ofSpring Security
in the controller:... @PreAuthorize("hasAuthority('SCOPE_CallHiApiRole')") @GetMapping("/hi") public String hi() { return "Hi"; } @PreAuthorize("hasAuthority('SCOPE_CallHelloApiRole')") @GetMapping("/hello") public String hello(@RequestParam(name = "name", required = true) final String name) { return String.format("Hello, %s", name); } ...
Notes: - Role/authority names match the
Application Roles
defined in Azure AD and haveSCOPE_
prefix. - UsehasAuthority
Security EL instead ofhasRole
Verify Resource Server setup
Will be checking the setup by performing a request to our API suing Access Token
obtained from our Authorization Server (Azure AD - see Part 1).
Request
Notes:
<access_token>
is the Tenant ID of your Azure AD instance (<Azure AD->Overview>
)
Response. Example - Successful
Response. Example - Access Denied
In case proper application role is not granted as permission to the client we will get a 403 error:
Summary
Spring Security provides a full support of OAuth 2.0 Resource Server implementation and is highly customizable. Check full source code of the above example on GitHub.