Heads up… this article is old! For an updated version of this article, see Simple Token Authentication for Java Apps on the Okta developer blog.

Update 5/12/2016: Building a Java application? JJWT is a Java library providing end-to-end JWT creation and verification, developed by our very own Les Hazlewood. Forever free and open-source (Apache License, Version 2.0), JJWT is simple to use and understand. It was designed with a builder-focused fluent interface hiding most of its complexity. We’d love to have you try it out, and let us know what you think! (And, if you’re a Node developer, check out NJWT!)

In my last post, we covered a lot of ground, including how we traditionally go about securing websites, some of the pitfalls of using cookies and sessions, and how to address those pitfalls by traditional means.

In this post we’ll go beyond the traditional and take a deep dive into how token authentication with JWTs (JSON Web Tokens) not only addresses these concerns, but also gives us the benefit of inspectable metadata and strong cryptographic signatures.

Token Authentication to the Rescue!

Let’s first examine what we mean by authentication and token in this context.

Authentication is proving that a user is who they say they are.

A token is a self-contained singular chunk of information. It could have intrinsic value or not. We are going to look at a particular type of token that does have intrinsic value and addresses a number of the concerns with session IDs.

JSON Web Tokens (JWTs)

JWTs are a URL-safe, compact, self-contained string with meaningful information that is usually digitally signed or encrypted. They’re quickly becoming a de-facto standard for token implementations across the web.

URL-safe is a fancy way of saying that the entire string is encoded so there are no special characters and the token can fit in a URL.

The string is opaque and can be used standalone in much the same way that session IDs are used. By opaque, I mean that looking at the string itself provides no additional information.

However, the string can also be decoded to pull out metadata and its signature can be cryptographically verified so that your application knows that the token has not been tampered with.

JWTs and OAuth2 Access Tokens

Many OAuth2 implementations are using JWTs for their access tokens. It should be stated that the OAuth2 and JWT specifications are completely separate from each other and don’t have any dependencies on each other. Using JWTs as the token mechanism for OAuth2 affords a lot of benefit as we’ll see below.

JWTs can be stored in cookies, but all the rules for cookies we discussed before still apply. You can entirely replace your session id with a JWT. You can then gain the additional benefit of accessing the meta-information directly from that session id.

In the wild, they look like just another ugly string:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOi8vdHJ1c3R5YXBwLmNvbS8iLCJleHAiOjEzMDA4MTkzODAsInN1YiI6InVzZXJzLzg5ODM0NjIiLCJzY29wZSI6InNlbGYgYXBpL2J1eSJ9.43DXvhrwMGeLLlP4P4izjgsBB2yrpo82oiUPhADakLs 1 2 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 . eyJpc3MiOiJodHRwOi8vdHJ1c3R5YXBwLmNvbS8iLCJleHAiOjEzMDA4MTkzODAsInN1YiI6InVzZXJzLzg5ODM0NjIiLCJzY29wZSI6InNlbGYgYXBpL2J1eSJ9 . 43DXvhrwMGeLLlP4P4izjgsBB2yrpo82oiUPhADakLs

If you look carefully, you can see that there are two periods in the string. These are significant as they delimit different sections of the JWT.

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 . eyJpc3MiOiJodHRwOi8vdHJ1c3R5YXBwLmNvbS8iLCJleHAiOjEzMDA4MTkzODAsInN1YiI6InVzZXJzLzg5ODM0NjIiLCJzY29wZSI6InNlbGYgYXBpL2J1eSJ9 . 43DXvhrwMGeLLlP4P4izjgsBB2yrpo82oiUPhADakLs 1 2 3 4 5 6 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 . eyJpc3MiOiJodHRwOi8vdHJ1c3R5YXBwLmNvbS8iLCJleHAiOjEzMDA4MTkzODAsInN1YiI6InVzZXJzLzg5ODM0NjIiLCJzY29wZSI6InNlbGYgYXBpL2J1eSJ9 . 43DXvhrwMGeLLlP4P4izjgsBB2yrpo82oiUPhADakLs

JWT Structure

JWTs have a three part structure, each of which is base64-encoded:

Here are the parts decoded:

Header

{ "typ": "JWT", "alg": "HS256" } 1 2 3 4 5 { "typ" : "JWT" , "alg" : "HS256" }

Claims

{ "iss":"http://trustyapp.com/", "exp": 1300819380, "sub": "users/8983462", "scope": "self api/buy" } 1 2 3 4 5 6 7 { "iss" : "http://trustyapp.com/" , "exp" : 1300819380 , "sub" : "users/8983462" , "scope" : "self api/buy" }

Cryptographic Signature

tß´—™à%O˜v+nî…SZu¯µ€U…8H× 1 2 t ß´—™à % O ˜ v + n î… SZu ¯µ€ U … 8H ×

JWT Claims

Let’s examine the claims sections. Each type of claim that is part of the JWT Specification can be found here.

iss is who issued the token.

exp is when the token expires.

sub is the subject of the token. This is usually a user identifier of some sort.

The above parts of the claim are all included in the JWT specification. scope is not included in the specification, but it is commonly used to provide authorization information. That is, what parts of the application the user has access to.

One advantage of JWTs is that arbitrary data can be encoded into the claims as with scope above. Another advantage is that the client can now react to this information without any further interaction with the server. For instance, a portion of the page may be hidden based on the data found in the scope claim.

NOTE: It is still critical and a best practice for the server to always verify actions taken by the client. If, for instance, some administrative action was being taken on the client, you would still want to verify on the application server that the current user had permission to perform that action. You would never rely on client side authorization information alone.

You may have picked up on another advantage: the cryptographic signature. The signature can be verified which proves that the JWT has not been tampered with. Note that the presence of a crytpographic signature does not guarantee confidentiality. Confidentiality is ensured only when the JWT is encrypted as well as signed.

Now, for the big kicker: statelessness. While the server will need to generate the JWT, it does not need to store it anywhere as all of the user metadata is encoded right in to the JWT. The server and client could pass the JWT back and forth and never store it. This scales very well.

Managing Bearer Token Security

Implicit trust is a tradeoff. These types of tokens are often referred to as Bearer Tokens because all that is required to gain access to the protected sections of an application is the presentation of a valid, unexpired token.

You have to address issues like: How long should the token be good for? How will you revoke it? (There’s a whole other post we could do on refresh tokens.)

You have to be mindful of what you store in the JWT if they are not encrypted. Do not store any sensitive information. It is generally accepted practice to store a user identifier in the form of the sub claim. When a JWT is signed, it’s referred to as a JWS. When it’s encrypted, it’s referred to as a JWE.

Java, JWT and You!

We are very proud of the JJWT project on Github. Primarily authored by Stormpath’s own CTO, Les Hazlewood, it’s a fully open-source JWT solution for Java. It’s the easiest to use and understand library for creating and verifying JSON Web Tokens on the JVM.

How do you create a JWT? Easy peasy!

import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; byte[] key = getSignatureKey(); String jwt = Jwts.builder().setIssuer("http://trustyapp.com/") .setSubject("users/1300819380") .setExpiration(expirationDate) .put("scope", "self api/buy") .signWith(SignatureAlgorithm.HS256,key) .compact(); 1 2 3 4 5 6 7 8 9 10 11 12 13 import io . jsonwebtoken . Jwts ; import io . jsonwebtoken . SignatureAlgorithm ; byte [ ] key = getSignatureKey ( ) ; String jwt = Jwts . builder ( ) . setIssuer ( "http://trustyapp.com/" ) . setSubject ( "users/1300819380" ) . setExpiration ( expirationDate ) . put ( "scope" , "self api/buy" ) . signWith ( SignatureAlgorithm . HS256 , key ) . compact ( ) ;

The first thing to notice is the fluent builder api used to create a JWT. Method calls are chained culminating in the compact call which returns the final JWT string.

Also notice that when we are setting one of the claims from the specification, we use a setter. For example: .setSubject("users/1300819380") . When a custom claim is set, we use a call to put and specify both the key and value. For example: .put("scope", "self api/buy")

It’s just as easy to verify a JWT.

String subject = "HACKER"; try { Jws jwtClaims = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt); subject = claims.getBody().getSubject(); //OK, we can trust this JWT } catch (SignatureException e) { //don't trust the JWT! } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 String subject = "HACKER" ; try { Jws jwtClaims = Jwts . parser ( ) . setSigningKey ( key ) . parseClaimsJws ( jwt ) ; subject = claims . getBody ( ) . getSubject ( ) ; //OK, we can trust this JWT } catch ( SignatureException e ) { //don't trust the JWT! }

If the JWT has been tampered with in any way, parsing the claims will throw a SignatureException and the value of the subject variable will stay HACKER . If it’s a valid JWT, then subject will be extracted from it: claims.getBody().getSubject()

What is OAuth?

In the next section, we’ll look at an example using Stormpath’s OAuth2 implementation, which makes use of JWTs.

There’s a lot of confusion around the OAuth2 spec. That’s, in part, because it is really an über spec – it has a lot of complexity. It’s also because OAuth1.a and OAuth2 are very different beasts. We are going to look at a very specific, easy to use, subset of the OAuth2 spec. We have an excellent post that goes into much more detail on What the Heck is OAuth. Here, we’ll give some brief background and then jump right into the examples.

OAuth2 is basically a protocol that supports authorization workflows. What this means is that it gives you a way to ensure that a specific user has permissions to do something.

That’s it.

OAuth2 isn’t meant to do stuff like validate a user’s identity — that’s taken care of by an Authentication service. Authentication is when you validate a user’s identity (like asking for a username / password to log in), whereas authorization is when you check to see what permissions an existing user already has.

Just remember that OAuth2 is a protocol for authorization.

Using OAuth Grant Types for Authorization

Let’s look at a typical OAuth2 interaction.

POST /oauth/token HTTP/1.1 Origin: https://foo.com Content-Type: application/x-www-form-urlencoded grant_type=password&username=username&password=password 1 2 3 4 5 6 POST / oauth / token HTTP / 1.1 Origin : https : //foo.com Content - Type : application / x - www - form - urlencoded grant_type = password &username = username &password = password

grant_type is required. The application/x-www-form-urlencoded content type is required for this type of interaction as well. Given that you are passing the username and password over the wire, you would always want the connection to be secure. The good thing, however, is that the response will have an OAuth2 bearer token. This token will then be used for every interaction between the browser and server going forward. There is a very brief exposure here where the username and password are passed over the wire. Assuming the authentication service on the server verifies the username and password, here’s the response:

HTTP/1.1 200 OK Content-Type: application/json;charset=UTF-8 Cache-Control: no-store Pragma: no-cache { "access_token":"2YotnFZFEjr1zCsicMWpAA...", "token_type":"example", "expires_in":3600, "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA...", "example_parameter":"example_value" } 1 2 3 4 5 6 7 8 9 10 11 12 13 HTTP / 1.1 200 OK Content - Type : application / json ; charset = UTF - 8 Cache - Control : no - store Pragma : no - cache { "access_token" : "2YotnFZFEjr1zCsicMWpAA..." , "token_type" : "example" , "expires_in" : 3600 , "refresh_token" : "tGzv3JOkF0XG5Qx2TlKWIA..." , "example_parameter" : "example_value" }

Notice the Cache-Control and Pragma headers. We don’t want this response being cached anywhere. The access_token is what will be used by the browser in subsequent requests. Again, there is not direct relationship between OAuth2 and JWT. However, the access_token can be a JWT. That’s where the extra benefit of the encoded metadata comes in. Here’s how the access token is leveraged in future requests:

GET /admin HTTP/1.1 Authorization: Bearer 2YotnFZFEjr1zCsicMW... 1 2 3 GET / admin HTTP / 1.1 Authorization : Bearer 2YotnFZFEjr1zCsicMW...

The Authorization header is a standard header. No custom headers are required to use OAuth2. Rather than the type being Basic , in this case the type is Bearer . The access token is included directly after the Bearer keyword. This completes the OAuth2 interaction for the password grant type. Every subsequent request from the browser can use the Authorizaion: Bearer header with the access token.

There’s another grant type known as client_credentials which uses client_id and client_secret , rather than username and password . This grant type is typically used for API interactions. While the client id and slient secret function similarly to a username and password, they are usually of a higher quality security and not necessarily human readable.

Take Us Home: OAuth2 Java Example

We’ve arrived! It’s time to dig into some specific code that demonstrates JWTs in action.

Spring Boot Web MVC

There are a number of examples in the Stormpath Java SDK. Here, we are going to look at a Spring Boot Web MVC example. Here’s the HelloController from the example:

@Controller public class HelloController { @RequestMapping("/") public String home(HttpServletRequest request, Model model) { String name = "World"; Account account = AccountResolver.INSTANCE.getAccount(request); if (account != null) { name = account.getGivenName(); model.addAttribute(account); } model.addAttribute("name", name); return "hello"; } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @ Controller public class HelloController { @ RequestMapping ( "/" ) public String home ( HttpServletRequest request , Model model ) { String name = "World" ; Account account = AccountResolver . INSTANCE . getAccount ( request ) ; if ( account ! = null ) { name = account . getGivenName ( ) ; model . addAttribute ( account ) ; } model . addAttribute ( "name" , name ) ; return "hello" ; } }

The key line, for the purposes of this demonstration is:

Account account = AccountResolver.INSTANCE.getAccount(request);

Behind the scenes, account will resolve to an Account object (and not be null ) ONLY if an authenticated session is present.

Build and Run the Example Code

To build and run this example, do the following:

git checkout stormpath-sdk-root-1.1.1 ... mvn clean install -Dmaven.test.skip=true -Dmaven.javadoc.skip=true ... [INFO] ------------------------------------------------------------------------ [INFO] Reactor Summary: [INFO] [INFO] Stormpath Java SDK ................................. SUCCESS [ 1.364 s] [INFO] Stormpath Java SDK :: API .......................... SUCCESS [ 1.716 s] [INFO] Stormpath Java SDK :: Impl ......................... SUCCESS [ 2.042 s] ... [INFO] Stormpath Java SDK :: Extensions ................... SUCCESS [ 0.058 s] [INFO] Stormpath Java SDK :: Examples ..................... SUCCESS [ 0.141 s] [INFO] Stormpath Java SDK :: Examples :: Servlet Container-based Webapp SUCCESS [ 1.225 s] [INFO] Stormpath Java SDK :: Examples :: Spring ........... SUCCESS [ 0.196 s] [INFO] Stormpath Java SDK :: Examples :: Spring Web MVC ... SUCCESS [ 0.931 s] [INFO] Stormpath Java SDK :: Examples :: Spring Boot ...... SUCCESS [ 0.497 s] [INFO] Stormpath Java SDK :: Examples :: Spring Boot Webapp SUCCESS [ 0.484 s] [INFO] Stormpath Java SDK :: Examples :: Spring Boot + Angular Webapp SUCCESS [ 0.491 s] [INFO] Stormpath Java SDK :: Examples :: Spring Security Web MVC SUCCESS [ 0.795 s] [INFO] Stormpath Java SDK :: Examples :: Spring Security :: Spring Boot Webapp SUCCESS [ 0.525 s] [INFO] Stormpath Java SDK :: Examples :: Spring Security :: Spring Boot :: Bare Bones SUCCESS [ 0.468 s] [INFO] Stormpath Java SDK :: Examples :: Spring Boot Default SUCCESS [ 0.467 s] [INFO] Stormpath Java SDK :: Examples :: Zuul Spring Cloud Starter SUCCESS [ 0.572 s] [INFO] Stormpath Java SDK :: Examples :: Quickstart Code .. SUCCESS [ 1.346 s] ... [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 21.703 s [INFO] Finished at: 2016-10-20T22:36:07-04:00 [INFO] Final Memory: 117M/1574M [INFO] ------------------------------------------------------------------------ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 git checkout stormpath - sdk - root - 1.1.1 . . . mvn clean install - Dmaven . test . skip = true - Dmaven . javadoc . skip = true . . . [ INFO ] ------------------------------------------------------------------------ [ INFO ] Reactor Summary : [ INFO ] [ INFO ] Stormpath Java SDK . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . SUCCESS [ 1.364 s ] [ INFO ] Stormpath Java SDK : : API . . . . . . . . . . . . . . . . . . . . . . . . . . SUCCESS [ 1.716 s ] [ INFO ] Stormpath Java SDK : : Impl . . . . . . . . . . . . . . . . . . . . . . . . . SUCCESS [ 2.042 s ] . . . [ INFO ] Stormpath Java SDK : : Extensions . . . . . . . . . . . . . . . . . . . SUCCESS [ 0.058 s ] [ INFO ] Stormpath Java SDK : : Examples . . . . . . . . . . . . . . . . . . . . . SUCCESS [ 0.141 s ] [ INFO ] Stormpath Java SDK : : Examples : : Servlet Container - based Webapp SUCCESS [ 1.225 s ] [ INFO ] Stormpath Java SDK : : Examples : : Spring . . . . . . . . . . . SUCCESS [ 0.196 s ] [ INFO ] Stormpath Java SDK : : Examples : : Spring Web MVC . . . SUCCESS [ 0.931 s ] [ INFO ] Stormpath Java SDK : : Examples : : Spring Boot . . . . . . SUCCESS [ 0.497 s ] [ INFO ] Stormpath Java SDK : : Examples : : Spring Boot Webapp SUCCESS [ 0.484 s ] [ INFO ] Stormpath Java SDK : : Examples : : Spring Boot + Angular Webapp SUCCESS [ 0.491 s ] [ INFO ] Stormpath Java SDK : : Examples : : Spring Security Web MVC SUCCESS [ 0.795 s ] [ INFO ] Stormpath Java SDK : : Examples : : Spring Security : : Spring Boot Webapp SUCCESS [ 0.525 s ] [ INFO ] Stormpath Java SDK : : Examples : : Spring Security : : Spring Boot : : Bare Bones SUCCESS [ 0.468 s ] [ INFO ] Stormpath Java SDK : : Examples : : Spring Boot Default SUCCESS [ 0.467 s ] [ INFO ] Stormpath Java SDK : : Examples : : Zuul Spring Cloud Starter SUCCESS [ 0.572 s ] [ INFO ] Stormpath Java SDK : : Examples : : Quickstart Code . . SUCCESS [ 1.346 s ] . . . [ INFO ] ------------------------------------------------------------------------ [ INFO ] BUILD SUCCESS [ INFO ] ------------------------------------------------------------------------ [ INFO ] Total time : 21.703 s [ INFO ] Finished at : 2016 - 10 - 20T22 : 36 : 07 - 04 : 00 [ INFO ] Final Memory : 117M / 1574M [ INFO ] ------------------------------------------------------------------------

Launch the Spring Boot Example

You can then launch the Spring Boot example like so:

cd examples/spring-boot-webmvc java -jar target/stormpath-sdk-examples-spring-boot-web-1.1.1.jar . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.4.0.RELEASE) 2016-10-20 22:40:49.347 INFO 29457 --- [ main] c.s.spring.boot.examples.Application : Starting Application v1.1.1 on Micah-MBP with PID 29457 (/Users/dogeared/Projects/StormPath/stormpath-sdk-java/examples/spring-boot-webmvc/target/stormpath-sdk-examples-spring-boot-web-1.1.1.jar started by dogeared in /Users/dogeared/Projects/StormPath/stormpath-sdk-java/examples/spring-boot-webmvc) ... skipped output ... 2016-10-20 22:40:53.523 INFO 29457 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http) 2016-10-20 22:40:53.527 INFO 29457 --- [ main] c.s.spring.boot.examples.Application : Started Application in 4.553 seconds (JVM running for 7.144) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 cd examples / spring - boot - webmvc java - jar target / stormpath - sdk - examples - spring - boot - web - 1.1.1.jar . ____ _ __ _ _ / \ \ / ___ '_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | ' _ | '_| | ' _ \ / _ ` | \ \ \ \ \ \ / ___ ) | | _ ) | | | | | | | ( _ | | ) ) ) ) ' | ____ | . __ | _ | | _ | _ | | _ \ __ , | / / / / ========= | _ | ============== | ___ /=/ _ / _ / _ / : : Spring Boot : : ( v1 . 4.0.RELEASE ) 2016 - 10 - 20 22 : 40 : 49.347 INFO 29457 --- [ main ] c . s . spring . boot . examples . Application : Starting Application v1 . 1.1 on Micah - MBP with PID 29457 ( / Users / dogeared / Projects / StormPath / stormpath - sdk - java / examples / spring - boot - webmvc / target / stormpath - sdk - examples - spring - boot - web - 1.1.1.jar started by dogeared in / Users / dogeared / Projects / StormPath / stormpath - sdk - java / examples / spring - boot - webmvc ) . . . skipped output . . . 2016 - 10 - 20 22 : 40 : 53.523 INFO 29457 --- [ main ] s . b . c . e . t . TomcatEmbeddedServletContainer : Tomcat started on port ( s ) : 8080 ( http ) 2016 - 10 - 20 22 : 40 : 53.527 INFO 29457 --- [ main ] c . s . spring . boot . examples . Application : Started Application in 4.553 seconds ( JVM running for 7.144 )

NOTE: This assumes that you’ve already setup a Stormpath account and that your api keys are located in ~/.stormpath/apiKey.properties . Look here for more information on quick setup up of Stormpath with Spring Boot.

Authenticate with a JSON Web Token (or Not)

Now, we can exercise the example and show some JWTs in action! First, hit your endpoint without any authentication. I like to use httpie, but any command line http client will do.

You’ll see in the HTML that’s returned:

<h1>Hello, World!</h1> 1 2 < h1 > Hello , World ! < / h1 >

The -v parameter produces verbose output and shows all the headers for the request and the response. In this case, the output message contains simply: Hello World! . This is because there is not an established session.

Authenticate with the Stormpath OAuth Endpoint

Now, let’s hit the oauth endpoint so that our server can authenticate with Stormpath. You may ask, “What oauth endpoint?” The controller above doesn’t indicate any such endpoint. Are there other controllers with other endpoints in the example? No, there are not! Stormpath gives you oauth (and many other) endpoints right out-of-the-box. Check it out:

http -v -f POST http://localhost:8080/oauth/token \ 'Origin:http://localhost:8080' \ grant_type=password username=micah+demo@stormpath.com password=123456aA POST /oauth/token HTTP/1.1 Content-Type: application/x-www-form-urlencoded; charset=utf-8 Host: localhost:8080 Origin: http://localhost:8080 User-Agent: HTTPie/0.9.2 grant_type=password&username=micah%2Bdemo.jsmith%40stormpath.com&password=123456aA HTTP/1.1 200 OK Cache-Control: no-store Content-Length: 325 Content-Type: application/json;charset=UTF-8 Date: Tue, 04 Aug 2015 16:02:08 GMT Pragma: no-cache Server: Apache-Coyote/1.1 Set-Cookie: account=eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxNDQyNm...wcXrS5yGtUoewAKqoqL5JhIQ109s1FMNopL_50HR_t4; Expires=Wed, 05-Aug-2015 16:02:08 GMT; Path=/; HttpOnly { "access_token": "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxNDQyNm...wcXrS5yGtUoewAKqoqL5JhIQ109s1FMNopL_50HR_t4", "expires_in": 259200, "token_type": "Bearer" } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 http - v - f POST http : //localhost:8080/oauth/token \ 'Origin:http://localhost:8080' \ grant_type = password username = micah + demo @ stormpath . com password = 123456aA POST / oauth / token HTTP / 1.1 Content - Type : application / x - www - form - urlencoded ; charset = utf - 8 Host : localhost : 8080 Origin : http : //localhost:8080 User - Agent : HTTPie / 0.9.2 grant_type = password &username=micah%2Bdemo.jsmith%40stormpath.com&password=123456aA HTTP/1.1 200 OK Cache-Control: no-store Content-Length: 325 Content-Type: application/json; charset = UTF - 8 Date : Tue , 04 Aug 2015 16 : 02 : 08 GMT Pragma : no - cache Server : Apache - Coyote / 1.1 Set - Cookie : account = eyJhbGciOiJIUzI1NiJ9 . eyJqdGkiOiIxNDQyNm . . . wcXrS5yGtUoewAKqoqL5JhIQ109s1FMNopL_50HR_t4 ; Expires = Wed , 05 - Aug - 2015 16 : 02 : 08 GMT ; Path =/ ; HttpOnly { "access_token" : "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxNDQyNm...wcXrS5yGtUoewAKqoqL5JhIQ109s1FMNopL_50HR_t4" , "expires_in" : 259200 , "token_type" : "Bearer" }

There’s a lot going on here, so let’s break it down.

On the first line, I am telling httpie that I want to make a form url-encoded POST – that’s what the -f and POST parameters do. I am hitting the /oauth/token endpoint of my locally running server. I specify an Origin header. This is required to interact with Stormpath for the security reasons we talked about previously. As per the OAuth2 spec, I am passing up grant_type=password along with a username and password .

The response has a Set-Cookie header as well as a JSON body containing the OAuth2 access token. And guess what? That access token is also a JWT. Here are the claims decoded:

{ "jti": "14426d13-f58b-4a41-bede-0b343fcd1ac0", "iat": 1438704128, "sub": "https://api.stormpath.com/v1/accounts/5oM4WI3P4xIwp4WiDbRj80", "exp": 1438963328 } 1 2 3 4 5 6 7 { "jti" : "14426d13-f58b-4a41-bede-0b343fcd1ac0" , "iat" : 1438704128 , "sub" : "https://api.stormpath.com/v1/accounts/5oM4WI3P4xIwp4WiDbRj80" , "exp" : 1438963328 }

Notice the sub key. That’s the full Stormpath URL to the account I authenticated as. Now, let’s hit our basic Hello World endpoint again, only this time, we will use the OAuth2 access token:

http -v localhost:8080 \ Authorization:"Bearer eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxNDQyNm...wcXrS5yGtUoewAKqoqL5JhIQ109s1FMNopL_50HR_t4" GET / HTTP/1.1 Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxNDQyNm...wcXrS5yGtUoewAKqoqL5JhIQ109s1FMNopL_50HR_t4 Connection: keep-alive Host: localhost:8080 User-Agent: HTTPie/0.9.2 1 2 3 4 5 6 7 8 9 http - v localhost : 8080 \ Authorization : "Bearer eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxNDQyNm...wcXrS5yGtUoewAKqoqL5JhIQ109s1FMNopL_50HR_t4" GET / HTTP / 1.1 Authorization : Bearer eyJhbGciOiJIUzI1NiJ9 . eyJqdGkiOiIxNDQyNm . . . wcXrS5yGtUoewAKqoqL5JhIQ109s1FMNopL_50HR_t4 Connection : keep - alive Host : localhost : 8080 User - Agent : HTTPie / 0.9.2

This time, the output contains:

<h1>Hello, Micah!</h1> 1 2 < h1 > Hello , Micah ! < / h1 >

Notice on that the message addresses us by name. Now that we’ve established an authenticated session with Stormpath using OAuth2, these lines in the controller retrieve the first name:

Account account = AccountResolver.INSTANCE.getAccount(request); if (account != null) { name = account.getGivenName(); } 1 2 3 4 5 Account account = AccountResolver . INSTANCE . getAccount ( request ) ; if ( account ! = null ) { name = account . getGivenName ( ) ; }

Summary: Token Authentication for Java Apps

In this post, we’ve looked at how token authentication with JWTs not only addresses the concerns of traditional approaches, but also gives us the benefit of inspectable metadata and strong cryptographic signatures.

We gave an overview of the OAuth2 protocol and went through a detailed example of how Stormpath’s implementation of OAuth2 uses JWTs.

Here are some other links to posts on token based authentication, JWTs and Spring Boot:

Token Based Authentication for Angular.js

JJWT – JSON Web Token for Java and Android

Spring Boot Webapp Sample Quickstart

JWT Specification

Feel free to drop a line over to email or to me personally anytime.