feat: add jose.decodeJwt utility

This commit is contained in:
Filip Skokan 2022-02-07 13:49:35 +01:00
parent 7835489adf
commit 3d2a2b8eee
5 changed files with 109 additions and 4 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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
View 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
}

View 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,
})
})