Jump to content
  • TIBCO BusinessWorks? Tutorial: Using JSON Web Tokens (JWT)


    Manoj Chaurasia

    Table of Contents


    About this tutorial

    Content and duration

    JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA.

    Here are some scenarios where JSON Web Tokens are useful:

    • Authentication: This is the most common scenario for using JWT. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token. Single Sign On is a feature that widely uses JWT nowadays, because of its small overhead and its ability to be easily used across different domains.
    • Information Exchange: JSON Web Tokens are a good way of securely transmitting information between parties, because as they can be signed, for example using public/private key pairs, you can be sure that the senders are who they say they are. Additionally, as the signature is calculated using the header and the payload, you can also verify that the content has not been tampered with.

    The tutorial, which should take you 15 to 20 minutes to complete, aims at illustrating how a JWT can be used for Authentication (and Authorisation) purposes when exposing a BusinessWorks REST Service. It also ships with Auth0's Java JWT library (version 3.2.0) packaged as a shared BusinessWorks module for reuse within your own BusinessWorks designs, or as a source of inspiration for you own packaging.

    What you will use

    • TIBCO BusinessWorks?
      • Classic from version 6.3
      • Container Edition from version 2.3
      • Cloud Integration with Studio version 1.0.4 V23 2017-02-24 - which is the one used for the attached projects
    • Java 1.8
    • JSON Web Token
    • Auth0's Java JWT Library

    Before you begin, prerequisites

    We strongly encourage you to go through the basics of JWT and to have experimented the creation of your own JWTs before setting forth.

    Moreover, we assume that you have some level of familiarity with one of the above listed versions of TIBCO BusinessWorks?, including:

    • A fully functional development and testing environment,
    • A reasonable command of the exposition of REST Services,
    • Ideally previous experience with BW mappings and JAVA activities.

    Finally, you will need an API/REST testing tool for the third step of the tutorial. We can recommend several options:

    • cURL,
    • Postdot Technologie's Postman (used in the illustrations of this tutorial),
    • SmartBear's Ready! API.

    Step 1: Producing a test JSON Web Token

    In order to have an end-to-end scenario, we will mimick the production of the JWT by a login page. Instead, we will produce the JWT by hand and use it to get access to the server-side resource.

    We shall use a nifty website that is practically the Swiss Army knife of JWTs, JWT Builder. Do fill in the first couple of sections as follows (note that Issued At is set to now and Expiration to in 1 year):

    jwt1.thumb.png.67a957124e23bd5752d4c9f863228267.png

    Then move to the fourth section, and enter TIBCOSoftware2017 as the Key (which is a type of shared secret between the producer and the consumer of the token). Different strengths of hash-based algorithms can be applied - and the JWT standard caters to cyphers based on RSA public/private keys instead. We shall keep the default HS256:

    jwt2.thumb.png.fe5fb6d7e6ec5ce0122736b335ebac2e.png

    This will produce a signed JWT as illustrated below, that you will want to copy into your clipboard or in a text editor.

    jwt3.thumb.png.fc6d18dbccb29c4b96f6821192063830.png

    Alternatively, use the following one (which is valid until 2018-05-24T01:51:16.628Z):

     eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ3d3cudGliY28uY29tIiwiaWF0IjoxNDk1NTkwNjU1LCJleHAiOjE1MjcxMjY2NzYsImF1ZCI6Imp3dHNhbXBsZS50aWJjby5jb20iLCJzdWIiOiJlc2Nod2VpdEB0aWJjby5jb20iLCJHaXZlbk5hbWUiOiJFbW1hbnVlbCIsIlN1cm5hbWUiOiJTY2h3ZWl0emVyIiwiQWNjZXNzIjoiQWxsIn0.nXjY7placf8fRqFFX_2gDIo0UkwPa0q3aTZxfj0A-h4

     

    Congratulations, you have generated your first JWT!


    Step 2: Importing the attached archive

    For the sake of speeding up our exploration, we have attached a TIBCO Business Studio archive to this page. Save it locally on your computer and let us import it into a fresh workspace:

    jwt4.thumb.png.caa469fc658551d29e5ff61a697914a9.png

    Select the Existing Studio Projects into Workspace option:

    jwt5.thumb.png.88842561a73edb6994f8589eb4d0698e.png

    We will pick the projects from the archive file you just downloaded:

    jwt6.png.a79a1138313fe13b2499be1e0bda8c01.png

    Then please navigate to the location where you placed the file and select it:

    jwt7.thumb.png.e8212f054f42236d89956c0313aece11.png

    Finally, confirm the import for all contained projects:

    jwt8.thumb.png.f6d05ca62a5736db11429c2a68fb9dea.png

    These should appear shortly in your Project Explorer.

    Congratulations, you have successfully imported the sample projects and are ready to test it out. But make sure you wait the build process completion before moving to the next step.


    Step 3: Testing

    We have put together a reusable BusinessWorks process to validate a JSON Web Token sent as authentication to an exposed REST Service. Our sample exposes a simplistic resource on http://localhost:8080/test for GET operations, returning a single String value. Feel free to adjust the path or port as required in your environment, especially if 8080 is a busy port.

    Find the details of the associated swagger below:

     {   "swagger" : "2.0",   "info" : {     "version" : "1.0",     "title" : "Test Resource",     "description" : "Test Resource"   },   "host" : "localhost:8080",   "basePath" : "/",   "schemes" : [ "http" ],   "paths" : {     "/test" : {       "get" : {         "description" : "",         "operationId" : "get-test",         "consumes" : [ "application/json" ],         "produces" : [ "application/json" ],         "parameters" : [ ],         "responses" : {           "200" : {             "description" : "a string to be returned",             "schema" : {               "type" : "string"             }           }         }       }     }   },   "definitions" : { } }

     

    The service implementation is equally simplistic, as illustrated below:

    • Validate the JSON Web Token against the current date and specific issuer, audience, and subject values which must be a perfect match with those of the token,
    • Log the validation status as well as the private claims,
    • Return OK if validated, KO otherwise.

    jwt10.thumb.png.41045c0419ec16c415e4a0f464a7dacd.png

    As you know (or may have learned when reviewing JWT), the tokens are usually sent as an HTTP Authorization header of the Bearer type.

    Run the JWT. Test the application locally, and let us perform a few invocations (all illustrations with Postman).

    No authorization token

    We first perform the GET operation on http://localhost:8080/test with only an Accept header:

     Accept: application/json,text/html;q=0.9,application/xhtml+xml;q=0.8,*/*;q=0.7

     

    jwt11.thumb.png.32112569457937e0dfdb6b58b8e41808.png

    As you can see, the authentication fails.

    Correct authorization token

    Let us reperform the test, but this time we shall add the correct Authorization header (feel free to replace with your own token details):

     Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ3d3cudGliY28uY29tIiwiaWF0IjoxNDk1NTkwNjU1LCJleHAiOjE1MjcxMjY2NzYsImF1ZCI6Imp3dHNhbXBsZS50aWJjby5jb20iLCJzdWIiOiJlc2Nod2VpdEB0aWJjby5jb20iLCJHaXZlbk5hbWUiOiJFbW1hbnVlbCIsIlN1cm5hbWUiOiJTY2h3ZWl0emVyIiwiQWNjZXNzIjoiQWxsIn0.nXjY7placf8fRqFFX_2gDIo0UkwPa0q3aTZxfj0A-h4

     

    jwt11.thumb.png.436654d7d057526913e6b83c1ab0e06c.png

    This time, the token is validated.

    Notice the content of the log. It confirms the validation status, and lists the private claims of the token as a comma-separated list of KeyValues:

     15:15:23.641 INFO  [bwEngThread:In-Memory Process Worker-2] c.t.b.p.g.Log.JWT.Test.module.Log - Validation true. AccessAll, GivenNameEmmanuel, SurnameSchweitzer

     

    The token subject or any of its private claims could potentially be used to make decisions when it comes to authorization.

    Tampered-with authorization token

    Replace a few characters of the token, or add them: the token will be refused. You can also try to build a token with a different key, or with characteristics that do not exactly match our expectations: the token will be refused as well.

    jwt13.thumb.png.5e6de5bf0a9857706a0cc057623f1a5a.png

    Congratulations, you have tested the basic principles of JWT in BusinessWorks!


    Step 4: Review the JWT.Validate shared module

    Note that this module is by no means the ultimate implementation, and should only be considered as an illustration. Feel free to suggest or share improvements!

    JWT.Validate.module is a wrapper around auth0's java-jwt library. It consists in a reusable sub-process, a Java class to perform the bulk of the validation, and all the required Java libraries (java-jwtjackson-corejackson-annotationsjackson-databind, and commons-codec).

    jwt.module.ValidateJWT sub-process

    The sub-process is simply a JavaInvoke operation followed by a transformation of its results into XML usable by the sub-process consumer:

    jwt14.thumb.png.7b5f9fe6bc3fa2bbb5d8f905ec03202f.png

    Its input signature is a follows:

     <xsd:element name="Request">   <xsd:complexType>     <xsd:sequence>       <xsd:element maxOccurs="1" minOccurs="1" name="token" type="xsd:string"/>       <xsd:element maxOccurs="1" minOccurs="1" name="secret" type="xsd:string"/>       <xsd:element maxOccurs="1" minOccurs="1" name="issuer" type="xsd:string"/>       <xsd:element maxOccurs="1" minOccurs="1" name="audience" type="xsd:string"/>       <xsd:element maxOccurs="1" minOccurs="1" name="subject" type="xsd:string"/>     </xsd:sequence>   </xsd:complexType> </xsd:element>

     

    and its output is as follows:

     <xsd:element name="Response">   <xsd:complexType>     <xsd:sequence>       <xsd:element maxOccurs="1" minOccurs="1" name="validated" type="xsd:boolean"/>       <xsd:element maxOccurs="unbounded" minOccurs="0" name="claims" type="tns:Claim"/     </xsd:sequence>   </xsd:complexType> </xsd:element>  <xsd:complexType name="Claim">   <xsd:sequence>     <xsd:element maxOccurs="1" minOccurs="1" name="key" type="xsd:string"/>     <xsd:element maxOccurs="1" minOccurs="0" name="value" type="xsd:string"/>   </xsd:sequence> </xsd:complexType>

     

    Other approaches could be considered, e.g. having the InvokeJava operation return the decoded token as a JSON string and leverage BusinessWorks JSON operations.

    jwt.tibco.com.Validator class

    The Validator class is a Java Bean easily consumed by BusinessWorks. It leverages java-jwt to decode the token, verify that it has not been tampered with using the secret/key, and perform validation checks (issuer, audience, subject, and dates). It then extracts the non-public claims, which are stored as jwt.tibco.com.Claim entries.

     package jwt.tibco.com;  import java.util.Date; import java.util.Vector; import java.util.Map;  import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; import com.auth0.jwt.exceptions.JWTVerificationException; import com.auth0.jwt.interfaces.Claim; import com.auth0.jwt.interfaces.DecodedJWT;   public class Validator {	 	private static String JWT_Type = "JWT"; 	 	protected boolean validated; 	protected Object[] claims; 	 	public Validator() { 		setValidated(false); 		setClaims(null); 	} 	 	public void validate(String token, String secret, String issuer, String audience, String subject) { 		DecodedJWT jwt = null; 		setValidated(false); 		 		if (token == null || secret == null || issuer == null || audience == null || subject == null) 			return; 		 		try { 			jwt = JWT.require(Algorithm.HMAC256(secret.getBytes())).build().verify(token); 		} catch (JWTVerificationException e) { 			return; 		} 		 		if (jwt == null || jwt.getType() == null || !jwt.getType().contentEquals(JWT_Type)) 			return; 		 		if (!jwt.getIssuer().contentEquals(issuer) || 			!jwt.getAudience().contains(audience) || 			!jwt.getSubject().contentEquals(subject)) 			return; 		 		Date now = new Date(); 		 		if ((jwt.getNotBefore() != null && jwt.getNotBefore().after(now)) || 			(jwt.getExpiresAt() != null && jwt.getExpiresAt().before(now))) 			return; 		 		setValidated(true);  		Map<String, Claim> claimsMap = jwt.getClaims(); 		Vector<jwt.tibco.com.Claim> claimsVector = new Vector<jwt.tibco.com.Claim>(); 		 		if (claimsMap != null) { 			for (Map.Entry<String, Claim> entry : claimsMap.entrySet()) { 				String key = entry.getKey(); 				if (key != null && !key.matches("aud|sub|iss|exp|iat")) {					 					claimsVector.add(new jwt.tibco.com.Claim(key, entry.getValue().asString())); 				} 			}		 		}  		setClaims(claimsVector.isEmpty() ? null : claimsVector.toArray()); 	}  	public boolean isValidated() { return validated; } 	public void setValidated(boolean val) { validated = val; }  	public Object[] getClaims() { return claims; } 	public void setClaims(Object[] val) { claims = (val == null ? new Object[0] : val); } }

     

    jwt.tibco.com.Claim class

    The Claim class is a very simple JavaBean to hold Claims.

     package jwt.tibco.com;  import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable;  public class Claim implements Serializable { 	private static final long serialVersionUID = 7960567494553263091L; 	 	private String key; 	private String value; 	 	public Claim() { setKey(""); setValue(""); } 	public Claim(String key, String value) { setKey(key); setValue(value); }  	public String getKey() { return this.key; } 	public void setKey(String key) { this.key = key; } 	 	public String getValue() { return this.value; } 	public void setValue(String value) { this.value = value; }  	private void readObject(ObjectInputStream aInputStream) throws ClassNotFoundException, IOException { 		//always perform the default de-serialization first 		aInputStream.defaultReadObject(); 	}  	private void writeObject(ObjectOutputStream aOutputStream) throws IOException { 		//perform the default serialization for all non-transient, non-static fields 		aOutputStream.defaultWriteObject(); 	} }

     

    Using the module

    Using the module is just a matter of referencing it your module's dependencies and calling the JWT.Validate.module.jwt.module.ValidateJWT subprocess and pass the appropriate input parameters:

    jwt15.thumb.png.e7dba7cffdd2b6e0a2d9c2018c248bf9.png

    Then, make use of the output parameters:

    jwt16.png.0f5b6710c04867d8fe17b73e91e1916e.png


    Conclusion

    You have now explored the basics of JWTs and how they can be applied to your BusinessWorks REST Services, both for authentication and authorization, once their consumers have identified through separate means and have obtained the JSON Web Token proving their credentials.

    Sit back and relax! And feel free to suggest and share improvements.


    Contributors: Emmanuel Schweitzer

    bw-jwt-validate-20170524.zip


    User Feedback

    Recommended Comments

    There are no comments to display.



    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

×
×
  • Create New...