├── .eslintrc
├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ └── main.yml
├── .gitignore
├── .husky
├── commit-msg
└── pre-commit
├── .prettierrc
├── README.md
├── bun.lockb
├── commitlint.config.cjs
├── package.json
├── src
└── index.ts
├── tests
├── fixtures
│ ├── private
│ └── public
└── index.test.ts
├── tsconfig.build.json
└── tsconfig.json
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "root": true,
3 | "extends": ["prettier", "plugin:prettier/recommended", "plugin:@typescript-eslint/recommended"],
4 | "plugins": ["@typescript-eslint", "prettier"],
5 | "parser": "@typescript-eslint/parser",
6 | "parserOptions": {
7 | "project": "./tsconfig.json"
8 | },
9 | "ignorePatterns": ["dist/", "node_modules/"],
10 | "overrides": [
11 | {
12 | "files": ["src/**/*.ts", "tests/**/*.ts"]
13 | }
14 | ],
15 | "rules": {
16 | "@typescript-eslint/explicit-module-boundary-types": "off",
17 | "@typescript-eslint/no-explicit-any": "off"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | issuehunt: talentlessguy
4 | github: [tinyhttp, talentlessguy]
5 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 | ---
8 |
9 | **Describe the bug**
10 |
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 |
15 | Steps to reproduce the behavior:
16 |
17 | 1. ...
18 | 2. ...
19 | 3. ...
20 |
21 | **Expected behavior**
22 |
23 | A clear and concise description of what you expected to happen.
24 |
25 | **Versions**
26 |
27 | - `node`: 14
28 | - `@tinyhttp/cli`: 1.X
29 |
30 | **Additional context**
31 |
32 | Add any other context about the problem here.
33 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 | ---
8 |
9 | **Is your feature request related to a problem? Please describe.**
10 | What problem does it solve?
11 |
12 | **Describe the solution you'd like**
13 | A clear and concise description of what you want to happen.
14 |
15 | **Describe alternatives you've considered**
16 | A clear and concise description of any alternative solutions or features you've considered.
17 |
18 | **Additional context**
19 | Add any other context or screenshots about the feature request here.
20 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | on: [push]
2 |
3 | jobs:
4 | test:
5 | runs-on: ubuntu-latest
6 | steps:
7 | - uses: actions/checkout@v3
8 | - uses: oven-sh/setup-bun@v1
9 | with:
10 | bun-version: latest
11 | - run: bun install
12 | - run: bun test:coverage
13 | - run: bun test:report
14 | - name: Coveralls
15 | uses: coverallsapp/github-action@master
16 | with:
17 | github-token: ${{ secrets.GITHUB_TOKEN }}
18 | path-to-lcov: ./lcov.info
19 | build:
20 | runs-on: ubuntu-latest
21 | steps:
22 | - uses: actions/checkout@v3
23 | - uses: oven-sh/setup-bun@v1
24 | with:
25 | bun-version: latest
26 | - run: bun install
27 | - run: bun run build
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | lcov.info
4 | coverage
--------------------------------------------------------------------------------
/.husky/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | bunx commitlint --config commitlint.config.cjs --edit "$1"
--------------------------------------------------------------------------------
/.husky/pre-commit:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | . "$(dirname "$0")/_/husky.sh"
3 |
4 | bun lint && bun run build && bun run test
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "semi": false,
3 | "singleQuote": true,
4 | "printWidth": 120,
5 | "trailingComma": "none",
6 | "tabWidth": 2
7 | }
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # @tinyhttp/jwt
4 |
5 | [![NPM][npm-badge]][npm-url] [![NPM][dl-badge]][npm-url] [![GitHub Workflow Status][actions-img]][github-actions] [![Coverage][cov-img]][cov-url]
6 |
7 | JWT middleware for HTTP servers.
8 |
9 |
10 |
11 | ## Install
12 |
13 | ```sh
14 | bun i @tinyhttp/jwt
15 | ```
16 |
17 | ## API
18 |
19 | ```ts
20 | import { jwt } from '@tinyhttp/jwt'
21 | ```
22 |
23 | ### Options
24 |
25 | #### `jwt(options)`
26 |
27 | - `secret`: can be an array of strings (in case you are using private / public key encryption), or just a string if you are using basic HMAC signing (see the examples below)
28 | - `algorithm? ("HS256")`: the algorithm used to sign and verify the token
29 | - `audience?`: the expected "audience" of the jwt token
30 | - `issuer?`: who issued this token
31 | - `expiresIn?`: expiration time of the token (ex: `1d` for 1 day)
32 | - `notBefore?`: not before date of the token (ex: `20m` for 20 minutes)
33 | - `requestHeaderName? ("Authorization")`: the name of the header contaning the Bearer token
34 | - `responseHeaderName? ("X-Token")`: the name of the response header containing the new signed token that will be used later on
35 | - `getToken(string)?: string`: the method used for ex
36 |
37 | ## Example
38 |
39 | ### Basic secret
40 |
41 | ```ts
42 | import { App } from '@tinyhttp/app'
43 | import { jwt } from '@tinyhttp/jwt'
44 |
45 | new App()
46 | .use(jwt({ secret: 'secret', algorithm: 'HS256' }))
47 | .get('/', (req, res) => res.send(`Data inside the payload: ${req['user']}`))
48 | .listen(8080)
49 | ```
50 |
51 | ### Private / Public key
52 |
53 | ```ts
54 | import { App } from '@tinyhttp/app'
55 | import { jwt } from '@tinyhttp/jwt'
56 |
57 | new App()
58 | .use(jwt({ secret: ['PRIVATE KEY', 'PUBLIC KEY'], algorithm: 'RS256' }))
59 | .get('/', (req, res) => res.send(`Data inside the payload: ${req['user']}`))
60 | .listen(8080)
61 | ```
62 |
63 | [npm-badge]: https://img.shields.io/npm/v/@tinyhttp/jwt?style=for-the-badge&color=hotpink&label=&logo=npm
64 | [npm-url]: https://npmjs.com/package/@tinyhttp/jwt
65 | [dl-badge]: https://img.shields.io/npm/dt/@tinyhttp/jwt?style=for-the-badge&color=hotpink
66 | [actions-img]: https://img.shields.io/github/actions/workflow/status/tinyhttp/jwt/main.yml?style=for-the-badge&logo=github&label=&color=hotpink
67 | [github-actions]: https://github.com/tinyhttp/jwt/actions
68 | [cov-img]: https://img.shields.io/coveralls/github/tinyhttp/jwt?style=for-the-badge&color=hotpink&a
69 | [cov-url]: https://coveralls.io/github/tinyhttp/jwt
70 |
--------------------------------------------------------------------------------
/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/tinyhttp/jwt/e1e5814601e9e5885980e57daa47557aab9ea2e0/bun.lockb
--------------------------------------------------------------------------------
/commitlint.config.cjs:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line @typescript-eslint/no-var-requires
2 | const defaultConfig = require('@commitlint/config-conventional')
3 |
4 | module.exports = {
5 | extends: ['@commitlint/config-conventional'],
6 | rules: {
7 | ...defaultConfig.rules,
8 | 'type-enum': [
9 | 2,
10 | 'always',
11 | ['fix', 'test', 'tooling', 'refactor', 'revert', 'example', 'docs', 'format', 'feat', 'chore']
12 | ]
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@tinyhttp/jwt",
3 | "version": "1.3.1",
4 | "type": "module",
5 | "description": "tiny JWT middleware for Node.js",
6 | "homepage": "https://github.com/tinyhttp/jwt#readme",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/tinyhttp/jwt.git"
10 | },
11 | "engines": {
12 | "node": ">=14.18 || >=16.20"
13 | },
14 | "types": "./dist/index.d.ts",
15 | "module": "./dist/index.js",
16 | "exports": "./dist/index.js",
17 | "keywords": [
18 | "tinyhttp",
19 | "nodejs",
20 | "web",
21 | "backend",
22 | "jwt"
23 | ],
24 | "author": "BRA1L0R",
25 | "contributors": [
26 | "BRA1L0R ",
27 | "v1rtl "
28 | ],
29 | "license": "MIT",
30 | "dependencies": {
31 | "jsonwebtoken": "^9.0.2"
32 | },
33 | "files": [
34 | "dist"
35 | ],
36 | "scripts": {
37 | "build": "tsc -p tsconfig.build.json",
38 | "test": "tsx tests/index.test.ts",
39 | "test:coverage": "c8 tsx --test tests/index.test.ts",
40 | "test:report": "c8 report --reporter=text-lcov > lcov.info",
41 | "lint": "eslint . --ext=ts",
42 | "format": "prettier --check \"./**/*.{ts,md}\"",
43 | "format:fix": "prettier --write \"./**/*.{ts,md}\"",
44 | "prepare": "husky install"
45 | },
46 | "devDependencies": {
47 | "@commitlint/cli": "^18.2.0",
48 | "@commitlint/config-conventional": "^18.1.0",
49 | "@types/jsonwebtoken": "^9.0.4",
50 | "@types/node": "^20.8.9",
51 | "@typescript-eslint/eslint-plugin": "^6.9.0",
52 | "@typescript-eslint/parser": "^6.9.0",
53 | "bun-types": "^1.0.7",
54 | "c8": "^8.0.1",
55 | "eslint": "^8.52.0",
56 | "eslint-config-prettier": "^9.0.0",
57 | "eslint-plugin-prettier": "^5.0.1",
58 | "expect": "^29.7.0",
59 | "husky": "^8.0.3",
60 | "prettier": "^3.0.3",
61 | "tsx": "^3.14.0",
62 | "typescript": "^5.2.2"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { IncomingMessage, ServerResponse } from 'node:http'
2 | import jwtoken, { Algorithm } from 'jsonwebtoken'
3 |
4 | export interface JwtMwProps {
5 | secret: string | string[]
6 | algorithm?: Algorithm
7 | audience?: string
8 | issuer?: string
9 | expiresIn?: string
10 | notBefore?: string
11 | requestHeaderName?: string
12 | responseHeaderName?: string
13 | getToken?: (header: string) => string
14 | }
15 |
16 | export interface RequestWithUser extends IncomingMessage {
17 | user?: any
18 | }
19 |
20 | const getTokenFromHeader = (header: string) => (!header ? '' : header.split(' ')[1])
21 |
22 | /**
23 | * JWT middleware
24 | */
25 | export const jwt = ({
26 | secret,
27 | algorithm = 'HS256',
28 | audience = '',
29 | issuer = '',
30 | expiresIn = '50y',
31 | notBefore = '0s',
32 | requestHeaderName = 'authorization',
33 | responseHeaderName = 'X-Token',
34 | getToken = getTokenFromHeader
35 | }: JwtMwProps) => {
36 | return function (req: RequestWithUser, res: ServerResponse, next: () => void) {
37 | const token: string = getToken((req.headers[requestHeaderName] as string) ?? '')
38 |
39 | try {
40 | // Verify the JWT token
41 | const verify = jwtoken.verify(token, Array.isArray(secret) ? secret[1] : secret, {
42 | algorithms: [algorithm],
43 | audience,
44 | issuer
45 | })
46 |
47 | req.user = verify
48 |
49 | res.setHeader(
50 | responseHeaderName,
51 | jwtoken.sign(req.user, Array.isArray(secret) ? secret[0] : secret, {
52 | audience,
53 | issuer,
54 | expiresIn,
55 | notBefore,
56 | algorithm
57 | })
58 | )
59 | next()
60 | } catch {
61 | next()
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tests/fixtures/private:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | 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
51 | XMrPObULdcgZLODNOYz8vNHQcM8fvw==
52 | -----END PRIVATE KEY-----
--------------------------------------------------------------------------------
/tests/fixtures/public:
--------------------------------------------------------------------------------
1 | -----BEGIN PUBLIC KEY-----
2 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnYLhjrHJthJTbcUzZNxR
3 | O8n1H2/PAwlM/B2lZ91SMIcZKOqROot3fRCO7uj1HBc0Oy3+E+Spn221t6FfxeOh
4 | fS3EoyW9mqBr5o6XW2dijYKrgYn0GutUCcgQHWc+z+4JSfRQINwt1eHhmUtZOORS
5 | /XCyd3Xelm5Rn1MVBbHRpD6YWcaftMxl8D93tq6HosO8M4btA+7wmiLMOXQm/aTa
6 | DH+nZQJly4YW4l0vhGtvvctppHzZ94cqLCdAAx0p+vyeI4haQVzYtiWGgCg7q2OE
7 | c/fZxO1r6SKLGq+nGcEoRIo532Anrv6NbfVMSDBXnWwR7je2N1+wL7P8kAk0+XJ7
8 | XT6HHE7vYvM1BQ6a8GVqFPB4tQcW/o7HrlrYC3EFZs+bYW9DxQO1YrkRe3Wjrl7d
9 | MmUM+90vU0qBsxzWn2bOT8lhn0X3ZPGILa4MYY59C+FBl+xEiDjaiZ00VYn6rqAN
10 | sStL/zBvtXITkSewBB2kMnh22lofkY2EyffTwGzJuGe+byjl/ZAYP/FxonvWxjXU
11 | xsqWECPxfTTYIhvur84ElpL+a7S6a/39Z7stwAtBOiEcle1UlMDDhk0NwOg/9LVS
12 | whakIBA+IdSxEJ88BoKMNlke0frHfCK1bKSGCRJn4SW7n9O9GkBSpLYBv+stMksY
13 | 9rEuuARFsNX3/tsnyzd8nlkCAwEAAQ==
14 | -----END PUBLIC KEY-----
15 |
--------------------------------------------------------------------------------
/tests/index.test.ts:
--------------------------------------------------------------------------------
1 | import { beforeEach, it } from 'node:test'
2 | import type { ServerResponse } from 'node:http'
3 | import fs from 'node:fs'
4 | import jsonwebtoken, { Algorithm } from 'jsonwebtoken'
5 | import { expect } from 'expect'
6 | import { jwt, RequestWithUser } from '../src/index.js'
7 |
8 | const req = {} as RequestWithUser
9 | const res = {} as ServerResponse
10 |
11 | beforeEach(() => {
12 | req.headers = {}
13 | req.user = {}
14 | })
15 |
16 | it('should work using default algorithm if authorization header is valid jsonwebtoken', () => {
17 | const secret = 'shhhhhh'
18 | const token = jsonwebtoken.sign({ foo: 'bar' }, secret)
19 |
20 | req.headers.authorization = 'Bearer ' + token
21 |
22 | jwt({ secret: secret })(req, res, () => {
23 | expect(req.user.foo).toBe('bar')
24 | })
25 | })
26 |
27 | it('should work with different HMAC algorithms', () => {
28 | const algorithms: Algorithm[] = ['HS512', 'HS256', 'HS384']
29 | const secret = 'shhhhhh'
30 |
31 | algorithms.forEach((algorithm) => {
32 | const token = jsonwebtoken.sign({ foo: 'bar' }, secret, { algorithm })
33 | req.headers.authorization = 'Bearer ' + token
34 |
35 | jwt({ secret: secret, algorithm })(req, res, () => {
36 | expect(req.user.foo).toBe('bar')
37 | })
38 | })
39 | })
40 |
41 | it('should work if authorization header is valid with a buffer secret', () => {
42 | const secret = Buffer.from('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', 'base64')
43 | const token = jsonwebtoken.sign({ foo: 'bar' }, secret)
44 |
45 | req.headers.authorization = 'Bearer ' + token
46 |
47 | jwt({ secret: secret.toString(), algorithm: 'HS256' })(req, res, () => {
48 | expect(req.user.foo).toBe('bar')
49 | })
50 | })
51 |
52 | it('should handle private key encryption', () => {
53 | const privateKey = fs.readFileSync('tests/fixtures/private', { encoding: 'utf-8' })
54 | const publicKey = fs.readFileSync('tests/fixtures/public', { encoding: 'utf-8' })
55 |
56 | req.headers.authorization = 'Bearer ' + jsonwebtoken.sign({ foo: 'bar' }, privateKey, { algorithm: 'RS256' })
57 |
58 | jwt({ secret: [privateKey, publicKey], algorithm: 'RS256' })(req, res, () => {
59 | expect(req.user.foo).toBe('bar')
60 | })
61 | })
62 |
63 | it('should work with different RSA algorithms', () => {
64 | const algorithms: Algorithm[] = ['RS256', 'RS384', 'RS512']
65 | const privateKey = fs.readFileSync('tests/fixtures/private', { encoding: 'utf-8' })
66 | const publicKey = fs.readFileSync('tests/fixtures/public', { encoding: 'utf-8' })
67 |
68 | algorithms.forEach((algorithm) => {
69 | req.headers.authorization = 'Bearer ' + jsonwebtoken.sign({ foo: 'bar' }, privateKey, { algorithm })
70 |
71 | jwt({ secret: [privateKey, publicKey], algorithm })(req, res, () => {
72 | expect(req.user.foo).toBe('bar')
73 | })
74 | })
75 | })
76 |
77 | it('should not work with malformed input', () => {
78 | const fake_secret = 'notmuchsecret'
79 | const true_secret = 'verysecret'
80 | const token = jsonwebtoken.sign({ foo: 'bar' }, fake_secret)
81 |
82 | req.headers.authorization = 'Bearer ' + token
83 |
84 | jwt({ secret: true_secret })(req, res, () => {
85 | expect(req.user.foo).toBeUndefined()
86 | })
87 | })
88 |
89 | it('should not work if authorization header is missing', () => {
90 | const secret = 'shhhhhh'
91 |
92 | jwt({ secret: secret })(req, res, () => {
93 | expect(req.user.foo).toBeUndefined()
94 | })
95 | })
96 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "dist",
5 | "declaration": true
6 | },
7 | "include": ["src"]
8 | }
9 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "NodeNext",
4 | "target": "esnext",
5 | "moduleResolution": "NodeNext",
6 | "strict": true,
7 | "skipLibCheck": true,
8 | "types": [
9 | "bun-types" // add Bun global
10 | ],
11 | "lib": ["DOM"]
12 | }
13 | }
14 |
--------------------------------------------------------------------------------