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
|
||||
insert_final_newline = false
|
||||
|
||||
[src/**/*.ts]
|
||||
[{src,tests}/*.ts]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
[{src,tests}/**/*.ts]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
26
README.md
26
README.md
@@ -84,11 +84,13 @@ Signs a payload and returns the token.
|
||||
|
||||
#### Arguments
|
||||
|
||||
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.
|
||||
`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))
|
||||
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.
|
||||
`secret` | `string` | required | - | A string which is used to sign the payload.
|
||||
`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`
|
||||
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.
|
||||
|
||||
Argument | Type | Status | Default | Description
|
||||
----------- | -------- | -------- | ------- | -----------
|
||||
`token` | `string` | required | - | The token string generated by `jwt.sign()`.
|
||||
`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))
|
||||
Argument | Type | Status | Default | Description
|
||||
------------------------ | ------------------ | -------- | ------- | -----------
|
||||
`token` | `string` | required | - | The token string generated by `jwt.sign()`.
|
||||
`secret` | `string` | required | - | The string which was used to sign the payload.
|
||||
`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`
|
||||
If `options.throwError` is `true` and the token is invalid, an error will be thrown.
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@tsndr/cloudflare-worker-jwt",
|
||||
"version": "2.4.7",
|
||||
"version": "2.5.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@tsndr/cloudflare-worker-jwt",
|
||||
"version": "2.4.7",
|
||||
"version": "2.5.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "^4.20240208.0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@tsndr/cloudflare-worker-jwt",
|
||||
"version": "2.4.7",
|
||||
"version": "2.5.0",
|
||||
"description": "A lightweight JWT implementation with ZERO dependencies for Cloudflare Worker",
|
||||
"type": "module",
|
||||
"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`)
|
||||
*/
|
||||
export type JwtVerifyOptions = {
|
||||
/**
|
||||
* Clock tolerance to help with slightly out of sync systems
|
||||
*/
|
||||
clockTolerance?: number
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @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')
|
||||
options = { algorithm: options, throwError: false }
|
||||
|
||||
options = { algorithm: 'HS256', throwError: false, ...options }
|
||||
options = { algorithm: options }
|
||||
options = { algorithm: 'HS256', clockTolerance: 0, throwError: false, ...options }
|
||||
|
||||
if (typeof token !== '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)
|
||||
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')
|
||||
|
||||
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')
|
||||
|
||||
const key = secret instanceof CryptoKey ? secret : await importKey(secret, algorithm, ['verify'])
|
||||
|
||||
@@ -120,3 +120,29 @@ describe.each(Object.entries(data) as [JwtAlgorithm, Dataset][])('%s', (algorith
|
||||
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