Docs

Spring Security 5 Java API: Authorization

View on Github

Spring Security 5 Java API: Authorization

Gravatar for jim.anderson@auth0.com
By Jim Anderson

This tutorial demonstrates how to add authorization to an API using Spring Security 5. We recommend you to Log in to follow this quickstart with examples configured for your account.

I want to explore a sample app

2 minutes

Get a sample configured with your account settings or check it out on Github.

View on Github
System requirements: Java 8 or above

This example demonstrates:

  • How to check for a JSON Web Token (JWT) in the Authorization header of an incoming HTTP request.

  • How to check if the token is valid, using the JSON Web Key Set (JWKS) for your Auth0 account. To learn more about validating Access Tokens, read the Validate an Access Token tutorial.

New to Auth0? Learn how Auth0 works and read about implementing API authentication and authorization using the OAuth 2.0 framework.

This Quickstart uses Spring MVC. If you are using Spring WebFlux, the steps to secure an API are similar, but some of the implementation details are different. Refer to the Spring Security WebFlux Sample Code to see how to integrate Auth0 with your Spring WebFlux API.

Configure Auth0 APIs

Create an API

In the APIs section of the Auth0 dashboard, click Create API. Provide a name and an identifier for your API, for example, https://quickstarts/api. You will use the identifier as an audience later, when you are configuring the Access Token verification. Leave the Signing Algorithm as RS256.

Create API

By default, your API uses RS256 as the algorithm for signing tokens. Since RS256 uses a private/public keypair, it verifies the tokens against the public key for your Auth0 account. The public key is in the JSON Web Key Set (JWKS) format, and can be accessed here.

Define Permissions

Permissions let you define how resources can be accessed on behalf of the user with a given access token. For example, you might choose to grant read access to the messages resource if users have the manager access level, and a write access to that resource if they have the administrator access level.

You can define allowed permissions in the Permissions tab of the Auth0 Dashboard's APIs section.

Configure Permissions

This example uses the read:messages scope.

Configure the Sample Project

The sample project uses a /src/main/resources/application.yml file, which configures it to use the correct Auth0 Domain and API Identifier for your API. If you download the code from this page it will be automatically configured. If you clone the example from GitHub, you will need to fill it in yourself.

auth0:
  audience: YOUR_API_IDENTIFIER
spring:
  security:
    oauth2:
        resourceserver:
            jwt:
                issuer-uri: https://YOUR_DOMAIN/
Attribute Description
auth0.audience The unique identifier for your API. If you are following the steps in this tutorial it would be https://quickstarts/api.
spring.security.oauth2.resourceserver.jwt.issuer-uri The issuer URI of the resource server, which will be the value of the iss claim in the JWT issued by Auth0. Spring Security will use this property to discover the authorization server's public keys and validate the JWT signature. The value will be your Auth0 domain with an https:// prefix and a / suffix (the trailing slash is important).

Validate Access Tokens

Install dependencies

If you are using Gradle, you can add the required dependencies using the Spring Boot Gradle Plugin and the Dependency Management Plugin to resolve dependency versions:

// build.gradle

plugins {
    id 'org.springframework.boot' version '2.1.6.RELEASE'
    id 'io.spring.dependency-management' version '1.0.7.RELEASE'
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.security:spring-security-oauth2-resource-server'
    implementation 'org.springframework.security:spring-security-oauth2-jose'
    implementation 'org.springframework.security:spring-security-config'
}

If you are using Maven, add the Spring dependencies to your pom.xml file:

// pom.xml

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.6.RELEASE</version>
    <relativePath/>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-resource-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-oauth2-jose</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
    </dependency>
</dependencies>

Configure the resource server

To configure the application as a Resource Server and validate the JWTs, create a class that extends WebSecurityConfigurerAdapter, add the @EnableWebSecurity annotation, and override the configure method:

// src/main/java/com/auth0/example/security/SecurityConfig.java

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.oauth2ResourceServer().jwt();
    }
}

Validate the audience

In addition to validating the JWT, you also need to validate that the JWT is intended for your API by checking the aud claim of the JWT. Create a new class named AudienceValidator that implements the OAuth2TokenValidator interface:

// src/main/java/com/auth0/example/security/AudienceValidator.java

class AudienceValidator implements OAuth2TokenValidator<Jwt> {
    private final String audience;

    AudienceValidator(String audience) {
        this.audience = audience;
    }

    public OAuth2TokenValidatorResult validate(Jwt jwt) {
        OAuth2Error error = new OAuth2Error("invalid_token", "The required audience is missing", null);
        
        if (jwt.getAudience().contains(audience)) {
            return OAuth2TokenValidatorResult.success();
        }
        return OAuth2TokenValidatorResult.failure(error);
    }
}

Update the SecurityConfig class to configure a JwtDecoder bean that uses the AudienceValidator:

// src/main/java/com/auth0/example/security/SecurityConfig.java

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${auth0.audience}")
    private String audience;

    @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
    private String issuer;

    @Bean
    JwtDecoder jwtDecoder() {
        NimbusJwtDecoderJwkSupport jwtDecoder = (NimbusJwtDecoderJwkSupport)
                JwtDecoders.fromOidcIssuerLocation(issuer);

        OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience);
        OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
        OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);

        jwtDecoder.setJwtValidator(withAudience);

        return jwtDecoder;
    }
}

Protect API Endpoints

The routes shown below are available for the following requests:

  • GET /api/public: available for non-authenticated requests
  • GET /api/private: available for authenticated requests containing an Access Token with no additional scopes
  • GET /api/private-scoped: available for authenticated requests containing an Access Token with the read:messages scope granted

The example below shows how to secure API methods using the HttpSecurity object provided in the configure() method of the SecurityConfig class. Route matchers are used to restrict access based on the level of authorization required:

// src/main/java/com/auth0/example/security/SecurityConfig.java

public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .mvcMatchers("/api/public").permitAll()
                .mvcMatchers("/api/private").authenticated()
                .mvcMatchers("api/private-scoped").hasAuthority("SCOPE_read:messages")
                .and()
                .oauth2ResourceServer().jwt();
    }
}

By default, Spring Security will create a GrantedAuthority for each scope in the scope claim of the JWT. This is what enables using the hasAuthority("SCOPE_read:messages") method to restrict access to a valid JWT that contains the read:messages scope.

If your use case requires different claims to make authorization decisions, see the Spring Security Reference Documentation to learn how to customize the extracted authorities.

Create the API controller

Create a new class named Message, which is the domain object the API will return:

// src/main/java/com/auth0/example/model/Message.java

public class Message {
    private final String message;

    Message(String message) {
        this.message = message;
    }

    public String getMessage() {
        return this.message;
    }
}

Create a new class named APIController to handle requests to the endpoints:

// src/main/java/com/auth0/example/web/APIController.java

@RestController
@RequestMapping(path = "api", produces = MediaType.APPLICATION_JSON_VALUE)
public class APIController {

    @GetMapping(value = "/public")
    public Message publicEndpoint() {
        return new Message("All good. You DO NOT need to be authenticated to call /api/public.");
    }

    @GetMapping(value = "/private")
    public Message privateEndpoint() {
        return new Message("All good. You can see this because you are Authenticated.");
    }

    @GetMapping(value = "/private-scoped")
    public Message privateScopedEndpoint() {
        return new Message("All good. You can see this because you are Authenticated with a Token granted the 'read:messages' scope");
    }
}

Run the Application

To build and run the sample project, execute the bootRun Gradle task.

Linux or macOS:

./gradlew bootRun

Windows:

gradlew.bat bootRun

If you are configuring your own application using Maven and the Spring Boot Maven Plugin, you can execute the spring-boot:run goal.

Linux or macOS:

mvn spring-boot:run

Windows:

mvn.cmd spring-boot:run

The sample application will be available at http://localhost:3010/. Read about how to test and use your API in the Using Your API article.

Use Auth0 for FREE