Subscribe to Martyr2's Programming Underground        RSS Feed
-----

Generating JSON Web Tokens (JWT) in Java or PHP

Icon Leave Comment
JSON Web Tokens (JWTs) are a mechanism for authentication that is simple to setup and easy to use. However, most of the time when you encounter this topic in a project, you get the advice to just use a JWT package. I am not quite sure why given that creating a token is relatively straight forward if you know how they are built. You can also implement such logic in just a few lines of code. I am not a big fan of adding a dependency just to use a minor feature that you can do yourself easily. In this blog we will show you how to implement this in two languages, Java and PHP. Each language has their own flavor of implementation, but you can see some of the similarities between them.

How are JWTs setup?

The basic structure of JWTs are three segments divided by a period. The first segment is the "header". The second segment is the "payload" and the last is the "signature". Each of these segments are base64UrlEncoded. An example of a JWT might be something like this...

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c




Here is a quick tip, if you are looking for a great resource to play with JWTs try the website jwt.io. The header is encoded from a JSON structure representing the header. This might include properties like the algorithm used and the type (which is often "JWT"). The second segment, the payload, is another JSON structure that has been encoded and may contain some required properties but also some custom ones, both which may be referred to as "claims". Required properties might be "iat" or "sub" but you might include a "name" or "mySpecialToken". JWT services often will use only what they recognize and need. That means that if you include extra data, they will likely ignore it. What each service may require can also differ so be sure to consult the service's website. Lastly, the "signature" is the header concatenated to the payload and run through an hmac algorithm with a special key (only known to you and the service) to generate this value.

Once the service receives the token, it will decode the header, decode the payload and verify the signature using the secret key you and the service shares. If the signatures match (the one provided by you and the one it calculates itself), then the services knows the header and payload have not been tampered with. It can then start processing the payload. For more in-depth information how JWTs are built, please check out the jwt.io website introduction section.

Let's take a look now at how we might implement this in Java...

JWT using Java

First let's start by showing what imports we will need.

import java.util.Base64;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;



Now we can get to the meat of the code.

/**
 * Generates a JWT Token as accepted by a service like Zoom. Adjust the header and payload to fit the
 * service you are interacting with. Be sure to account for all spaces in header/payload! Also token string
 * is without padding. If you need padding, be sure to remove "withoutPadding()" calls.
 * @param  secretKey  Secret key used during encoding.
 * @return returns JWT token string based on header, payload and secretKey
 */
public String generateJWTToken(String secretKey) throws RuntimeException {
   String header = "{\"typ\":\"JWT\",\"alg\":\"HS256\"}";
   String base64UrlHeader = Base64.getUrlEncoder().withoutPadding().encodeToString(header.getBytes());

   // JWT token expires 60 seconds from now
   long timeSecs = (System.currentTimeMillis() / 1000) + 60;

   String payload = "{\"iss\":\"some_key\",\"exp\":" + String.valueOf(timeSecs) + "}";
   String base64UrlPayload = Base64.getUrlEncoder().withoutPadding().encodeToString(payload.getBytes());

   try {
      String base64UrlSignature = hmacEncode(base64UrlHeader + "." + base64UrlPayload, secretKey);

      return base64UrlHeader + "." + base64UrlPayload + "." + base64UrlSignature;
   } catch (Exception e) {
      throw new RuntimeException("Unable to generate a JWT token.");
   }
}

/**
 * Helper method that encodes data using HmacSHA256 and key.
 * @param  data data to encode
 * @param  key  Secret key used during encoding.
 * @return Base64UrlEncoded string without padding
 */
private String hmacEncode(String data, String key) throws Exception {
   Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
   SecretKeySpec secret_key = new SecretKeySpec(key.getBytes(), "HmacSHA256");
   sha256_HMAC.init(secret_key);

   return Base64.getUrlEncoder().withoutPadding().encodeToString(sha256_HMAC.doFinal(data.getBytes()));
}




As you can see from the example we have a function that builds the header and payload then calls to a helper function to generate the signature. We use a few imports to bring in some basic cryptograhy libraries and encoding for base64. In the helper function we use a hmacSHA256 algorithm to generate the signature using the passed in header, payload and key. We return the signature back to our main token function and assemble it with the encoded header and payload to form our token. That is it!


JWT using PHP

By contrast we can do a similar process for PHP (or any language for that matter). Here we don't need any fancy crypto imports, but we can use PHP's built in hash_hmac function (with the raw output flag set to true) and base64_encode to leverage much of the work we need to do. We also created a simple little helper function to change our encoding to base64 URL encoding. If you use this functionality, and match its input/output to that of the Java implementation, you should see they are the same.

Note: Remember that every character counts in the strings you encrypt, even spaces! If you see the two tokens don't match, you might want to look at the input strings for header and footer and make sure you don't have any stray characters lying around.

<?php


function signed_token($secretKey) {
   $header = json_encode(["typ" => "JWT", "alg" => "HS256"]);
   $base64UrlHeader = base64urlencode($header);

   $payload = json_encode(["iss" => "some_key", "exp" => time() + 60]);
   $base64UrlPayload = base64urlencode($payload);

   $signature = hash_hmac("sha256", $base64UrlHeader . "." . $base64UrlPayload, $secretKey, true);
   $base64UrlSignature = base64urlencode($signature);

   return $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;
}


function base64urlencode($str) {
   return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($str));
}

echo signed_token();



Conclusion

Feel free to play around with these implementations and learn from them to write the same process in other languages. In almost any language you use, you will see someone recommending their own JWT library to manage token generation. Rest assured that with the knowledge you have gained here, you won't need a hefty implementation unless you need more. We talked a bit about how JWTs are setup and then showed two implementations of how these tokens can be generated... one in Java and one in PHP. I was able to use this process to tie into Zoom's API and can easily leverage this for other integrations like Zendesk. As with all code on the lexicon, feel free to steal it and make it your own. Let me know if you were able to build something incredible with it!

If you liked this code and are interested in applying it to your own project, but have no idea what project to do, please check out our ebook called "The Programmers Idea Book" which features 200 project ideas, tips for getting started and tons of resources. We also rate each project to make sure that you are not tackling projects over your pay grade. ;)

Thank you for reading!

0 Comments On This Entry

 

December 2019

S M T W T F S
1234567
891011121314
15 16 1718192021
22232425262728
293031    

Recent Entries

Recent Comments

Search My Blog

2 user(s) viewing

2 Guests
0 member(s)
0 anonymous member(s)

Google