Introduction to api gateway using express gateway ( part 2 — authorization using jwt)

Let us consider a typical authentication and authorization scenario for api

  • User passes id/password to the server as part of authentication .
  • password is validated , server generates a token and saves the token in the database against the user id.
  • Server sends a token to the client.
  • Client will send the token back in all subsequent api request .
  • Server will validate the token against the one saved in the database and allow the request to proceed ,provided user has correct authorization on the api .
Authorization issues in case of micro services communication

In micro services architecture , services communicate to each other by using api calls . Important point to note here is that each micro service is independent and does not share the database . How does the authorization happen for a individual micro service ?

Since databases of the micro services are not shared , Saving the token in the database will not help . Token saved in the database of one micro service can not be accessed by other micro service. Here self verifiable token ( like JWT) can help . First service gets a token using authentication and can pass the the token to other service . Since the token is self verifiable , second service can verify the same . An example of self verifiable token is JWT

JWT ( JSON web token )https://jwt.io/

  • In its true definition , JWT 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 or ECDSA.

Verifying JWT

Important point is that JWT is self contained and digitally signed by central auth server. JWT Signature can be verified , which can ascertain that this JWT is indeed generated by Central Auth Server.

During micro services communication , JWT can be passed in the request and at the entry point of the api , JWT can be verified and access control for the user can be ascertained.

Micro services communication ( use of JWT token )

Following is the way communication between microservice can happen

  • First api when it is getting authenticated , will send the userid/password to the central auth server .
  • Auth server will verify password and will generate the JWT . Point to note here is that JWT need not be saved in the database ( after all it is self contained and verifiable )
  • Client ( first api in the chain) receives the JWT .
  • First api will pass the JWT token in request header to second api
  • Second api will verify the JWT . JWT also contains a payload . Payload can have claims , as well as additional information such as role . This information can be used to check if user has authorisation on the api .

JWT verification

  • Typically api at the entry point will do the JWT verification .
  • With api gateway ( express ) , this work will be offloaded to api gateway .

Express gateway JWT verification

  • Till now , we were talking about generic definition of JWT . Now onwards we will talk about how express gateway can be set up to verify JWT . Please note that intention here is not to show end to end automated flow but only JWT verification

In previous part ( part-1) , we talked about two services , first one customer and second one a product . In this article we will demonstrate the protecting product api with JWT

Step 1 : Setting up users for express gateway

  • Go to the path , where express gateway is installed
npm start 
my-gateway@1.0.0 start /home/tanmay/express-gateway/my-gateway
> node server.js
gateway http server listening on :::8080
admin http server listening on 127.0.0.1:9876

Express gateway is started . Create one user ( kevin) through cli command

tanmay@tanmay-VPCEB44EN:~/express-gateway$ eg users create
? Enter firstname [required]: kevin
? Enter lastname [required]: systrom
? Enter username [required]: kevin
? Enter email: kevin.systrom@instagram.com
? Enter redirectUri: http://localhost:4000/api/products
Created 523c71a2-9f80-434f-a555-9b193ba66444
{
"firstname": "kevin",
"lastname": "systrom",
"username": "kevin",
"email": "kevin.systrom@instagram.com",
"redirectUri": "http://localhost:4000/api/products",
"isActive": true,
"id": "523c71a2-9f80-434f-a555-9b193ba66444",
"createdAt": "Thu Oct 11 2018 23:19:16 GMT+0530 (IST)",
"updatedAt": "Thu Oct 11 2018 23:19:16 GMT+0530 (IST)"

Step 2: Generating credentials for JWT

tanmay@tanmay-VPCEB44EN:~/express-gateway$ eg credentials create -c kevin -t jwt
✔ Created 7dSTbOnvJ7mUF3CtNBCEst
{
"isActive": true,
"createdAt": "Thu Oct 11 2018 23:22:40 GMT+0530 (IST)",
"updatedAt": "Thu Oct 11 2018 23:22:40 GMT+0530 (IST)",
"keyId": "7dSTbOnvJ7mUF3CtNBCEst",
"keySecret": "7Ex0letChBSw23RfcPSqGr"
,
"scopes": null,
"consumerId": "523c71a2-9f80-434f-a555-9b193ba66444",
"id": "7dSTbOnvJ7mUF3CtNBCEst"
}

KeySecret would be used to sign the JWT and KeyId also would also be as input for generation of JWT.

Step 3: Generating JWT token

Here lets take a programmatic approach of generating JWT for illustration purpose. Alternatively we can use online generation of JWT at https://jwt.io/ . Here is the code for the same .

let jwt = require('jsonwebtoken');
let secret = null;
secret = '7Ex0letChBSw23RfcPSqGr' ; // this is the keySecret generated by eg credential create
let token = jwt.sign( {
"sub": "7dSTbOnvJ7mUF3CtNBCEst", //keyId generated by eg create
"name": "Kevin Systrom",
"iat": 1538828706
},secret);
console.log(token);
//verifying JWT token.
let decoded = jwt.verify(token ,secToken); //Decoding JWT using keySecret
console.dir(decoded, { depth : null ,colors : true});

Output

tanmay@tanmay-VPCEB44EN:~/json-web-token$ node json-web-token.js
JWT : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3ZFNUYk9udko3bVVGM0N0TkJDRXN0IiwibmFtZSI6IktldmluIFN5c3Ryb20iLCJpYXQiOjE1Mzg4Mjg3MDZ9.gByvBq67l97PJ3Li_AeBeajKLXdxC8ILCth3aERKEHo
decoded JWT payload:
{ sub: '7dSTbOnvJ7mUF3CtNBCEst',
name: 'Kevin Systrom',
iat: 1538828706 }

Step 4 — Protect api route using express gateway .

Now that JWT is generated , lets set up express gateway config to protect the product api . gateway.config.json has a placeholder for keeping secret for JWT decoding .

JWT is policy defined by express gateway , just like proxy. Please note that JWT policy is attached only to default-2 pipeline . and default-2 pipeline is defined for /api/product api ( apiEndPoint product ) .

Important parameters are as below

"jwt": [
{
"action": {
"secretOrPublicKey": "7Ex0letChBSw23RfcPSqGr",
"checkCredentialExistence" : "false"

}
}
]

secretOrPublicKey is the key we generated using eg create credential command. This secret will be used to decode the JWT token .We will come back to checkCredentialExistence property slightly later. Complete gateway.config.json is as follows.

{
"http": {
"port": 8080
},
"admin": {
"port": 9876,
"hostname": "localhost"
},
"apiEndpoints": {
"api": {
"host": "localhost",
"paths": "/ip"
},
"cust": {
"host": "localhost",
"paths": "/api/customers"
},
"product": {
"host": "localhost",
"paths": "/api/products"
}
},
"serviceEndpoints": {
"httpbin": {
"url": "https://httpbin.org"
},
"custsrv": {
"url": "http://localhost:3000/"
},
"prodsrv": {
"url": "http://localhost:4000/"
}
},
"policies": [
"basic-auth",
"key-auth",
"cors",
"expression",
"log",
"oauth2",
"proxy",
"rate-limit",
"jwt"
],
"pipelines": [
{
"name": "default",
"apiEndpoints": [
"api"
],
"policies": [
{
"proxy": [
{
"action": {
"serviceEndpoint": "httpbin",
"changeOrigin": true
}
}
]
}
]
},
{
"name": "default-1",
"apiEndpoints": [
"cust"
],
"policies": [

{
"proxy": [
{
"action": {
"serviceEndpoint": "custsrv"
}
}
]
}
]
},
{
"name": "default-2",
"apiEndpoints": [
"product"
],
"policies": [
{
"jwt": [
{
"action": {
"secretOrPublicKey": "2URIWeWGe5srtOptNidOyP",
"checkCredentialExistence" : "false"

}
}
]
},
{
"proxy": [
{
"action": {
"serviceEndpoint": "prodsrv"
}
}
]
}
]
}
]
}

Step 5- Accessing product api ( without JWT)

Here comes the moment of truth . We will try to access the protected api ( product) without passing JWT token . Will launch postman for testing api

As expected without passing JWT , we get error from api getway ( unauthorised) . We access /api/products without passing any JWT tokeny gi and api gateway will give error : unauthorised.

Step 6- Accessing product api ( with JWT)

Pass the JWT in header of the request as follows . Syntax would be

Authorization : Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3ZFNUYk9udko3bVVGM0N0TkJDRXN0IiwibmFtZSI6IktldmluIFN5c3Ryb20iLCJpYXQiOjE1Mzg4Mjg3MDZ9.gByvBq67l97PJ3Li_AeBeajKLXdxC8ILCth3aERKEHo

This time you will get successful response from the api as JWT will be decoded by api gateway

Successful product api response post JWT verification

Few Points to note

  • By default , all users and credentials are saved in in memory . Recommendation is to use in memory for dev and redis db for production scenario.
  • If we bring down the gateway , all users and their credentials are lost
  • For redis setting refer to system.config.yml in the config folder . Here emulate parameter clearly indicate that redis is running in “in-memory” mode.
db:
redis:
emulate: true
namespace: EG
  • when gateway is restarted and users and credentials are lost , still gateway will allow access to products api . It would be because of below parameter . Here in this case only signature verification is done however existence of credential check is not done.
 checkCredentialExistence : true

Summary

  • Api gateway like express , can make life easy for api communication and api authorisation .
  • They can protect apis/resources by variety of access mechanism ( like key-auth , oauth2 , jwt )
  • Today we saw a brief example of protecting resources using JWT .

Interests : software design ,architecture , search, open banking , machine learning ,mobility