This site contains the documentation that is relevant to older WSO2 product versions and offerings.
For the latest WSO2 documentation, visit https://wso2.com/documentation/.

Securing Web Applications Using JWT

This page describes how to use WSO2 App Manager to secure an unsecured Web app. App Manager supports Web apps and mobile apps out of the box. When an existing Web app is published through App Manager, all HTTP requests sent to the Web app go through the Gateway component of App Manager. This feature enables you to intercept the call and augment a plain Web app with authentication, authorisation, stat collection etc.

App Manager uses SAML and JWT to handle authentication scenarios. JWT is a standard to securely share user claims between two parties. When the Gateway receives an HTTP call, it first checks whether the call is received from an authenticated user. The Gateway decides whether a request is authenticated by checking whether the request has a valid session associated with it or whether the request has a valid SAML response from the trusted identity provider. If the user is not authenticated he/she will be redirected to the IDP for authentication. If the user is authenticated, a JWT will be generated, signed by the Gateway and sent to the Web app along with the SAML response received from the identity provider. In this scenario, the Gateway handles authentication on behalf of the Web app.

When the Web app receives the HTTP request, it can read the JWT, which is normally sent as an HTTP header. It is then decoded and the user information is used accordingly. For example, the Web app can extract user information, store it in the user session and use that information to give a personalised view for users. 

If the Web app developer has to develop the authentication part from scratch, then it would take more time. Having App Manager in between reduces that time and cost.

Decoding a JWT and verifying the signature requires some coding. There are a lot of libraries that can do that for you, for example, jwt.io where you can grab information about different JWT implementations in different languages. Nimbus-JOSE-JWT is a good option for a Java implementation. 

The following is an example Jaggery module to wrap the functionality of the above library:

jwt-client.js
var JWTClient;

(function() {

  var log = new Log();

  JWTClient = function(request, jwtHeaderName, certificatePath){
    this.request = request;
    this.jwtHeaderName = jwtHeaderName;
    this.certificatePath = certificatePath;
    this.jwt = null;
    this.signedJWT = null;
  }

  JWTClient.prototype = {

    init : function() {
      this.jwt = request.getHeader(this.jwtHeaderName);
    },

    isJWTPresent : function(){
      return this.jwt ? true : false;
    },

    parse : function(){
      this.signedJWT = Packages.com.nimbusds.jwt.SignedJWT.parse(this.jwt);
    },

    verify : function(){
      var publicKey = getPublicKey(this.certificatePath);
      var verifier = new Packages.com.nimbusds.jose.crypto.RSASSAVerifier(publicKey);
      return this.signedJWT.verify(verifier);
    },

    getSubject : function(){
      return this.signedJWT.getJWTClaimsSet().getSubject();
    },

    getIssuer : function(){
      return this.signedJWT.getJWTClaimsSet().getIssuer();
    },

    getClaim : function(name){
      return this.signedJWT.getJWTClaimsSet().getClaim(name);
    },

    getClaims : function(){
      return this.signedJWT.getJWTClaimsSet().getAllClaims();
    }

  }

  function getPublicKey(certificatePath){

    var inputStream = null;

    try{

      log.debug("Reading certificate from "+ certificatePath);

      var certificateFactory = Packages.java.security.cert.CertificateFactory.getInstance("X.509");

      inputStream = new Packages.java.io.FileInputStream(certificatePath);
      var certificate = certificateFactory.generateCertificate(inputStream);
      var key = certificate.getPublicKey();

      return key;
    }catch(e){
      log.error("Error while getting the public key. " + e);
      throw e;
    }finally{
      if(inputStream){
        inputStream.close();
      }
    }

    return null;

  }

})();

Below is a code snippet of its usage:

index.jag
var config = require('config.json');
var jwtClientModule = require('/modules/jwt-client.js');
var jwtClient = new jwtClientModule.JWTClient(request, config.jwtClient.headerName, config.jwtClient.certificatePath);

jwtClient.init();

log.debug("JWT = " + jwtClient.jwt);

if(jwtClient.isJWTPresent()){
  include('includes/jwt_login.jag');
}else{
  // Send an HTTP 401
}
jwt_login.jag
authenticateAndAuthorize();

function authenticateAndAuthorize(){

  try{
    jwtClient.parse();

    if(jwtClient.verify()){
      log.debug("Verified the signature of the JWT.");

      var subject = jwtClient.getSubject();
      var issuer = jwtClient.getIssuer();
      var claims = jwtClient.getClaims();

      setSessionAttributes(subject, claims);

      // Implement your authorization logic based on the claim values.

    }else{
      logAndShowError("Authentication failure. Cannot verify the JWT signature.");
    }
  }catch(e){
    logAndShowError("Authentication failure. Something went wrong ", e);
  }

}

function setSessionAttributes(subject, claims){

  session.put("logged-in", "true");
  session.put("username", subject);
  
  var roles = claims.get("http://wso2.org/claims/role");
  if(roles){
    roles = roles.split(',');
    session.put("roles", roles);
  }

}