@usync/oauth2 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/dist/index.js +68 -33
- package/dist/providers/base.d.ts +5 -1
- package/dist/providers/dropbox.d.ts +2 -0
- package/dist/providers/google.d.ts +1 -0
- package/dist/providers/microsoft.d.ts +1 -0
- package/dist/types.d.ts +18 -0
- package/dist/util.d.ts +2 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
# @usync/oauth2
|
|
2
2
|
|
|
3
|
-
[](https://
|
|
3
|
+
[](https://npmx.dev/package/@usync/oauth2)
|
|
4
4
|

|
|
5
|
-
[](https://www.jsdocs.io/package/@usync/oauth2)
|
|
6
5
|
|
|
7
6
|
OAuth2 support for provider-based authorization flows and token management.
|
|
8
7
|
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,18 @@ function o() {
|
|
|
18
18
|
function s() {
|
|
19
19
|
return e(64);
|
|
20
20
|
}
|
|
21
|
-
|
|
21
|
+
function c() {
|
|
22
|
+
return e(32);
|
|
23
|
+
}
|
|
24
|
+
function l(e) {
|
|
25
|
+
let t = e.split(".");
|
|
26
|
+
if (t.length !== 3) throw Error("Invalid JWT");
|
|
27
|
+
let n = atob(t[1].replace(/-/g, "+").replace(/_/g, "/")), r = new Uint8Array(n.length);
|
|
28
|
+
for (let e = 0; e < n.length; e++) r[e] = n.charCodeAt(e);
|
|
29
|
+
let i = new TextDecoder("utf-8").decode(r);
|
|
30
|
+
return JSON.parse(i);
|
|
31
|
+
}
|
|
32
|
+
async function u(e) {
|
|
22
33
|
let t = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(e));
|
|
23
34
|
return {
|
|
24
35
|
codeChallenge: a(new Uint8Array(t)),
|
|
@@ -27,9 +38,9 @@ async function c(e) {
|
|
|
27
38
|
}
|
|
28
39
|
//#endregion
|
|
29
40
|
//#region src/providers/base.ts
|
|
30
|
-
var
|
|
41
|
+
var d = class {
|
|
31
42
|
constructor(e, t) {
|
|
32
|
-
this.options = e, this._accessToken = null, this._refreshToken = null, t && (this.setRefreshToken(t.refreshToken), this.setAccessToken(t.accessToken), this.session = t.session);
|
|
43
|
+
this.options = e, this._accessToken = null, this._refreshToken = null, this._idToken = null, t && (this.setRefreshToken(t.refreshToken), this.setAccessToken(t.accessToken), this.session = t.session);
|
|
33
44
|
}
|
|
34
45
|
_getValidToken(e) {
|
|
35
46
|
return e && (!e.expiresAt || Date.now() < e.expiresAt) ? e.token : void 0;
|
|
@@ -57,7 +68,13 @@ var l = class {
|
|
|
57
68
|
setRefreshToken(e) {
|
|
58
69
|
this._refreshToken = e ?? null;
|
|
59
70
|
}
|
|
60
|
-
|
|
71
|
+
getClaims() {
|
|
72
|
+
if (!this._idToken) throw new i(2, "OIDC not enabled or claims not available");
|
|
73
|
+
let e = l(this._idToken);
|
|
74
|
+
if (this.session?.nonce && e.nonce !== this.session.nonce) throw new i(2, "nonce mismatch");
|
|
75
|
+
return e;
|
|
76
|
+
}
|
|
77
|
+
}, f = "https://www.dropbox.com/oauth2/authorize", p = "https://api.dropbox.com/oauth2/token", m = class extends d {
|
|
61
78
|
static {
|
|
62
79
|
this.Scopes = { account: "account_info.read" };
|
|
63
80
|
}
|
|
@@ -66,7 +83,7 @@ var l = class {
|
|
|
66
83
|
state: o(),
|
|
67
84
|
codeVerifier: s()
|
|
68
85
|
};
|
|
69
|
-
let { codeChallenge: e, codeChallengeMethod: t } = await
|
|
86
|
+
let { codeChallenge: e, codeChallengeMethod: t } = await u(this.session.codeVerifier), n = new URL(f);
|
|
70
87
|
return Object.entries({
|
|
71
88
|
client_id: this.options.clientId,
|
|
72
89
|
code_challenge: e,
|
|
@@ -95,7 +112,7 @@ var l = class {
|
|
|
95
112
|
}).forEach(([e, t]) => {
|
|
96
113
|
t != null && n.append(e, t);
|
|
97
114
|
});
|
|
98
|
-
let r = await fetch(
|
|
115
|
+
let r = await fetch(p, {
|
|
99
116
|
method: "POST",
|
|
100
117
|
body: n
|
|
101
118
|
}), a = await r.json();
|
|
@@ -103,8 +120,7 @@ var l = class {
|
|
|
103
120
|
status: r.status,
|
|
104
121
|
data: a
|
|
105
122
|
};
|
|
106
|
-
|
|
107
|
-
return this._updateRefreshToken({
|
|
123
|
+
return a.refresh_token && this._updateRefreshToken({
|
|
108
124
|
token: a.refresh_token,
|
|
109
125
|
scope: a.scope
|
|
110
126
|
}), this._updateAccessToken({
|
|
@@ -122,7 +138,7 @@ var l = class {
|
|
|
122
138
|
}).forEach(([t, n]) => {
|
|
123
139
|
n != null && e.append(t, n);
|
|
124
140
|
});
|
|
125
|
-
let n = await fetch(
|
|
141
|
+
let n = await fetch(p, {
|
|
126
142
|
method: "POST",
|
|
127
143
|
body: e
|
|
128
144
|
}), r = await n.json();
|
|
@@ -135,7 +151,10 @@ var l = class {
|
|
|
135
151
|
expiresAt: Date.now() + r.expires_in * 1e3
|
|
136
152
|
}), r.access_token;
|
|
137
153
|
}
|
|
138
|
-
|
|
154
|
+
getClaims() {
|
|
155
|
+
throw new i(2, "OIDC not supported by Dropbox");
|
|
156
|
+
}
|
|
157
|
+
}, h = "https://accounts.google.com/o/oauth2/v2/auth", g = "https://oauth2.googleapis.com/token", _ = class extends d {
|
|
139
158
|
static {
|
|
140
159
|
this.Scopes = {
|
|
141
160
|
account: "https://www.googleapis.com/auth/userinfo.profile",
|
|
@@ -143,19 +162,24 @@ var l = class {
|
|
|
143
162
|
imap: "https://mail.google.com/"
|
|
144
163
|
};
|
|
145
164
|
}
|
|
165
|
+
get isOidc() {
|
|
166
|
+
return this.options.scope?.split(/\s+/).includes("openid") ?? !1;
|
|
167
|
+
}
|
|
146
168
|
async buildAuthUrl() {
|
|
147
169
|
this.session = {
|
|
148
170
|
state: o(),
|
|
149
|
-
codeVerifier: s()
|
|
171
|
+
codeVerifier: s(),
|
|
172
|
+
...this.isOidc ? { nonce: c() } : {}
|
|
150
173
|
};
|
|
151
|
-
let { codeChallenge: e, codeChallengeMethod: t } = await
|
|
174
|
+
let { codeChallenge: e, codeChallengeMethod: t } = await u(this.session.codeVerifier), n = new URL(h);
|
|
152
175
|
return Object.entries({
|
|
153
|
-
access_type:
|
|
176
|
+
access_type: this.options.provider?.google?.accessType,
|
|
154
177
|
client_id: this.options.clientId,
|
|
155
178
|
code_challenge: e,
|
|
156
179
|
code_challenge_method: t,
|
|
157
180
|
include_granted_scopes: "true",
|
|
158
|
-
|
|
181
|
+
nonce: this.session.nonce,
|
|
182
|
+
prompt: this.options.provider?.google?.prompt,
|
|
159
183
|
redirect_uri: this.options.redirectUrl,
|
|
160
184
|
response_type: "code",
|
|
161
185
|
scope: this.options.scope,
|
|
@@ -179,7 +203,7 @@ var l = class {
|
|
|
179
203
|
}).forEach(([e, t]) => {
|
|
180
204
|
t != null && n.append(e, t);
|
|
181
205
|
});
|
|
182
|
-
let r = await fetch(
|
|
206
|
+
let r = await fetch(g, {
|
|
183
207
|
method: "POST",
|
|
184
208
|
body: n
|
|
185
209
|
}), a = await r.json();
|
|
@@ -187,14 +211,17 @@ var l = class {
|
|
|
187
211
|
status: r.status,
|
|
188
212
|
data: a
|
|
189
213
|
};
|
|
190
|
-
if (
|
|
191
|
-
return this._updateRefreshToken({
|
|
214
|
+
if (a.refresh_token && this._updateRefreshToken({
|
|
192
215
|
token: a.refresh_token,
|
|
193
216
|
scope: a.scope
|
|
194
217
|
}), this._updateAccessToken({
|
|
195
218
|
token: a.access_token,
|
|
196
219
|
expiresAt: Date.now() + a.expires_in * 1e3
|
|
197
|
-
}),
|
|
220
|
+
}), this.isOidc) {
|
|
221
|
+
if (!a.id_token) throw new i(2, "OIDC enabled but no id_token returned");
|
|
222
|
+
this._idToken = a.id_token;
|
|
223
|
+
}
|
|
224
|
+
return a.access_token;
|
|
198
225
|
}
|
|
199
226
|
async refreshToken() {
|
|
200
227
|
let e = new URLSearchParams(), t = this.getRefreshToken();
|
|
@@ -207,7 +234,7 @@ var l = class {
|
|
|
207
234
|
}).forEach(([t, n]) => {
|
|
208
235
|
n != null && e.append(t, n);
|
|
209
236
|
});
|
|
210
|
-
let n = await fetch(
|
|
237
|
+
let n = await fetch(g, {
|
|
211
238
|
method: "POST",
|
|
212
239
|
body: e
|
|
213
240
|
}), r = await n.json();
|
|
@@ -220,26 +247,31 @@ var l = class {
|
|
|
220
247
|
expiresAt: Date.now() + r.expires_in * 1e3
|
|
221
248
|
}), r.access_token;
|
|
222
249
|
}
|
|
223
|
-
},
|
|
250
|
+
}, v = class extends d {
|
|
224
251
|
static {
|
|
225
252
|
this.Scopes = {
|
|
226
253
|
imap: "offline_access https://outlook.office.com/IMAP.AccessAsUser.All",
|
|
227
254
|
onedrive: "openid profile Files.ReadWrite.AppFolder offline_access"
|
|
228
255
|
};
|
|
229
256
|
}
|
|
257
|
+
get isOidc() {
|
|
258
|
+
return this.options.scope?.split(/\s+/).includes("openid") ?? !1;
|
|
259
|
+
}
|
|
230
260
|
oauth2Url(e) {
|
|
231
261
|
return `https://login.microsoftonline.com/${this.options.provider?.microsoft?.accountType ?? "common"}/oauth2/v2.0/${e}`;
|
|
232
262
|
}
|
|
233
263
|
async buildAuthUrl() {
|
|
234
264
|
this.session = {
|
|
235
265
|
state: o(),
|
|
236
|
-
codeVerifier: s()
|
|
266
|
+
codeVerifier: s(),
|
|
267
|
+
...this.isOidc ? { nonce: c() } : {}
|
|
237
268
|
};
|
|
238
|
-
let { codeChallenge: e, codeChallengeMethod: t } = await
|
|
269
|
+
let { codeChallenge: e, codeChallengeMethod: t } = await u(this.session.codeVerifier), n = new URL(this.oauth2Url("authorize"));
|
|
239
270
|
return Object.entries({
|
|
240
271
|
client_id: this.options.clientId,
|
|
241
272
|
code_challenge: e,
|
|
242
273
|
code_challenge_method: t,
|
|
274
|
+
nonce: this.session.nonce,
|
|
243
275
|
redirect_uri: this.options.redirectUrl,
|
|
244
276
|
response_mode: "query",
|
|
245
277
|
response_type: "code",
|
|
@@ -273,14 +305,17 @@ var l = class {
|
|
|
273
305
|
status: r.status,
|
|
274
306
|
data: a
|
|
275
307
|
};
|
|
276
|
-
if (
|
|
277
|
-
return this._updateRefreshToken({
|
|
308
|
+
if (a.refresh_token || this._updateRefreshToken({
|
|
278
309
|
token: a.refresh_token,
|
|
279
310
|
scope: a.scope
|
|
280
311
|
}), this._updateAccessToken({
|
|
281
312
|
token: a.access_token,
|
|
282
313
|
expiresAt: Date.now() + a.expires_in * 1e3
|
|
283
|
-
}),
|
|
314
|
+
}), this.isOidc) {
|
|
315
|
+
if (!a.id_token) throw new i(2, "OIDC enabled but no id_token returned");
|
|
316
|
+
this._idToken = a.id_token;
|
|
317
|
+
}
|
|
318
|
+
return a.access_token;
|
|
284
319
|
}
|
|
285
320
|
async refreshToken() {
|
|
286
321
|
let e = new URLSearchParams(), t = this.getRefreshToken();
|
|
@@ -306,17 +341,17 @@ var l = class {
|
|
|
306
341
|
expiresAt: Date.now() + r.expires_in * 1e3
|
|
307
342
|
}), r.access_token;
|
|
308
343
|
}
|
|
309
|
-
},
|
|
310
|
-
dropbox:
|
|
311
|
-
google:
|
|
312
|
-
microsoft:
|
|
344
|
+
}, y = {
|
|
345
|
+
dropbox: m,
|
|
346
|
+
google: _,
|
|
347
|
+
microsoft: v
|
|
313
348
|
};
|
|
314
349
|
//#endregion
|
|
315
350
|
//#region src/index.ts
|
|
316
|
-
function
|
|
317
|
-
return new
|
|
351
|
+
function b(e, t) {
|
|
352
|
+
return new y[t.provider](e);
|
|
318
353
|
}
|
|
319
|
-
async function
|
|
354
|
+
async function x(e, t) {
|
|
320
355
|
let n;
|
|
321
356
|
try {
|
|
322
357
|
n = e.getAccessToken();
|
|
@@ -338,4 +373,4 @@ async function y(e, t) {
|
|
|
338
373
|
return n;
|
|
339
374
|
}
|
|
340
375
|
//#endregion
|
|
341
|
-
export {
|
|
376
|
+
export { m as DropboxAuthorizer, _ as GoogleAuthorizer, v as MicrosoftAuthorizer, n as OAUTH2_AUTH_ERROR, t as OAUTH2_NEED_REFRESH, r as OAUTH2_UNAUTHORIZED, d as OAuth2Authorizer, y as OAuth2Authorizers, i as OAuth2Error, x as ensureAccessToken, b as getAuthorizer };
|
package/dist/providers/base.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { OAuth2AuthorizerOptions, TokenData } from "../types";
|
|
1
|
+
import type { IdTokenClaims, OAuth2AuthorizerOptions, TokenData } from "../types";
|
|
2
2
|
export declare abstract class OAuth2Authorizer {
|
|
3
3
|
protected options: OAuth2AuthorizerOptions;
|
|
4
4
|
abstract buildAuthUrl(): Promise<string>;
|
|
@@ -6,9 +6,11 @@ export declare abstract class OAuth2Authorizer {
|
|
|
6
6
|
abstract refreshToken(): Promise<string>;
|
|
7
7
|
protected _accessToken: TokenData | null;
|
|
8
8
|
protected _refreshToken: TokenData | null;
|
|
9
|
+
protected _idToken: string | null;
|
|
9
10
|
session: {
|
|
10
11
|
state: string;
|
|
11
12
|
codeVerifier: string;
|
|
13
|
+
nonce?: string;
|
|
12
14
|
} | undefined;
|
|
13
15
|
constructor(options: OAuth2AuthorizerOptions, initialData?: {
|
|
14
16
|
accessToken?: TokenData;
|
|
@@ -16,6 +18,7 @@ export declare abstract class OAuth2Authorizer {
|
|
|
16
18
|
session?: {
|
|
17
19
|
state: string;
|
|
18
20
|
codeVerifier: string;
|
|
21
|
+
nonce?: string;
|
|
19
22
|
};
|
|
20
23
|
});
|
|
21
24
|
protected _getValidToken(value?: TokenData | null): string | undefined;
|
|
@@ -25,4 +28,5 @@ export declare abstract class OAuth2Authorizer {
|
|
|
25
28
|
getRefreshToken(): string;
|
|
26
29
|
setAccessToken(value?: TokenData | null): void;
|
|
27
30
|
setRefreshToken(value?: TokenData | null): void;
|
|
31
|
+
getClaims(): IdTokenClaims;
|
|
28
32
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { IdTokenClaims } from "../types.ts";
|
|
1
2
|
import { OAuth2Authorizer } from "./base.ts";
|
|
2
3
|
export declare class DropboxAuthorizer extends OAuth2Authorizer {
|
|
3
4
|
/**
|
|
@@ -9,4 +10,5 @@ export declare class DropboxAuthorizer extends OAuth2Authorizer {
|
|
|
9
10
|
buildAuthUrl(): Promise<string>;
|
|
10
11
|
finishAuth(url: URL): Promise<string>;
|
|
11
12
|
refreshToken(): Promise<string>;
|
|
13
|
+
getClaims(): IdTokenClaims;
|
|
12
14
|
}
|
|
@@ -11,6 +11,7 @@ export declare class GoogleAuthorizer extends OAuth2Authorizer {
|
|
|
11
11
|
"drive.appdata": string;
|
|
12
12
|
imap: string;
|
|
13
13
|
};
|
|
14
|
+
protected get isOidc(): boolean;
|
|
14
15
|
buildAuthUrl(): Promise<string>;
|
|
15
16
|
finishAuth(url: URL): Promise<string>;
|
|
16
17
|
refreshToken(): Promise<string>;
|
package/dist/types.d.ts
CHANGED
|
@@ -4,6 +4,20 @@ export interface TokenData {
|
|
|
4
4
|
expiresAt?: number;
|
|
5
5
|
scope?: string;
|
|
6
6
|
}
|
|
7
|
+
export interface IdTokenClaims {
|
|
8
|
+
iss?: string;
|
|
9
|
+
sub?: string;
|
|
10
|
+
aud?: string;
|
|
11
|
+
exp?: number;
|
|
12
|
+
iat?: number;
|
|
13
|
+
nonce?: string;
|
|
14
|
+
email?: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
picture?: string;
|
|
17
|
+
given_name?: string;
|
|
18
|
+
family_name?: string;
|
|
19
|
+
email_verified?: boolean;
|
|
20
|
+
}
|
|
7
21
|
export interface OAuth2Config {
|
|
8
22
|
clientId: string;
|
|
9
23
|
/** clientSecret may be absent for client-side apps. */
|
|
@@ -11,6 +25,10 @@ export interface OAuth2Config {
|
|
|
11
25
|
redirectUrl: string;
|
|
12
26
|
scope?: string;
|
|
13
27
|
provider?: {
|
|
28
|
+
google?: {
|
|
29
|
+
accessType?: string;
|
|
30
|
+
prompt?: string;
|
|
31
|
+
};
|
|
14
32
|
microsoft?: {
|
|
15
33
|
/**
|
|
16
34
|
* Must match the account type of the application registered in https://portal.azure.com/.
|
package/dist/util.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export declare function getState(): string;
|
|
|
7
7
|
* and a maximum length of 128 characters.
|
|
8
8
|
*/
|
|
9
9
|
export declare function getCodeVerifier(): string;
|
|
10
|
+
export declare function getNonce(): string;
|
|
11
|
+
export declare function decodeJwtPayload(token: string): Record<string, unknown>;
|
|
10
12
|
export declare function getCodeChallenge(codeVerifier: string): Promise<{
|
|
11
13
|
codeChallenge: string;
|
|
12
14
|
codeChallengeMethod: string;
|