@usync/oauth2 0.1.3 → 0.1.5
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.d.ts +2 -2
- package/dist/index.js +67 -29
- package/dist/providers/base.d.ts +7 -3
- 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 +21 -1
- package/dist/util.d.ts +2 -0
- package/package.json +8 -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.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { OAuth2Authorizer } from "./providers";
|
|
2
|
-
import type { IOAuth2Account,
|
|
2
|
+
import type { IOAuth2Account, OAuth2AuthorizerOptions } from "./types";
|
|
3
3
|
export * from "./common";
|
|
4
4
|
export * from "./providers";
|
|
5
5
|
export * from "./types";
|
|
6
|
-
export declare function getAuthorizer(options:
|
|
6
|
+
export declare function getAuthorizer(options: OAuth2AuthorizerOptions, auth: IOAuth2Account): import("./providers").DropboxAuthorizer | import("./providers").GoogleAuthorizer | import("./providers").MicrosoftAuthorizer;
|
|
7
7
|
export declare function ensureAccessToken(authorizer: OAuth2Authorizer, handleOAuth2?: (url: string) => Promise<string>): Promise<string>;
|
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();
|
|
@@ -122,7 +139,7 @@ var l = class {
|
|
|
122
139
|
}).forEach(([t, n]) => {
|
|
123
140
|
n != null && e.append(t, n);
|
|
124
141
|
});
|
|
125
|
-
let n = await fetch(
|
|
142
|
+
let n = await fetch(p, {
|
|
126
143
|
method: "POST",
|
|
127
144
|
body: e
|
|
128
145
|
}), r = await n.json();
|
|
@@ -135,7 +152,10 @@ var l = class {
|
|
|
135
152
|
expiresAt: Date.now() + r.expires_in * 1e3
|
|
136
153
|
}), r.access_token;
|
|
137
154
|
}
|
|
138
|
-
|
|
155
|
+
getClaims() {
|
|
156
|
+
throw new i(2, "OIDC not supported by Dropbox");
|
|
157
|
+
}
|
|
158
|
+
}, h = "https://accounts.google.com/o/oauth2/v2/auth", g = "https://oauth2.googleapis.com/token", _ = class extends d {
|
|
139
159
|
static {
|
|
140
160
|
this.Scopes = {
|
|
141
161
|
account: "https://www.googleapis.com/auth/userinfo.profile",
|
|
@@ -143,19 +163,24 @@ var l = class {
|
|
|
143
163
|
imap: "https://mail.google.com/"
|
|
144
164
|
};
|
|
145
165
|
}
|
|
166
|
+
get isOidc() {
|
|
167
|
+
return this.options.scope?.split(/\s+/).includes("openid") ?? !1;
|
|
168
|
+
}
|
|
146
169
|
async buildAuthUrl() {
|
|
147
170
|
this.session = {
|
|
148
171
|
state: o(),
|
|
149
|
-
codeVerifier: s()
|
|
172
|
+
codeVerifier: s(),
|
|
173
|
+
...this.isOidc ? { nonce: c() } : {}
|
|
150
174
|
};
|
|
151
|
-
let { codeChallenge: e, codeChallengeMethod: t } = await
|
|
175
|
+
let { codeChallenge: e, codeChallengeMethod: t } = await u(this.session.codeVerifier), n = new URL(h);
|
|
152
176
|
return Object.entries({
|
|
153
|
-
access_type:
|
|
177
|
+
access_type: this.options.provider?.google?.accessType,
|
|
154
178
|
client_id: this.options.clientId,
|
|
155
179
|
code_challenge: e,
|
|
156
180
|
code_challenge_method: t,
|
|
157
181
|
include_granted_scopes: "true",
|
|
158
|
-
|
|
182
|
+
nonce: this.session.nonce,
|
|
183
|
+
prompt: this.options.provider?.google?.prompt,
|
|
159
184
|
redirect_uri: this.options.redirectUrl,
|
|
160
185
|
response_type: "code",
|
|
161
186
|
scope: this.options.scope,
|
|
@@ -179,7 +204,7 @@ var l = class {
|
|
|
179
204
|
}).forEach(([e, t]) => {
|
|
180
205
|
t != null && n.append(e, t);
|
|
181
206
|
});
|
|
182
|
-
let r = await fetch(
|
|
207
|
+
let r = await fetch(g, {
|
|
183
208
|
method: "POST",
|
|
184
209
|
body: n
|
|
185
210
|
}), a = await r.json();
|
|
@@ -188,13 +213,17 @@ var l = class {
|
|
|
188
213
|
data: a
|
|
189
214
|
};
|
|
190
215
|
if (!a.refresh_token) throw new i(2, "Failed to get refresh_token");
|
|
191
|
-
|
|
216
|
+
if (this._updateRefreshToken({
|
|
192
217
|
token: a.refresh_token,
|
|
193
218
|
scope: a.scope
|
|
194
219
|
}), this._updateAccessToken({
|
|
195
220
|
token: a.access_token,
|
|
196
221
|
expiresAt: Date.now() + a.expires_in * 1e3
|
|
197
|
-
}),
|
|
222
|
+
}), this.isOidc) {
|
|
223
|
+
if (!a.id_token) throw new i(2, "OIDC enabled but no id_token returned");
|
|
224
|
+
this._idToken = a.id_token;
|
|
225
|
+
}
|
|
226
|
+
return a.access_token;
|
|
198
227
|
}
|
|
199
228
|
async refreshToken() {
|
|
200
229
|
let e = new URLSearchParams(), t = this.getRefreshToken();
|
|
@@ -207,7 +236,7 @@ var l = class {
|
|
|
207
236
|
}).forEach(([t, n]) => {
|
|
208
237
|
n != null && e.append(t, n);
|
|
209
238
|
});
|
|
210
|
-
let n = await fetch(
|
|
239
|
+
let n = await fetch(g, {
|
|
211
240
|
method: "POST",
|
|
212
241
|
body: e
|
|
213
242
|
}), r = await n.json();
|
|
@@ -220,26 +249,31 @@ var l = class {
|
|
|
220
249
|
expiresAt: Date.now() + r.expires_in * 1e3
|
|
221
250
|
}), r.access_token;
|
|
222
251
|
}
|
|
223
|
-
},
|
|
252
|
+
}, v = class extends d {
|
|
224
253
|
static {
|
|
225
254
|
this.Scopes = {
|
|
226
255
|
imap: "offline_access https://outlook.office.com/IMAP.AccessAsUser.All",
|
|
227
256
|
onedrive: "openid profile Files.ReadWrite.AppFolder offline_access"
|
|
228
257
|
};
|
|
229
258
|
}
|
|
259
|
+
get isOidc() {
|
|
260
|
+
return this.options.scope?.split(/\s+/).includes("openid") ?? !1;
|
|
261
|
+
}
|
|
230
262
|
oauth2Url(e) {
|
|
231
263
|
return `https://login.microsoftonline.com/${this.options.provider?.microsoft?.accountType ?? "common"}/oauth2/v2.0/${e}`;
|
|
232
264
|
}
|
|
233
265
|
async buildAuthUrl() {
|
|
234
266
|
this.session = {
|
|
235
267
|
state: o(),
|
|
236
|
-
codeVerifier: s()
|
|
268
|
+
codeVerifier: s(),
|
|
269
|
+
...this.isOidc ? { nonce: c() } : {}
|
|
237
270
|
};
|
|
238
|
-
let { codeChallenge: e, codeChallengeMethod: t } = await
|
|
271
|
+
let { codeChallenge: e, codeChallengeMethod: t } = await u(this.session.codeVerifier), n = new URL(this.oauth2Url("authorize"));
|
|
239
272
|
return Object.entries({
|
|
240
273
|
client_id: this.options.clientId,
|
|
241
274
|
code_challenge: e,
|
|
242
275
|
code_challenge_method: t,
|
|
276
|
+
nonce: this.session.nonce,
|
|
243
277
|
redirect_uri: this.options.redirectUrl,
|
|
244
278
|
response_mode: "query",
|
|
245
279
|
response_type: "code",
|
|
@@ -274,13 +308,17 @@ var l = class {
|
|
|
274
308
|
data: a
|
|
275
309
|
};
|
|
276
310
|
if (!a.refresh_token) throw new i(2, "Failed to get refresh_token");
|
|
277
|
-
|
|
311
|
+
if (this._updateRefreshToken({
|
|
278
312
|
token: a.refresh_token,
|
|
279
313
|
scope: a.scope
|
|
280
314
|
}), this._updateAccessToken({
|
|
281
315
|
token: a.access_token,
|
|
282
316
|
expiresAt: Date.now() + a.expires_in * 1e3
|
|
283
|
-
}),
|
|
317
|
+
}), this.isOidc) {
|
|
318
|
+
if (!a.id_token) throw new i(2, "OIDC enabled but no id_token returned");
|
|
319
|
+
this._idToken = a.id_token;
|
|
320
|
+
}
|
|
321
|
+
return a.access_token;
|
|
284
322
|
}
|
|
285
323
|
async refreshToken() {
|
|
286
324
|
let e = new URLSearchParams(), t = this.getRefreshToken();
|
|
@@ -306,17 +344,17 @@ var l = class {
|
|
|
306
344
|
expiresAt: Date.now() + r.expires_in * 1e3
|
|
307
345
|
}), r.access_token;
|
|
308
346
|
}
|
|
309
|
-
},
|
|
310
|
-
dropbox:
|
|
311
|
-
google:
|
|
312
|
-
microsoft:
|
|
347
|
+
}, y = {
|
|
348
|
+
dropbox: m,
|
|
349
|
+
google: _,
|
|
350
|
+
microsoft: v
|
|
313
351
|
};
|
|
314
352
|
//#endregion
|
|
315
353
|
//#region src/index.ts
|
|
316
|
-
function
|
|
317
|
-
return new
|
|
354
|
+
function b(e, t) {
|
|
355
|
+
return new y[t.provider](e);
|
|
318
356
|
}
|
|
319
|
-
async function
|
|
357
|
+
async function x(e, t) {
|
|
320
358
|
let n;
|
|
321
359
|
try {
|
|
322
360
|
n = e.getAccessToken();
|
|
@@ -338,4 +376,4 @@ async function y(e, t) {
|
|
|
338
376
|
return n;
|
|
339
377
|
}
|
|
340
378
|
//#endregion
|
|
341
|
-
export {
|
|
379
|
+
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,21 +1,24 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { IdTokenClaims, OAuth2AuthorizerOptions, TokenData } from "../types";
|
|
2
2
|
export declare abstract class OAuth2Authorizer {
|
|
3
|
-
protected options:
|
|
3
|
+
protected options: OAuth2AuthorizerOptions;
|
|
4
4
|
abstract buildAuthUrl(): Promise<string>;
|
|
5
5
|
abstract finishAuth(url: URL): Promise<string>;
|
|
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
|
-
constructor(options:
|
|
15
|
+
constructor(options: OAuth2AuthorizerOptions, initialData?: {
|
|
14
16
|
accessToken?: TokenData;
|
|
15
17
|
refreshToken?: TokenData;
|
|
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,4 +1,5 @@
|
|
|
1
1
|
import { OAuth2Authorizer } from "./base.ts";
|
|
2
|
+
import type { IdTokenClaims } from "../types.ts";
|
|
2
3
|
export declare class DropboxAuthorizer extends OAuth2Authorizer {
|
|
3
4
|
/**
|
|
4
5
|
* Ref: https://www.dropbox.com/developers/documentation/http/documentation
|
|
@@ -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,13 +4,31 @@ export interface TokenData {
|
|
|
4
4
|
expiresAt?: number;
|
|
5
5
|
scope?: string;
|
|
6
6
|
}
|
|
7
|
-
export interface
|
|
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
|
+
}
|
|
21
|
+
export interface OAuth2Config {
|
|
8
22
|
clientId: string;
|
|
9
23
|
/** clientSecret may be absent for client-side apps. */
|
|
10
24
|
clientSecret?: string;
|
|
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/.
|
|
@@ -21,6 +39,8 @@ export interface IOAuth2Options {
|
|
|
21
39
|
accountType?: "common" | "consumers";
|
|
22
40
|
};
|
|
23
41
|
};
|
|
42
|
+
}
|
|
43
|
+
export interface OAuth2AuthorizerOptions extends OAuth2Config {
|
|
24
44
|
onSetAccessToken?: (value: TokenData | null) => void;
|
|
25
45
|
onSetRefreshToken?: (value: TokenData | null) => void;
|
|
26
46
|
}
|
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;
|
package/package.json
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usync/oauth2",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "OAuth2 support for provider-based authorization flows and token management.",
|
|
4
5
|
"license": "ISC",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/usync-dev/usync.git",
|
|
9
|
+
"directory": "packages/oauth2"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/usync-dev/usync/tree/main/packages/oauth2",
|
|
5
12
|
"files": [
|
|
6
13
|
"dist"
|
|
7
14
|
],
|