Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add(std/jwt): Implement the new jwt module #7991

Merged
merged 96 commits into from
Oct 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
02f3eec
Setup module - Copy relevant files from djwt
timonson Oct 2, 2020
9844540
rename functions
timreichen Oct 2, 2020
d5bd740
remove RS256
timreichen Oct 2, 2020
ccfe528
remove JwtError, throw Error instances
timreichen Oct 2, 2020
8b42c39
relative std import specifiers
timreichen Oct 2, 2020
e908fd6
remove JSON types
timreichen Oct 2, 2020
bfd93a2
redundant file
timreichen Oct 2, 2020
9d1b309
rename tests
timreichen Oct 2, 2020
7fdf1a3
relative std module specifiers
timreichen Oct 2, 2020
7c5d342
rename Config type
timreichen Oct 2, 2020
10810c2
Remove assertNever
timonson Oct 2, 2020
9e2ce22
restructure tests
timreichen Oct 2, 2020
6df15a1
Merge branch 'std/jwt' of https://github.com/timonson/deno into std/jwt
timreichen Oct 2, 2020
8105c26
rename Header type
timreichen Oct 2, 2020
6920ff0
rename tests
timreichen Oct 2, 2020
a2ed5a6
restructure validate function
timreichen Oct 2, 2020
9a9fced
Create _util.ts and move helper functions to it
timonson Oct 2, 2020
8837788
change validate return value
timreichen Oct 2, 2020
7d84a4d
Merge branch 'std/jwt' of https://github.com/timonson/deno into std/jwt
timreichen Oct 2, 2020
f46de04
cleanup exports
timreichen Oct 2, 2020
6bcce2d
add string literals
timreichen Oct 2, 2020
882612d
remove exports
timreichen Oct 2, 2020
7653960
add default config values
timreichen Oct 2, 2020
6055cfe
Change export statements
timonson Oct 3, 2020
67ab762
restructure methods and exports
timreichen Oct 3, 2020
d7b4c61
toplevel static reservedWords
timreichen Oct 4, 2020
da590fd
cleanup
timreichen Oct 4, 2020
aab3f10
correct test names
timreichen Oct 4, 2020
4ca53a4
move tests
timreichen Oct 4, 2020
32d9758
move types, prevent circular imports
timreichen Oct 4, 2020
d3b5c67
rename functions
timreichen Oct 4, 2020
f0a6a74
split functionality
timreichen Oct 4, 2020
e31ab9d
split tests
timreichen Oct 4, 2020
495beb9
Clarify thrown errors
timonson Oct 4, 2020
6267e10
Fix typo
timonson Oct 4, 2020
8834f31
Change Algorithm[] type
timonson Oct 7, 2020
58d576f
Remove header.ts and improve types
timonson Oct 7, 2020
71ed952
Improve types and logic
timonson Oct 7, 2020
8c2ec43
Run deno fmt
timonson Oct 7, 2020
e585b1b
Add isObject to _utils.ts
timonson Oct 7, 2020
ae7a73c
Change return type of 'verify' to unknown
timonson Oct 7, 2020
93690b2
Update example
timonson Oct 7, 2020
7d5e9f3
Update example
timonson Oct 7, 2020
558aae6
Fix isTokenObject
timonson Oct 7, 2020
80adce2
Made necessary restructuring
timonson Oct 7, 2020
ae1c296
Change error message for crit
timonson Oct 7, 2020
67ce363
Improve validation
timonson Oct 8, 2020
098ae72
Add comments and made minor changes
timonson Oct 8, 2020
8c182d7
Move functions from _util.ts to validation.ts
timonson Oct 8, 2020
694bdff
Add comment
timonson Oct 8, 2020
dc95ec0
Change argument type for type predicates from unknown to any
timonson Oct 10, 2020
78d3333
various improvements
timreichen Oct 10, 2020
12f5a5f
fix tests
timreichen Oct 10, 2020
98ce8c4
rename param
timreichen Oct 11, 2020
3716d28
make async, rename parse to decode
timreichen Oct 11, 2020
849bd02
Merge pull request #1 from timonson/proposal-1
timreichen Oct 11, 2020
1354e6f
Add first README draft
timonson Oct 13, 2020
71b61d7
Merge branch 'master' into std/jwt
timonson Oct 13, 2020
90a2cc4
Remove base64 module and import base64 from /std/encoding
timonson Oct 13, 2020
c246060
update README.md
timreichen Oct 13, 2020
508d400
move encoder and fmt
timreichen Oct 13, 2020
e9dd016
update README.md
timreichen Oct 13, 2020
342ba92
update README.md
timreichen Oct 13, 2020
f509a9f
Merge branch 'update-readme' of https://github.com/timonson/deno into…
timreichen Oct 13, 2020
a044e64
Update README.md
timreichen Oct 13, 2020
0351f2b
Merge pull request #2 from timonson/update-readme
timreichen Oct 13, 2020
0feb734
Remove examples and testdata
timonson Oct 13, 2020
aa7cd7d
Don't stringify strings before converting to base64url
timonson Oct 14, 2020
bfbb28c
Fix tryToParsePayload
timonson Oct 14, 2020
b31f5bc
Assign decoder and move tryToParsePayload out of function
timonson Oct 14, 2020
f07591e
add more tests
timreichen Oct 14, 2020
cdc23a7
Merge branch 'std/jwt' of https://github.com/timonson/deno into std/jwt
timreichen Oct 14, 2020
f15c663
Change algorithm comment
timonson Oct 14, 2020
d03c4a2
adjust payload
timreichen Oct 15, 2020
0a159fe
Add Serialization to readme and improve comments
timonson Oct 15, 2020
12765ed
Add algorithm test for type string
timonson Oct 15, 2020
2d4e9e4
Add algorithm test for type string
timonson Oct 15, 2020
068448b
Add JSDoc
timonson Oct 15, 2020
7c39434
Change isTokenObject to reduce evaluations
timonson Oct 16, 2020
d326a71
Improve types
timonson Oct 16, 2020
eb1521a
Add comment to document reason for leeway
timonson Oct 16, 2020
87ad4c4
Improve error messages
timonson Oct 16, 2020
8d86fe7
Remove 'none' because it is redundant
timonson Oct 16, 2020
bda726f
Add type VerifyOptions
timonson Oct 16, 2020
a5a2f89
Fix JSDoc
timonson Oct 16, 2020
3a5926c
Add underscore to signature and algorithm files and change setExpirat…
timonson Oct 16, 2020
6c7db34
Merge the functions isTokenObject and decode
timonson Oct 17, 2020
04ecc8b
Change destructuring assignment
timonson Oct 17, 2020
594ecee
Remove createExpiration and add example to readme instead
timonson Oct 17, 2020
86fcd00
rename param
timreichen Oct 17, 2020
e301deb
update README.md
timreichen Oct 17, 2020
168ef35
update tests
timreichen Oct 17, 2020
e6cb730
tweaks
timreichen Oct 17, 2020
20e91f0
fmt
timreichen Oct 17, 2020
a5249b8
remove type assertions
timreichen Oct 17, 2020
549f299
remove obsolete code
timreichen Oct 17, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions std/jwt/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# 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/token/mod.ts";

const payload = { foo: "bar" };
const key = "secret";

const token = await create(payload, key); // eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.4i-Q1Y0oDZunLgaorkqbYNcNfn5CgdF49UvJ7dUQ4GVTQvpsMLHABkZBWp9sghy3qVOsec6hOcu4RnbFkS30zQ
```

**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/token/mod.ts";

const token =
"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.4i-Q1Y0oDZunLgaorkqbYNcNfn5CgdF49UvJ7dUQ4GVTQvpsMLHABkZBWp9sghy3qVOsec6hOcu4RnbFkS30zQ";
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/token/mod.ts";

const token =
"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIifQ.4i-Q1Y0oDZunLgaorkqbYNcNfn5CgdF49UvJ7dUQ4GVTQvpsMLHABkZBWp9sghy3qVOsec6hOcu4RnbFkS30zQ";

const { payload, signature, header } = await decode(token); // { header: { alg: "HS512", typ: "JWT" }, payload: { foo: "bar" }, signature: "e22f90d58d280d9ba72e06a8ae4a9b60d70d7e7e4281d178f54bc9edd510e0655342fa6c30b1c00646415a9f6c821cb7a953ac79cea139cbb84676c5912df4cd" }
```

## 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)
17 changes: 17 additions & 0 deletions std/jwt/_algorithm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* 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;
}
11 changes: 11 additions & 0 deletions std/jwt/_algorithm_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
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);
});
63 changes: 63 additions & 0 deletions std/jwt/_signature.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
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));
}
46 changes: 46 additions & 0 deletions std/jwt/_signature_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
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);
});
Loading