Table of Contents
- About this tutorial
- Content and duration
- What you will use
- Before you begin, prerequisites
- Step 1: Producing a test JSON Web Token
- Step 2: Importing the attached archive
- Step 3: Testing
- No authorization token
- Correct authorization token
- Tampered-with authorization token
- Step 4: Review the JWT.Validate shared module
- jwt.module.ValidateJWT sub-process
- jwt.tibco.com.Validator class
- jwt.tibco.com.Claim class
- Using the module
- Conclusion
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):
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:
This will produce a signed JWT as illustrated below, that you will want to copy into your clipboard or in a text editor.
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:
Select the Existing Studio Projects into Workspace option:
We will pick the projects from the archive file you just downloaded:
Then please navigate to the location where you placed the file and select it:
Finally, confirm the import for all contained projects:
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.
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
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
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.
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-jwt, jackson-core, jackson-annotations, jackson-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:
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:
Then, make use of the output parameters:
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
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 accountSign in
Already have an account? Sign in here.
Sign In Now