JWT Based Authorization

JWT Based Authorization

Ref

  1. JWT
  2. JSON Web Token Tutorial: An Example in Laravel and AngularJS

Usage

  • An open standard(RFC 7519) for transmitting encrypted, digitally signed JSON object information which can be verified and trusted.
  • Most common use is putting it in the Authorization header to authenticate request identity. Also a good fit for SSO authentication.

Structure

Header.Payload.Signature

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 // Header. Algorithm and type. Encoded in base64.
base64enc({ "alg": "HS256",
"typ": "JWT"
}),
// Payload. Encoded in base64.
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
},
// Signature.
// Use HMAC SHA256 algorithm to take the encoded header, the encoded payload, a secret, the algorithm specified in the header all together and sign that.
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)

Properties in payload part is called claims. Standard claim names see here.

The signature is used to verify the message wasn’t tampered with along the way, and, in the case of tokens signed with a private key, it can also verify that the sender of the JWT is who it says it is.

Example JWT

Example JWT

Online playground here

Things to note

  • If claim exp is not set, then the JWT token will never expire.

Node.js example

Here’s an example using jsonwebtoken and sqlite to authenticate user.
It also supports nuxt.js local authentication strategy.

var express = require('express')
var jwt = require('jsonwebtoken')
var cors = require('cors')
// init app
var app = express()
app.use(express.json())
app.use(express.urlencoded({extended: true}))
app.use(cors())
// init db
var sqlite3 = require('sqlite3').verbose()
var { open } = require('sqlite')
var db
(async function () {
db = await open({
filename: ":memory:",
driver: sqlite3.Database
})
db.run("CREATE TABLE if not exists users(username TEXT primary key, email TEXT unqiue, password TEXT)")
})()
// Define JWT secret
const JWT_SECRET = "SECRETPASSWORD"
// Parse token middleware
const verifyToken = async (req, res, next) => {
var token = req.headers.authorization
if (!token || (token && !token.startsWith('Bearer '))) {
return res.status(401).send({
status: 'failure',
message: 'Bearer token required'
})
}
token = token.split(' ')[1]
var user
try {
user = jwt.verify(token, JWT_SECRET)
} catch (e) {
return res.status(401).send({
status: 'failure',
message: e.message
})
}
if (!user || (user && !user.username)) {
return res.status(401).send({
status: 'failure',
message: `invalid token payload: ${user}`
})
}
var username = user.username
user = await db.get('SELECT * FROM users WHERE username = ?', [username])
if (!user) {
return res.status(404).send({
status: 'failure',
message: `no user named ${username}`
})
}
req.user = user
next()
}
// Register
app.post("/register", async (req, res) => {
const {username, password, email} = req.body
if (!username || !password || !email) {
return res.status(400).json({
status: 'failure',
message: 'no username or password or email'
})
}
try {
let result = await db.run("INSERT INTO users(email, username, password) VALUES(?, ?, ?)", [email, username, password])
if (result.changes > 0) {
return res.json({
status: 'success',
data: {
token: jwt.sign({ username }, JWT_SECRET)
}
})
}
return res.status(401).json({
status: 'failure',
message: "register failed"
})
} catch (e) {
return res.status(500).json({
status: 'error',
message: e.message
})
}
})
// Login
app.post("/login", async (req, res) => {
const { email, username, password } = req.body
if ((!email && !username) || !password) {
return res.status(400).json({
status: 'failure',
message: 'no email|username or password'
})
}
try {
let user = await db.get(
`SELECT * FROM users WHERE 1=1
${email ? ' and email = :email ' : ''}
${username ? ' and username = :username ' : ''}
and password = :password`,
{':email': email, ':username': username, ':password': password})
if (user) {
return res.json({
status: 'success',
data: {
token: jwt.sign({ username: user.username }, JWT_SECRET),
refreshToken: null
}
})
}
return res.status(401).json({
status: 'failure',
message: "invalid username or password"
})
} catch (e) {
console.log(e)
return res.status(500).json({
status: 'error',
message: e.message
})
}
})
// Me
app.get("/me", verifyToken, (req, res) => {
return res.json({
status: 'success',
data: req.user
})
})
app.listen(3001)
console.log('server started.')
Author

Chendongtian

Posted on

2022-10-20

Updated on

2022-11-18

Licensed under

Comments