1
0

Compare commits

...

7 Commits

Author SHA1 Message Date
Leo Developer
b05345279d allow using cryptokey directly 2024-01-18 20:47:05 +01:00
Richard Lee
55bc15bec4 Fix typing to make build work 2024-01-18 20:36:33 +01:00
kira924age
b0d4084a0f refactor: ๐Ÿ’ก reafactor decodePayload 2023-12-28 23:09:07 +01:00
kira924age
3fd594bbb5 feat: ๐ŸŽธ update decodePayload 2023-12-28 23:09:07 +01:00
kira924age
f8a216574a fix: ๐Ÿ› update decodePayload 2023-12-28 23:09:07 +01:00
kira924age
1f511549f5 feat: ๐ŸŽธ update decodePayload 2023-12-28 23:09:07 +01:00
Leo Developer
4be64469d3 fix header not being typed when using sign 2023-12-21 13:50:50 +01:00

View File

@@ -75,8 +75,8 @@ export type JwtOptions = {
* @extends JwtOptions * @extends JwtOptions
* @prop {JwtHeader} [header] * @prop {JwtHeader} [header]
*/ */
export type JwtSignOptions = { export type JwtSignOptions<T> = {
header?: JwtHeader header?: JwtHeader<T>
} & JwtOptions } & JwtOptions
/** /**
@@ -198,7 +198,10 @@ async function importKey(key: string | JsonWebKey, algorithm: SubtleCryptoImport
function decodePayload<T = any>(raw: string): T | undefined { function decodePayload<T = any>(raw: string): T | undefined {
try { try {
return JSON.parse(atob(raw)) const bytes = Array.from(atob(raw), char => char.charCodeAt(0));
const decodedString = new TextDecoder('utf-8').decode(new Uint8Array(bytes));
return JSON.parse(decodedString);
} catch { } catch {
return return
} }
@@ -208,22 +211,22 @@ function decodePayload<T = any>(raw: string): T | undefined {
* Signs a payload and returns the token * Signs a payload and returns the token
* *
* @param {JwtPayload} payload The payload object. To use `nbf` (Not Before) and/or `exp` (Expiration Time) add `nbf` and/or `exp` to the payload. * @param {JwtPayload} payload The payload object. To use `nbf` (Not Before) and/or `exp` (Expiration Time) add `nbf` and/or `exp` to the payload.
* @param {string | JsonWebKey} secret A string which is used to sign the payload. * @param {string | JsonWebKey | CryptoKey} secret A string which is used to sign the payload.
* @param {JwtSignOptions | JwtAlgorithm | string} [options={ algorithm: 'HS256', header: { typ: 'JWT' } }] The options object or the algorithm. * @param {JwtSignOptions | JwtAlgorithm | string} [options={ algorithm: 'HS256', header: { typ: 'JWT' } }] The options object or the algorithm.
* @throws {Error} If there's a validation issue. * @throws {Error} If there's a validation issue.
* @returns {Promise<string>} Returns token as a `string`. * @returns {Promise<string>} Returns token as a `string`.
*/ */
export async function sign<Payload = {}>(payload: JwtPayload<Payload>, secret: string | JsonWebKey, options: JwtSignOptions | JwtAlgorithm = 'HS256'): Promise<string> { export async function sign<Payload = {}, Header = {}>(payload: JwtPayload<Payload>, secret: string | JsonWebKey, options: JwtSignOptions<Header> | JwtAlgorithm = 'HS256'): Promise<string> {
if (typeof options === 'string') if (typeof options === 'string')
options = { algorithm: options } options = { algorithm: options }
options = { algorithm: 'HS256', header: { typ: 'JWT' }, ...options } options = { algorithm: 'HS256', header: { typ: 'JWT' } as JwtHeader<Header>, ...options }
if (!payload || typeof payload !== 'object') if (!payload || typeof payload !== 'object')
throw new Error('payload must be an object') throw new Error('payload must be an object')
if (!secret || (typeof secret !== 'string' && typeof secret !== 'object')) if (!secret || (typeof secret !== 'string' && typeof secret !== 'object'))
throw new Error('secret must be a string or a JWK object') throw new Error('secret must be a string, a JWK object or a CryptoKey object')
if (typeof options.algorithm !== 'string') if (typeof options.algorithm !== 'string')
throw new Error('options.algorithm must be a string') throw new Error('options.algorithm must be a string')
@@ -238,7 +241,7 @@ export async function sign<Payload = {}>(payload: JwtPayload<Payload>, secret: s
const partialToken = `${textToBase64Url(JSON.stringify({ ...options.header, alg: options.algorithm }))}.${textToBase64Url(JSON.stringify(payload))}` const partialToken = `${textToBase64Url(JSON.stringify({ ...options.header, alg: options.algorithm }))}.${textToBase64Url(JSON.stringify(payload))}`
const key = await importKey(secret, algorithm) const key = secret instanceof CryptoKey ? secret : await importKey(secret, algorithm)
const signature = await crypto.subtle.sign(algorithm, key, textToArrayBuffer(partialToken)) const signature = await crypto.subtle.sign(algorithm, key, textToArrayBuffer(partialToken))
return `${partialToken}.${arrayBufferToBase64Url(signature)}` return `${partialToken}.${arrayBufferToBase64Url(signature)}`
@@ -248,12 +251,12 @@ export async function sign<Payload = {}>(payload: JwtPayload<Payload>, secret: s
* Verifies the integrity of the token and returns a boolean value. * Verifies the integrity of the token and returns a boolean value.
* *
* @param {string} token The token string generated by `jwt.sign()`. * @param {string} token The token string generated by `jwt.sign()`.
* @param {string | JsonWebKey} secret The string which was used to sign the payload. * @param {string | JsonWebKey | CryptoKey} secret The string which was used to sign the payload.
* @param {JWTVerifyOptions | JWTAlgorithm} options The options object or the algorithm. * @param {JWTVerifyOptions | JWTAlgorithm} options The options object or the algorithm.
* @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, options: JwtVerifyOptions | JwtAlgorithm = { algorithm: 'HS256', throwError: false }): Promise<boolean> { export async function verify(token: string, secret: string | JsonWebKey | CryptoKey, options: JwtVerifyOptions | JwtAlgorithm = { algorithm: 'HS256', throwError: false }): Promise<boolean> {
if (typeof options === 'string') if (typeof options === 'string')
options = { algorithm: options, throwError: false } options = { algorithm: options, throwError: false }
@@ -263,7 +266,7 @@ export async function verify(token: string, secret: string | JsonWebKey, options
throw new Error('token must be a string') throw new Error('token must be a string')
if (typeof secret !== 'string' && typeof secret !== 'object') if (typeof secret !== 'string' && typeof secret !== 'object')
throw new Error('secret must be a string or a JWK object') throw new Error('secret must be a string, a JWK object or a CryptoKey object')
if (typeof options.algorithm !== 'string') if (typeof options.algorithm !== 'string')
throw new Error('options.algorithm must be a string') throw new Error('options.algorithm must be a string')
@@ -290,7 +293,7 @@ export async function verify(token: string, secret: string | JsonWebKey, options
if (payload.exp && payload.exp <= Math.floor(Date.now() / 1000)) if (payload.exp && payload.exp <= Math.floor(Date.now() / 1000))
throw new Error('EXPIRED') throw new Error('EXPIRED')
const key = await importKey(secret, algorithm) const key = secret instanceof CryptoKey ? secret : await importKey(secret, algorithm)
return await crypto.subtle.verify(algorithm, key, base64UrlToArrayBuffer(tokenParts[2]), textToArrayBuffer(`${tokenParts[0]}.${tokenParts[1]}`)) return await crypto.subtle.verify(algorithm, key, base64UrlToArrayBuffer(tokenParts[2]), textToArrayBuffer(`${tokenParts[0]}.${tokenParts[1]}`))
} catch(err) { } catch(err) {