summaryrefslogtreecommitdiff
path: root/std/jwt
diff options
context:
space:
mode:
Diffstat (limited to 'std/jwt')
-rw-r--r--std/jwt/README.md90
-rw-r--r--std/jwt/_algorithm.ts17
-rw-r--r--std/jwt/_algorithm_test.ts11
-rw-r--r--std/jwt/_signature.ts63
-rw-r--r--std/jwt/_signature_test.ts46
-rw-r--r--std/jwt/mod.ts208
-rw-r--r--std/jwt/test.ts304
7 files changed, 0 insertions, 739 deletions
diff --git a/std/jwt/README.md b/std/jwt/README.md
deleted file mode 100644
index fe895dafc..000000000
--- a/std/jwt/README.md
+++ /dev/null
@@ -1,90 +0,0 @@
-# jwt
-
-Create and verify JSON Web Tokens.
-
-## JSON Web Token
-
-### create
-
-Takes a `payload`, `key` and `header` and returns the url-safe encoded `token`.
-
-```typescript
-import { create } from "https://deno.land/std@$STD_VERSION/jwt/mod.ts";
-
-const payload = { foo: "bar" };
-const key = "secret";
-
-const token = await create(payload, key); // eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.WePl7achkd0oGNB8XRF_LJwxlyiPZqpdNgdKpDboAjSTsWq-aOGNynTp8TOv8KjonFym8vwFwppXOLoLXbkIaQ
-```
-
-**Specific algorithm**
-
-```typescript
-const token = await create(payload, key, { header: { alg: "HS256" } });
-```
-
-### verify
-
-Takes a `token`, `key` and an optional `options` object and returns the
-`payload` of the `token` if the `token` is valid. Otherwise it throws an
-`Error`.
-
-```typescript
-import { verify } from "https://deno.land/std@$STD_VERSION/jwt/mod.ts";
-
-const token =
- "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.WePl7achkd0oGNB8XRF_LJwxlyiPZqpdNgdKpDboAjSTsWq-aOGNynTp8TOv8KjonFym8vwFwppXOLoLXbkIaQ";
-const key = "secret";
-
-const payload = await verify(token, key); // { foo: "bar" }
-```
-
-**Specific algorithm**
-
-```ts
-const payload = await verify(token, key, { algorithm: "HS256" });
-```
-
-### decode
-
-Takes a `token` to return an object with the `header`, `payload` and `signature`
-properties if the `token` is valid. Otherwise it throws an `Error`.
-
-```typescript
-import { decode } from "https://deno.land/std@$STD_VERSION/jwt/mod.ts";
-
-const token =
- "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.WePl7achkd0oGNB8XRF_LJwxlyiPZqpdNgdKpDboAjSTsWq-aOGNynTp8TOv8KjonFym8vwFwppXOLoLXbkIaQ";
-
-const { payload, signature, header } = await decode(token); // { header: { alg: "HS512", typ: "JWT" }, payload: { foo: "bar" }, signature: "59e3e5eda72191dd2818d07c5d117f2c9c3197288f66aa5d36074aa436e8023493b16abe68e18dca74e9f133aff0a8e89c5ca6f2fc05c29a5738ba0b5db90869" }
-```
-
-## Expiration
-
-The optional **exp** claim in the payload (number of seconds since January 1,
-1970, 00:00:00 UTC) that identifies the expiration time on or after which the
-JWT must not be accepted for processing. This module checks if the current
-date/time is before the expiration date/time listed in the **exp** claim.
-
-```typescript
-const oneHour = 60 * 60;
-const token = await create({ exp: Date.now() + oneHour }, "secret");
-```
-
-## Algorithms
-
-The following signature and MAC algorithms have been implemented:
-
-- HS256 (HMAC SHA-256)
-- HS512 (HMAC SHA-512)
-- none ([_Unsecured JWTs_](https://tools.ietf.org/html/rfc7519#section-6)).
-
-## Serialization
-
-This application uses the JWS Compact Serialization only.
-
-## Specifications
-
-- [JSON Web Token](https://tools.ietf.org/html/rfc7519)
-- [JSON Web Signature](https://www.rfc-editor.org/rfc/rfc7515.html)
-- [JSON Web Algorithms](https://www.rfc-editor.org/rfc/rfc7518.html)
diff --git a/std/jwt/_algorithm.ts b/std/jwt/_algorithm.ts
deleted file mode 100644
index c9c5257e1..000000000
--- a/std/jwt/_algorithm.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * JSW §1: Cryptographic algorithms and identifiers for use with this specification
- * are described in the separate JSON Web Algorithms (JWA) specification:
- * https://www.rfc-editor.org/rfc/rfc7518
- */
-export type Algorithm = "none" | "HS256" | "HS512";
-export type AlgorithmInput = Algorithm | Array<Exclude<Algorithm, "none">>;
-/**
- * Verify the algorithm
- * @param algorithm as string or multiple algorithms in an array excluding 'none'
- * @param the algorithm from the jwt header
- */
-export function verify(algorithm: AlgorithmInput, jwtAlg: string): boolean {
- return Array.isArray(algorithm)
- ? (algorithm as string[]).includes(jwtAlg)
- : algorithm === jwtAlg;
-}
diff --git a/std/jwt/_algorithm_test.ts b/std/jwt/_algorithm_test.ts
deleted file mode 100644
index 99583bd79..000000000
--- a/std/jwt/_algorithm_test.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-import { assertEquals } from "../testing/asserts.ts";
-
-import { verify as verifyAlgorithm } from "./_algorithm.ts";
-
-Deno.test("[jwt] verify algorithm", function () {
- assertEquals(verifyAlgorithm("HS512", "HS512"), true);
- assertEquals(verifyAlgorithm("HS512", "HS256"), false);
- assertEquals(verifyAlgorithm(["HS512"], "HS512"), true);
- assertEquals(verifyAlgorithm(["HS256", "HS512"], "HS512"), true);
- assertEquals(verifyAlgorithm(["HS512"], "HS256"), false);
-});
diff --git a/std/jwt/_signature.ts b/std/jwt/_signature.ts
deleted file mode 100644
index 81c1309d1..000000000
--- a/std/jwt/_signature.ts
+++ /dev/null
@@ -1,63 +0,0 @@
-import type { Algorithm } from "./_algorithm.ts";
-import { HmacSha256 } from "../hash/sha256.ts";
-import { HmacSha512 } from "../hash/sha512.ts";
-import { encode as convertUint8ArrayToBase64url } from "../encoding/base64url.ts";
-import { decodeString as convertHexToUint8Array } from "../encoding/hex.ts";
-
-export function convertHexToBase64url(input: string): string {
- return convertUint8ArrayToBase64url(convertHexToUint8Array(input));
-}
-
-function encrypt(
- algorithm: Algorithm,
- key: string,
- message: string,
-): string {
- switch (algorithm) {
- case "none":
- return "";
- case "HS256":
- return new HmacSha256(key).update(message).toString();
- case "HS512":
- return new HmacSha512(key).update(message).toString();
- default:
- throw new RangeError(
- `The algorithm of '${algorithm}' in the header is not supported.`,
- );
- }
-}
-
-/**
- * Create a signature
- * @param algorithm
- * @param key
- * @param input
- */
-export async function create(
- algorithm: Algorithm,
- key: string,
- input: string,
-): Promise<string> {
- return convertHexToBase64url(await encrypt(algorithm, key, input));
-}
-
-/**
- * Verify a signature
- * @param signature
- * @param key
- * @param alg
- * @param signingInput
- */
-export async function verify({
- signature,
- key,
- algorithm,
- signingInput,
-}: {
- signature: string;
- key: string;
- algorithm: Algorithm;
- signingInput: string;
-}): Promise<boolean> {
- return signature === (await encrypt(algorithm, key, signingInput));
-}
diff --git a/std/jwt/_signature_test.ts b/std/jwt/_signature_test.ts
deleted file mode 100644
index c02f0f4bc..000000000
--- a/std/jwt/_signature_test.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { assertEquals } from "../testing/asserts.ts";
-import { create, decode } from "./mod.ts";
-
-import {
- convertHexToBase64url,
- create as createSignature,
- verify as verifySignature,
-} from "./_signature.ts";
-
-const algorithm = "HS256";
-const key = "m$y-key";
-
-Deno.test("[jwt] create signature", async function () {
- // https://www.freeformatter.com/hmac-generator.html
- const computedHmacInHex =
- "2b9e6619fa7f2c8d8b3565c88365376b75b1b0e5d87e41218066fd1986f2c056";
- assertEquals(
- await createSignature(algorithm, key, "thisTextWillBeEncrypted"),
- convertHexToBase64url(computedHmacInHex),
- );
-
- const anotherVerifiedSignatureInBase64Url =
- "p2KneqJhji8T0PDlVxcG4DROyzTgWXbDhz_mcTVojXo";
- assertEquals(
- await createSignature(
- algorithm,
- key,
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ",
- ),
- anotherVerifiedSignatureInBase64Url,
- );
-});
-
-Deno.test("[jwt] verify signature", async function () {
- const jwt = await create({}, key);
- const { header, signature } = decode(jwt);
-
- const validSignature = await verifySignature({
- signature,
- key,
- algorithm: header.alg,
- signingInput: jwt.slice(0, jwt.lastIndexOf(".")),
- });
-
- assertEquals(validSignature, true);
-});
diff --git a/std/jwt/mod.ts b/std/jwt/mod.ts
deleted file mode 100644
index 09485c8c6..000000000
--- a/std/jwt/mod.ts
+++ /dev/null
@@ -1,208 +0,0 @@
-import type { Algorithm, AlgorithmInput } from "./_algorithm.ts";
-import * as base64url from "../encoding/base64url.ts";
-import { encodeToString as convertUint8ArrayToHex } from "../encoding/hex.ts";
-import {
- create as createSignature,
- verify as verifySignature,
-} from "./_signature.ts";
-import { verify as verifyAlgorithm } from "./_algorithm.ts";
-
-/*
- * JWT §4.1: The following Claim Names are registered in the IANA
- * "JSON Web Token Claims" registry established by Section 10.1. None of the
- * claims defined below are intended to be mandatory to use or implement in all
- * cases, but rather they provide a starting point for a set of useful,
- * interoperable claims.
- * Applications using JWTs should define which specific claims they use and when
- * they are required or optional.
- */
-export interface PayloadObject {
- iss?: string;
- sub?: string;
- aud?: string[] | string;
- exp?: number;
- nbf?: number;
- iat?: number;
- jti?: string;
- [key: string]: unknown;
-}
-
-export type Payload = PayloadObject | string;
-
-/*
- * JWS §4.1.1: The "alg" value is a case-sensitive ASCII string containing a
- * StringOrURI value. This Header Parameter MUST be present and MUST be
- * understood and processed by implementations.
- */
-export interface Header {
- alg: Algorithm;
- [key: string]: unknown;
-}
-
-const encoder = new TextEncoder();
-const decoder = new TextDecoder();
-
-/*
- * JWT §4.1.4: Implementers MAY provide for some small leeway to account for
- * clock skew.
- */
-function isExpired(exp: number, leeway = 0): boolean {
- return exp + leeway < Date.now() / 1000;
-}
-
-function tryToParsePayload(input: string): unknown {
- try {
- return JSON.parse(input);
- } catch {
- return input;
- }
-}
-
-/**
- * Decodes a token into an { header, payload, signature } object.
- * @param token
- */
-export function decode(
- token: string,
-): {
- header: Header;
- payload: unknown;
- signature: string;
-} {
- const parsedArray = token
- .split(".")
- .map(base64url.decode)
- .map((uint8Array, index) => {
- switch (index) {
- case 0:
- try {
- return JSON.parse(decoder.decode(uint8Array));
- } catch {
- break;
- }
- case 1:
- return tryToParsePayload(decoder.decode(uint8Array));
- case 2:
- return convertUint8ArrayToHex(uint8Array);
- }
- throw TypeError("The serialization is invalid.");
- });
-
- const [header, payload, signature] = parsedArray;
-
- if (
- !(
- (typeof signature === "string" &&
- typeof header?.alg === "string") && payload?.exp !== undefined
- ? typeof payload.exp === "number"
- : true
- )
- ) {
- throw new Error(`The token is invalid.`);
- }
-
- if (
- typeof payload?.exp === "number" &&
- isExpired(payload.exp)
- ) {
- throw RangeError("The token is expired.");
- }
-
- return {
- header,
- payload,
- signature,
- };
-}
-
-export type VerifyOptions = {
- algorithm?: AlgorithmInput;
-};
-
-/**
- * Verifies a token.
- * @param token
- * @param key
- * @param object with property 'algorithm'
- */
-export async function verify(
- token: string,
- key: string,
- { algorithm = "HS512" }: VerifyOptions = {},
-): Promise<unknown> {
- const { header, payload, signature } = decode(token);
-
- if (!verifyAlgorithm(algorithm, header.alg)) {
- throw new Error(
- `The token's algorithm does not match the specified algorithm '${algorithm}'.`,
- );
- }
-
- /*
- * JWS §4.1.11: The "crit" (critical) Header Parameter indicates that
- * extensions to this specification and/or [JWA] are being used that MUST be
- * understood and processed.
- */
- if ("crit" in header) {
- throw new Error(
- "The 'crit' header parameter is currently not supported by this module.",
- );
- }
-
- if (
- !(await verifySignature({
- signature,
- key,
- algorithm: header.alg,
- signingInput: token.slice(0, token.lastIndexOf(".")),
- }))
- ) {
- throw new Error(
- "The token's signature does not match the verification signature.",
- );
- }
-
- return payload;
-}
-
-/*
- * JSW §7.1: The JWS Compact Serialization represents digitally signed or MACed
- * content as a compact, URL-safe string. This string is:
- * BASE64URL(UTF8(JWS Protected Header)) || '.' ||
- * BASE64URL(JWS Payload) || '.' ||
- * BASE64URL(JWS Signature)
- */
-function createSigningInput(header: Header, payload: Payload): string {
- return `${
- base64url.encode(
- encoder.encode(JSON.stringify(header)),
- )
- }.${
- base64url.encode(
- encoder.encode(
- typeof payload === "string" ? payload : JSON.stringify(payload),
- ),
- )
- }`;
-}
-
-/**
- * Creates a token.
- * @param payload
- * @param key
- * @param object with property 'header'
- */
-export async function create(
- payload: Payload,
- key: string,
- {
- header = { alg: "HS512", typ: "JWT" },
- }: {
- header?: Header;
- } = {},
-): Promise<string> {
- const signingInput = createSigningInput(header, payload);
- const signature = await createSignature(header.alg, key, signingInput);
-
- return `${signingInput}.${signature}`;
-}
diff --git a/std/jwt/test.ts b/std/jwt/test.ts
deleted file mode 100644
index deaa857e2..000000000
--- a/std/jwt/test.ts
+++ /dev/null
@@ -1,304 +0,0 @@
-import { create, decode, Header, Payload, verify } from "./mod.ts";
-
-import {
- assertEquals,
- assertThrows,
- assertThrowsAsync,
-} from "../testing/asserts.ts";
-
-const header: Header = {
- alg: "HS256",
- typ: "JWT",
-};
-
-const payload: Payload = {
- name: "John Doe",
-};
-
-const key = "secret";
-
-Deno.test({
- name: "[jwt] create",
- fn: async function () {
- assertEquals(
- await create("", key),
- "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9..B0lmJDC8zSfMJstPqLdOAWfM265-5Svj0XrACZm8DKa1y6VJA0W7d0VoGGKJo0quKxWUdf1B1ueElNk2Yl_cLw",
- );
- assertEquals(
- await create({}, key),
- "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.e30.dGumW8J3t2BlAwqqoisyWDC6ov2hRtjTAFHzd-Tlr4DUScaHG4OYqTHXLHEzd3hU5wy5xs87vRov6QzZnj410g",
- );
- assertEquals(
- await create({ foo: "bar" }, key),
- "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.WePl7achkd0oGNB8XRF_LJwxlyiPZqpdNgdKpDboAjSTsWq-aOGNynTp8TOv8KjonFym8vwFwppXOLoLXbkIaQ",
- );
- assertEquals(
- await create("null", key),
- "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.bnVsbA.tv7DbhvALc5Eq2sC61Y9IZlG2G15hvJoug9UO6iwmE_UZOLva8EC-9PURg7IIj6f-F9jFWix8vCn9WaAMHR1AA",
- );
- assertEquals(
- await create("[]", key),
- "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.W10.BqmZ-tVI9a-HDx6PpMiBdMq6lzcaqO9sW6pImw-NRajCCmRrVi6IgMhEw7lvOG6sxhteceVMl8_xFRGverJJWw",
- );
- },
-});
-
-Deno.test({
- name: "[jwt] verify",
- fn: async function () {
- assertEquals(
- await verify(await create("", key, { header: header }), key, {
- algorithm: "HS256",
- }),
- "",
- );
- assertEquals(
- await verify(
- await create("abc", key, { header: header }),
- key,
- {
- algorithm: "HS256",
- },
- ),
- "abc",
- );
-
- await assertEquals(
- await verify(await create("null", key), key),
- null,
- );
-
- await assertEquals(
- await verify(await create("true", key), key),
- true,
- );
-
- assertEquals(
- await verify(
- await create(payload, key, { header: header }),
- key,
- {
- algorithm: "HS256",
- },
- ),
- payload,
- );
- await assertEquals(
- await verify(await create({}, key), key),
- {},
- );
- await assertEquals(
- await verify(await create("[]", key), key),
- [],
- );
- await assertEquals(
- await verify(await create(`["a", 1, true]`, key), key),
- ["a", 1, true],
- );
-
- await assertThrowsAsync(
- async () => {
- // payload = { "exp": false }
- await verify(
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOmZhbHNlfQ.LXb8M9J6ar14CTq7shnqDMWmSsoH_zyIHiD44Rqd6uI",
- key,
- );
- },
- Error,
- "The token is invalid.",
- );
-
- await assertThrowsAsync(
- async () => {
- await verify("", key);
- },
- Error,
- "The serialization is invalid.",
- );
-
- await assertThrowsAsync(
- async () => {
- await verify("invalid", key);
- },
- Error,
- "The serialization is invalid.",
- );
-
- await assertThrowsAsync(
- async () => {
- await verify(
- await create({
- // @ts-ignore */
- exp: "invalid",
- }, key),
- key,
- );
- },
- Error,
- "The token is invalid.",
- );
- },
-});
-
-Deno.test({
- name: "[jwt] decode",
- fn: async function () {
- assertEquals(
- decode(
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.TVCeFl1nnZWUMQkAQKuSo_I97YeIZAS8T1gOkErT7F8",
- ),
- {
- header: { alg: "HS256", typ: "JWT" },
- payload: {},
- signature:
- "4d509e165d679d959431090040ab92a3f23ded87886404bc4f580e904ad3ec5f",
- },
- );
- assertThrows(
- () => {
- decode("aaa");
- },
- TypeError,
- "The serialization is invalid.",
- );
-
- assertThrows(
- () => {
- decode("a");
- },
- TypeError,
- "Illegal base64url string!",
- );
-
- assertThrows(
- () => {
- // "ImEi" === base64url("a")
- decode("ImEi.ImEi.ImEi.ImEi");
- },
- TypeError,
- "The serialization is invalid.",
- );
-
- const jwt =
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
- const header: Header = {
- alg: "HS256",
- typ: "JWT",
- };
- const payload = {
- sub: "1234567890",
- name: "John Doe",
- iat: 1516239022,
- };
- assertEquals(decode(jwt), {
- header,
- payload,
- signature:
- "49f94ac7044948c78a285d904f87f0a4c7897f7e8f3a4eb2255fda750b2cc397",
- });
- assertEquals(await create(payload, "your-256-bit-secret", { header }), jwt);
- },
-});
-
-Deno.test({
- name: "[jwt] expired token",
- fn: async function () {
- const payload = {
- iss: "joe",
- jti: "123456789abc",
- exp: 20000,
- };
- const header: Header = {
- alg: "HS256",
- dummy: 100,
- };
-
- await assertThrowsAsync(
- async () => {
- await verify(await create({ exp: 0 }, key), key);
- },
- Error,
- "The token is expired.",
- );
-
- await assertThrowsAsync(
- async () => {
- await verify(
- await create(payload, key, { header }),
- key,
- { algorithm: "HS256" },
- );
- },
- Error,
- "The token is expired.",
- );
- },
-});
-
-Deno.test({
- name: "[jwt] none algorithm",
- fn: async function () {
- const payload = {
- iss: "joe",
- jti: "123456789abc",
- };
- const header: Header = {
- alg: "none",
- dummy: 100,
- };
- const jwt = await create(payload, key, { header });
- const validatedPayload = await verify(jwt, "keyIsIgnored", {
- algorithm: "none",
- });
- assertEquals(validatedPayload, payload);
- },
-});
-
-Deno.test({
- name: "[jwt] HS256 algorithm",
- fn: async function () {
- const header: Header = {
- alg: "HS256",
- typ: "JWT",
- };
- const payload = {
- sub: "1234567890",
- name: "John Doe",
- iat: 1516239022,
- };
- const jwt = await create(payload, key, { header });
- const validatedPayload = await verify(jwt, key, { algorithm: "HS256" });
- assertEquals(
- jwt,
- "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.XbPfbIHMI6arZ3Y922BhjWgQzWXcXNrz0ogtVhfEd2o",
- );
- assertEquals(validatedPayload, payload);
- assertThrowsAsync(
- async () => {
- const invalidJwt = // jwt with not supported crypto algorithm in alg header:
- "eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.bQTnz6AuMJvmXXQsVPrxeQNvzDkimo7VNXxHeSBfClLufmCVZRUuyTwJF311JHuh";
- await verify(invalidJwt, "", {
- algorithm: "HS256",
- });
- },
- Error,
- `The token's algorithm does not match the specified algorithm 'HS256'.`,
- );
- },
-});
-
-Deno.test({
- name: "[jwt] HS512 algorithm",
- fn: async function () {
- const header: Header = { alg: "HS512", typ: "JWT" };
- const payload = {
- sub: "1234567890",
- name: "John Doe",
- admin: true,
- iat: 1516239022,
- };
- const jwt = await create(payload, key, { header });
- const validatedPayload = await verify(jwt, key, { algorithm: "HS512" });
- assertEquals(validatedPayload, payload);
- },
-});