mirror of
https://github.com/danbulant/jose
synced 2026-05-19 04:18:52 +00:00
feat: add jose.decodeJwt utility
This commit is contained in:
parent
7835489adf
commit
3d2a2b8eee
5 changed files with 109 additions and 4 deletions
|
|
@ -42,10 +42,10 @@ import * as jose from 'https://deno.land/x/jose/index.ts'
|
|||
|
||||
- JSON Web Tokens (JWT)
|
||||
- [Signing](docs/classes/jwt_sign.SignJWT.md#readme)
|
||||
- [Verification & Claims Set Validation](docs/functions/jwt_verify.jwtVerify.md#readme)
|
||||
- [Verification & JWT Claims Set Validation](docs/functions/jwt_verify.jwtVerify.md#readme)
|
||||
- Encrypted JSON Web Tokens
|
||||
- [Encryption](docs/classes/jwt_encrypt.EncryptJWT.md#readme)
|
||||
- [Decryption & Claims Set Validation](docs/functions/jwt_decrypt.jwtDecrypt.md#readme)
|
||||
- [Decryption & JWT Claims Set Validation](docs/functions/jwt_decrypt.jwtDecrypt.md#readme)
|
||||
- Key Import
|
||||
- [JWK Import](docs/functions/key_import.importJWK.md#readme)
|
||||
- [Public Key Import (SPKI)](docs/functions/key_import.importSPKI.md#readme)
|
||||
|
|
@ -72,6 +72,7 @@ import * as jose from 'https://deno.land/x/jose/index.ts'
|
|||
- [Public Key Export](docs/functions/key_export.exportSPKI.md#readme)
|
||||
- Utilities
|
||||
- [Decoding Token's Protected Header](docs/functions/util_decode_protected_header.decodeProtectedHeader.md#readme)
|
||||
- [Decoding JWT Claims Set](docs/functions/util_decode_jwt.decodeJwt.md#readme)
|
||||
- [Unsecured JWT](docs/classes/jwt_unsecured.UnsecuredJWT.md#readme)
|
||||
- [JOSE Errors](docs/modules/util_errors.md#readme)
|
||||
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@ import * as jose from 'https://deno.land/x/jose/index.ts'
|
|||
|
||||
- JSON Web Tokens (JWT)
|
||||
- [Signing](classes/jwt_sign.SignJWT.md#readme)
|
||||
- [Verification & Claims Set Validation](functions/jwt_verify.jwtVerify.md#readme)
|
||||
- [Verification & JWT Claims Set Validation](functions/jwt_verify.jwtVerify.md#readme)
|
||||
- Encrypted JSON Web Tokens
|
||||
- [Encryption](classes/jwt_encrypt.EncryptJWT.md#readme)
|
||||
- [Decryption & Claims Set Validation](functions/jwt_decrypt.jwtDecrypt.md#readme)
|
||||
- [Decryption & JWT Claims Set Validation](functions/jwt_decrypt.jwtDecrypt.md#readme)
|
||||
- Key Import
|
||||
- [JWK Import](functions/key_import.importJWK.md#readme)
|
||||
- [Public Key Import (SPKI)](functions/key_import.importSPKI.md#readme)
|
||||
|
|
@ -55,6 +55,7 @@ import * as jose from 'https://deno.land/x/jose/index.ts'
|
|||
- [Public Key Export](functions/key_export.exportSPKI.md#readme)
|
||||
- Utilities
|
||||
- [Decoding Token's Protected Header](functions/util_decode_protected_header.decodeProtectedHeader.md#readme)
|
||||
- [Decoding JWT Claims Set](functions/util_decode_jwt.decodeJwt.md#readme)
|
||||
- [Unsecured JWT](classes/jwt_unsecured.UnsecuredJWT.md#readme)
|
||||
- [JOSE Errors](modules/util_errors.md#readme)
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ export { importSPKI, importPKCS8, importX509, importJWK } from './key/import.js'
|
|||
export type { PEMImportOptions } from './key/import.js'
|
||||
|
||||
export { decodeProtectedHeader } from './util/decode_protected_header.js'
|
||||
export { decodeJwt } from './util/decode_jwt.js'
|
||||
export type { ProtectedHeaderParameters } from './util/decode_protected_header.js'
|
||||
|
||||
export * as errors from './util/errors.js'
|
||||
|
|
|
|||
48
src/util/decode_jwt.ts
Normal file
48
src/util/decode_jwt.ts
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
import { decode as base64url } from './base64url.js'
|
||||
import { decoder } from '../lib/buffer_utils.js'
|
||||
import isObject from '../lib/is_object.js'
|
||||
import type { JWTPayload } from '../types.d'
|
||||
import { JWTInvalid } from './errors.js'
|
||||
|
||||
/**
|
||||
* Decodes a signed JSON Web Token payload. This does not validate the JWT Claims Set
|
||||
* types or values. This does not validate the JWS Signature. For a proper
|
||||
* Signed JWT Claims Set validation and JWS signature verification use `jose.jwtVerify()`.
|
||||
* For an encrypted JWT Claims Set validation and JWE decryption use `jose.jwtDecrypt()`.
|
||||
*
|
||||
* @param jwt JWT token in compact JWS serialization.
|
||||
*
|
||||
* @example Usage
|
||||
* ```js
|
||||
* const claims = jose.decodeJwt(token)
|
||||
* console.log(claims)
|
||||
* ```
|
||||
*/
|
||||
export function decodeJwt(jwt: string) {
|
||||
if (typeof jwt !== 'string')
|
||||
throw new JWTInvalid('JWTs must use Compact JWS serialization, JWT must be a string')
|
||||
|
||||
const { 1: payload, length } = jwt.split('.')
|
||||
|
||||
if (length === 5) throw new JWTInvalid('Only JWTs using Compact JWS serialization can be decoded')
|
||||
if (length !== 3) throw new JWTInvalid('Invalid JWT')
|
||||
if (!payload) throw new JWTInvalid('JWTs must contain a payload')
|
||||
|
||||
let decoded: Uint8Array
|
||||
try {
|
||||
decoded = base64url(payload)
|
||||
} catch {
|
||||
throw new JWTInvalid('Failed to parse the base64url encoded payload')
|
||||
}
|
||||
|
||||
let result: unknown
|
||||
try {
|
||||
result = JSON.parse(decoder.decode(decoded))
|
||||
} catch {
|
||||
throw new JWTInvalid('Failed to parse the decoded payload as JSON')
|
||||
}
|
||||
|
||||
if (!isObject<JWTPayload>(result)) throw new JWTInvalid('Invalid JWT Claims Set')
|
||||
|
||||
return result
|
||||
}
|
||||
54
test/util/decode_jwt.test.mjs
Normal file
54
test/util/decode_jwt.test.mjs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import test from 'ava'
|
||||
|
||||
const root = !('WEBCRYPTO' in process.env) ? '#dist' : '#dist/webcrypto'
|
||||
const { decodeJwt, errors, base64url } = await import(root)
|
||||
|
||||
test('invalid inputs', (t) => {
|
||||
const jwt =
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'
|
||||
|
||||
const parts = jwt.split('.')
|
||||
|
||||
t.throws(() => decodeJwt(null), {
|
||||
instanceOf: errors.JWTInvalid,
|
||||
message: 'JWTs must use Compact JWS serialization, JWT must be a string',
|
||||
})
|
||||
|
||||
t.throws(() => decodeJwt('....'), {
|
||||
instanceOf: errors.JWTInvalid,
|
||||
message: 'Only JWTs using Compact JWS serialization can be decoded',
|
||||
})
|
||||
|
||||
t.throws(() => decodeJwt('.'), {
|
||||
instanceOf: errors.JWTInvalid,
|
||||
message: 'Invalid JWT',
|
||||
})
|
||||
|
||||
t.throws(() => decodeJwt([parts[0], '', parts[2]].join('.')), {
|
||||
instanceOf: errors.JWTInvalid,
|
||||
message: 'JWTs must contain a payload',
|
||||
})
|
||||
|
||||
t.throws(() => decodeJwt([parts[0], base64url.encode('null'), parts[2]].join('.')), {
|
||||
instanceOf: errors.JWTInvalid,
|
||||
message: 'Invalid JWT Claims Set',
|
||||
})
|
||||
|
||||
t.throws(() => decodeJwt([parts[0], base64url.encode('[]'), parts[2]].join('.')), {
|
||||
instanceOf: errors.JWTInvalid,
|
||||
message: 'Invalid JWT Claims Set',
|
||||
})
|
||||
|
||||
t.throws(() => decodeJwt([parts[0], base64url.encode('{"notajson'), parts[2]].join('.')), {
|
||||
instanceOf: errors.JWTInvalid,
|
||||
message: 'Failed to parse the decoded payload as JSON',
|
||||
})
|
||||
|
||||
t.deepEqual(decodeJwt([parts[0], base64url.encode('{}'), parts[2]].join('.')), {})
|
||||
|
||||
t.deepEqual(decodeJwt(jwt), {
|
||||
sub: '1234567890',
|
||||
name: 'John Doe',
|
||||
iat: 1516239022,
|
||||
})
|
||||
})
|
||||
Loading…
Reference in a new issue