Compare commits
4 Commits
196f63a8d7
...
v2.5.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
83678c92eb
|
|||
|
37fdf4f602
|
|||
|
e503b163e9
|
|||
|
3f62636645
|
@@ -4,7 +4,12 @@ root = true
|
|||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
insert_final_newline = false
|
insert_final_newline = false
|
||||||
|
|
||||||
[src/**/*.ts]
|
[{src,tests}/*.ts]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 4
|
||||||
|
|
||||||
|
[{src,tests}/**/*.ts]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
28
README.md
28
README.md
@@ -84,11 +84,13 @@ Signs a payload and returns the token.
|
|||||||
|
|
||||||
#### Arguments
|
#### Arguments
|
||||||
|
|
||||||
Argument | Type | Status | Default | Description
|
Argument | Type | Status | Default | Description
|
||||||
----------- | -------- | -------- | ------- | -----------
|
------------------------ | ------------------ | -------- | ----------- | -----------
|
||||||
`payload` | `object` | required | - | The payload object. To use `nbf` (Not Before) and/or `exp` (Expiration Time) add `nbf` and/or `exp` to the payload.
|
`payload` | `object` | required | - | The payload object. To use `nbf` (Not Before) and/or `exp` (Expiration Time) add `nbf` and/or `exp` to the payload.
|
||||||
`secret` | `string` | required | - | A string which is used to sign the payload.
|
`secret` | `string` | required | - | A string which is used to sign the payload.
|
||||||
`options` | `object` | optional | `{ algorithm: 'HS256' }` | The options object supporting `algorithm` and `keyid`. (See [Available Algorithms](#available-algorithms))
|
`options` | `string`, `object` | optional | `HS256` | Either the `algorithm` string or an object.
|
||||||
|
`options.algorithm` | `string` | optional | `HS256` | See [Available Algorithms](#available-algorithms)
|
||||||
|
`options.keyid` | `string` | optional | `undefined` | The `keyid` or `kid` to be set in the header of the resulting JWT.
|
||||||
|
|
||||||
#### `return`
|
#### `return`
|
||||||
Returns token as a `string`.
|
Returns token as a `string`.
|
||||||
@@ -100,11 +102,15 @@ Returns token as a `string`.
|
|||||||
|
|
||||||
Verifies the integrity of the token and returns a boolean value.
|
Verifies the integrity of the token and returns a boolean value.
|
||||||
|
|
||||||
Argument | Type | Status | Default | Description
|
Argument | Type | Status | Default | Description
|
||||||
----------- | -------- | -------- | ------- | -----------
|
------------------------ | ------------------ | -------- | ------- | -----------
|
||||||
`token` | `string` | required | - | The token string generated by `jwt.sign()`.
|
`token` | `string` | required | - | The token string generated by `jwt.sign()`.
|
||||||
`secret` | `string` | required | - | The string which was used to sign the payload.
|
`secret` | `string` | required | - | The string which was used to sign the payload.
|
||||||
`options` | `object` | optional | `{ algorithm: 'HS256', skipValidation: false, throwError: false }` | The options object supporting `algorithm`, `skipValidation` and `throwError`. (See [Available Algorithms](#available-algorithms))
|
`options` | `string`, `object` | optional | `HS256` | Either the `algorithm` string or an object.
|
||||||
|
`options.algorithm` | `string` | optional | `HS256` | See [Available Algorithms](#available-algorithms)
|
||||||
|
`options.clockTolerance` | `number` | optional | `0` | Clock tolerance in seconds, to help with slighly out of sync systems.
|
||||||
|
`options.throwError` | `boolean` | optional | `false` | By default this we will only throw implementation errors, only set this to `true` if you want verification errors to be thrown as well.
|
||||||
|
|
||||||
|
|
||||||
#### `throws`
|
#### `throws`
|
||||||
If `options.throwError` is `true` and the token is invalid, an error will be thrown.
|
If `options.throwError` is `true` and the token is invalid, an error will be thrown.
|
||||||
@@ -147,4 +153,4 @@ Returns an `object` containing `header` and `payload`:
|
|||||||
- HS512
|
- HS512
|
||||||
- RS256
|
- RS256
|
||||||
- RS384
|
- RS384
|
||||||
- RS512
|
- RS512
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@tsndr/cloudflare-worker-jwt",
|
"name": "@tsndr/cloudflare-worker-jwt",
|
||||||
"version": "2.4.7",
|
"version": "2.5.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@tsndr/cloudflare-worker-jwt",
|
"name": "@tsndr/cloudflare-worker-jwt",
|
||||||
"version": "2.4.7",
|
"version": "2.5.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/workers-types": "^4.20240208.0",
|
"@cloudflare/workers-types": "^4.20240208.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@tsndr/cloudflare-worker-jwt",
|
"name": "@tsndr/cloudflare-worker-jwt",
|
||||||
"version": "2.4.7",
|
"version": "2.5.0",
|
||||||
"description": "A lightweight JWT implementation with ZERO dependencies for Cloudflare Worker",
|
"description": "A lightweight JWT implementation with ZERO dependencies for Cloudflare Worker",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": "./index.js",
|
"exports": "./index.js",
|
||||||
|
|||||||
18
src/index.ts
18
src/index.ts
@@ -99,6 +99,11 @@ export type JwtSignOptions<T> = {
|
|||||||
* @prop {boolean} [throwError=false] If `true` throw error if checks fail. (default: `false`)
|
* @prop {boolean} [throwError=false] If `true` throw error if checks fail. (default: `false`)
|
||||||
*/
|
*/
|
||||||
export type JwtVerifyOptions = {
|
export type JwtVerifyOptions = {
|
||||||
|
/**
|
||||||
|
* Clock tolerance to help with slightly out of sync systems
|
||||||
|
*/
|
||||||
|
clockTolerance?: number
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If `true` throw error if checks fail. (default: `false`)
|
* If `true` throw error if checks fail. (default: `false`)
|
||||||
*
|
*
|
||||||
@@ -178,11 +183,10 @@ export async function sign<Payload = {}, Header = {}>(payload: JwtPayload<Payloa
|
|||||||
* @throws {Error | string} Throws an error `string` if the token is invalid or an `Error-Object` if there's a validation issue.
|
* @throws {Error | string} Throws an error `string` if the token is invalid or an `Error-Object` if there's a validation issue.
|
||||||
* @returns {Promise<boolean>} Returns `true` if signature, `nbf` (if set) and `exp` (if set) are valid, otherwise returns `false`.
|
* @returns {Promise<boolean>} Returns `true` if signature, `nbf` (if set) and `exp` (if set) are valid, otherwise returns `false`.
|
||||||
*/
|
*/
|
||||||
export async function verify(token: string, secret: string | JsonWebKey | CryptoKey, options: JwtVerifyOptions | JwtAlgorithm = { algorithm: 'HS256', throwError: false }): Promise<boolean> {
|
export async function verify(token: string, secret: string | JsonWebKey | CryptoKey, options: JwtVerifyOptions | JwtAlgorithm = 'HS256'): Promise<boolean> {
|
||||||
if (typeof options === 'string')
|
if (typeof options === 'string')
|
||||||
options = { algorithm: options, throwError: false }
|
options = { algorithm: options }
|
||||||
|
options = { algorithm: 'HS256', clockTolerance: 0, throwError: false, ...options }
|
||||||
options = { algorithm: 'HS256', throwError: false, ...options }
|
|
||||||
|
|
||||||
if (typeof token !== 'string')
|
if (typeof token !== 'string')
|
||||||
throw new Error('token must be a string')
|
throw new Error('token must be a string')
|
||||||
@@ -215,10 +219,12 @@ export async function verify(token: string, secret: string | JsonWebKey | Crypto
|
|||||||
if (!payload)
|
if (!payload)
|
||||||
throw new Error('PARSE_ERROR')
|
throw new Error('PARSE_ERROR')
|
||||||
|
|
||||||
if (payload.nbf && payload.nbf > Math.floor(Date.now() / 1000))
|
const now = Math.floor(Date.now() / 1000)
|
||||||
|
|
||||||
|
if (payload.nbf && Math.abs(payload.nbf - now) > (options.clockTolerance ?? 0))
|
||||||
throw new Error('NOT_YET_VALID')
|
throw new Error('NOT_YET_VALID')
|
||||||
|
|
||||||
if (payload.exp && payload.exp <= Math.floor(Date.now() / 1000))
|
if (payload.exp && Math.abs(payload.exp - now) > (options.clockTolerance ?? 0))
|
||||||
throw new Error('EXPIRED')
|
throw new Error('EXPIRED')
|
||||||
|
|
||||||
const key = secret instanceof CryptoKey ? secret : await importKey(secret, algorithm, ['verify'])
|
const key = secret instanceof CryptoKey ? secret : await importKey(secret, algorithm, ['verify'])
|
||||||
|
|||||||
@@ -119,4 +119,30 @@ describe.each(Object.entries(data) as [JwtAlgorithm, Dataset][])('%s', (algorith
|
|||||||
const verified = await jwt.verify(token, data.public, algorithm)
|
const verified = await jwt.verify(token, data.public, algorithm)
|
||||||
expect(verified).toBeTruthy()
|
expect(verified).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Verify', async () => {
|
||||||
|
const secret = 'super-secret'
|
||||||
|
|
||||||
|
const now = Math.floor(Date.now() / 1000)
|
||||||
|
const off = 30 // 30 seconds
|
||||||
|
const nbf = now + off // Not valid before 30 seconds from now
|
||||||
|
const exp = now - off // Expired 30 seconds ago
|
||||||
|
|
||||||
|
const notYetValidToken = await jwt.sign({ sub: 'me', nbf }, secret)
|
||||||
|
const expiredToken = await jwt.sign({ sub: 'me', exp }, secret)
|
||||||
|
|
||||||
|
test('Not yet valid', () => {
|
||||||
|
expect(jwt.verify(notYetValidToken, secret, { throwError: true })).rejects.toThrowError('NOT_YET_VALID')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Expired', () => {
|
||||||
|
console.log({ exp, now: Math.floor(Date.now() / 1000) })
|
||||||
|
expect(jwt.verify(expiredToken, secret, { throwError: true })).rejects.toThrowError('EXPIRED')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('Clock offset', () => {
|
||||||
|
expect(jwt.verify(notYetValidToken, secret, { clockTolerance: off, throwError: true })).resolves.toBe(true)
|
||||||
|
expect(jwt.verify(expiredToken, secret, { clockTolerance: off, throwError: true })).resolves.toBe(true)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
Reference in New Issue
Block a user