Web development & API design

07: "Sessions" with JWT

Problem: we need to identify clients (users)

Create user

  1. Find user by email

  2. Compare passwords

  3. Generate token

  4. Respond with token

Send authenticated request

  1. Verify token from Authorization header
  2. Decode token, identify user
  3. (Authorize user to see their own todos)
  4. Fetch all todos for the user

Cookies & tokens

Tokens <3

  • Play nice across domains
     
  • Stateless (if you want)
     
  • Decoupled from specific auth scheme
     
  • No need for session store
     
  • Cookies are for browsers

Tokens…

  • Client requests a token with…
    • Username
    • Password
       
  • Server…
    • generates
    • signs
    • (encrypts)
    • sends
       
  • Can store data (not just an ID)

JSON web token (JWT) "jot"

  • A handy token definition
  • Signed with a "secret" known only to the server
     
  • Faster than session stores (decoded using math)
     
  • Not encrypted by default—just signed

JWT under the hood

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InZhbGlkVXNlcm5hbWUifQ.N5ppD4k72pbST4_DESbHzqjrFCZK4QEzhGHI0H49GtM

header: {  "typ": "JWT",  "alg": "HS256" }

payload: { "username": "validUsername" }
 

<signature>

signature = HMACSHA256(

base64UrlEncode(header) + "." +

base64UrlEncode(payload) + 

<secret>)

JWT with Node

const jwt = require('jwt-simple');

const secret = 'some top secret string';

const payload = {
  username: 'theneva',
};

// Token for sending
const signedToken = jwt.encode(payload, secret);
console.log(signedToken); // eyJ0eXAiOiJKV1QiLC…

// Decode the token using the secret
const decodedPayload = jwt.decode(signedToken, secret);
console.log(decodedPayload); // { username: 'theneva' }

There's a package for that!

jwt-simple to the rescue

JWT with Express

const app = require('express')();
const jwt = require('jwt-simple');

app.post('/sessions', (req, res) => {
	// get and validate login info

	if (invalid) {
		// respond with 401 unauthorized
	} else {
		// encode jwt with payload & sign with secret
		// return the jwt
	}
});

app.get('/user', (req, res) => {
	// verify & decode jwt
	// get jwt payload username
	// retrieve user by username from DB
	// return user info
});

app.listen(1234, () => console.log('listening on port 1234'));

Example

Client: LocalStorage

// Store
localStorage.token = 'something';

// Get
const token = localStorage.token;
  • window.localStorage
    • or just localStorage
       
  • A "plain" object!

JWT + Client

function authenticate() {
  const url = api + '/authenticate';

  const body = {
    username: 'blah',
    password: 'glola'
  };

 fetch(url, {
    method: 'post',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(body),
  })
  .then(res => res.text())
  .then(token => localStorage.token = token);
}
function getWithToken(url) {
 return fetch(url, {
  headers: {
   'X-Token': localStorage.token
  }
 });
}

THIS IS PLAIN JS!

Bcrypt

const bcrypt = require('bcryptjs');

const password = 'ananas';

const passwordHash = bcrypt.hashSync(password, 10);
console.log(password + ' -> ' + passwordHash);

const isMatch = bcrypt.compareSync(password, passwordHash);
console.log(isMatch);
  • Password in plaintext?
  • Encrypted?

Demo!

Assignment 10

  • Continue from A01—or start over!
     
  • Database connection (MongoDB)
     
  • Login (db, users stored in DB, bcrypt)
     
  • Assignment text later today
     
  • Deadline Oct 29

Exercise

  • Build an API for creating users
     
  • Use bcrypt to hash passwords
     
  • Allow authentication
     
  • Make forms in React for
    • Registering
    • Signing in

PG6300-17-07 "Sessions" with JWT

By theneva

PG6300-17-07 "Sessions" with JWT

  • 543