-
-
Notifications
You must be signed in to change notification settings - Fork 90
Expand file tree
/
Copy pathindex.ts
More file actions
163 lines (132 loc) · 3.43 KB
/
index.ts
File metadata and controls
163 lines (132 loc) · 3.43 KB
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
/*!
* basic-auth
* Copyright(c) 2013 TJ Holowaychuk
* Copyright(c) 2014 Jonathan Ong
* Copyright(c) 2015-2016 Douglas Christopher Wilson
* MIT Licensed
*/
export = auth;
/**
* Parse the Authorization header field of a request
* @public
*/
function auth(req: auth.IncomingMessageLike): auth.Credentials | undefined {
if (!req) {
throw new TypeError('argument req is required');
}
if (typeof req !== 'object') {
throw new TypeError('argument req is required to be an object');
}
// get header
const header = getAuthorization(req);
// parse header
return auth.parse(header ?? '');
}
namespace auth {
/**
* Object to represent user credentials.
*/
export interface Credentials {
name: string;
pass: string;
}
export interface IncomingMessageLike {
headers?: {
authorization?: string;
};
}
/**
* Parse basic auth to object.
*
* @param {string} string
* @return {object}
* @public
*/
export function parse(string: string): auth.Credentials | undefined {
if (typeof string !== 'string') {
return undefined;
}
// parse header
const match = CREDENTIALS_REGEXP.exec(string);
if (!match) {
return undefined;
}
// decode user pass
const userPass = USER_PASS_REGEXP.exec(decodeBase64(match[1]));
if (!userPass) {
return undefined;
}
// return credentials object
return new CredentialsImpl(userPass[1], userPass[2]);
}
}
/**
* RegExp for basic auth credentials
*
* credentials = auth-scheme 1*SP token68
* auth-scheme = "Basic" ; case insensitive
* token68 = 1*( ALPHA / DIGIT / "-" / "." / "_" / "~" / "+" / "/" ) *"="
* @private
*/
const CREDENTIALS_REGEXP =
/^ *(?:[Bb][Aa][Ss][Ii][Cc]) +([A-Za-z0-9._~+/-]+=*) *$/;
/**
* RegExp for basic auth user/pass
*
* user-pass = userid ":" password
* userid = *<TEXT excluding ":">
* password = *TEXT
* @private
*/
const USER_PASS_REGEXP = /^([^:]*):(.*)$/;
type Uint8ArrayWithBase64 = typeof Uint8Array & {
fromBase64?: (str: string) => Uint8Array;
};
type BufferLike = {
from(
input: string,
encoding: 'base64',
): { toString(encoding: 'utf-8'): string };
};
const NodeBuffer = (globalThis as any).Buffer as BufferLike | undefined;
const textDecoder = new TextDecoder('utf-8');
/**
* Decode base64 string.
* @private
*/
const decodeBase64: (str: string) => string = (() => {
// 1) Modern Web / some runtimes
if (typeof (Uint8Array as Uint8ArrayWithBase64).fromBase64 === 'function') {
return (str: string) =>
textDecoder.decode((Uint8Array as Uint8ArrayWithBase64).fromBase64!(str));
}
// 2) Node.js (fast path)
if (typeof NodeBuffer?.from === 'function') {
return (str: string) => NodeBuffer.from(str, 'base64').toString('utf-8');
}
// 3) Browser fallback
return (str: string) => {
const binary = atob(str);
return textDecoder.decode(
Uint8Array.from(binary, (char) => char.charCodeAt(0)),
);
};
})();
/**
* Get the Authorization header from request object.
* @private
*/
function getAuthorization(req: auth.IncomingMessageLike) {
if (!req.headers || typeof req.headers !== 'object') {
throw new TypeError('argument req is required to have headers property');
}
return req.headers.authorization;
}
class CredentialsImpl implements auth.Credentials {
name: string;
pass: string;
constructor(name: string, pass: string) {
this.name = name;
this.pass = pass;
}
}