FC
FACADE
CODE
Lesson 25 of 28
FC
FACADE
CODE

Learn Spring Security

Contents

01. Bootstrap The Application
1. Introduction
2. Install Spring Security
3. Enable Basic Auth
4. Authentication with AppUser
5. Password Encoder
02. Web Layer Security - RBAC
6. Permit Public APIs
7. Role Based Authorization
8. Disable CSRF
9. Current Authenticated User
03. Web Layer Security - PBAC
10. Permission Based Authorization
11. Define Permissions
12. Assign Permissions
13. Remove Role Based Access
04. Service Layer Security
14. PreAuthorize
15. PostAuthorize
16. Authorize Using Spring Beans
05. Domain Object Instance Security
17. Domain Object Instance Security
18. PermissionEvaluator Interface
19. PermissionEvaluator Strategy
20. DB Backed UserDetailsService
06. Token Based Authentication
21. Basic Authentication Revisited
22. Generate Token
23. Persist Token
24. Verify Token
25. Invalidate Token
07. Token Based Authentication and Authorization
26. JSON Web Token
27. Generate JWT
28. Verify JWT

Invalidate Token

Any web application allows the user to voluntarily logout or expires their session (not necessarily HttpSession) in case of inactivity for a long time. This can be done from SPA (Single Page Application) by just getting rid of the token stored somewhere and redirecting to the Login page. But the proper way to make this happen is to handle it with a dedicated and secured API to invalidate the token.

Invalidate token service

We will create a deleteToken() in UserService in order to specifically update the token and tokenExpiryTime to null for the authenticated user identified by username.


@Service
public class UserService {
    // Other methods omitted for brevity

    public void deleteToken(String username) {
        AppUser appUser = this.get(username);
        appUser.setToken(null);
        appUser.setTokenExpiryTime(null);
        appUserRepository.save(appUser);
    }
}

Now we will call the above method from AuthenticationService by passing the username retrieved from the authenticated Authentication object stored in SecurityContext.


@Service
public class AuthenticationService {
    // Other methods omitted for brevity

    public void invalidateToken() {
        userService.deleteToken(authenticationFacade.getAuthentication().getName());
    }
}

Invalidate token API

Let's create a REST API which calls the above service method to invalidate the token.


@RestController
@RequestMapping("auth")
public class AuthenticationController {
    // Other details omitted for brevity

    @DeleteMapping("token")
    public ResponseEntity invalidateToken() {
        authenticationService.invalidateToken();
        return ResponseEntity.noContent().build();
    }
}

Secure delete token service

Similar to updateToken() service method we should not allow anyone to delete the token anonymously. We will secure the deleteToken() service method with the same pre-authorize condition as in updateToken() to ensure only the authenticated user deletes his own token. Let's define a constant variable in Authority class exclusively for authorizing the user to delete the token and annotate the deleteToken() like below:


@PreAuthorize(Authority.DELETE_TOKEN)
public void deleteToken(String username) {
    // Other details omitted for brevity
}

Note

Please note we do not need to change anything in HttpSecurity, as we have configured /auth/token endpoint to be public only for POST method. So DELETE method is secured by default letting only the authenticated user to delete/invalidate his own token.

Restart the application, and generate a token from GenerateToken API with Admin user credential. Now send a DELETE request to the same endpoint with the generated token as Bearer Token. This will reset the token and tokenExpiryTime column for the Admin user to null in the APP_USER table. We can no more use the same token for any protected APIs for the Admin user.

Downside of Opaque Tokens

With so many steps to implement token-based authentication mechanism, it is quite not simpler than the out of the box Basic Auth. Though it is quite effective and efficient and addresses most of the issues of Basic Auth but it also comes along with its own limitations. And this is not because of the authentication mechanism itself, but because of the type of the token we are using so far.

  • These tokens are called opaque tokens, as the name implies we can not get any information out of the random string. There is no way to verify from the token to whom it was issued to (Subject).
  • Single Page Applications (SPA) receiving this token can not decide what the user is allowed to do in the user interface from the token. It has to depend on a separate endpoint (in most cases it ends with url /me or /profile) to get the user profile and their permissions. These tokens can only be used as a means of authentication and not for authorization.
  • As these are opaque tokens, it should always be backed by a persistent datastore. In our case we had to associate the token with the user and it's expiry time in the database along with the AppUser record.
  • API request spanning multiple microservices, where the token has to be relayed across each microservice requires a round trip to the datastore in each microservice just to validate the token.