Docs

Java Spring Security

View on Github

Java Spring Security

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

This tutorial demonstrates how to add user login to a Java Spring Security web application. 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 | Gradle 5.4.1 | Spring Boot 1.5.21

New to Auth? Learn How Auth0 works, how it integrates with Regular Web Applications and which protocol it uses.

Configure Auth0

Get Your Application Keys

When you signed up for Auth0, a new application was created for you, or you could have created a new one.

You will need some details about that application to communicate with Auth0. You can get these details from the Application Settings section in the Auth0 dashboard.

You need the following information:

  • Domain
  • Client ID
  • Client Secret

If you download the sample from the top of this page these details are filled out for you.

If you have more than one application in your account, the sample comes with the values for your Default App.

App Dashboard

Configure Callback URLs

A callback URL is a URL in your application where Auth0 redirects the user after they have authenticated.

The callback URL for your app must be whitelisted in the Allowed Callback URLs field in your Application Settings. If this field is not set, users will be unable to log in to the application and will get an error.

If you are following along with the sample project you downloaded from the top of this page, the callback URL you need to whitelist in the Allowed Callback URLs field is http://localhost:3000/callback.

Configure Logout URLs

A logout URL is a URL in your application that Auth0 can return to after the user has been logged out of the authorization server. This is specified in the returnTo query parameter.

The logout URL for your app must be whitelisted in the Allowed Logout URLs field in your Application Settings. If this field is not set, users will be unable to log out from the application and will get an error.

If you are following along with the sample project you downloaded from the top of this page, the logout URL you need to whitelist in the Allowed Logout URLs field is http://localhost:3000/.

Configure Spring Security to Use Auth0

Setup Dependencies

You'll need to configure Spring Boot in your project first. You can generate the base project in this link, choosing Web in the dependencies and clicking the button "Generate Project". The downloaded project has the Spring Boot dependencies and plugin applied. The next step is to add the auth0-java-mvc-commons library. This allows you to use Auth0 with Java for server-side MVC web apps. It generates the Authorize URL that you need to call in order to authenticate and validates the result received on the way back to finally obtain the Auth0 Tokens that identify the user. You can always check the latest version in the library's GitHub.

If you are using Gradle, add it to your build.gradle:

// build.gradle

compile 'com.auth0:mvc-auth-commons:1.+'

If you are using Maven, add it to your pom.xml:

<!-- pom.xml -->

<dependency>
  <groupId>com.auth0</groupId>
  <artifactId>mvc-auth-commons</artifactId>
  <version>(1.0,2.0)</version>
</dependency>

Configure your Java Spring Security App

Your Java Spring Security App needs some information in order to authenticate against your Auth0 account. The samples read this information from the properties file src/main/resources/auth0.properties, but you could store them anywhere else. The required information is:

// src/main/resources/auth0.properties

com.auth0.domain: YOUR_DOMAIN
com.auth0.clientId: YOUR_CLIENT_ID
com.auth0.clientSecret: 'YOUR_CLIENT_SECRET'

The library we're using has this default behavior:

  • Request the scope openid, needed to call the /userinfo endpoint later to verify the User's identity.
  • Request the code Response Type and later perform a Code Exchange to obtain the tokens.
  • Use the HS256 Algorithm along with the Client Secret to verify the tokens.

But it also allows us to customize its behavior:

  • To use the RS256 Algorithm along with the Public Key obtained dynamically from the Auth0 hosted JWKs file, pass a JwkProvider instance to the AuthenticationController builder.
  • To use a different Response Type, set the desired value in the AuthenticationController builder. Any combination of code token id_token is allowed.
  • To request a different scope, set the desired value in the AuthorizeUrl received after calling AuthenticationController#buildAuthorizeUrl().
  • To specify the audience, set the desired value in the AuthorizeUrl received after calling AuthenticationController#buildAuthorizeUrl().

Check populated attributes

If you download the seed using our Download Sample button then the domain, clientId and clientSecret attributes will be populated for you, unless you are not logged in or you do not have at least one registered application. In any case, you should verify that the values are correct if you have multiple applications in your account and you might want to use another than the one we set the information for.

Project Structure

The Login project sample has the following structure:

.
├── build.gradle
├── settings.gradle
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── auth0
    │   │           └── example
    │   │               ├── App.java
    │   │               ├── mvc
    │   │               │   ├── CallbackController.java
    │   │               │   ├── ErrorController.java
    │   │               │   ├── HomeController.java
    │   │               │   ├── LoginController.java
    │   │               │   ├── LogoutController.java
    │   │               │   └── ProfileController.java
    │   │               ├── security
    │   │               │   ├── AppConfig.java
    │   │               │   └── TokenAuthentication.java
    │   │               └── util
    │   │                   └── TokenUtils.java
    │   └── resources
    │       ├── application.properties
    │       ├── auth0.properties
    │       └── templates
    │           ├── fragments
    │           │   ├── footer.html
    │           │   ├── header.html
    │           │   └── navbar.html
    │           ├── index.html
    │           ├── layouts
    │           │   └── default.html
    │           └── profile.html

The application uses Thymeleaf for view rendering. The two views in our application are:

  • index.html: Displays the home page. If the user is logged in, it will display the user's profile picture and a menu to see the user profile data.
  • profile.html: Displays the user profile page, showing the profile data from the authenticated user's ID token. This view should only be accessible to authenticated users.

The access control is handled by the Spring Security framework. A few rules in the AppConfig.java class will suffice to check for existing tokens before giving the user access to our protected /profile path. If the tokens don't exist, the request will be redirected by the ErrorController to the LoginController.

The project contains six Controllers:

  • LoginController.java: Invoked when the user attempts to login. The controller uses the client_id and domain parameters to create a valid Authorize URL and redirects the user there.
  • CallbackController.java: Captures requests to our Callback URL and processes the data to obtain the credentials. After a successful login, the credentials are then saved to the request's HttpSession.
  • HomeController.java: Renders the index.html resource. If the user is logged in, it sets the user's profile information on the Model so the view can display information about the authenticated user.
  • ProfileController.java: Sets the authenticated user's profile information on the Model and renders the profile.html resource.
  • LogoutController.java: Invoked when the user clicks the logout link. The controller invalidates the user session and redirects the user to the home page.
  • ErrorController.java: The controller triggers upon any non-handled exception and redirects the user to the /login path.

Trigger Authentication

Let's begin by making your Auth0 credentials available on the App. In the AppConfig class we tell Spring to map the properties defined in the auth0.properties file to the corresponding fields by using the @Configuration and @Value annotations.

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

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AppConfig extends WebSecurityConfigurerAdapter {

    @Value(value = "${com.auth0.domain}")
    private String domain;
    
    @Value(value = "${com.auth0.clientId}")
    private String clientId;
    
    @Value(value = "${com.auth0.clientSecret}")
    private String clientSecret;

    //...
}

Next, define the rules that will prevent unauthenticated users to access our protected resources. You do that by allowing anyone to access the /login and /callback endpoints (and our static view resources) in order to be able to complete the login flow, and blocking them from accessing any other endpoint if they are not authenticated. We also configure Spring Security to handle logout by registering our LogoutController.

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

@Bean
public LogoutSuccessHandler logoutSuccessHandler() {
    return new LogoutController();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable();
    http
        .authorizeRequests()
        .antMatchers("/callback", "/login", "/", "/*.png", "/css/**", "/js/**").permitAll()
        .anyRequest().authenticated()
        .and()
        .logout().logoutSuccessHandler(logoutSuccessHandler()).permitAll();
}

Now create the AuthenticationController instance that will create the Authorize URLs and handle the request received in the callback. Do that by defining a method that returns the Bean in the same AppConfig class. Any customization on the behavior of the component should be done here, such as requesting a different response type or using a different signature verification algorithm.

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

@Bean
public AuthenticationController authenticationController() throws UnsupportedEncodingException {
    return AuthenticationController.newBuilder(domain, clientId, clientSecret)
            .build();
}

To authenticate the users you will redirect them to the login page which uses Universal Login. This page is accessible from what we call the "Authorize URL". By using this library you can generate it with a simple method call. It will require a HttpServletRequest to store the call context in the session and the URI to redirect the authentication result to. This URI is normally the address where your app is running plus the path where the result will be parsed, which happens to be also the "Callback URL" whitelisted before. We also request the "User Info" audience in order to obtain an Open ID Connect compliant response. Finally, request the scopes "openid profile email" to get back user profile information in the ID token upon login. After you create the Authorize URL, you redirect the request there so the user can enter their credentials. The following code snippet is located on the LoginController class of our sample.

// src/main/java/com/auth0/example/mvc/LoginController.java

@RequestMapping(value = "/login", method = RequestMethod.GET)
protected String login(final HttpServletRequest req) {
    String redirectUri = req.getScheme() + "://" + req.getServerName();
    if ((req.getScheme().equals("http") && req.getServerPort() != 80) || (req.getScheme().equals("https") && req.getServerPort() != 443)) {
        redirectUri += ":" + req.getServerPort();
    }
    redirectUri += "/callback";

    String authorizeUrl = controller.buildAuthorizeUrl(req, redirectUri)
            .withAudience(String.format("https://%s/userinfo", appConfig.getDomain()))
            .withScope("openid profile email")
            .build();
    return "redirect:" + authorizeUrl;
}

After the user logs in the result will be received in our CallbackController, either via a GET or a POST Http method. The request holds the call context that the library have previously set by generating the Authorize URL with the controller. When you pass it to the controller, you get back either a valid Tokens instance or an Exception indicating what went wrong. In the case of a successful call, you need to create a new TokenAuthentication instance with the ID Token and set it to the SecurityContextHolder. You can modify this class to accept an Access Token as well, but this is not covered in this tutorial. If an exception is raised instead, you need to clear any existing Authentication from the SecurityContextHolder.

// src/main/java/com/auth0/example/mvc/CallbackController.java

@RequestMapping(value = "/callback", method = RequestMethod.GET)
protected void getCallback(final HttpServletRequest req, final HttpServletResponse res) throws ServletException, IOException {
  try {
      Tokens tokens = controller.handle(req);
      TokenAuthentication tokenAuth = new TokenAuthentication(JWT.decode(tokens.getIdToken()));
      SecurityContextHolder.getContext().setAuthentication(tokenAuth);
      res.sendRedirect(redirectOnSuccess);
  } catch (AuthenticationException | IdentityVerificationException e) {
      e.printStackTrace();
      SecurityContextHolder.clearContext();
      res.sendRedirect(redirectOnFail);
  }
}

Display the Home Page

When the user visits the home page, it should display whether they are logged in or not. We can then take advantage of the Thymeleaf Spring Security integration to conditionally render content based on the authentication status.

The HomeController checks if there is an existing TokenAuthentication on the request, and sets the user's profile data on the Model for the view to use:

// src/main/java/com/auth0/example/mvc/HomeController.java

@RequestMapping(value = "/", method = RequestMethod.GET)
protected String home(final Model model, final Authentication authentication) {
    if (authentication instanceof TokenAuthentication) {
        TokenAuthentication tokenAuthentication = (TokenAuthentication) authentication;
        model.addAttribute("profile", tokenAuthentication.getClaims());
    }

    return "index";
}

In the navbar.html Thymeleaf fragment, we use the sec:authorize tag to check if the user is authenticated. If they are authenticated, we display the profile picture and the main menu. If they are not authenticated, a Login button is displayed:

<!-- src/main/resources/templates/fragments/navbar.html -->

<ul>
    <!-- Login button: show if NOT authenticated -->
    <li sec:authorize="!isAuthenticated()">
        <form th:action="@{/login}" method="GET">
            <input type="submit" value="Login"/>
        </form>
    </li>

    <!-- Menu: show if authenticated -->
    <li sec:authorize="isAuthenticated()">
        <a href="#">
            <!-- Profile image should be set to the profile picture from the id token -->
            <img th:src="${profile.get('picture').asString()}"/>
        </a>
        <div>
            <!-- Show the user's full name from the id token here -->
            <div th:text="${profile.get('name').asString()}"</div>
            <a href="/profile">Profile</a>
            <a th:href="@{/logout}">Log out</a>
        </div>
    </li>
</ul>

Display the Profile Page

Now that the user is authenticated (the tokens exists), the framework will allow them to access our protected resources. In the ProfileController we get the Authentication object, then set the profile information on the Model for use by the view:

// src/main/java/com/auth0/example/mvc/ProfileController.java

@RequestMapping(value = "/profile", method = RequestMethod.GET)
protected String profile(final Model model, final Authentication authentication) {
    TokenAuthentication tokenAuthentication = (TokenAuthentication) authentication;
    model.addAttribute("profile", tokenAuthentication.getClaims());
    return "profile";
}

In profile.html, we don't need to check for authentication status, since we've protected this resource using Spring Security. We simply use the profile attribute sent by ProfileController to render information about the authenticated user:

<!— src/main/resources/templates/fragments/profile.html —>

<img th:src="${profile.get('picture').asString()}"/>
<h2 th:text="${profile.get('name').asString()}"/>
<p th:text="${profile.get('email').asString()}"/>

Handle Logout

To properly handle logout, we need to clear the session and log the user out of Auth0. This is handled in the LogoutController of our sample application.

First, we clear the session by calling request.getSession().invalidate(). We then construct the logout URL, being sure to include the returnTo query parameter, which is where the user will be redirected to after logging out. Finally, we redirect the response to our logout URL.

// src/main/java/com/auth0/example/mvc/LogoutController.java

@Override
public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse res, Authentication authentication) {
    invalidateSession(req);

    String returnTo = req.getScheme() + "://" + req.getServerName();
    if ((req.getScheme().equals("http") && req.getServerPort() != 80) || (req.getScheme().equals("https") && req.getServerPort() != 443)) {
        returnTo += ":" + req.getServerPort();
    }
    returnTo += "/";
    
    // Build logout URL like:
    // https://{YOUR-DOMAIN}/v2/logout?client_id={YOUR-CLIENT-ID}&returnTo=http://localhost:3000/
    String logoutUrl = String.format(
            "https://%s/v2/logout?client_id=%s&returnTo=%s",
            appConfig.getDomain(),
            appConfig.getClientId(),
            returnTo);
    try {
        res.sendRedirect(logoutUrl);
    } catch(IOException e){
        e.printStackTrace();
    }
}

private void invalidateSession(HttpServletRequest request) {
    if (request.getSession() != null) {
        request.getSession().invalidate();
    }
}

Run the Sample

To build an run the project, use the command:

./gradlew clean bootRun

Or if you are using Windows:

gradlew.cmd clean bootRun

Point your browser to http://localhost:3000. Follow the Log In link to log in or sign up to your Auth0 tenant.

login page

Upon successful login or signup, you will see the user's profile picture and a drop-down menu where the Log In link was. You can then view the user's profile page by clicking the Profile link.

Log out by clicking the Logout link in the drop-down menu. To verify that the profile page is restricted to authenticated users, point your browser to http://localhost:3000/profile, and note how you are directed to the login page.

Use Auth0 for FREE