DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Coding
  3. Languages
  4. From JSON Web Token to Single Sign-On Part 1: Creating the Token

From JSON Web Token to Single Sign-On Part 1: Creating the Token

This is the first entry in a series of articles that offer a solution to Single Sign-on using the JSON Web Token (JWT) standard.

Basel Almustafa user avatar by
Basel Almustafa
·
Sep. 13, 15 · Tutorial
Like (6)
Save
Tweet
Share
49.76K Views

Join the DZone community and get the full member experience.

Join For Free

This is the first entry in a series of articles that offer a method for building Single Sign-on using the JSON Web Token (JWT) standard1. In this entry, you will learn how to implement an authentication service that generates encrypted tokens based on user credentials. This encrypted token can be used to access other sites sharing the same top level domain. It is assumed that each user has a single account, which is used to access multiple sites. If you are not familiar with JWT, I highly recommend reading “The Anatomy of a JSON Web Token.”2

The Authentication Service

The primary goal of the authentication service is to perform the actual authentication of users and to initiate the creation of the token. A user would send an email address and password to a predefined URL, such as https://dzone.com/services/auth, using the POST method over HTTPS. To keep things simple, these credentials would be handled by a servlet’s doPost() method.

@WebServlet("/auth")
public class AuthServlet extends HttpServlet {

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// retrieve the email and password from the request
String email = request.getParameter("email");
String password = request.getParameter("password");

// authenticate user
User user = new User(email, password);
// the authenticate() method code was omitted because it’s domain specific
boolean authenticated = new AuthService().authenticate(user);

if(authenticated) {
	String token = null;
	TokenService tokenService = new TokenService();
	try {
		token = tokenService.generateToken(user);
	} 
	catch (NoSuchAlgorithmException | JOSEException | IOException e) {
// TODO handle exceptions
	}
// TODO store token and send response
} 
else {
	response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
	return;
}
}
}

At this point, the token is ready for storage and the response should have been sent. The details of token storage are discussed next.

Generating an Encrypted JWT

As highlighted in the above code, token generation is handled by the TokenService class. Its implementation is based on the Nimbus JOSE + JWT3 library, which supports both the JSON Web Signature (JWS) and JSON Web Encryption (JWE) standards. To use these two standards, you would need a key to sign and encrypt the token. The code snippet below shows you how to generate a secret key using the KeyGenerator4 class from javax.crypto package. Once the key is generated, its primary encoding format is obtained. This format is needed for signing and encryption of the token.

// generate 256-bit AES key
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(256);
SecretKey key = generator.generateKey();

// get the key in encoded format
byte[] encodedKey = key.getEncoded();

Now that the key is ready, it is time to create the token. This can be done by defining a set of claims that form the payload of the token.

public class TokenService {

private static final long HOUR = 3600 * 1000;
public String generateToken(User user) throws NoSuchAlgorithmException, JOSEException, IOException{
String encryptedToken = null;
        // Prepare JWT with claims set
        Date issueDate = new Date();
        Date expireDate = new Date(issueDate.getTime() + 12 * HOUR);

        JWTClaimsSet claims = new JWTClaimsSet();
        claims.setSubject("authentication token");
        claims.setIssueTime(issueDate);
        claims.setExpirationTime(expireDate);
        claims.setIssuer("https://dzone.com");
        claims.setCustomClaim("email", user.getEmail());
        claims.setCustomClaim("password", user.getPassword());
        // the implementation of getEncodedKey() is similar to the code snippet above
        byte[] encodedKey = new KeyService().getEncodedKey();

// TODO create a signed and encrypted JWT

return encryptedToken;

}
}

The generateToken() methods starts by defining six claims, four of which are used to verify the validity of the token while the remaining two are application specific. These two custom claims are used to hold the email address and password of the user. This set of claims is used in the following code snippet, which is part of the generateToken() method, to create the signed and encrypted token.

// create a signed and encrypted JWT
// the implementation of getEncodedKey() is shown above as a code snippet
byte[] encodedKey = new KeyService().getEncodedKey();
JWSSigner signer = new MACSigner(encodedKey);

// create a token based on the SHA-256 hash algorithm
SignedJWT signedToken = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claims);
signedToken.sign(signer);

// create JWE header that directly uses 256 bit symmetric key for block encryption (AES)
JWEHeader jweHeader = new JWEHeader.Builder( JWEAlgorithm.DIR, EncryptionMethod.A256GCM).contentType("JWT").build();

// create JWE object with the signed token as the payload
JWEObject jweObject = new JWEObject(jweHeader, new Payload(signedToken));

// encryption the token and serialize it to get its compact form
jweObject.encrypt(new DirectEncrypter(encodedKey));
encryptedToken = jweObject.serialize();

This compact form of the token is used to verify user identity on sites sharing the same top level domain. The next section briefly discusses the approach used to share the token between the sites.

Storage of the Encrypted JWT

As previously mentioned, in the doPost() method under “The Authentication Service” section, the token would need to be stored and sent back as part of the response. This is needed because the client who invoked the authentication service would need the token in future communication with other sites. The approach used here to store the token by using a secure cookie. This cookie would be sent by the browser with each request to any of the sites sharing the same top level domain. Here is the code to create the secure cookie:

// create a secure cookie to hold the token
Cookie cookie = new Cookie("jwt-auth-token", token);
// ensure that the cookie is sent over HTTPS only
cookie.setSecure(true);
// ensure that the cookie cannot be accessed from JavaScript
cookie.setHttpOnly(true);
// a negative value means that the cookie is not stored persistently and will be deleted when the Web browser exits
cookie.setMaxAge(-1);
// the domain name within which this cookie is visible; form is according to RFC 2109
cookie.setDomain(".dzone.com");
// make the cookie visible to all pages
cookie.setPath("/");

// add the cookie to the response and return from the doPost() method
response.addCookie(getCookie(token));
response.setStatus(HttpServletResponse.SC_CREATED);
return;

What is Next?

In the next entry of the series, you will learn how to retrieve the token, verify its content, and access the claim set it holds.

References

  1. JSON Web Token (JWT) RFC 7519 https://tools.ietf.org/html/rfc7519
  2. The Anatomy of a JSON Web Token https://scotch.io/tutorials/the-anatomy-of-a-json-web-token
  3. Nimbus JOSE + JWT library http://connect2id.com/products/nimbus-jose-jwt
  4. KeyGenerator API http://docs.oracle.com/javase/7/docs/api/javax/crypto/KeyGenerator.html
JSON Web Service

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Why Open Source Is Much More Than Just a Free Tier
  • Asynchronous HTTP Requests With RxJava
  • The Importance of Delegation in Management Teams
  • Why You Should Automate Code Reviews

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: