├── .eslintignore ├── docs ├── .gitignore ├── favicon.ico ├── og-logo.jpg ├── pages │ ├── reference │ │ ├── index.md │ │ └── main │ │ │ ├── joseAlgorithmES256.md │ │ │ ├── joseAlgorithmHS256.md │ │ │ ├── joseAlgorithmRS256.md │ │ │ ├── JWSRegisteredHeaders │ │ │ ├── jwk.md │ │ │ ├── type.md │ │ │ ├── hasJWK.md │ │ │ ├── keyId.md │ │ │ ├── hasType.md │ │ │ ├── hasKeyId.md │ │ │ ├── algorithm.md │ │ │ ├── hasX509URL.md │ │ │ ├── hasAlgorithm.md │ │ │ ├── hasCritical.md │ │ │ ├── hasJWKSetURL.md │ │ │ ├── critical.md │ │ │ ├── hasX509CertificateChain.md │ │ │ ├── x509URL.md │ │ │ ├── jwtSetURL.md │ │ │ ├── hasX509CertificateSHA1Thumbprint.md │ │ │ ├── hasX509CertificateSHA256Thumbprint.md │ │ │ ├── x509CertificateSHA1Thumbprint.md │ │ │ ├── x509CertificateChain.md │ │ │ ├── x509CertificateSHA256Thumbprint.md │ │ │ └── index.md │ │ │ ├── JWTRegisteredClaims │ │ │ ├── jwtId.md │ │ │ ├── issuer.md │ │ │ ├── hasJWTId.md │ │ │ ├── subject.md │ │ │ ├── hasIssuer.md │ │ │ ├── hasSubject.md │ │ │ ├── hasIssuedAt.md │ │ │ ├── hasAudiences.md │ │ │ ├── hasExpiration.md │ │ │ ├── hasNotBefore.md │ │ │ ├── issuedAt.md │ │ │ ├── notBefore.md │ │ │ ├── audiences.md │ │ │ ├── expiration.md │ │ │ ├── verifyExpiration.md │ │ │ ├── verifyNotBefore.md │ │ │ └── index.md │ │ │ ├── encodeJWT.md │ │ │ ├── decodeJWT.md │ │ │ ├── parseJWT.md │ │ │ └── index.md │ ├── guides │ │ ├── create-tokens.md │ │ └── verify-tokens.md │ └── index.md ├── malta.config.json └── logo.svg ├── .github ├── FUNDING.yml └── workflows │ └── publish.yaml ├── .gitignore ├── .prettierrc.json ├── .prettierignore ├── tsconfig.build.json ├── CHANGELOG.md ├── tsconfig.json ├── .eslintrc.cjs ├── README.md ├── package.json ├── LICENSE ├── CONTRIBUTING.md └── src ├── index.test.ts └── index.ts /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/** -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: pilcrowOnPaper 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | pnpm-lock.yaml 2 | dist 3 | node_modules 4 | package-lock.json -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslo-project/jwt/HEAD/docs/favicon.ico -------------------------------------------------------------------------------- /docs/og-logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslo-project/jwt/HEAD/docs/og-logo.jpg -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "trailingComma": "none", 4 | "printWidth": 120 5 | } 6 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | pnpm-lock.yaml 6 | package-lock.json 7 | yarn.lock 8 | 9 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "include": ["src"], 4 | "exclude": ["src/**/*.test.ts"] 5 | } 6 | -------------------------------------------------------------------------------- /docs/pages/reference/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "API reference" 3 | --- 4 | 5 | # API reference 6 | 7 | ## Modules 8 | 9 | - [`@oslojs/jwt`](/reference/main) 10 | -------------------------------------------------------------------------------- /docs/pages/reference/main/joseAlgorithmES256.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "joseAlgorithmES256" 3 | --- 4 | 5 | # joseAlgorithmES256 6 | 7 | The JOSE algorithm ID for ES256 registered on IANA. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | const joseAlgorithmES256 = "ES256"; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/joseAlgorithmHS256.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "joseAlgorithmHS256" 3 | --- 4 | 5 | # joseAlgorithmHS256 6 | 7 | The JOSE algorithm ID for HS256 registered on IANA. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | const joseAlgorithmHS256 = "HS256"; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/joseAlgorithmRS256.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "joseAlgorithmRS256" 3 | --- 4 | 5 | # joseAlgorithmRS256 6 | 7 | The JOSE algorithm ID for RS256 registered on IANA. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | const joseAlgorithmRS256 = "RS256"; 13 | ``` 14 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # @oslojs/jwt 2 | 3 | ## 0.3.0 4 | 5 | - [Breaking] Rename `JWTClaims` to `JWTRegisteredClaims`. 6 | 7 | ## 0.2.0 8 | 9 | - [Breaking] Rename `JWSRegisteredHeaderParameters` to `JWSRegisteredHeaders` 10 | - [Breaking] Update `createJWT()`, `parseJWT()` and `createJWTSignatureMessage()` 11 | - Add `decodeJWT()` 12 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/jwk.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.jwk()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.jwk() 6 | 7 | Return the `jwk` parameter value. Throws an `Error` if the parameter doesn't exist or the value isn't a string. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function jwk(): string; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/type.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.type()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.type() 6 | 7 | Return the `typ` parameter value. Throws an `Error` if the parameter doesn't exist or the value isn't a string. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function type(): string; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/jwtId.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.jwtId()" 3 | --- 4 | 5 | # JWTRegisteredClaims.jwtId() 6 | 7 | Return the `jti` parameter value. Throws an `Error` if the parameter doesn't exist or the value isn't a string. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function jwtId(): string; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/hasJWK.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.hasJWK()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.hasJWK() 6 | 7 | Returns `true` if the `jwk` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasJWK(): boolean; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/keyId.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.keyId()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.keyId() 6 | 7 | Return the `kid` parameter value. Throws an `Error` if the parameter doesn't exist or the value isn't a string. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function keyId(): string; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/issuer.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.issuer()" 3 | --- 4 | 5 | # JWTRegisteredClaims.issuer() 6 | 7 | Return the `iss` parameter value. Throws an `Error` if the parameter doesn't exist or the value isn't a string. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function issuer(): string; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/hasType.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.hasType()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.hasType() 6 | 7 | Returns `true` if the `typ` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasType(): boolean; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/hasJWTId.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.hasJWTId()" 3 | --- 4 | 5 | # JWTRegisteredClaims.hasJWTId() 6 | 7 | Returns `true` if the `jti` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasJWTId(): boolean; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/subject.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.subject()" 3 | --- 4 | 5 | # JWTRegisteredClaims.subject() 6 | 7 | Return the `sub` parameter value. Throws an `Error` if the parameter doesn't exist or the value isn't a string. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function subject(): string; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/hasKeyId.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.hasKeyId()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.hasKeyId() 6 | 7 | Returns `true` if the `kid` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasKeyId(): boolean; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/hasIssuer.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.hasIssuer()" 3 | --- 4 | 5 | # JWTRegisteredClaims.hasIssuer() 6 | 7 | Returns `true` if the `iss` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasIssuer(): boolean; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/algorithm.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.algorithm()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.algorithm() 6 | 7 | Return the `alg` parameter value. Throws an `Error` if the parameter doesn't exist or the value isn't a string. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function algorithm(): string; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/hasSubject.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.hasSubject()" 3 | --- 4 | 5 | # JWTRegisteredClaims.hasSubject() 6 | 7 | Returns `true` if the `sub` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasSubject(): boolean; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/hasX509URL.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.hasX509URL()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.hasX509URL() 6 | 7 | Returns `true` if the `x5u` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasType(): hasX509URL; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/hasIssuedAt.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.hasIssuedAt()" 3 | --- 4 | 5 | # JWTRegisteredClaims.hasIssuedAt() 6 | 7 | Returns `true` if the `iat` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasIssuedAt(): boolean; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/hasAlgorithm.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.hasAlgorithm()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.hasAlgorithm() 6 | 7 | Returns `true` if the `alg` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasAlgorithm(): boolean; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/hasCritical.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.hasCritical()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.hasCritical() 6 | 7 | Returns `true` if the `crit` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasCritical(): boolean; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/hasJWKSetURL.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.hasJWKSetURL()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.hasJWKSetURL() 6 | 7 | Returns `true` if the `jku` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasJWKSetURL(): boolean; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/hasAudiences.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.hasAudiences()" 3 | --- 4 | 5 | # JWTRegisteredClaims.hasAudiences() 6 | 7 | Returns `true` if the `alg` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasAudiences(): boolean; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/hasExpiration.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.hasExpiration()" 3 | --- 4 | 5 | # JWTRegisteredClaims.hasExpiration() 6 | 7 | Returns `true` if the `exp` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasExpiration(): boolean; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/hasNotBefore.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.hasNotBefore()" 3 | --- 4 | 5 | # JWTRegisteredClaims.hasNotBefore() 6 | 7 | Returns `true` if the `nbf` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasNotBefore(): boolean; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/issuedAt.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.issuedAt()" 3 | --- 4 | 5 | # JWTRegisteredClaims.issuedAt() 6 | 7 | Return the `iat` parameter value as a standard `Date` object. Throws an `Error` if the parameter doesn't exist or the value isn't a positive integer. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function issuedAt(): Date; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/notBefore.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.notBefore()" 3 | --- 4 | 5 | # JWTRegisteredClaims.notBefore() 6 | 7 | Return the `nbf` parameter value as a standard `Date` object. Throws an `Error` if the parameter doesn't exist or the value isn't a positive integer. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function notBefore(): Date; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/audiences.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.audiences()" 3 | --- 4 | 5 | # JWTRegisteredClaims.audiences() 6 | 7 | Return the `aud` parameter value. Throws an `Error` if the parameter doesn't exist or the value isn't an array of string. Can return an empty array. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function audiences(): string[]; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/expiration.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.expiration()" 3 | --- 4 | 5 | # JWTRegisteredClaims.expiration() 6 | 7 | Return the `exp` parameter value as a standard `Date` object. Throws an `Error` if the parameter doesn't exist or the value isn't a positive integer. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function expiration(): Date; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/critical.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.critical()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.critical() 6 | 7 | Return the `crit` parameter value. Throws an `Error` if the parameter doesn't exist, the value isn't an array of string, or the value is an empty array. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function critical(): string[]; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/encodeJWT.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "encodeJWT()" 3 | --- 4 | 5 | # encodeJWT() 6 | 7 | Encodes a header object, payload object, and signature into a JSON web token. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function encodeJWT(header: object, payload: object, signature: Uint8Array): string; 13 | ``` 14 | 15 | ### Parameters 16 | 17 | - `header` 18 | - `payload` 19 | - `signature` 20 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "dist", 4 | "declaration": true, 5 | "esModuleInterop": true, 6 | "skipLibCheck": true, 7 | "target": "es2022", 8 | "verbatimModuleSyntax": true, 9 | "allowJs": true, 10 | "resolveJsonModule": true, 11 | "moduleDetection": "force", 12 | "strict": true, 13 | "moduleResolution": "NodeNext", 14 | "module": "NodeNext" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs/pages/reference/main/decodeJWT.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "decodeJWT()" 3 | --- 4 | 5 | # decodeJWT() 6 | 7 | Parses and returns the token's payload. This does not check if the header or signature is well-formed, nor verify the payload claims, such as expiration, or the signature. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function decodeJWT(jwt: string): object; 13 | ``` 14 | 15 | ### Parameters 16 | 17 | - `jwt` 18 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/hasX509CertificateChain.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.hasX509CertificateChain()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.hasX509CertificateChain() 6 | 7 | Returns `true` if the `x5c` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasType(): hasX509CertificateChain; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/x509URL.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.x509URL()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.x509URL() 6 | 7 | Return the `x5u` parameter value. Throws an `Error` if the parameter doesn't exist or the value isn't a string. This method does not validate whether the value is a well-formed URI. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function x509URL(): string; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/jwtSetURL.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.jwkSetURL()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.jwkSetURL() 6 | 7 | Return the `jku` parameter value. Throws an `Error` if the parameter doesn't exist or the value isn't a string. This method does not validate whether the value is a well-formed URI. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function jwkSetURL(): string; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/hasX509CertificateSHA1Thumbprint.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.hasX509CertificateSHA1Thumbprint()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.hasX509CertificateSHA1Thumbprint() 6 | 7 | Returns `true` if the `x5t` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasType(): hasX509CertificateSHA1Thumbprint; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/parseJWT.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "parseJWT()" 3 | --- 4 | 5 | # parseJWT() 6 | 7 | Parses a JSON web token. Throws an `Error` if the token is not well-formed. This method does not verify the payload claims, such as expiration, or the signature. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function parseJWT(jwt: string): [header: object, payload: header, signature: Uint8Array]; 13 | ``` 14 | 15 | ### Parameters 16 | 17 | - `jwt` 18 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/verifyExpiration.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.verifyExpiration()" 3 | --- 4 | 5 | # JWTRegisteredClaims.verifyExpiration() 6 | 7 | Return the `true` if the current time from `Date.now()` is before the expiration time. Throws an `Error` if the `exp` parameter doesn't exist or its value isn't a positive integer. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function verifyExpiration(): boolean; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/verifyNotBefore.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims.verifyNotBefore()" 3 | --- 4 | 5 | # JWTRegisteredClaims.verifyNotBefore() 6 | 7 | Return the `true` if the current time from `Date.now()` is at or after the not-before time. Throws an `Error` if the `nbf` parameter doesn't exist or its value isn't a positive integer. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function verifyNotBefore(): boolean; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/hasX509CertificateSHA256Thumbprint.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.hasX509CertificateSHA256Thumbprint()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.hasX509CertificateSHA256Thumbprint() 6 | 7 | Returns `true` if the `x5t#S256` parameter exists. This method does not check whether the parameter value is valid. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function hasType(): hasX509CertificateSHA256Thumbprint; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/x509CertificateSHA1Thumbprint.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.x509CertificateSHA1Thumbprint()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.x509CertificateSHA1Thumbprint() 6 | 7 | Return the `x5t` parameter value as a byte array. Throws an `Error` if the parameter doesn't exist or the value isn't a base64url encoded string. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function x509CertificateSHA1Thumbprint(): Uint8Array; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/x509CertificateChain.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.x509CertificateChain()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.x509CertificateChain() 6 | 7 | Return the `x5c` parameter value as an array of byte arrays. Throws an `Error` if the parameter doesn't exist, the value isn't an array of base64 encoded strings, or the value is an empty array. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function x509CertificateChain(): Uint8Array[]; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/x509CertificateSHA256Thumbprint.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders.x509CertificateSHA256Thumbprint()" 3 | --- 4 | 5 | # JWSRegisteredHeaders.x509CertificateSHA256Thumbprint() 6 | 7 | Return the `x5t#S256` parameter value as a byte array. Throws an `Error` if the parameter doesn't exist or the value isn't a base64url encoded string. 8 | 9 | ## Definition 10 | 11 | ```ts 12 | function x509CertificateSHA256Thumbprint(): Uint8Array; 13 | ``` 14 | -------------------------------------------------------------------------------- /docs/malta.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@oslojs/jwt", 3 | "description": "Parse and encode JSON web tokens.", 4 | "domain": "https://jwt.oslojs.dev", 5 | "twitter": "@oslo_project", 6 | "asset_hashing": true, 7 | "sidebar": [ 8 | { 9 | "title": "API reference", 10 | "pages": [["@oslojs/jwt", "/reference/main"]] 11 | }, 12 | { 13 | "title": "Links", 14 | "pages": [ 15 | ["GitHub", "https://github.com/oslo-project/jwt"], 16 | ["Oslo", "https://oslojs.dev"], 17 | ["Twitter", "https://twitter.com/pilcrowonpaper"], 18 | ["Donate", "https://github.com/sponsors/pilcrowOnPaper"] 19 | ] 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /docs/pages/reference/main/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "@oslojs/jwt" 3 | --- 4 | 5 | # @oslojs/jwt 6 | 7 | ## Constants 8 | 9 | - [`joseAlgorithmES256`](/reference/main/joseAlgorithmES256) 10 | - [`joseAlgorithmHS256`](/reference/main/joseAlgorithmHS256) 11 | - [`joseAlgorithmRS256`](/reference/main/joseAlgorithmRS256) 12 | 13 | ## Classes 14 | 15 | - [`JWSRegisteredHeaders`](/reference/main/JWSRegisteredHeaders) 16 | - [`JWTRegisteredClaims`](/reference/main/JWTRegisteredClaims) 17 | 18 | ## Functions 19 | 20 | - [`decodeJWT()`](/reference/main/decodeJWT) 21 | - [`encodeJWT()`](/reference/main/encodeJWT) 22 | - [`parseJWT()`](/reference/main/parseJWT) 23 | -------------------------------------------------------------------------------- /docs/pages/guides/create-tokens.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Create tokens" 3 | --- 4 | 5 | # Create tokens 6 | 7 | Use `encodeJWT()` to encode a header object, payload object, and a signature into a token. 8 | 9 | ```ts 10 | import { joseAlgorithmHS256, createJWTSignatureMessage, encodeJWT } from "@oslojs/jwt"; 11 | 12 | const headerJSON = JSON.stringify({ 13 | alg: joseAlgorithmHS256, 14 | typ: "JWT" 15 | }); 16 | const payloadJSON = JSON.stringify({ 17 | exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 30, 18 | name: "John Doe" 19 | }); 20 | const signatureBuffer = await crypto.subtle.sign("HMAC", key, createJWTSignatureMessage(headerJSON, payloadJSON)); 21 | const jwt = encodeJWT(headerJSON, payloadJSON, new Uint8Array(signatureBuffer)); 22 | ``` 23 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | "@typescript-eslint/no-explicit-any": "off", 4 | "@typescript-eslint/no-empty-function": "off", 5 | "@typescript-eslint/ban-types": "off", 6 | "@typescript-eslint/no-unused-vars": [ 7 | "error", 8 | { 9 | argsIgnorePattern: "^_", 10 | varsIgnorePattern: "^_", 11 | caughtErrorsIgnorePattern: "^_" 12 | } 13 | ], 14 | "@typescript-eslint/no-empty-interface": "off", 15 | "@typescript-eslint/explicit-function-return-type": "error", 16 | "no-async-promise-executor": "off", 17 | "no-useless-catch": "off" 18 | }, 19 | parser: "@typescript-eslint/parser", 20 | extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"], 21 | plugins: ["@typescript-eslint"], 22 | env: { 23 | node: true 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @oslojs/jwt 2 | 3 | **Documentation: https://jwt.oslojs.dev** 4 | 5 | A JavaScript library for parsing and encoding JSON web tokens (JWT) by [Oslo](https://oslojs.dev). Only signed tokens are supported. 6 | 7 | - Runtime-agnostic 8 | - No third-party dependencies 9 | - Fully typed 10 | 11 | ```ts 12 | import { parseJWT, JWSRegisteredHeaders, JWTRegisteredClaims, joseAlgorithmHS256 } from "@oslojs/jwt"; 13 | 14 | const [header, payload, signature] = parseJWT(jwt); 15 | const headerParameters = new JWSRegisteredHeaders(header); 16 | if (headerParameters.algorithm() !== joseAlgorithmHS256) { 17 | throw new Error("Unsupported algorithm"); 18 | } 19 | const claims = new JWTRegisteredClaims(payload); 20 | if (!claims.verifyExpiration()) { 21 | throw new Error("Expired token"); 22 | } 23 | if (claims.hasNotBefore() && !claims.verifyNotBefore()) { 24 | throw new Error("Invalid token"); 25 | } 26 | ``` 27 | 28 | ## Installation 29 | 30 | ``` 31 | npm i @oslojs/jwt 32 | ``` 33 | -------------------------------------------------------------------------------- /docs/pages/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "@oslojs/jwt" 3 | --- 4 | 5 | # @oslojs/jwt 6 | 7 | A JavaScript library for parsing and encoding JSON web tokens (JWT) by [Oslo](https://oslojs.dev). Only signed tokens are supported. 8 | 9 | - Runtime-agnostic 10 | - No third-party dependencies 11 | - Fully typed 12 | 13 | ```ts 14 | import { parseJWT, JWSRegisteredHeaders, JWTRegisteredClaims, joseAlgorithmHS256 } from "@oslojs/jwt"; 15 | 16 | const [header, payload, signature] = parseJWT(jwt); 17 | const headerParameters = new JWSRegisteredHeaders(header); 18 | if (headerParameters.algorithm() !== joseAlgorithmHS256) { 19 | throw new Error("Unsupported algorithm"); 20 | } 21 | const claims = new JWTRegisteredClaims(payload); 22 | if (!claims.verifyExpiration()) { 23 | throw new Error("Expired token"); 24 | } 25 | if (claims.hasNotBefore() && !claims.verifyNotBefore()) { 26 | throw new Error("Invalid token"); 27 | } 28 | ``` 29 | 30 | ## Installation 31 | 32 | ``` 33 | npm i @oslojs/jwt 34 | ``` 35 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: "Publish" 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | env: 8 | CLOUDFLARE_API_TOKEN: ${{secrets.CLOUDFLARE_PAGES_API_TOKEN}} 9 | 10 | jobs: 11 | publish: 12 | name: Publish 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: setup actions 16 | uses: actions/checkout@v3 17 | - name: setup node 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: 20.5.1 21 | registry-url: https://registry.npmjs.org 22 | - name: install malta 23 | working-directory: docs 24 | run: | 25 | curl -o malta.tgz -L https://github.com/pilcrowonpaper/malta/releases/latest/download/linux-amd64.tgz 26 | tar -xvzf malta.tgz 27 | - name: build 28 | working-directory: docs 29 | run: ./linux-amd64/malta build 30 | - name: install wrangler 31 | run: npm i -g wrangler 32 | - name: deploy 33 | run: wrangler pages deploy docs/dist --project-name oslo-jwt --branch main 34 | -------------------------------------------------------------------------------- /docs/pages/guides/verify-tokens.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Verify tokens" 3 | --- 4 | 5 | # Verify tokens 6 | 7 | Use `parseJWT()` to get each of the individual parts of the token. Use `JWSRegisteredHeaders` and `JWTRegisteredClaims` to parse the header and payload claims. 8 | 9 | ```ts 10 | import { parseJWT, JWSRegisteredHeaders, JWTRegisteredClaims } from "@oslojs/jwt"; 11 | 12 | const [header, payload, signature, signatureMessage] = parseJWT(jwt); 13 | const headerParameters = new JWSRegisteredHeaders(header); 14 | if (headerParameters.algorithm() !== joseAlgorithmHS256) { 15 | throw new Error("Unsupported algorithm"); 16 | } 17 | const validSignature = await crypto.subtle.verify("HMAC", key, signature, signatureMessage); 18 | if (!validSignature) { 19 | throw new Error("Invalid signature"); 20 | } 21 | const claims = new JWTRegisteredClaims(payload); 22 | if (claims.hasExpiration() && !claims.verifyExpiration()) { 23 | throw new Error("Expired token"); 24 | } 25 | if (claims.hasNotBefore() && !claims.verifyNotBefore()) { 26 | throw new Error("Invalid token"); 27 | } 28 | ``` 29 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@oslojs/jwt", 3 | "type": "module", 4 | "version": "0.3.0", 5 | "description": "Parse and encoding JSON web tokens", 6 | "main": "dist/index.js", 7 | "types": "dist/index.d.ts", 8 | "module": "dist/index.js", 9 | "scripts": { 10 | "build": "rm -rf dist/* && tsc --project tsconfig.build.json", 11 | "format": "prettier -w .", 12 | "lint": "eslint src", 13 | "test": "vitest run --sequence.concurrent" 14 | }, 15 | "files": [ 16 | "/dist/" 17 | ], 18 | "keywords": [ 19 | "auth", 20 | "jwt" 21 | ], 22 | "repository": { 23 | "type": "git", 24 | "url": "https://github.com/oslo-project/jwt" 25 | }, 26 | "author": "pilcrowOnPaper", 27 | "license": "MIT", 28 | "devDependencies": { 29 | "@types/node": "^20.8.6", 30 | "@typescript-eslint/eslint-plugin": "^6.7.5", 31 | "@typescript-eslint/parser": "^6.7.5", 32 | "auri": "^2.0.0", 33 | "eslint": "^8.51.0", 34 | "prettier": "^3.0.3", 35 | "typescript": "^5.2.2", 36 | "vitest": "^0.34.6" 37 | }, 38 | "dependencies": { 39 | "@oslojs/encoding": "0.4.1" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /docs/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 pilcrowOnPaper 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributor manual 2 | 3 | ## Contributing to the docs 4 | 5 | We welcome all contributions to the docs, especially grammar fixes. Oslo uses [Malta](https://malta-cli.pages.dev) for generating documentation sites. All pages are markdown files located in the `docs/pages` directory. Make sure to update `malta.config.json` if you need a page to appear in the sidebar. 6 | 7 | ## Contributing to the source code 8 | 9 | We are open to most contributions, but please open a new issue before creating a pull request, especially for new features. It's likely your PR will be rejected if not. We have intentionally limited the scope of the project and we would like to keep the package lean. 10 | 11 | ### Set up 12 | 13 | Install dependencies with PNPM. 14 | 15 | ``` 16 | pnpm i 17 | ``` 18 | 19 | ### Testing 20 | 21 | Run `pnpm test` to run tests and `pnpm build` to build the package. 22 | 23 | ``` 24 | pnpm test 25 | 26 | pnpm build 27 | ``` 28 | 29 | ### Creating changesets 30 | 31 | When creating a PR, create a changeset with `pnpm auri add`. If you made multiple changes, create multiple changesets. Use `minor` for new features, and use `patch` for bug fixes: 32 | 33 | ``` 34 | pnpm auri add minor 35 | pnpm auri add patch 36 | ``` 37 | 38 | A new markdown file should be created in `.changesets` directory. Write a short summary of the change: 39 | 40 | ``` 41 | Fix: Handle negative numbers in `sqrt()` 42 | ``` 43 | 44 | ``` 45 | Feat: Add `greet()` 46 | ``` 47 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWTRegisteredClaims/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWTRegisteredClaims" 3 | --- 4 | 5 | # JWTRegisteredClaims 6 | 7 | Represents registered JWT payload claims defined in [RFC 7519](https://datatracker.ietf.org/doc/html/rfc7519). 8 | 9 | ## Constructor 10 | 11 | ```ts 12 | function constructor(payload: object): this; 13 | ``` 14 | 15 | ### Parameters 16 | 17 | - `payload`: JSON-decoded JWT payload object 18 | 19 | ## Methods 20 | 21 | - [`JWTRegisteredClaims.audiences()`](/reference/main/JWTRegisteredClaims/audiences) 22 | - [`JWTRegisteredClaims.expiration()`](/reference/main/JWTRegisteredClaims/expiration) 23 | - [`JWTRegisteredClaims.hasAudiences()`](/reference/main/JWTRegisteredClaims/hasAudiences) 24 | - [`JWTRegisteredClaims.hasExpiration()`](/reference/main/JWTRegisteredClaims/hasExpiration) 25 | - [`JWTRegisteredClaims.hasIssuedAt()`](/reference/main/JWTRegisteredClaims/hasIssuedAt) 26 | - [`JWTRegisteredClaims.hasIssuer()`](/reference/main/JWTRegisteredClaims/hasIssuer) 27 | - [`JWTRegisteredClaims.hasJWTId()`](/reference/main/JWTRegisteredClaims/hasJWTId) 28 | - [`JWTRegisteredClaims.hasNotBefore()`](/reference/main/JWTRegisteredClaims/hasNotBefore) 29 | - [`JWTRegisteredClaims.hasSubject()`](/reference/main/JWTRegisteredClaims/hasSubject) 30 | - [`JWTRegisteredClaims.issuedAt()`](/reference/main/JWTRegisteredClaims/issuedAt) 31 | - [`JWTRegisteredClaims.issuer()`](/reference/main/JWTRegisteredClaims/issuer) 32 | - [`JWTRegisteredClaims.jwtId()`](/reference/main/JWTRegisteredClaims/jwtId) 33 | - [`JWTRegisteredClaims.notBefore()`](/reference/main/JWTRegisteredClaims/notBefore) 34 | - [`JWTRegisteredClaims.subject()`](/reference/main/JWTRegisteredClaims/subject) 35 | - [`JWTRegisteredClaims.verifyExpiration()`](/reference/main/JWTRegisteredClaims/verifyExpiration) 36 | - [`JWTRegisteredClaims.verifyNotBefore()`](/reference/main/JWTRegisteredClaims/verifyNotBefore) 37 | -------------------------------------------------------------------------------- /src/index.test.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import { decodeJWT, encodeJWT, parseJWT } from "./index.js"; 3 | 4 | test("parseJWT()", () => { 5 | expect( 6 | parseJWT( 7 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" 8 | ) 9 | ).toStrictEqual([ 10 | { 11 | alg: "HS256", 12 | typ: "JWT" 13 | }, 14 | { 15 | sub: "1234567890", 16 | name: "John Doe", 17 | iat: 1516239022 18 | }, 19 | new Uint8Array([ 20 | 73, 249, 74, 199, 4, 73, 72, 199, 138, 40, 93, 144, 79, 135, 240, 164, 199, 137, 127, 126, 143, 58, 78, 178, 37, 21 | 95, 218, 117, 11, 44, 195, 151 22 | ]), 23 | new Uint8Array([ 24 | 101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 73, 85, 122, 73, 49, 78, 105, 73, 115, 73, 110, 82, 53, 99, 67, 25 | 73, 54, 73, 107, 112, 88, 86, 67, 74, 57, 46, 101, 121, 74, 122, 100, 87, 73, 105, 79, 105, 73, 120, 77, 106, 77, 26 | 48, 78, 84, 89, 51, 79, 68, 107, 119, 73, 105, 119, 105, 98, 109, 70, 116, 90, 83, 73, 54, 73, 107, 112, 118, 97, 27 | 71, 52, 103, 82, 71, 57, 108, 73, 105, 119, 105, 97, 87, 70, 48, 73, 106, 111, 120, 78, 84, 69, 50, 77, 106, 77, 28 | 53, 77, 68, 73, 121, 102, 81 29 | ]) 30 | ]); 31 | }); 32 | 33 | test("decodeJWT()", () => { 34 | expect( 35 | decodeJWT( 36 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" 37 | ) 38 | ).toStrictEqual({ 39 | sub: "1234567890", 40 | name: "John Doe", 41 | iat: 1516239022 42 | }); 43 | }); 44 | 45 | test("encodeJWT()", () => { 46 | const headerJSON = '{"alg":"HS256","typ":"JWT"}'; 47 | const payloadJSON = '{"sub":"1234567890","name":"John Doe","iat":1516239022}'; 48 | expect( 49 | encodeJWT( 50 | headerJSON, 51 | payloadJSON, 52 | new Uint8Array([ 53 | 73, 249, 74, 199, 4, 73, 72, 199, 138, 40, 93, 144, 79, 135, 240, 164, 199, 137, 127, 126, 143, 58, 78, 178, 37, 54 | 95, 218, 117, 11, 44, 195, 151 55 | ]) 56 | ) 57 | ).toBe( 58 | "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" 59 | ); 60 | }); 61 | -------------------------------------------------------------------------------- /docs/pages/reference/main/JWSRegisteredHeaders/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: "JWSRegisteredHeaders" 3 | --- 4 | 5 | # JWSRegisteredHeaders 6 | 7 | Represents registered JWS header parameters defined in [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515). 8 | 9 | ## Constructor 10 | 11 | ```ts 12 | function constructor(header: object): this; 13 | ``` 14 | 15 | ### Parameters 16 | 17 | - `header`: JSON-decoded JWS header object 18 | 19 | ## Methods 20 | 21 | - [`JWSRegisteredHeaders.algorithm()`](/reference/main/JWSRegisteredHeaders/algorithm) 22 | - [`JWSRegisteredHeaders.critical()`](/reference/main/JWSRegisteredHeaders/critical) 23 | - [`JWSRegisteredHeaders.hasAlgorithm()`](/reference/main/JWSRegisteredHeaders/hasAlgorithm) 24 | - [`JWSRegisteredHeaders.hasCritical()`](/reference/main/JWSRegisteredHeaders/hasCritical) 25 | - [`JWSRegisteredHeaders.hasJWK()`](/reference/main/JWSRegisteredHeaders/hasJWK) 26 | - [`JWSRegisteredHeaders.hasJWKSetURL()`](/reference/main/JWSRegisteredHeaders/hasJWKSetURL) 27 | - [`JWSRegisteredHeaders.hasKeyId()`](/reference/main/JWSRegisteredHeaders/hasKeyId) 28 | - [`JWSRegisteredHeaders.hasType()`](/reference/main/JWSRegisteredHeaders/hasType) 29 | - [`JWSRegisteredHeaders.hasX509CertificateChain()`](/reference/main/JWSRegisteredHeaders/hasX509CertificateChain) 30 | - [`JWSRegisteredHeaders.hasX509CertificateSHA1Thumbprint()`](/reference/main/JWSRegisteredHeaders/hasX509CertificateSHA1Thumbprint) 31 | - [`JWSRegisteredHeaders.hasX509CertificateSHA256Thumbprint()`](/reference/main/JWSRegisteredHeaders/hasX509CertificateSHA256Thumbprint) 32 | - [`JWSRegisteredHeaders.hasX509URL()`](/reference/main/JWSRegisteredHeaders/hasX509URL) 33 | - [`JWSRegisteredHeaders.jwk()`](/reference/main/JWSRegisteredHeaders/jwk) 34 | - [`JWSRegisteredHeaders.jwkSetURL()`](/reference/main/JWSRegisteredHeaders/jwkSetURL) 35 | - [`JWSRegisteredHeaders.keyId()`](/reference/main/JWSRegisteredHeaders/keyId) 36 | - [`JWSRegisteredHeaders.type()`](/reference/main/JWSRegisteredHeaders/type) 37 | - [`JWSRegisteredHeaders.x509CertificateChain()`](/reference/main/JWSRegisteredHeaders/x509CertificateChain) 38 | - [`JWSRegisteredHeaders.x509CertificateSHA1Thumbprint()`](/reference/main/JWSRegisteredHeaders/x509CertificateSHA1Thumbprint) 39 | - [`JWSRegisteredHeaders.x509CertificateSHA256Thumbprint()`](/reference/main/JWSRegisteredHeaders/x509CertificateSHA256Thumbprint) 40 | - [`JWSRegisteredHeaders.x509URL()`](/reference/main/JWSRegisteredHeaders/x509URL) 41 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { decodeBase64, decodeBase64urlIgnorePadding, encodeBase64urlNoPadding } from "@oslojs/encoding"; 2 | 3 | export function parseJWT( 4 | jwt: string 5 | ): [header: object, payload: object, signature: Uint8Array, signatureMessage: Uint8Array] { 6 | const parts = jwt.split("."); 7 | if (parts.length !== 3) { 8 | throw new Error("Invalid JWT"); 9 | } 10 | let jsonHeader: string; 11 | let jsonPayload: string; 12 | let signature: Uint8Array; 13 | try { 14 | jsonHeader = new TextDecoder().decode(decodeBase64urlIgnorePadding(parts[0])); 15 | jsonPayload = new TextDecoder().decode(decodeBase64urlIgnorePadding(parts[1])); 16 | signature = decodeBase64urlIgnorePadding(parts[2]); 17 | } catch { 18 | throw new Error("Invalid JWT: Invalid base64url encoding"); 19 | } 20 | let header: unknown; 21 | let payload: unknown; 22 | try { 23 | header = JSON.parse(jsonHeader); 24 | payload = JSON.parse(jsonPayload); 25 | } catch { 26 | throw new Error("Invalid JWT: Invalid JSON encoding"); 27 | } 28 | if (typeof header !== "object" || header === null) { 29 | throw new Error("Invalid JWT: Invalid header"); 30 | } 31 | if (typeof payload !== "object" || payload === null) { 32 | throw new Error("Invalid JWT: Invalid payload"); 33 | } 34 | const signatureMessage = new TextEncoder().encode(parts[0] + "." + parts[1]); 35 | return [header as object, payload as object, signature, signatureMessage]; 36 | } 37 | 38 | export function decodeJWT(jwt: string): object { 39 | const parts = jwt.split("."); 40 | if (parts.length !== 3) { 41 | throw new Error("Invalid JWT"); 42 | } 43 | let jsonPayload: string; 44 | try { 45 | jsonPayload = new TextDecoder().decode(decodeBase64urlIgnorePadding(parts[1])); 46 | } catch { 47 | throw new Error("Invalid JWT: Invalid base64url encoding"); 48 | } 49 | let payload: unknown; 50 | try { 51 | payload = JSON.parse(jsonPayload); 52 | } catch { 53 | throw new Error("Invalid JWT: Invalid JSON encoding"); 54 | } 55 | if (typeof payload !== "object" || payload === null) { 56 | throw new Error("Invalid JWT: Invalid payload"); 57 | } 58 | return payload as object; 59 | } 60 | 61 | export function encodeJWT(headerJSON: string, payloadJSON: string, signature: Uint8Array): string { 62 | const encodedHeader = encodeBase64urlNoPadding(new TextEncoder().encode(headerJSON)); 63 | const encodedPayload = encodeBase64urlNoPadding(new TextEncoder().encode(payloadJSON)); 64 | const encodedSignature = encodeBase64urlNoPadding(signature); 65 | const jwt = encodedHeader + "." + encodedPayload + "." + encodedSignature; 66 | return jwt; 67 | } 68 | 69 | export function createJWTSignatureMessage(headerJSON: string, payloadJSON: string): Uint8Array { 70 | const encodedHeader = encodeBase64urlNoPadding(new TextEncoder().encode(headerJSON)); 71 | const encodedPayload = encodeBase64urlNoPadding(new TextEncoder().encode(payloadJSON)); 72 | const message = encodedHeader + "." + encodedPayload; 73 | return new TextEncoder().encode(message); 74 | } 75 | 76 | export class JWTRegisteredClaims { 77 | private target: object; 78 | 79 | constructor(target: object) { 80 | this.target = target; 81 | } 82 | 83 | public hasIssuer(): boolean { 84 | return "iss" in this.target; 85 | } 86 | 87 | public issuer(): string { 88 | if ("iss" in this.target && typeof this.target.iss === "string") { 89 | return this.target.iss; 90 | } 91 | throw new Error("Invalid or missing 'iss' claim"); 92 | } 93 | 94 | public hasSubject(): boolean { 95 | return "sub" in this.target; 96 | } 97 | 98 | public subject(): string { 99 | if ("sub" in this.target && typeof this.target.sub === "string") { 100 | return this.target.sub; 101 | } 102 | throw new Error("Invalid or missing 'sub' claim"); 103 | } 104 | 105 | public hasAudiences(): boolean { 106 | return "aud" in this.target; 107 | } 108 | 109 | public audiences(): string[] { 110 | if ("aud" in this.target && typeof this.target.aud === "string") { 111 | const audiences = [this.target.aud]; 112 | return audiences; 113 | } 114 | if ("aud" in this.target && Array.isArray(this.target.aud)) { 115 | for (const audience in this.target.aud) { 116 | if (typeof audience !== "string") { 117 | throw new Error("Invalid or missing 'aud' claim"); 118 | } 119 | } 120 | return this.target.aud as string[]; 121 | } 122 | throw new Error("Invalid or missing 'aud' claim"); 123 | } 124 | 125 | public hasExpiration(): boolean { 126 | return "exp" in this.target; 127 | } 128 | 129 | public expiration(): Date { 130 | if ( 131 | "exp" in this.target && 132 | typeof this.target.exp === "number" && 133 | this.target.exp >= 0 && 134 | Number.isInteger(this.target.exp) 135 | ) { 136 | return new Date(this.target.exp * 1000); 137 | } 138 | throw new Error("Invalid or missing 'exp' claim"); 139 | } 140 | 141 | public verifyExpiration(): boolean { 142 | if ( 143 | "exp" in this.target && 144 | typeof this.target.exp === "number" && 145 | this.target.exp >= 0 && 146 | Number.isInteger(this.target.exp) 147 | ) { 148 | return Date.now() < this.target.exp * 1000; 149 | } 150 | throw new Error("Invalid or missing 'exp' claim"); 151 | } 152 | 153 | public hasNotBefore(): boolean { 154 | return "nbf" in this.target; 155 | } 156 | 157 | public notBefore(): Date { 158 | if ( 159 | "nbf" in this.target && 160 | typeof this.target.nbf === "number" && 161 | this.target.nbf >= 0 && 162 | Number.isInteger(this.target.nbf) 163 | ) { 164 | return new Date(this.target.nbf * 1000); 165 | } 166 | throw new Error("Invalid or missing 'nbf' claim"); 167 | } 168 | 169 | public verifyNotBefore(): boolean { 170 | if ( 171 | "nbf" in this.target && 172 | typeof this.target.nbf === "number" && 173 | this.target.nbf >= 0 && 174 | Number.isInteger(this.target.nbf) 175 | ) { 176 | return Date.now() >= this.target.nbf * 1000; 177 | } 178 | throw new Error("Invalid or missing 'nbf' claim"); 179 | } 180 | 181 | public hasIssuedAt(): boolean { 182 | return "iat" in this.target; 183 | } 184 | 185 | public issuedAt(): Date { 186 | if ( 187 | "iat" in this.target && 188 | typeof this.target.iat === "number" && 189 | this.target.iat >= 0 && 190 | Number.isInteger(this.target.iat) 191 | ) { 192 | return new Date(this.target.iat * 1000); 193 | } 194 | throw new Error("Invalid or missing 'iat' claim"); 195 | } 196 | 197 | public hasJWTId(): boolean { 198 | return "jti" in this.target; 199 | } 200 | 201 | public jwtId(): string { 202 | if ("jti" in this.target && typeof this.target.jti === "string") { 203 | return this.target.jti; 204 | } 205 | throw new Error("Invalid or missing 'jti' claim"); 206 | } 207 | } 208 | 209 | export class JWSRegisteredHeaders { 210 | private target: object; 211 | 212 | constructor(target: object) { 213 | this.target = target; 214 | } 215 | 216 | public hasAlgorithm(): boolean { 217 | return "alg" in this.target; 218 | } 219 | 220 | public algorithm(): string { 221 | if ("alg" in this.target && typeof this.target.alg === "string") { 222 | return this.target.alg; 223 | } 224 | throw new Error("Invalid or missing 'alg' claim"); 225 | } 226 | 227 | public hasJWKSetURL(): boolean { 228 | return "jku" in this.target; 229 | } 230 | 231 | public jwkSetURL(): string { 232 | if ("jku" in this.target && typeof this.target.jku === "string") { 233 | return this.target.jku; 234 | } 235 | throw new Error("Invalid or missing 'jku' claim"); 236 | } 237 | 238 | public hasJWK(): boolean { 239 | return "jwk" in this.target; 240 | } 241 | 242 | public jwk(): string { 243 | if ("jwk" in this.target && typeof this.target.jwk === "string") { 244 | return this.target.jwk; 245 | } 246 | throw new Error("Invalid or missing 'jwk' claim"); 247 | } 248 | 249 | public hasKeyId(): boolean { 250 | return "kid" in this.target; 251 | } 252 | 253 | public keyId(): string { 254 | if ("kid" in this.target && typeof this.target.kid === "string") { 255 | return this.target.kid; 256 | } 257 | throw new Error("Invalid or missing 'kid' claim"); 258 | } 259 | 260 | public hasX509URL(): boolean { 261 | return "x5u" in this.target; 262 | } 263 | 264 | public x509URL(): string { 265 | if ("x5u" in this.target && typeof this.target.x5u === "string") { 266 | return this.target.x5u; 267 | } 268 | throw new Error("Invalid or missing 'x5u' claim"); 269 | } 270 | 271 | public hasX509CertificateChain(): boolean { 272 | return "x5c" in this.target; 273 | } 274 | 275 | public x509CertificateChain(): Uint8Array[] { 276 | if ("x5c" in this.target && Array.isArray(this.target.x5c)) { 277 | if (this.target.x5c.length === 0) { 278 | throw new Error("Invalid or missing 'x5c' claim"); 279 | } 280 | const chain: Uint8Array[] = []; 281 | for (const encoded of this.target.x5c) { 282 | if (typeof encoded !== "string") { 283 | throw new Error("Invalid or missing 'x5c' claim"); 284 | } 285 | try { 286 | chain.push(decodeBase64(encoded)); 287 | } catch { 288 | throw new Error("Invalid or missing 'x5c' claim"); 289 | } 290 | } 291 | return chain; 292 | } 293 | throw new Error("Invalid or missing 'x5c' claim"); 294 | } 295 | 296 | public hasX509CertificateSHA1Thumbprint(): boolean { 297 | return "x5t" in this.target; 298 | } 299 | 300 | public x509CertificateSHA1Thumbprint(): Uint8Array { 301 | if ("x5t" in this.target && typeof this.target.x5t === "string") { 302 | try { 303 | const thumbprint = decodeBase64urlIgnorePadding(this.target.x5t); 304 | return thumbprint; 305 | } catch { 306 | throw new Error("Invalid or missing 'x5t' claim"); 307 | } 308 | } 309 | throw new Error("Invalid or missing 'x5t' claim"); 310 | } 311 | 312 | public hasX509CertificateSHA256Thumbprint(): boolean { 313 | return "x5t#S256" in this.target; 314 | } 315 | 316 | public x509CertificateSHA256Thumbprint(): Uint8Array { 317 | if ("x5t#S256" in this.target && typeof this.target["x5t#S256"] === "string") { 318 | try { 319 | const thumbprint = decodeBase64urlIgnorePadding(this.target["x5t#S256"]); 320 | return thumbprint; 321 | } catch { 322 | throw new Error("Invalid or missing 'x5t#S256' claim"); 323 | } 324 | } 325 | throw new Error("Invalid or missing 'x5t#S256' claim"); 326 | } 327 | 328 | public hasType(): boolean { 329 | return "typ" in this.target; 330 | } 331 | 332 | public type(): string { 333 | if ("typ" in this.target && typeof this.target.typ === "string") { 334 | return this.target.typ; 335 | } 336 | throw new Error("Invalid or missing 'typ' claim"); 337 | } 338 | 339 | public hasContentType(): boolean { 340 | return "cty" in this.target; 341 | } 342 | 343 | public contentType(): string { 344 | if ("cty" in this.target && typeof this.target.cty === "string") { 345 | return this.target.cty; 346 | } 347 | throw new Error("Invalid or missing 'cty' claim"); 348 | } 349 | 350 | public hasCritical(): boolean { 351 | return "crit" in this.target; 352 | } 353 | 354 | public critical(): string[] { 355 | if ("crit" in this.target && Array.isArray(this.target.crit)) { 356 | if (this.target.crit.length === 0) { 357 | throw new Error("Invalid or missing 'crit' claim"); 358 | } 359 | for (const audience in this.target.crit) { 360 | if (typeof audience !== "string") { 361 | throw new Error("Invalid or missing 'crit' claim"); 362 | } 363 | } 364 | return this.target.crit as string[]; 365 | } 366 | throw new Error("Invalid or missing 'crit' claim"); 367 | } 368 | } 369 | 370 | export const joseAlgorithmHS256 = "HS256"; 371 | export const joseAlgorithmES256 = "ES256"; 372 | export const joseAlgorithmRS256 = "RS256"; 373 | --------------------------------------------------------------------------------