This site is the archived OWASP Foundation Wiki and is no longer accepting Account Requests.
To view the new OWASP Foundation website, please visit https://owasp.org

Difference between revisions of "JSON Web Token (JWT) Cheat Sheet for Java"

From OWASP
Jump to: navigation, search
m (Objective)
m (Point to the official site)
 
(9 intermediate revisions by 5 users not shown)
Line 1: Line 1:
Last revision (mm/dd/yy): '''{{REVISIONMONTH}}/{{REVISIONDAY}}/{{REVISIONYEAR}}'''
+
__NOTOC__
 +
<div style="width:100%;height:160px;border:0,margin:0;overflow: hidden;">[[File:Cheatsheets-header.jpg|link=]]</div>
  
= Introduction  =
+
The Cheat Sheet Series project has been moved to [https://github.com/OWASP/CheatSheetSeries GitHub]!
  
Many application use '''JSON Web Tokens''' (JWT) to allow the client to indicate is identity for further exchange after authentication.
+
Please visit [https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_Cheat_Sheet_for_Java.html JSON Web Token (JWT) Cheat Sheet for Java] to see the latest version of the cheat sheet.
 
 
From ''https://jwt.io/introduction'':
 
 
 
''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.''
 
 
 
JSON Web Token is used to carry information related to the identity and characteristics (claims) of a client. This "container" is signed by the server in order to avoid that a client tamper it in order to change, for example, the identity or any characteristics (example: change the role from simple user to admin or change the client login). This token is created during authentication (is provided in case of successful authentication) and is verified by the server before any processing. It is used by an application to allow a client to present a token representing his "identity card" (container with all user information about him) to server and allow the server to verify the validity and integrity of the token in a secure way, all of this in a stateless and portable approach (portable in the way that client and server technologies can be different including also the transport channel even if HTTP is the most often used).
 
 
 
= Objective =
 
 
 
This article have for objective to provide several tips in a form of a cheats sheet, for Java technology, in order to prevent common security issues meet when using JWT.
 
 
 
About the code samples provided in tips, a global java project sample has been created in order to show the creation and the validation of a token in a secure way.
 
 
 
This project is available [https://github.com/righettod/poc-jwt here] and it use official [https://jwt.io/#libraries JWT library] with [https://github.com/brix/crypto-js Crypto-JS library].
 
 
 
In the rest of the article, the term '''token''' refer to the '''JSON Web Tokens''' (JWT).
 
 
 
= Issues =
 
 
 
== NONE hashing algorithm ==
 
 
 
=== Symptom ===
 
 
 
This attack, described [https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries/ here] occur when a attacker alter the token and change the hashing algorithm to indicate, through, the ''none'' keyword, that the integrity of the token has already been verified. As explained in the link above ''some libraries treated tokens signed with the none algorithm as a valid token with a verified signature'', so an attacker can alter the token claims and tkey will be trusted by the application.
 
 
 
=== How to prevent ===
 
 
 
First, use a JWT library that is not exposed to this vulnerability.
 
 
 
Last, during token validation, explicitly request that the expected algorithm was used.
 
 
 
=== Implementation example ===
 
 
 
<pre>
 
// HMAC key - Block serialization and storage as String in JVM memory
 
private transient byte[] keyHMAC = ...;
 
 
 
...
 
 
 
//Create a verification context for the token requesting explicitly the use of the HMAC-256 hashing algorithm
 
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(keyHMAC)).build();
 
 
 
//Verify the token, if the verification fail then a exception is throwed
 
DecodedJWT decodedToken = verifier.verify(token);
 
</pre>
 
 
 
== Token sidejacking ==
 
 
 
=== Symptom ===
 
 
 
This attack occur when a token has been intercepted/stolen by a attacker and this one use it to gain access to the system using targeted user identity.
 
 
 
=== How to prevent ===
 
 
 
A way to protect, is to add "user context" in the token. User context here can be composed by the following information:
 
 
 
* Browser unique fingerprint.
 
* IP address: Use this context information when the target users are expected to use the site/service for a "single objective" and during a "short" amount of time like for example a wire transfer  in a bank (connect, perform the transfer and disconnect). There some situation in which IP address can change during the same session like for example when a user access an application through his mobile and he change mobile operator then he change legitimately (often) is IP address. In the same way don’t use only the IP address because this context information will not uniquely identify a user if he access the application through a proxy (for example a corporate proxy).
 
 
 
During token validation, if the received token do not contains the rights context so, it is replayed and then it must be rejected.
 
 
 
=== Implementation example ===
 
 
 
==== Client side: Generate a browser unique fingerprint ====
 
 
 
JavaScript code to generate the browser fingerprint.
 
 
 
<pre>
 
/* Generate a fingerprint string for the browser */
 
function generateFingerprint(){
 
    //Generate a string based on "stable" information taken from the browser
 
    //We call here "stable information", information that normally don't change during the user
 
    //browse the application just after authentication
 
    var fingerprint = [];
 
 
 
    //Take plugins
 
    for(var i = 0; i < navigator.plugins.length; i++){
 
      fingerprint.push(navigator.plugins[i].name);
 
      fingerprint.push(navigator.plugins[i].filename);
 
      fingerprint.push(navigator.plugins[i].description);
 
      fingerprint.push(navigator.plugins[i].version);
 
    }
 
 
 
    //Take User Agent
 
    fingerprint.push(navigator.userAgent);
 
 
 
    //Take Screen resolution
 
    fingerprint.push(screen.availHeight);
 
    fingerprint.push(screen.availWidth);
 
    fingerprint.push(screen.colorDepth);
 
    fingerprint.push(screen.height);
 
    fingerprint.push(screen.pixelDepth);
 
    fingerprint.push(screen.width);
 
 
 
    //Take Graphical card info
 
    //See http://output.jsbin.com/ovekor/3/
 
    try {
 
        //Add a Canvas element if the body do not contains one
 
        if ( $("#glcanvas").length == 0 ){
 
            $(document.body).append("<canvas id='glcanvas'></canvas>");
 
        }
 
        //Get ref on Canvas
 
        var canvas = document.getElementById("glcanvas");
 
        //Retrieve Canvas properties
 
    gl = canvas.getContext("experimental-webgl");
 
    gl.viewportWidth = canvas.width;
 
    gl.viewportHeight = canvas.height;
 
        fingerprint.push(gl.getParameter(gl.VERSION));
 
        fingerprint.push(gl.getParameter(gl.SHADING_LANGUAGE_VERSION));
 
        fingerprint.push(gl.getParameter(gl.VENDOR));
 
        fingerprint.push(gl.getParameter(gl.RENDERER));
 
        fingerprint.push(gl.getSupportedExtensions().join());
 
    } catch (e) {
 
        //Get also error because it's will be stable too..
 
        fingerprint.push(e);
 
    }
 
 
 
    //Last and, in order to made this browser unique, generate a random ID that we will store
 
    //in local storage (in order to be persistent after browser close/reopen)
 
    //Add this ID because, in Enterprise, most of the time browser have the same configuration
 
    var browserUniqueID = localStorage.getItem("browserUniqueID");
 
    if (browserUniqueID === null) {
 
      localStorage.setItem("browserUniqueID", CryptoJS.lib.WordArray.random(80));
 
      browserUniqueID = localStorage.getItem("browserUniqueID");
 
    }
 
    fingerprint.push(browserUniqueID);
 
 
 
    return fingerprint.join();
 
}
 
</pre>
 
 
 
 
 
JavaScript code to generate a hash of the fingerprint that will be sent to the server for each request.
 
A hash is send in order to avoid to send user browser properties on the wire.
 
 
 
<pre>
 
//Call the fingerprint dedicated function
 
var fingerprint = generateFingerprint();
 
//Use CryptoJS library ot generate a hex encoded string of the hash of the fingerprint
 
var fingerprintHash = CryptoJS.SHA256(fingerprint);
 
</pre>
 
 
 
 
 
==== Server side: Token creation and validation ====
 
 
 
Code to extract the client IP.
 
 
 
'''Remark:'''
 
 
 
An issue exists in this implementation proposal about the retrieving of the client IP. Indeed, if the header ''X-Forwarded-For'' is present
 
then the method ''retrieveClientIP()'' will get the client IP from this header according to the header expected content.
 
 
 
The issue occur when the Web Application Firewall append the existing header ''X-Forwarded-For'' of the incoming request (add the client real network IP at the end of the list)
 
instead of overwriting the header content with the real network IP of the incoming request.
 
In this way, an attacker can add the header ''X-Forwarded-For'' in his request and set an arbitrary IP that will be taken as the client IP by the the method ''retrieveClientIP()''.
 
 
 
If you don't have any Web Application Firewall or Reverse Proxy in front of your application then use directly the IP from the ''HttpServletRequest'' instance.
 
 
 
<pre>
 
/**
 
* Return the client IP address
 
* @param request Incoming HTTP request
 
* @return The client IP address
 
* @throws IllegalArgumentException If the IP retrieved is invalid
 
* @see "https://en.wikipedia.org/wiki/X-Forwarded-For"
 
*/
 
private String retrieveClientIP(HttpServletRequest request) throws IllegalArgumentException{
 
String address;
 
//Get IP from X-Forwarded-For header or from the request object directly
 
String ip = request.getHeader("X-Forwarded-For");
 
if(ip != null){
 
    if(ip.contains(",")){
 
ip = ip.split(",")[0];
 
    }
 
    address = ip.trim();
 
}else{
 
    address = request.getRemoteAddr();
 
}
 
//Validate IP format
 
//"InetAddressValidator" came from "commons-validator" API
 
if(!InetAddressValidator.getInstance().isValid(address)){
 
    throw new IllegalArgumentException("Invalid IP address !");
 
}
 
 
 
return address;
 
}
 
</pre>
 
 
 
 
 
Code to create the token after success authentication.
 
 
 
<pre>
 
// HMAC key - Block serialization and storage as String in JVM memory
 
private transient byte[] keyHMAC = ...;
 
 
 
...
 
 
 
//Create the token with a validity of 15 minutes and client context (IP + Browser fingerprint SHA256 digest HEX encoded) information
 
Calendar c = Calendar.getInstance();
 
Date now = c.getTime();
 
c.add(Calendar.MINUTE, 15);
 
Date expirationDate = c.getTime();
 
Map<String, Object> headerClaims = new HashMap<>();
 
headerClaims.put("typ", "JWT");
 
String token = JWT.create().withSubject(login)
 
                      .withExpiresAt(expirationDate)
 
                      .withIssuer(issuerID)
 
                      .withIssuedAt(now)
 
                      .withNotBefore(now)
 
                      .withClaim("clientIP", retrieveClientIP(request))
 
                      .withClaim("browserFingerprintDigest", browserFingerprintDigest)
 
                      .withHeader(headerClaims)
 
                      .sign(Algorithm.HMAC256(keyHMAC));
 
</pre>
 
 
 
 
 
Code to validate the token.
 
 
 
<pre>
 
// HMAC key - Block serialization and storage as String in JVM memory
 
private transient byte[] keyHMAC = ...;
 
 
 
...
 
 
 
//Create a verification context for the token
 
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(keyHMAC))
 
                              .withIssuer(issuerID)
 
                              .withClaim("clientIP", retrieveClientIP(request))
 
                              .withClaim("browserFingerprintDigest", browserFingerprintDigest)
 
                              .build();
 
 
 
//Verify the token, if the verification fail then a exception is throwed
 
DecodedJWT decodedToken = verifier.verify(token);
 
</pre>
 
 
 
 
 
== Token information disclosure ==
 
 
 
=== Symptom ===
 
 
 
This attack occur when a attacker access to a token (or a set of tokens) and extract information stored into it (JWT token information are base64 encoded at the basis) in order to obtains information about the system. Information can be for example the security roles, login format...
 
 
 
=== How to prevent ===
 
 
 
A way to protect, is to cipher the token using for example a symetric algorithm.
 
 
 
It's also important to protect the ciphered data against attack like [[Testing_for_Padding_Oracle_(OTG-CRYPST-002)|Padding Oracle]] or any other attack using cryptanalysis.
 
 
 
In order to achieve all these goals, the algorithm ''AES-GCM'' can be used in conjunction with ''Additional Authentication Data (AAD)'' feature.
 
 
 
A database can be used to store the ''NONCE'' and the ''AAD'' associated to a token.
 
 
 
'''Note:'''
 
 
 
Here ciphering is added mainly to hide internal information but it's very important to remember that the first protection against tampering of the JWT token is the signature so, the token signature and is verification must be always in place.
 
 
 
=== Implementation example ===
 
 
 
==== Token ciphering ====
 
 
 
Database structure.
 
 
 
<pre>
 
create table if not exists nonce(jwt_token_digest varchar(255) primary key, gcm_nonce varchar(255) not null unique, gcm_aad varchar(255) not null unique);
 
create index if not exists idx_nonce on nonce(gcm_nonce);
 
</pre>
 
 
 
 
 
Code in charge of managing the ciphering.
 
 
 
<pre>
 
/**
 
* Handle ciphering and deciphering of the token using AES-GCM.
 
* Use a DB in order to link a GCM NONCE to a ciphered message and ensure that a NONCE is never reused
 
* and also allow use of several application nodes in load balancing.
 
*/
 
public class TokenCipher {
 
 
 
    /** AES-GCM parameters */
 
    private static final int GCM_NONCE_LENGTH = 12; // in bytes
 
 
 
    /** AES-GCM parameters */
 
    private static final int GCM_TAG_LENGTH = 16; // in bytes
 
 
 
    /**Secure random generator */
 
    private final SecureRandom secRandom = new SecureRandom();
 
 
 
    /** DB Connection */
 
    @Resource("jdbc/storeDS")
 
    private DataSource storeDS;
 
 
 
    /**
 
    * Cipher a JWT
 
    * @param jwt Token to cipher
 
    * @param key Ciphering key
 
    * @return The ciphered version of the token encoded in HEX
 
    * @throws Exception If any issue occur during token ciphering operation
 
    */
 
    public String cipherToken(String jwt, byte[] key) throws Exception {
 
        //Verify parameters
 
        if(jwt == null || jwt.isEmpty() || key == null || key.length == 0){
 
            throw new IllegalArgumentException("Both parameters must be specified !");
 
        }
 
 
 
        //Generate a NONCE
 
        //NOTE: As in the DB, the column to store the NONCE is flagged UNIQUE then the insert will fail
 
        //if the NONCE already exists, normally as we use the Java Secure Random implementation
 
        //it will never happen.
 
        final byte[] nonce = new byte[GCM_NONCE_LENGTH];
 
        secRandom.nextBytes(nonce);
 
 
 
        //Prepare ciphering key from bytes provided
 
        SecretKey aesKey = new SecretKeySpec(key, 0, key.length, "AES");
 
 
 
        //Setup Cipher
 
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "SunJCE");
 
        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
 
        cipher.init(Cipher.ENCRYPT_MODE, aesKey, spec);
 
 
 
        //Add "Additional Authentication Data" (AAD) in order to operate in AEAD mode - Generate it
 
        byte[] aad = new byte[32];
 
        secRandom.nextBytes(aad);
 
        cipher.updateAAD(aad);
 
 
 
        //Cipher the token
 
        byte[] cipheredToken = cipher.doFinal(jwt.getBytes("utf-8"));
 
 
 
        //Compute a SHA256 of the ciphered token
 
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
 
        byte[] cipheredTokenDigest = digest.digest(cipheredToken);
 
 
 
        //Store GCM NONCE and GCM AAD
 
        this.storeNonceAndAAD(DatatypeConverter.printHexBinary(nonce), DatatypeConverter.printHexBinary(aad),
 
        DatatypeConverter.printHexBinary(cipheredTokenDigest));
 
 
 
        return DatatypeConverter.printHexBinary(cipheredToken);
 
    }
 
 
 
    /**
 
    * Decipher a JWT
 
    * @param jwtInHex Token to decipher encoded in HEX
 
    * @param key Ciphering key
 
    * @return The token in clear text
 
    * @throws Exception If any issue occur during token deciphering operation
 
    */
 
    public String decipherToken(String jwtInHex, byte[] key) throws Exception{
 
        //Verify parameters
 
        if(jwtInHex == null || jwtInHex.isEmpty() || key == null || key.length == 0){
 
            throw new IllegalArgumentException("Both parameters must be specified !");
 
        }
 
 
 
        //Decode the ciphered token
 
        byte[] cipheredToken = DatatypeConverter.parseHexBinary(jwtInHex);
 
 
 
        //Compute a SHA256 of the ciphered token
 
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
 
        byte[] cipheredTokenDigest = digest.digest(cipheredToken);
 
 
 
        //Read the GCM NONCE and GCM AAD associated from the DB
 
        Map<String,String> gcmInfos = this.readNonceAndAAD(DatatypeConverter.printHexBinary(cipheredTokenDigest));
 
        if(gcmInfos == null){
 
            throw new Exception("Cannot found a NONCE and AAD associated to the token provided !");
 
        }
 
        byte[] nonce = DatatypeConverter.parseHexBinary(gcmInfos.get("NONCE"));
 
        byte[] aad = DatatypeConverter.parseHexBinary(gcmInfos.get("AAD"));
 
 
 
        //Prepare ciphering key from bytes provided
 
        SecretKey aesKey = new SecretKeySpec(key, 0, key.length, "AES");
 
 
 
        //Setup Cipher
 
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "SunJCE");
 
        GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, nonce);
 
        cipher.init(Cipher.DECRYPT_MODE, aesKey, spec);
 
 
 
        //Add "Additional Authentication Data" (AAD) in order to operate in AEAD mode
 
        cipher.updateAAD(aad);
 
 
 
        //Decipher the token
 
        byte[] decipheredToken = cipher.doFinal(cipheredToken);
 
 
 
        return new String(decipheredToken);
 
    }
 
 
 
 
 
    /**
 
    * Store GCM NONCE and GCM AAD in the DB
 
    * @param nonceInHex Nonce encoded in HEX
 
    * @param aadInHex AAD encoded in HEX
 
    * @param jwtTokenDigestInHex SHA256 of the JWT ciphered token encoded in HEX
 
    * @throws Exception If any issue occur during communication with DB
 
    */
 
    private void storeNonceAndAAD(String nonceInHex, String aadInHex, String jwtTokenDigestInHex) throws Exception {
 
        try (Connection con = this.storeDS.getConnection()) {
 
            String query = "insert into nonce(jwt_token_digest, gcm_nonce, gcm_aad) values(?, ?, ?)";
 
            int insertedRecordCount;
 
            try (PreparedStatement pStatement = con.prepareStatement(query)) {
 
                pStatement.setString(1, jwtTokenDigestInHex);
 
                pStatement.setString(2, nonceInHex);
 
                pStatement.setString(3, aadInHex);
 
                insertedRecordCount = pStatement.executeUpdate();
 
            }
 
            if (insertedRecordCount != 1) {
 
                throw new IllegalStateException("Number of inserted record is invalid, 1 expected but is " + insertedRecordCount);
 
            }
 
        }
 
    }
 
 
 
    /**
 
    * Read GCM NONCE and GCM AAD from the DB
 
    * @param jwtTokenDigestInHex SHA256 of the JWT ciphered token encoded in HEX for which we must read the NONCE and AAD
 
    * @return A dict containing the NONCE and AAD if they exists for the specified token
 
    * @throws Exception If any issue occur during communication with DB
 
    */
 
    private  Map<String,String> readNonceAndAAD(String jwtTokenDigestInHex) throws Exception{
 
        Map<String,String> gcmInfos = null;
 
        try (Connection con = this.storeDS.getConnection()) {
 
            String query = "select gcm_nonce, gcm_aad from nonce where jwt_token_digest = ?";
 
            try (PreparedStatement pStatement = con.prepareStatement(query)) {
 
                pStatement.setString(1, jwtTokenDigestInHex);
 
                try (ResultSet rSet = pStatement.executeQuery()) {
 
                    while (rSet.next()) {
 
                        gcmInfos = new HashMap<>(2);
 
                        gcmInfos.put("NONCE", rSet.getString(1));
 
                        gcmInfos.put("AAD", rSet.getString(2));
 
                    }
 
                }
 
            }
 
        }
 
 
 
        return gcmInfos;
 
    }
 
 
 
}
 
</pre>
 
 
 
==== Creation / Validation of the token ====
 
 
 
Use of the token ciphering during the creation and the validation of the token.
 
 
 
Load keys and setup cipher.
 
 
 
<pre>
 
//Load keys from configuration text files in order to avoid to store keys as String in JVM memory
 
private transient byte[] keyHMAC = Files.readAllBytes(Paths.get("key-hmac.txt"));
 
private transient byte[] keyCiphering = Files.readAllBytes(Paths.get("key-ciphering.txt"));
 
 
 
//Load issuer ID from configuration text file
 
private transient String issuerID = Files.readAllLines(Paths.get("issuer-id.txt")).get(0);
 
 
 
//Init token ciphering handler
 
TokenCipher tokenCipher = new TokenCipher();
 
</pre>
 
 
 
Token creation.
 
 
 
<pre>
 
//Create the token with a validity of 15 minutes and client context (IP + Browser fingerprint SHA256 digest HEX encoded) information
 
Calendar c = Calendar.getInstance();
 
Date now = c.getTime();
 
c.add(Calendar.MINUTE, 15);
 
Date expirationDate = c.getTime();
 
Map<String, Object> headerClaims = new HashMap<>();
 
headerClaims.put("typ", "JWT");
 
String token = JWT.create().withSubject(login)
 
                      .withExpiresAt(expirationDate)
 
                      .withIssuer(issuerID)
 
                      .withIssuedAt(now)
 
                      .withNotBefore(now)
 
                      .withClaim("clientIP", retrieveClientIP(request))
 
                      .withClaim("browserFingerprintDigest", browserFingerprintDigest)
 
                      .withHeader(headerClaims)
 
                      .sign(Algorithm.HMAC256(keyHMAC));
 
//Cipher the token
 
String cipheredToken = tokenCipher.cipherToken(token, keyCiphering);
 
</pre>
 
 
 
Token validation.
 
 
 
<pre>
 
//Decipher the token
 
String token = tokenCipher.decipherToken(cipheredToken, keyCiphering);
 
 
 
//Create a verification context for the token
 
JWTVerifier verifier = JWT.require(Algorithm.HMAC256(this.keyHMAC))
 
                              .withIssuer(issuerID)
 
                              .withClaim("clientIP", retrieveClientIP(request))
 
                              .withClaim("browserFingerprintDigest", browserFingerprintDigest)
 
                              .build();
 
 
 
//Verify the token, if the verification fail then a exception is throwed
 
DecodedJWT decodedToken = verifier.verify(token);
 
</pre>
 
 
 
== Token storage on client side ==
 
 
 
=== Symptom ===
 
 
 
It's occur when a application store the token in a way allowing this one:
 
 
 
* To be automatically sent by the browser (Cookie storage).
 
* To be retrieved even if the browser is restarted (Use of localStorage container).
 
 
 
=== How to prevent ===
 
 
 
Store the token using the browser ''sessionStorage'' container and add it as a ''Bearer'' with JavaScript when calling service.
 
 
 
=== Implementation example ===
 
 
 
 
 
JavaScript code to store the token after authentication.
 
 
 
<pre>
 
/* Handle request for JWT token and local storage*/
 
function getToken(){
 
    var login = $("#login").val();
 
    var password = $("#password").val();
 
    var fingerprint = generateFingerprint();
 
    var fingerprintHash = CryptoJS.SHA256(fingerprint);
 
    var postData = "login=" + encodeURIComponent(login) + "&password=" + encodeURIComponent(password) +"&browserFingerprintDigest="
 
    + encodeURIComponent(fingerprintHash);
 
 
 
    $.post("/services/authenticate", postData,function (data){
 
        if(data.status == "Authentication successful !"){    
 
            sessionStorage.setItem("token", data.token);
 
        }else{
 
            sessionStorage.removeItem("token");
 
        }
 
    })
 
    .fail(function(jqXHR, textStatus, error){
 
        sessionStorage.removeItem("token");
 
    });
 
}
 
</pre>
 
 
 
JavaScript code to add the token as ''Beader'' when calling a service, for example a service to validate token here.
 
 
 
<pre>
 
/* Handle request for JWT token validation */
 
function validateToken(){
 
    var token = sessionStorage.getItem("token");
 
    var fingerprint = generateFingerprint();
 
    var fingerprintHash = CryptoJS.SHA256(fingerprint);
 
    var postData = "browserFingerprintDigest=" + encodeURIComponent(fingerprintHash);
 
 
 
    $.ajax({
 
        url: "/services/validate",
 
        type: "POST",
 
        beforeSend: function(xhr) {
 
            xhr.setRequestHeader("Authorization", "bearer " + token);
 
        },
 
        data: postData,
 
        success: function(data) {
 
...
 
        },
 
        error: function(jqXHR, textStatus, error) {
 
...
 
        },
 
    });
 
}
 
</pre>
 
 
 
= Authors and Primary Editors  =
 
 
 
[[User:Dominique_RIGHETTO|Dominique Righetto]] - [email protected]
 
 
 
= Other Cheatsheets =
 
 
 
{{Cheatsheet_Navigation_Body}}
 
 
 
[[Category:Cheatsheets]]
 

Latest revision as of 13:48, 15 July 2019

Cheatsheets-header.jpg

The Cheat Sheet Series project has been moved to GitHub!

Please visit JSON Web Token (JWT) Cheat Sheet for Java to see the latest version of the cheat sheet.