Oauth2 JWT Verifier for Spring Boot
Once a human or machine is successfully authenticated by an Oauth2 provider (via the Authorization Code flow for humans or the Client Credentials flow for machines), they receive a Json Web Token (JWT, see https://jwt.io) in the form of an access_token
or an id_token
. This token is usually included as an HTTP request header that looks like:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIx...
For more information on verifying access tokens:
https://auth0.com/docs/api-auth/tutorials/verify-access-token
Java services receiving such tokens as request headers on any of its API endpoints will need to verify that the token is not counterfeit and has not expired. Additionally the API endpoint may have requirements governing the claims in the token, such as whether the right scopes or groups are specified in the token, and may want to do further claim verification.
There are other libraries that aim to help java services with this:
So what's special about Oauth2 JWT Verifier for Spring Boot?
Intuitive annotation-based authorization in Spring Boot
This library considerably simplifies the code you'd otherwise write by allowing the service to specify its authorization requirements via annotations applied to the API method and its parameters. This reduces boilerplate, and makes it easy for different API endpoints to customize their authorization requirements, while making explicit at the API method level, what those requirements are.
Here is the simplest example showing how the @Authorize
and @Jwt
annotations are used:
@Authorize
public T someApiCall(@Jwt String authorization) {
...
}
The requirement above says - "make sure that the incoming JWT is legitimate". If the JWT were not legitimate, an UnauthorizedException
would be thrown, aborting execution before reaching the code in the method.
Here's a more involved example:
@Authorize(scopes = {"contacts.read", "contacts.write"})
@GetMapping
public List<Contacts> getContacts(
@MatchClaim("sub") String userEmail,
int requestedContactCount,
@BindClaim("max_contacts") Integer maxContactCount,
@Jwt String authorization) {
...
// Custom logic checking that requestedContactCount <= maxContactCount...
...
}
This requirement says -
- Make sure the JWT passed in the
authorization
parameter is legitimate, AND - That the token has at least the
contacts.read
orcontacts.write
scope, AND - The
sub
claim must match the value passed in to theuserEmail
parameter. - Also, please update the
maxContactCount
parameter with the value of themax_contacts
claim found in the JWT so that we can do our own custom authorization logic.
There are more capabilities such as validating the groups
claim, or being either lenient or strict with the @MatchClaim
directive. See the code documentation for these annotations for more detail.
- @Authorize
- @Jwt
- @MatchClaim
- @BindClaim
Dependencies
Add this library to your service via:
<dependency>
<groupId>space.crickets</groupId>
<artifactId>oauth2-jwt-verifier-springboot</artifactId>
<version>0.1.0</version>
</dependency>
Providing the remote Json Web Keys endpoint(s)
Assuming your service is already working with an Oauth2 provider like Okta or Auth0, your service will need to add the following bean in one of its @Configuration
classes like this:
@Bean
public JwksEndpoints jwksEndpoints() {
return new JwksEndpoints(
"https://yourcompany.okta.com/oauth2/abc123/v1/keys",
"https://yourothercompany.okta.com/oauth2/abc123/v1/keys"
);
}
This will allow the library to get the public Json Web Keys it needs to verify JWTs.
That's about it. Start using the annotations.