-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparse.ts
114 lines (95 loc) · 3.4 KB
/
parse.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Copyright 2023-latest the httpland authors. All rights reserved. MIT license.
// This module is browser compatible.
import { divideWhile, duplicate, isToken68, trimStartBy } from "./utils.ts";
import {
head,
isString,
isToken,
parseListFields,
type QuotedString,
type Token,
toLowerCase,
} from "./deps.ts";
import { Msg } from "./constants.ts";
import type { Authorization, AuthParams } from "./types.ts";
/** Parse string into {@link Authorization}.
*
* @example
* ```ts
* import { parseAuthorization } from "https://deno.land/x/authorization_parser@$VERSION/parse.ts";
* import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
*
* const result = parseAuthorization("Basic token68");
*
* assertEquals(parseAuthorization("Basic token68"), {
* authScheme: "Basic",
* params: "token68",
* });
* assertEquals(
* parseAuthorization(`Bearer realm="example", error="invalid_token"`),
* {
* authScheme: "Bearer",
* params: {
* realm: `"example"`,
* error: `"invalid_token"`,
* },
* },
* );
* ```
*
* @throws {SyntaxError} If the input is invalid.
* @throws {Error} If the auth param key is duplicated.
*/
export function parseAuthorization(input: string): Authorization {
const result = divideWhile(input, isToken);
if (!result) throw new SyntaxError(Msg.InvalidSyntax);
const [authScheme, rest] = result;
// challenge = auth-scheme
if (!rest) return { authScheme, params: null };
if (!rest.startsWith(" ")) throw new SyntaxError(Msg.InvalidSyntax);
const maybeToken68OrAuthParam = trimStartBy(rest, " ");
// challenge = auth-scheme [ 1*SP ( token68 ) ]
if (isToken68(maybeToken68OrAuthParam)) {
return { authScheme, params: maybeToken68OrAuthParam };
}
// challenge = auth-scheme [ 1*SP ( #auth-param ) ]
const params = parseAuthParams(maybeToken68OrAuthParam);
return { authScheme, params };
}
type AuthParamGroups =
& { key: string }
& ({ token: string; quotedString: never } | {
token: never;
quotedString: string;
});
/** Parse string into {@link AuthParams}.
* @throws {SyntaxError} It the input is invalid [auth-param](https://www.rfc-editor.org/rfc/rfc9110.html#section-11.2-5).
* @throws {Error} If the auth param key is duplicated.
*/
export function parseAuthParams(input: string): AuthParams {
const list = parseListFields(input);
const entries = list.map(parseAuthParam);
const duplicates = duplicate(
entries
.map<string>(head)
.map(toLowerCase),
);
if (duplicates.length) throw Error(Msg.DuplicatedKeys);
return Object.fromEntries(entries);
}
type AuthParam = [key: string, value: Token | QuotedString];
/** Generate from _abnf.ts. */
const reAuthParam =
/^(?<key>[\w!#$%&'*+.^`|~-]+?)[\t ]*?=[\t ]*?(?:(?<token>[\w!#$%&'*+.^`|~-]+?)|(?<quotedString>"(?:[\t !\x23-\x5B\x5D-\x7E\x80-\xFF]|\\[\t \x21-\x7E\x80-\xFF])*?"))$/;
/** Parse string into {@link AuthParam}.
* @throws {SyntaxError} It the input is invalid [auth-param](https://www.rfc-editor.org/rfc/rfc9110.html#section-11.2-5).
*/
function parseAuthParam(input: string): AuthParam {
const result = reAuthParam.exec(input);
if (!result || !result.groups) throw new SyntaxError(Msg.InvalidSyntax);
const groups = result.groups as AuthParamGroups;
const value = isString(groups.token)
? groups.token as Token
: groups.quotedString.replace(/\\(.)/g, "$1") as QuotedString;
return [groups.key as Token, value];
}