93
Copyright © 2016 M/Gateway Developments Ltd EWD 3 Training Course Part 43 Using JSON Web Tokens with QEWD REST Services Rob Tweed Director, M/Gateway Developments Ltd Twitter: @rtweed

EWD 3 Training Course Part 43: Using JSON Web Tokens with QEWD REST Services

Embed Size (px)

Citation preview

Copyright © 2016 M/Gateway Developments Ltd

EWD 3 Training CoursePart 43

Using JSON Web Tokenswith QEWD REST Services

Rob TweedDirector, M/Gateway Developments Ltd

Twitter: @rtweed

Copyright © 2016 M/Gateway Developments Ltd

Using JSON Web Tokens with QEWD REST Services

• This part of the course assumes that you've taken, at least:

• Part 2: Overview of EWD 3– https://www.slideshare.net/robtweed/ewd-3-overview

• Part 4: Installing & Configuring QEWD– https://www.slideshare.net/robtweed/installing-configuring-ewdxpress

• Part 31: Using QEWD for Web and REST Services

– http://www.slideshare.net/robtweed/ewd-3-training-course-part-31-ewdxpress-for-web-and-rest-services

Copyright © 2016 M/Gateway Developments Ltd

JSON Web Tokens: Background

• For an introduction on JSON Web Tokens (JWTs), what they are, how they work and when and why they should be considered, see this presentation on QEWD and JWTs:

• https://www.slideshare.net/robtweed/qewdjs-json-web-tokens-microservices

Copyright © 2016 M/Gateway Developments Ltd

Moving to JWTs

• In this course, we'll change the example application that was described in Part 31– From using QEWD server-side Sessions– To using browser-side JWTs

Copyright © 2016 M/Gateway Developments Ltd

Our original QEWD startup file

var config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD REST Server', port: 8080, poolSize: 2, database: { type: 'gtm' }};

var routes = [ {path: '/api', module: 'myRestService'}];

var qewd = require('qewd').master;qewd.start(config, routes);

~/qewd/rest.js

Copyright © 2016 M/Gateway Developments Ltd

Tell QEWD to enable JWTsvar config = { managementPassword: 'keepThisSecret!', serverName: 'QEWD REST Server', port: 8080, poolSize: 2, database: { type: 'gtm' },

jwt: {

secret: 'someSecret123'

}};

var routes = [ {path: '/api', module: 'myRestService'}];

var qewd = require('qewd').master;qewd.start(config, routes);

The secret can be anytext string you want.

The longer and lessguessable the better

Copyright © 2016 M/Gateway Developments Ltd

Modify the Worker Process Handler Module

• Instead of standard server-side QEWD Sessions:– /api/login should create a new JWT

• Send it as the token response just as before

Copyright © 2016 M/Gateway Developments Ltd

Modify the Worker Process Handler Module

• Instead of standard server-side QEWD Sessions:– /api/login should create a new JWT

• Send it as the token response just as before

– All other routes should include the JWT in the Authorization Header

• Authorization: Bearer {{JWT}}

Copyright © 2016 M/Gateway Developments Ltd

Modify the Worker Process Handler Module

• Instead of standard server-side QEWD Sessions:– /api/login should create a new JWT

• Send it as the token response just as before

– All other routes should include the JWT in the Authorization Header

• Authorization: Bearer {{JWT}}

– Validate the incoming JWT and use its payload to get/set session values

Copyright © 2016 M/Gateway Developments Ltd

Edit the handler module

• ~/qewd/node_modules/myRestService.js

• All we need to do is:– Modify the login() function– Modify the beforeHandler() function

Copyright © 2016 M/Gateway Developments Ltd

Modify the Worker Process Handler Module

• Instead of standard server-side QEWD Sessions:– /api/login should create a new JWT

• Send it as the token response just as before

– All other routes should include the JWT in the Authorization Header

• Authorization: Bearer {{JWT}}

– Validate the incoming JWT and use its payload to get/set session values

Copyright © 2016 M/Gateway Developments Ltd

Original Login Functionfunction login(args, finished) { var username = args.req.body.username; if (!username || username === '') { return finished({error: 'You must provide a username'}); } var password = args.req.body.password; if (!password || password === '') { return finished({error: 'You must provide a password'}); } if (username !== 'rob') return finished({error: 'Invalid username'}); if (password !== 'secret') return finished({error: 'Invalid password'}); var session = this.sessions.create('testWebService', 3600); session.authenticated = true; session.data.$('username').value = username; finished({token: session.token});}

Copyright © 2016 M/Gateway Developments Ltd

Change to use JWTsfunction login(args, finished) { var username = args.req.body.username; if (!username || username === '') { return finished({error: 'You must provide a username'}); } var password = args.req.body.password; if (!password || password === '') { return finished({error: 'You must provide a password'}); } if (username !== 'rob') return finished({error: 'Invalid username'}); if (password !== 'secret') return finished({error: 'Invalid password'}); var session = this.jwt.handlers.createRestSession.call(this, args); session.welcomeText = 'Welcome ' + username; session.username = username; session.authenticated = true; session.timeout = 1200; var jwt = this.jwt.handlers.setJWT.call(this, session);

finished({token: jwt});}

Copyright © 2016 M/Gateway Developments Ltd

Change to use JWTsfunction login(args, finished) { var username = args.req.body.username; if (!username || username === '') { return finished({error: 'You must provide a username'}); } var password = args.req.body.password; if (!password || password === '') { return finished({error: 'You must provide a password'}); } if (username !== 'rob') return finished({error: 'Invalid username'}); if (password !== 'secret') return finished({error: 'Invalid password'}); var session = this.jwt.handlers.createRestSession.call(this, args); session.welcomeText = 'Welcome ' + username; session.username = username; session.authenticated = true; session.timeout = 1200; var jwt = this.jwt.handlers.setJWT.call(this, session);

finished({token: jwt});}

Create a new JWT-compatibleSession object, usinginformation from the request

this.jwt.handlers providesthe APIs you'll need for JWTHandling

Copyright © 2016 M/Gateway Developments Ltd

Change to use JWTsfunction login(args, finished) { var username = args.req.body.username; if (!username || username === '') { return finished({error: 'You must provide a username'}); } var password = args.req.body.password; if (!password || password === '') { return finished({error: 'You must provide a password'}); } if (username !== 'rob') return finished({error: 'Invalid username'}); if (password !== 'secret') return finished({error: 'Invalid password'}); var session = this.jwt.handlers.createRestSession.call(this, args); session.welcomeText = 'Welcome ' + username; session.username = username; session.authenticated = true; session.timeout = 1200; var jwt = this.jwt.handlers.setJWT.call(this, session);

finished({token: jwt});}

Add some extra propertiesof our own to the Session Object

Copyright © 2016 M/Gateway Developments Ltd

Change to use JWTsfunction login(args, finished) { var username = args.req.body.username; if (!username || username === '') { return finished({error: 'You must provide a username'}); } var password = args.req.body.password; if (!password || password === '') { return finished({error: 'You must provide a password'}); } if (username !== 'rob') return finished({error: 'Invalid username'}); if (password !== 'secret') return finished({error: 'Invalid password'}); var session = this.jwt.handlers.createRestSession.call(this, args); session.welcomeText = 'Welcome ' + username; session.username = username; session.authenticated = true; session.timeout = 1200; var jwt = this.jwt.handlers.setJWT.call(this, session);

finished({token: jwt});}

The authenticated andtimeout properties arespecial built-in ones

timeout specifies howlong before the JWT

expires

Copyright © 2016 M/Gateway Developments Ltd

Change to use JWTsfunction login(args, finished) { var username = args.req.body.username; if (!username || username === '') { return finished({error: 'You must provide a username'}); } var password = args.req.body.password; if (!password || password === '') { return finished({error: 'You must provide a password'}); } if (username !== 'rob') return finished({error: 'Invalid username'}); if (password !== 'secret') return finished({error: 'Invalid password'}); var session = this.jwt.handlers.createRestSession.call(this, args); session.welcomeText = 'Welcome ' + username; session.username = username; session.authenticated = true; session.timeout = 1200; var jwt = this.jwt.handlers.setJWT.call(this, session);

finished({token: jwt});}

Create a JWT fromthe Session data.

The secret you defined in thestartup file is used by QEWD

to sign the JWT

Copyright © 2016 M/Gateway Developments Ltd

Change to use JWTsfunction login(args, finished) { var username = args.req.body.username; if (!username || username === '') { return finished({error: 'You must provide a username'}); } var password = args.req.body.password; if (!password || password === '') { return finished({error: 'You must provide a password'}); } if (username !== 'rob') return finished({error: 'Invalid username'}); if (password !== 'secret') return finished({error: 'Invalid password'}); var session = this.jwt.handlers.createRestSession.call(this, args); session.welcomeText = 'Welcome ' + username; session.username = username; session.authenticated = true; session.timeout = 1200; var jwt = this.jwt.handlers.setJWT.call(this, session);

finished({token: jwt});}

Finally, return theJWT as the response

Copyright © 2016 M/Gateway Developments Ltd

Start QEWD using your startup file

cd ~/qewdnode rest

Copyright © 2016 M/Gateway Developments Ltd

Try out the /api/login Request

Copyright © 2016 M/Gateway Developments Ltd

Here's our JWT

Copyright © 2016 M/Gateway Developments Ltd

Inspecting a JWT

• Use:– https://jwt.io/

Copyright © 2016 M/Gateway Developments Ltd

Inspecting a JWT

Copyright © 2016 M/Gateway Developments Ltd

Here's our JWT

A JWT has 3 Base64-encoded parts, separated by a "."

Copyright © 2016 M/Gateway Developments Ltd

Here's our JWT

We're only really interested in the middle bit – the payload

Copyright © 2016 M/Gateway Developments Ltd

Our JWT Payload: Decoded

Anyone can Base64 Decode the payload of a JWT

Secret is not required for this

Copyright © 2016 M/Gateway Developments Ltd

Our JWT Payload: Decoded

Here's the timeout we set

Copyright © 2016 M/Gateway Developments Ltd

Our JWT Payload: Decoded

…and the other properties we set in the Session

Copyright © 2016 M/Gateway Developments Ltd

Our JWT Payload: Decoded

These are reserved JWT payload propertiesQEWD created them for you

Known as "reserved claims"

Copyright © 2016 M/Gateway Developments Ltd

Our JWT Payload: Decoded

This was added by QEWD, using the incomingRequest payload data. It's used by QEWDinternally

Copyright © 2016 M/Gateway Developments Ltd

Our JWT Payload: Decoded

The qewd property/claim is encrypted using theSecret, so is only usable by anyone with accessto the Secret. It contains data for use in the QEWDBack-end only

Copyright © 2016 M/Gateway Developments Ltd

Our JWT Payload: Decoded

You can optionally add data into this private claim,but only from within your worker process handlerfunctions: see later

Copyright © 2016 M/Gateway Developments Ltd

Our JWT Payload: Decoded

If a client returning a JWT makes any changes tothe payload, the signature will no longer match,so it will be rejected by QEWD

Copyright © 2016 M/Gateway Developments Ltd

Modify the Worker Process Handler Module

• Instead of standard server-side QEWD Sessions:– /api/login should create a new JWT

• Send it as the token response just as before

– All other routes should include the JWT in the Authorization Header

• Authorization: Bearer {{JWT}}

– Validate the incoming JWT and use its payload to get/set session values

Copyright © 2016 M/Gateway Developments Ltd

Add the JWT Authorization Header

Copyright © 2016 M/Gateway Developments Ltd

Modify the Worker Process Handler Module

• Instead of standard server-side QEWD Sessions:– /api/login should create a new JWT

• Send it as the token response just as before

– All other routes should include the JWT in the Authorization Header

• Authorization: Bearer {{JWT}}

– Validate the incoming JWT and use its payload to get/set session values

Copyright © 2016 M/Gateway Developments Ltd

Edit the handler module

• ~/qewd/node_modules/myRestService.js

• All we need to do is:– Modify the login() function– Modify the beforeHandler() function

Copyright © 2016 M/Gateway Developments Ltd

Here's the original beforeHandler() function

beforeHandler: function(req, finished) { if (req.path !== '/api/login') { return this.sessions.authenticateRestRequest(req, finished); } },

Using server-sideQEWD Sessions

Copyright © 2016 M/Gateway Developments Ltd

Modified to use JWTs

beforeHandler: function(req, finished) { if (req.path !== '/api/login') { return this.jwt.handlers.validateRestRequest.call(this, req, finished); } },

Change to use this API

- Checks the JWT's signature using the Secret

- Checks if the JWT has expired

- Unpacks the JWT payload into the Session object

- Decrypts the qewd claim

Copyright © 2016 M/Gateway Developments Ltd

We'll try the /api/search API

• But first we need to check its handler function:

init: function(application) { routes = [ { url: '/api/search', //method: 'GET', handler: search },

Copyright © 2016 M/Gateway Developments Ltd

Original /api/search handler function

function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); finished({ test: 'finished ok', username: args.session.data.$('username').value });}

Copyright © 2016 M/Gateway Developments Ltd

Original /api/search handler function

function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); finished({ test: 'finished ok', username: args.session.data.$('username').value });}

Will display the args objectTo the QEWD Node.js console

Copyright © 2016 M/Gateway Developments Ltd

Original /api/search handler function

function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); finished({ test: 'finished ok', username: args.session.data.$('username').value });}

Returns the username fromthe back-end QEWD Session

Copyright © 2016 M/Gateway Developments Ltd

Change it to use the JWT-derived Session

function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); finished({ test: 'finished ok', username: args.session.username });}

args.session now containsthe Session object, containingthe JWT payload

QEWD did this for usautomatically

Copyright © 2016 M/Gateway Developments Ltd

Restart QEWD and Try It

Copyright © 2016 M/Gateway Developments Ltd

Restart QEWD and Try It

Copyright © 2016 M/Gateway Developments Ltd

Take a look at the QEWD Node.js Console Log

*** search args: { "req": { "type": "ewd-qoper8-express", "path": "/api/search", "method": "GET", "headers": { "host": "192.168.1.117:8080", "authorization": "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MD…", "content-type": "application/json" }, "params": { "type": "search" }, "query": {}, "body": {}, "ip": "::ffff:192.168.1.74", "ips": [], "application": "api", "expressType": "search", …...

Here's the args object

Copyright © 2016 M/Gateway Developments Ltd

Scroll down further… "session": { "exp": 1503046402, "iat": 1503045202, "iss": "qewd.jwt", "application": "api", "timeout": 1200, "qewd": { "ipAddress": "::ffff:192.168.1.74", "authenticated": true }, "welcomeText": "Welcome rob", "username": "rob", "qewd_list": { "ipAddress": true, "authenticated": true }, "ipAddress": "::ffff:192.168.1.74", "authenticated": true }}

Here's args.session

Contains the JWT payloadvalues

Copyright © 2016 M/Gateway Developments Ltd

args.session "session": { "exp": 1503046402, "iat": 1503045202, "iss": "qewd.jwt", "application": "api", "timeout": 1200, "qewd": { "ipAddress": "::ffff:192.168.1.74", "authenticated": true }, "welcomeText": "Welcome rob", "username": "rob", "qewd_list": { "ipAddress": true, "authenticated": true }, "ipAddress": "::ffff:192.168.1.74", "authenticated": true }}

Here's the usernamevalue that was addedto the JWT Sessionby the login function

Copyright © 2016 M/Gateway Developments Ltd

args.session "session": { "exp": 1503046402, "iat": 1503045202, "iss": "qewd.jwt", "application": "api", "timeout": 1200, "qewd": { "ipAddress": "::ffff:192.168.1.74", "authenticated": true }, "welcomeText": "Welcome rob", "username": "rob", "qewd_list": { "ipAddress": true, "authenticated": true }, "ipAddress": "::ffff:192.168.1.74", "authenticated": true }}

But also notice theqewd claim

It's been decrypted

Copyright © 2016 M/Gateway Developments Ltd

args.session "session": { "exp": 1503046402, "iat": 1503045202, "iss": "qewd.jwt", "application": "api", "timeout": 1200, "qewd": { "ipAddress": "::ffff:192.168.1.74", "authenticated": true }, "welcomeText": "Welcome rob", "username": "rob", "qewd_list": { "ipAddress": true, "authenticated": true }, "ipAddress": "::ffff:192.168.1.74", "authenticated": true }}

And its properties havebeen copied toargs.session, asproperties in theirown right

Copyright © 2016 M/Gateway Developments Ltd

JWT-based Session

• Your handler functions can add/change/delete properties within the Session by accessing the args.session properties

• Don't modify the reserved claim properties– iss– exp– iat– application

Copyright © 2016 M/Gateway Developments Ltd

JWT-based Session

• By default, any properties you add to the Session will be visible within the JWT payload (after Base64 decoding)

• We saw this with the username and welcomeText fields– Added in the login() function– We cut and pasted the JWT into the jwt.io

Inspector screen

Copyright © 2016 M/Gateway Developments Ltd

Here's the login() functionfunction login(args, finished) { var username = args.req.body.username; if (!username || username === '') { return finished({error: 'You must provide a username'}); } var password = args.req.body.password; if (!password || password === '') { return finished({error: 'You must provide a password'}); } if (username !== 'rob') return finished({error: 'Invalid username'}); if (password !== 'secret') return finished({error: 'Invalid password'}); var session = this.jwt.handlers.createRestSession.call(this, args); session.welcomeText = 'Welcome ' + username; session.username = username; session.authenticated = true; session.timeout = 1200; var jwt = this.jwt.handlers.setJWT.call(this, session);

finished({token: jwt});}

username andwelcomeTextadded to the

Session Object

Copyright © 2016 M/Gateway Developments Ltd

Inspecting a JWT

Copyright © 2016 M/Gateway Developments Ltd

Our JWT Payload: Decoded

Copyright © 2016 M/Gateway Developments Ltd

JWT-based Session

• There may be properties/values that you want to add to the JWT/Session that are visible and usable in your back-end handler functions, but too sensitive to be visible to the client/user

• QEWD makes this easy

Copyright © 2016 M/Gateway Developments Ltd

JWT-based Session

• There may be properties/values that you want to add to the JWT/Session that are visible and usable in your back-end handler functions, but too sensitive to be visible to the client/user

• QEWD makes this easy– Let's make the username secret, but leave the

welcomeText visible

Copyright © 2016 M/Gateway Developments Ltd

Edit the login() functionfunction login(args, finished) { var username = args.req.body.username; if (!username || username === '') { return finished({error: 'You must provide a username'}); } var password = args.req.body.password; if (!password || password === '') { return finished({error: 'You must provide a password'}); } if (username !== 'rob') return finished({error: 'Invalid username'}); if (password !== 'secret') return finished({error: 'Invalid password'}); var session = this.jwt.handlers.createRestSession.call(this, args); session.welcomeText = 'Welcome ' + username; session.username = username; session.makeSecret('username'); session.authenticated = true; session.timeout = 1200; var jwt = this.jwt.handlers.setJWT.call(this, session);

finished({token: jwt});}

Just add this

Copyright © 2016 M/Gateway Developments Ltd

Restart QEWD. Send /api/login

Copyright © 2016 M/Gateway Developments Ltd

Restart QEWD. Send /api/login

Copy the JWT

Copyright © 2016 M/Gateway Developments Ltd

Paste it into the jwt.io Inspector

Copyright © 2016 M/Gateway Developments Ltd

Paste it into the jwt.io Inspector

welcomeText is still visiblebut username has disappeared

It's been encrypted into the qewdclaim

Copyright © 2016 M/Gateway Developments Ltd

Now send /api/search using new JWT

Copyright © 2016 M/Gateway Developments Ltd

Now send /api/search using new JWT

username appears as before

Copyright © 2016 M/Gateway Developments Ltd

Take a look at args.session in the QEWD Node.js Console Log

"session": { "exp": 1503049897, "iat": 1503048697, "iss": "qewd.jwt", "application": "api", "timeout": 1200, "qewd": { "ipAddress": "::ffff:192.168.1.74", "authenticated": true, "username": "rob" }, "welcomeText": "Welcome rob", "qewd_list": { "ipAddress": true, "authenticated": true, "username": true }, "ipAddress": "::ffff:192.168.1.74", "authenticated": true, "username": "rob" }

Copyright © 2016 M/Gateway Developments Ltd

Take a look at args.session in the QEWD Node.js Console Log

"session": { "exp": 1503049897, "iat": 1503048697, "iss": "qewd.jwt", "application": "api", "timeout": 1200, "qewd": { "ipAddress": "::ffff:192.168.1.74", "authenticated": true, "username": "rob" }, "welcomeText": "Welcome rob", "qewd_list": { "ipAddress": true, "authenticated": true, "username": true }, "ipAddress": "::ffff:192.168.1.74", "authenticated": true, "username": "rob" }

username is inside theqewd sub-object

Copyright © 2016 M/Gateway Developments Ltd

Take a look at args.session in the QEWD Node.js Console Log

"session": { "exp": 1503049897, "iat": 1503048697, "iss": "qewd.jwt", "application": "api", "timeout": 1200, "qewd": { "ipAddress": "::ffff:192.168.1.74", "authenticated": true, "username": "rob" }, "welcomeText": "Welcome rob", "qewd_list": { "ipAddress": true, "authenticated": true, "username": true }, "ipAddress": "::ffff:192.168.1.74", "authenticated": true, "username": "rob" }

And has been added as a property in its own right toargs.session

Copyright © 2016 M/Gateway Developments Ltd

Take a look at args.session in the QEWD Node.js Console Log

"session": { "exp": 1503049897, "iat": 1503048697, "iss": "qewd.jwt", "application": "api", "timeout": 1200, "qewd": { "ipAddress": "::ffff:192.168.1.74", "authenticated": true, "username": "rob" }, "welcomeText": "Welcome rob", "qewd_list": { "ipAddress": true, "authenticated": true, "username": true }, "ipAddress": "::ffff:192.168.1.74", "authenticated": true, "username": "rob" }

If the JWT is updated, usernameremains secret. It is removed asa property in its own right before theJWT is created

Copyright © 2016 M/Gateway Developments Ltd

Take a look at args.session in the QEWD Node.js Console Log

"session": { "exp": 1503049897, "iat": 1503048697, "iss": "qewd.jwt", "application": "api", "timeout": 1200, "qewd": { "ipAddress": "::ffff:192.168.1.74", "authenticated": true, "username": "rob" }, "welcomeText": "Welcome rob", "qewd_list": { "ipAddress": true, "authenticated": true, "username": true }, "ipAddress": "::ffff:192.168.1.74", "authenticated": true, "username": "rob" }

If the JWT is updated, usernameremains secret. It is removed asa property in its own right before theJWT is created

That's the purpose of this QEWD-managed sub-object

Copyright © 2016 M/Gateway Developments Ltd

Modifying/Updating the JWT

• You may want to add values/objects/arrays to the JWT-based session within your handler functions

• If so, you need to create a new JWT using the args.session object and return it with the response

• It's a good idea to return an updated JWT even if you don't add/change session data– To update the JWT's expiry time

Copyright © 2016 M/Gateway Developments Ltd

Modifying/Updating the JWT

• Let's make the /api/search handler update the JWT

Copyright © 2016 M/Gateway Developments Ltd

Here's our search() function

function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); finished({ test: 'finished ok', username: args.session.username });}

Copyright © 2016 M/Gateway Developments Ltd

Edit the search() function

function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); args.session.ranSearchAt = Date.now(); finished({ test: 'finished ok', username: args.session.username });}

Add a new property – ranSearchAt -to args.session

Copyright © 2016 M/Gateway Developments Ltd

Edit the search() function

function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); args.session.ranSearchAt = Date.now(); var jwt = this.jwt.handlers.setJWT.call(this, args.session); finished({ test: 'finished ok', username: args.session.username });} Create a new JWT using args.session

Copyright © 2016 M/Gateway Developments Ltd

Edit the search() function

function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); args.session.ranSearchAt = Date.now(); var jwt = this.jwt.handlers.setJWT.call(this, args.session); finished({ test: 'finished ok', username: args.session.username, token: jwt });}

Return the new JWT with the response

Copyright © 2016 M/Gateway Developments Ltd

Restart QEWD and send /api/search

Copyright © 2016 M/Gateway Developments Ltd

Restart QEWD and send /api/search

Here's the new JWT – let's inspect it

Copyright © 2016 M/Gateway Developments Ltd

The new JWT

Copyright © 2016 M/Gateway Developments Ltd

The new JWT

Here's the field we added

Copyright © 2016 M/Gateway Developments Ltd

Restart QEWD and send /api/search

Where is username?

Copyright © 2016 M/Gateway Developments Ltd

Look in the search() function

function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); args.session.ranSearchAt = Date.now(); var jwt = this.jwt.handlers.setJWT.call(this, args.session); finished({ test: 'finished ok', username: args.session.username, token: jwt });}

We're asking it to send the usernameusing the value in args.session.username

Copyright © 2016 M/Gateway Developments Ltd

Look in the search() function

function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); args.session.ranSearchAt = Date.now(); var jwt = this.jwt.handlers.setJWT.call(this, args.session); finished({ test: 'finished ok', username: args.session.username, token: jwt });}

We're asking it to send the usernameusing the value in args.session.username

But we're doing this after we created thenew JWT. In doing so, all secret fieldshave been removed from args.session

So args.session.username no longerexists

Copyright © 2016 M/Gateway Developments Ltd

Edit the search() function

function search(args, finished) { console.log('*** search args: ' + JSON.stringify(args, null, 2)); args.session.ranSearchAt = Date.now(); var username = args.session.username; var jwt = this.jwt.handlers.setJWT.call(this, args.session); finished({ test: 'finished ok', username: username, token: jwt });} Let's get hold of the username value

before the JWT is created, andsend that value in the response

Copyright © 2016 M/Gateway Developments Ltd

Restart QEWD and Try Again

Copyright © 2016 M/Gateway Developments Ltd

Restart QEWD and Try Again

Now username appears as expected

Copyright © 2016 M/Gateway Developments Ltd

Using JWTs with QEWD REST Services

• You now know everything needed to use JWTs with QEWD.js REST Services

• JWTs allow a very different approach to authentication and session management– Completely stateless architecture– Does not require any database for session

management– State/session info is held by client in the JWT

Copyright © 2016 M/Gateway Developments Ltd

Using JWTs with QEWD REST Services

• JWTs allow you to scale out your QEWD.js systems– Multiple QEWD systems just need to share

the same secret• Each defines the same secret text string in their

QEWD startup file

Copyright © 2016 M/Gateway Developments Ltd

JWT security

Server

Client

Both servers share same secret key

Server

Copyright © 2016 M/Gateway Developments Ltd

JWT security

Server

Client

Server

Create initial JWT

Copyright © 2016 M/Gateway Developments Ltd

JWT security

Server

Client

Server

Validate and use JWT

Copyright © 2016 M/Gateway Developments Ltd

QEWD.js Architecture

ewd-qoper8queue

Express

Node.js

socket.io

CacheGT.M,YottaDBRedisNode.js

WorkerProcess

CacheGT.M,YottaDBRedisNode.js

WorkerProcess

CacheGT.M,YottaDBRedisNode.js

WorkerProcess

ewd-qoper8queue

Express

Node.js

socket.io

CacheGT.M,YottaDBRedisNode.js

WorkerProcess

CacheGT.M,YottaDBRedisNode.js

WorkerProcess

CacheGT.M,YottaDBRedisNode.js

WorkerProcess

ewd-qoper8queue

Express

Node.js

socket.io

CacheGT.M,YottaDBRedisNode.js

WorkerProcess

CacheGT.M,YottaDBRedisNode.js

WorkerProcess

CacheGT.M,YottaDBRedisNode.js

WorkerProcess

Load BalancerOr Proxy (eg nginx)

Same Secret

Copyright © 2016 M/Gateway Developments Ltd

Using JWTs with QEWD REST Services

• JWTs also provide the basis by which you can break out your QEWD.js applications into fine-grained MicroServices, each running in their own separate machine/VM/container

• This will be described in the next part of this course.