@bagelink/auth 1.4.178 → 1.4.180
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 +11 -8
- package/dist/index.cjs +461 -34
- package/dist/index.d.cts +291 -8
- package/dist/index.d.mts +291 -8
- package/dist/index.d.ts +291 -8
- package/dist/index.mjs +450 -35
- package/package.json +1 -1
- package/src/api.ts +54 -36
- package/src/index.ts +1 -0
- package/src/sso.ts +565 -0
- package/src/types.ts +33 -2
- package/src/useAuth.ts +87 -5
- package/src/utils.ts +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
2
|
import { computed, ref } from 'vue';
|
|
3
3
|
|
|
4
|
-
function createAxiosInstance(baseURL
|
|
4
|
+
function createAxiosInstance(baseURL) {
|
|
5
5
|
return axios.create({
|
|
6
|
-
baseURL
|
|
6
|
+
baseURL,
|
|
7
|
+
withCredentials: true,
|
|
7
8
|
headers: {
|
|
8
|
-
"Content-Type": "application/json"
|
|
9
|
-
"withCredentials": true
|
|
9
|
+
"Content-Type": "application/json"
|
|
10
10
|
}
|
|
11
11
|
});
|
|
12
12
|
}
|
|
@@ -43,16 +43,12 @@ class EventEmitter {
|
|
|
43
43
|
|
|
44
44
|
class AuthApi {
|
|
45
45
|
api;
|
|
46
|
-
constructor(
|
|
47
|
-
this.api =
|
|
46
|
+
constructor(baseURL = "") {
|
|
47
|
+
this.api = createAxiosInstance(baseURL);
|
|
48
48
|
this.setupInterceptors();
|
|
49
49
|
}
|
|
50
50
|
setupInterceptors() {
|
|
51
51
|
this.api.interceptors.request.use((config) => {
|
|
52
|
-
const sessionToken = localStorage.getItem("session_token");
|
|
53
|
-
if (sessionToken !== null && config.headers) {
|
|
54
|
-
config.headers.Authorization = `Bearer ${sessionToken}`;
|
|
55
|
-
}
|
|
56
52
|
const urlParams = new URLSearchParams(window.location.search);
|
|
57
53
|
const resetToken = urlParams.get("token");
|
|
58
54
|
if (resetToken !== null && config.headers) {
|
|
@@ -74,45 +70,67 @@ class AuthApi {
|
|
|
74
70
|
* Register a new account
|
|
75
71
|
*/
|
|
76
72
|
async register(data) {
|
|
77
|
-
|
|
73
|
+
return this.api.post("/authentication/register", {
|
|
78
74
|
...data,
|
|
79
75
|
email: data.email.toLowerCase()
|
|
80
76
|
});
|
|
81
|
-
if (response.data.session_token) {
|
|
82
|
-
localStorage.setItem("session_token", response.data.session_token);
|
|
83
|
-
}
|
|
84
|
-
return response;
|
|
85
77
|
}
|
|
86
78
|
/**
|
|
87
79
|
* Login with password
|
|
88
80
|
*/
|
|
89
81
|
async login(email, password) {
|
|
90
|
-
|
|
82
|
+
return this.api.post("/authentication/login/password", {
|
|
91
83
|
email: email.toLowerCase(),
|
|
92
84
|
password
|
|
93
85
|
});
|
|
94
|
-
if (response.data.session_token) {
|
|
95
|
-
localStorage.setItem("session_token", response.data.session_token);
|
|
96
|
-
}
|
|
97
|
-
return response;
|
|
98
86
|
}
|
|
99
87
|
/**
|
|
100
88
|
* Logout and clear session
|
|
101
89
|
*/
|
|
102
90
|
async logout() {
|
|
103
|
-
|
|
104
|
-
localStorage.removeItem("session_token");
|
|
105
|
-
return response;
|
|
91
|
+
return this.api.post("/authentication/logout", {});
|
|
106
92
|
}
|
|
107
93
|
/**
|
|
108
94
|
* Refresh current session
|
|
109
95
|
*/
|
|
110
96
|
async refreshSession() {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
97
|
+
return this.api.post("/authentication/refresh", {});
|
|
98
|
+
}
|
|
99
|
+
// ============================================
|
|
100
|
+
// SSO Authentication Methods
|
|
101
|
+
// ============================================
|
|
102
|
+
/**
|
|
103
|
+
* Initiate SSO login flow
|
|
104
|
+
* Returns authorization URL to redirect user to
|
|
105
|
+
*/
|
|
106
|
+
async initiateSSO(data) {
|
|
107
|
+
return this.api.post(`/authentication/sso/${data.provider}/initiate`, {
|
|
108
|
+
redirect_uri: data.redirect_uri,
|
|
109
|
+
state: data.state
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Complete SSO login after callback from provider
|
|
114
|
+
*/
|
|
115
|
+
async ssoCallback(data) {
|
|
116
|
+
return this.api.post(`/authentication/sso/${data.provider}/callback`, {
|
|
117
|
+
code: data.code,
|
|
118
|
+
state: data.state
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Link an SSO provider to existing account
|
|
123
|
+
*/
|
|
124
|
+
async linkSSOProvider(data) {
|
|
125
|
+
return this.api.post(`/authentication/sso/${data.provider}/link`, {
|
|
126
|
+
code: data.code
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Unlink an SSO provider from account
|
|
131
|
+
*/
|
|
132
|
+
async unlinkSSOProvider(provider) {
|
|
133
|
+
return this.api.delete(`/authentication/sso/${provider}/unlink`);
|
|
116
134
|
}
|
|
117
135
|
// ============================================
|
|
118
136
|
// Current User (Me) Methods
|
|
@@ -133,9 +151,7 @@ class AuthApi {
|
|
|
133
151
|
* Delete current user account
|
|
134
152
|
*/
|
|
135
153
|
async deleteCurrentUser() {
|
|
136
|
-
|
|
137
|
-
localStorage.removeItem("session_token");
|
|
138
|
-
return response;
|
|
154
|
+
return this.api.delete("/authentication/me");
|
|
139
155
|
}
|
|
140
156
|
// ============================================
|
|
141
157
|
// Account Management (Admin)
|
|
@@ -245,6 +261,356 @@ class AuthApi {
|
|
|
245
261
|
}
|
|
246
262
|
}
|
|
247
263
|
|
|
264
|
+
let authApiRef = null;
|
|
265
|
+
function setAuthContext(authApi) {
|
|
266
|
+
authApiRef = authApi;
|
|
267
|
+
}
|
|
268
|
+
function getAuthApi() {
|
|
269
|
+
if (!authApiRef) {
|
|
270
|
+
throw new Error("SSO auth context not initialized. Make sure to call useAuth() before using SSO methods.");
|
|
271
|
+
}
|
|
272
|
+
return authApiRef;
|
|
273
|
+
}
|
|
274
|
+
class SSOError extends Error {
|
|
275
|
+
constructor(message, code) {
|
|
276
|
+
super(message);
|
|
277
|
+
this.code = code;
|
|
278
|
+
this.name = "SSOError";
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
class PopupBlockedError extends SSOError {
|
|
282
|
+
constructor() {
|
|
283
|
+
super("Popup was blocked. Please allow popups for this site.", "POPUP_BLOCKED");
|
|
284
|
+
this.name = "PopupBlockedError";
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
class PopupClosedError extends SSOError {
|
|
288
|
+
constructor() {
|
|
289
|
+
super("Popup was closed by user", "POPUP_CLOSED");
|
|
290
|
+
this.name = "PopupClosedError";
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
class PopupTimeoutError extends SSOError {
|
|
294
|
+
constructor() {
|
|
295
|
+
super("Popup authentication timed out", "POPUP_TIMEOUT");
|
|
296
|
+
this.name = "PopupTimeoutError";
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
class StateMismatchError extends SSOError {
|
|
300
|
+
constructor() {
|
|
301
|
+
super("State mismatch - possible CSRF attack", "STATE_MISMATCH");
|
|
302
|
+
this.name = "StateMismatchError";
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
function generateState() {
|
|
306
|
+
const array = new Uint8Array(32);
|
|
307
|
+
crypto.getRandomValues(array);
|
|
308
|
+
return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
309
|
+
}
|
|
310
|
+
function openPopup(url, width = 500, height = 600) {
|
|
311
|
+
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
312
|
+
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
313
|
+
return window.open(
|
|
314
|
+
url,
|
|
315
|
+
"oauth-popup",
|
|
316
|
+
`width=${width},height=${height},left=${left},top=${top},toolbar=no,menubar=no,location=no,status=no`
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
function waitForPopupCallback(popup, provider, timeoutMs = 9e4) {
|
|
320
|
+
return new Promise((resolve, reject) => {
|
|
321
|
+
let done = false;
|
|
322
|
+
const finish = (fn) => {
|
|
323
|
+
if (!done) {
|
|
324
|
+
done = true;
|
|
325
|
+
fn();
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
const timer = setTimeout(() => {
|
|
329
|
+
finish(() => {
|
|
330
|
+
reject(new PopupTimeoutError());
|
|
331
|
+
});
|
|
332
|
+
}, timeoutMs);
|
|
333
|
+
function onMessage(ev) {
|
|
334
|
+
try {
|
|
335
|
+
if (ev.origin !== window.location.origin) return;
|
|
336
|
+
const data = ev.data || {};
|
|
337
|
+
if (data.type !== "auth:complete" || data.provider !== provider) return;
|
|
338
|
+
cleanup();
|
|
339
|
+
if (data.error) {
|
|
340
|
+
reject(new SSOError(data.error, "OAUTH_ERROR"));
|
|
341
|
+
} else if (data.code) {
|
|
342
|
+
resolve({ code: data.code, state: data.state });
|
|
343
|
+
}
|
|
344
|
+
} catch {
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const pollInterval = setInterval(() => {
|
|
348
|
+
try {
|
|
349
|
+
if (popup.closed) {
|
|
350
|
+
cleanup();
|
|
351
|
+
reject(new PopupClosedError());
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
const url = new URL(popup.location.href);
|
|
355
|
+
if (url.origin === window.location.origin) {
|
|
356
|
+
const code = url.searchParams.get("code");
|
|
357
|
+
const state = url.searchParams.get("state") ?? void 0;
|
|
358
|
+
const error = url.searchParams.get("error");
|
|
359
|
+
if (code || error) {
|
|
360
|
+
cleanup();
|
|
361
|
+
try {
|
|
362
|
+
popup.close();
|
|
363
|
+
} catch {
|
|
364
|
+
}
|
|
365
|
+
if (error) {
|
|
366
|
+
reject(new SSOError(error, "OAUTH_ERROR"));
|
|
367
|
+
} else if (code) {
|
|
368
|
+
resolve({ code, state });
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
} catch {
|
|
373
|
+
}
|
|
374
|
+
}, 150);
|
|
375
|
+
function cleanup() {
|
|
376
|
+
finish(() => {
|
|
377
|
+
clearInterval(pollInterval);
|
|
378
|
+
clearTimeout(timer);
|
|
379
|
+
window.removeEventListener("message", onMessage);
|
|
380
|
+
try {
|
|
381
|
+
popup.close();
|
|
382
|
+
} catch {
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
window.addEventListener("message", onMessage);
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
function createSSOProvider(config) {
|
|
390
|
+
const getDefaultRedirectUri = () => {
|
|
391
|
+
if (typeof window !== "undefined") {
|
|
392
|
+
return `${window.location.origin}/auth/callback`;
|
|
393
|
+
}
|
|
394
|
+
return `/auth/callback`;
|
|
395
|
+
};
|
|
396
|
+
const getStateKey = () => `oauth_state:${config.id}`;
|
|
397
|
+
return {
|
|
398
|
+
...config,
|
|
399
|
+
async redirect(options = {}) {
|
|
400
|
+
const auth = getAuthApi();
|
|
401
|
+
const redirectUri = options.redirectUri ?? getDefaultRedirectUri();
|
|
402
|
+
const state = options.state ?? generateState();
|
|
403
|
+
if (typeof sessionStorage !== "undefined") {
|
|
404
|
+
sessionStorage.setItem(getStateKey(), state);
|
|
405
|
+
sessionStorage.setItem(`oauth_provider:${state}`, config.id);
|
|
406
|
+
}
|
|
407
|
+
const authUrl = await auth.initiateSSO({
|
|
408
|
+
provider: config.id,
|
|
409
|
+
redirect_uri: redirectUri,
|
|
410
|
+
state,
|
|
411
|
+
scopes: options.scopes ?? config.defaultScopes,
|
|
412
|
+
params: options.params
|
|
413
|
+
});
|
|
414
|
+
window.location.href = authUrl;
|
|
415
|
+
},
|
|
416
|
+
async popup(options = {}) {
|
|
417
|
+
const auth = getAuthApi();
|
|
418
|
+
const redirectUri = options.redirectUri ?? getDefaultRedirectUri();
|
|
419
|
+
const state = options.state ?? generateState();
|
|
420
|
+
const timeout = options.popupTimeout ?? 9e4;
|
|
421
|
+
if (typeof sessionStorage !== "undefined") {
|
|
422
|
+
sessionStorage.setItem(getStateKey(), state);
|
|
423
|
+
sessionStorage.setItem(`oauth_provider:${state}`, config.id);
|
|
424
|
+
}
|
|
425
|
+
const authUrl = await auth.initiateSSO({
|
|
426
|
+
provider: config.id,
|
|
427
|
+
redirect_uri: redirectUri,
|
|
428
|
+
state,
|
|
429
|
+
scopes: options.scopes ?? config.defaultScopes,
|
|
430
|
+
params: options.params
|
|
431
|
+
});
|
|
432
|
+
const { width = 500, height = 600 } = options.popupDimensions ?? {};
|
|
433
|
+
const popupWindow = openPopup(authUrl, width, height);
|
|
434
|
+
if (!popupWindow) {
|
|
435
|
+
throw new PopupBlockedError();
|
|
436
|
+
}
|
|
437
|
+
const result = await waitForPopupCallback(popupWindow, config.id, timeout);
|
|
438
|
+
return auth.loginWithSSO({
|
|
439
|
+
provider: config.id,
|
|
440
|
+
code: result.code,
|
|
441
|
+
state: result.state
|
|
442
|
+
});
|
|
443
|
+
},
|
|
444
|
+
async callback(code, state) {
|
|
445
|
+
const auth = getAuthApi();
|
|
446
|
+
if (typeof sessionStorage !== "undefined" && state) {
|
|
447
|
+
const storedState = sessionStorage.getItem(getStateKey());
|
|
448
|
+
sessionStorage.removeItem(getStateKey());
|
|
449
|
+
sessionStorage.removeItem(`oauth_provider:${state}`);
|
|
450
|
+
if (storedState && storedState !== state) {
|
|
451
|
+
throw new StateMismatchError();
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
return auth.loginWithSSO({
|
|
455
|
+
provider: config.id,
|
|
456
|
+
code,
|
|
457
|
+
state
|
|
458
|
+
});
|
|
459
|
+
},
|
|
460
|
+
async link(code) {
|
|
461
|
+
const auth = getAuthApi();
|
|
462
|
+
await auth.linkSSOProvider({
|
|
463
|
+
provider: config.id,
|
|
464
|
+
code
|
|
465
|
+
});
|
|
466
|
+
},
|
|
467
|
+
async unlink() {
|
|
468
|
+
const auth = getAuthApi();
|
|
469
|
+
await auth.unlinkSSOProvider(config.id);
|
|
470
|
+
},
|
|
471
|
+
async getAuthUrl(options = {}) {
|
|
472
|
+
const auth = getAuthApi();
|
|
473
|
+
const redirectUri = options.redirectUri ?? getDefaultRedirectUri();
|
|
474
|
+
const state = options.state ?? generateState();
|
|
475
|
+
return auth.initiateSSO({
|
|
476
|
+
provider: config.id,
|
|
477
|
+
redirect_uri: redirectUri,
|
|
478
|
+
state,
|
|
479
|
+
scopes: options.scopes ?? config.defaultScopes,
|
|
480
|
+
params: options.params
|
|
481
|
+
});
|
|
482
|
+
},
|
|
483
|
+
supportsPopup: true
|
|
484
|
+
// Default, can be overridden per provider
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
const sso = {
|
|
488
|
+
/**
|
|
489
|
+
* Google OAuth Provider
|
|
490
|
+
* https://developers.google.com/identity/protocols/oauth2
|
|
491
|
+
*/
|
|
492
|
+
google: createSSOProvider({
|
|
493
|
+
id: "google",
|
|
494
|
+
name: "Google",
|
|
495
|
+
color: "#4285F4",
|
|
496
|
+
icon: "google",
|
|
497
|
+
defaultScopes: ["openid", "email", "profile"],
|
|
498
|
+
metadata: {
|
|
499
|
+
authDomain: "accounts.google.com",
|
|
500
|
+
buttonText: "Continue with Google"
|
|
501
|
+
}
|
|
502
|
+
}),
|
|
503
|
+
/**
|
|
504
|
+
* Microsoft OAuth Provider (Azure AD / Microsoft Entra ID)
|
|
505
|
+
* https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow
|
|
506
|
+
*/
|
|
507
|
+
microsoft: createSSOProvider({
|
|
508
|
+
id: "microsoft",
|
|
509
|
+
name: "Microsoft",
|
|
510
|
+
color: "#00A4EF",
|
|
511
|
+
icon: "microsoft",
|
|
512
|
+
defaultScopes: ["openid", "email", "profile", "User.Read"],
|
|
513
|
+
metadata: {
|
|
514
|
+
authDomain: "login.microsoftonline.com",
|
|
515
|
+
buttonText: "Continue with Microsoft"
|
|
516
|
+
}
|
|
517
|
+
}),
|
|
518
|
+
/**
|
|
519
|
+
* GitHub OAuth Provider
|
|
520
|
+
* https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps
|
|
521
|
+
*/
|
|
522
|
+
github: createSSOProvider({
|
|
523
|
+
id: "github",
|
|
524
|
+
name: "GitHub",
|
|
525
|
+
color: "#24292E",
|
|
526
|
+
icon: "github",
|
|
527
|
+
defaultScopes: ["read:user", "user:email"],
|
|
528
|
+
metadata: {
|
|
529
|
+
authDomain: "github.com",
|
|
530
|
+
buttonText: "Continue with GitHub"
|
|
531
|
+
}
|
|
532
|
+
}),
|
|
533
|
+
/**
|
|
534
|
+
* Okta OAuth Provider
|
|
535
|
+
* https://developer.okta.com/docs/guides/implement-grant-type/authcode/main/
|
|
536
|
+
*/
|
|
537
|
+
okta: createSSOProvider({
|
|
538
|
+
id: "okta",
|
|
539
|
+
name: "Okta",
|
|
540
|
+
color: "#007DC1",
|
|
541
|
+
icon: "okta",
|
|
542
|
+
defaultScopes: ["openid", "email", "profile"],
|
|
543
|
+
metadata: {
|
|
544
|
+
buttonText: "Continue with Okta"
|
|
545
|
+
}
|
|
546
|
+
}),
|
|
547
|
+
/**
|
|
548
|
+
* Apple Sign In Provider
|
|
549
|
+
* https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api
|
|
550
|
+
* Note: Apple works best with redirect flow on web
|
|
551
|
+
*/
|
|
552
|
+
apple: {
|
|
553
|
+
...createSSOProvider({
|
|
554
|
+
id: "apple",
|
|
555
|
+
name: "Apple",
|
|
556
|
+
color: "#000000",
|
|
557
|
+
icon: "apple",
|
|
558
|
+
defaultScopes: ["name", "email"],
|
|
559
|
+
metadata: {
|
|
560
|
+
authDomain: "appleid.apple.com",
|
|
561
|
+
buttonText: "Continue with Apple"
|
|
562
|
+
}
|
|
563
|
+
}),
|
|
564
|
+
supportsPopup: false,
|
|
565
|
+
// Apple prefers redirect on web
|
|
566
|
+
// Override popup to use redirect for better UX
|
|
567
|
+
async popup(options) {
|
|
568
|
+
return this.redirect(options);
|
|
569
|
+
}
|
|
570
|
+
},
|
|
571
|
+
/**
|
|
572
|
+
* Facebook OAuth Provider
|
|
573
|
+
* https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow
|
|
574
|
+
*/
|
|
575
|
+
facebook: createSSOProvider({
|
|
576
|
+
id: "facebook",
|
|
577
|
+
name: "Facebook",
|
|
578
|
+
color: "#1877F2",
|
|
579
|
+
icon: "facebook",
|
|
580
|
+
defaultScopes: ["email", "public_profile"],
|
|
581
|
+
metadata: {
|
|
582
|
+
authDomain: "www.facebook.com",
|
|
583
|
+
buttonText: "Continue with Facebook"
|
|
584
|
+
}
|
|
585
|
+
})
|
|
586
|
+
};
|
|
587
|
+
const ssoProviders = Object.values(sso);
|
|
588
|
+
function getSSOProvider(provider) {
|
|
589
|
+
return sso[provider];
|
|
590
|
+
}
|
|
591
|
+
function getAllSSOProviders() {
|
|
592
|
+
return ssoProviders;
|
|
593
|
+
}
|
|
594
|
+
function isSupportedProvider(provider) {
|
|
595
|
+
return provider in sso;
|
|
596
|
+
}
|
|
597
|
+
async function handleOAuthCallback() {
|
|
598
|
+
if (typeof window === "undefined") {
|
|
599
|
+
return null;
|
|
600
|
+
}
|
|
601
|
+
const urlParams = new URLSearchParams(window.location.search);
|
|
602
|
+
const code = urlParams.get("code");
|
|
603
|
+
const state = urlParams.get("state");
|
|
604
|
+
if (!code || !state) {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
const provider = sessionStorage.getItem(`oauth_provider:${state}`);
|
|
608
|
+
if (!provider || !isSupportedProvider(provider)) {
|
|
609
|
+
throw new Error("Unable to determine OAuth provider. State may have expired.");
|
|
610
|
+
}
|
|
611
|
+
return sso[provider].callback(code, state);
|
|
612
|
+
}
|
|
613
|
+
|
|
248
614
|
var AuthState = /* @__PURE__ */ ((AuthState2) => {
|
|
249
615
|
AuthState2["LOGIN"] = "login";
|
|
250
616
|
AuthState2["LOGOUT"] = "logout";
|
|
@@ -285,7 +651,7 @@ function accountToUser(account) {
|
|
|
285
651
|
metadata: account.entity.metadata
|
|
286
652
|
};
|
|
287
653
|
}
|
|
288
|
-
const emailMethod = account.authentication_methods
|
|
654
|
+
const emailMethod = account.authentication_methods.find(
|
|
289
655
|
(m) => m.type === "password" || m.type === "email_token"
|
|
290
656
|
);
|
|
291
657
|
return {
|
|
@@ -304,11 +670,10 @@ let authApi = null;
|
|
|
304
670
|
let eventEmitter = null;
|
|
305
671
|
const accountInfo = ref(null);
|
|
306
672
|
function initAuth({
|
|
307
|
-
axios,
|
|
308
673
|
baseURL
|
|
309
674
|
}) {
|
|
310
675
|
if (authApi === null) {
|
|
311
|
-
authApi = new AuthApi(
|
|
676
|
+
authApi = new AuthApi(baseURL);
|
|
312
677
|
}
|
|
313
678
|
if (eventEmitter === null) {
|
|
314
679
|
eventEmitter = new EventEmitter();
|
|
@@ -344,6 +709,29 @@ function useAuth() {
|
|
|
344
709
|
}
|
|
345
710
|
const api = authApi;
|
|
346
711
|
const emitter = eventEmitter;
|
|
712
|
+
const authMethods = {
|
|
713
|
+
initiateSSO: async (params) => {
|
|
714
|
+
const { data } = await api.initiateSSO(params);
|
|
715
|
+
return data.authorization_url;
|
|
716
|
+
},
|
|
717
|
+
loginWithSSO: async (params) => {
|
|
718
|
+
const { data } = await api.ssoCallback(params);
|
|
719
|
+
if (data.success === true && data.requires_verification !== true) {
|
|
720
|
+
await checkAuth();
|
|
721
|
+
}
|
|
722
|
+
emitter.emit(AuthState.LOGIN);
|
|
723
|
+
return data;
|
|
724
|
+
},
|
|
725
|
+
linkSSOProvider: async (params) => {
|
|
726
|
+
await api.linkSSOProvider(params);
|
|
727
|
+
await checkAuth();
|
|
728
|
+
},
|
|
729
|
+
unlinkSSOProvider: async (provider) => {
|
|
730
|
+
await api.unlinkSSOProvider(provider);
|
|
731
|
+
await checkAuth();
|
|
732
|
+
}
|
|
733
|
+
};
|
|
734
|
+
setAuthContext(authMethods);
|
|
347
735
|
const user = computed(() => accountToUser(accountInfo.value));
|
|
348
736
|
const getFullName = () => {
|
|
349
737
|
return user.value?.name ?? "";
|
|
@@ -482,11 +870,33 @@ function useAuth() {
|
|
|
482
870
|
}
|
|
483
871
|
await api.revokeAllSessions(id);
|
|
484
872
|
}
|
|
873
|
+
async function initiateSSO(params) {
|
|
874
|
+
const { data } = await api.initiateSSO(params);
|
|
875
|
+
return data.authorization_url;
|
|
876
|
+
}
|
|
877
|
+
async function loginWithSSO(params) {
|
|
878
|
+
const { data } = await api.ssoCallback(params);
|
|
879
|
+
if (data.success === true && data.requires_verification !== true) {
|
|
880
|
+
await checkAuth();
|
|
881
|
+
}
|
|
882
|
+
emitter.emit(AuthState.LOGIN);
|
|
883
|
+
return data;
|
|
884
|
+
}
|
|
885
|
+
async function linkSSOProvider(params) {
|
|
886
|
+
await api.linkSSOProvider(params);
|
|
887
|
+
await checkAuth();
|
|
888
|
+
}
|
|
889
|
+
async function unlinkSSOProvider(provider) {
|
|
890
|
+
await api.unlinkSSOProvider(provider);
|
|
891
|
+
await checkAuth();
|
|
892
|
+
}
|
|
485
893
|
return {
|
|
486
894
|
// Primary State (use this!)
|
|
487
895
|
user,
|
|
488
896
|
// Full account info (for advanced use cases)
|
|
489
897
|
accountInfo,
|
|
898
|
+
// SSO Providers (ready to use!)
|
|
899
|
+
sso,
|
|
490
900
|
// Getters
|
|
491
901
|
getFullName,
|
|
492
902
|
getIsLoggedIn,
|
|
@@ -501,6 +911,11 @@ function useAuth() {
|
|
|
501
911
|
signup,
|
|
502
912
|
checkAuth,
|
|
503
913
|
refreshSession,
|
|
914
|
+
// SSO Authentication (lower-level - prefer using sso.google.redirect())
|
|
915
|
+
initiateSSO,
|
|
916
|
+
loginWithSSO,
|
|
917
|
+
linkSSOProvider,
|
|
918
|
+
unlinkSSOProvider,
|
|
504
919
|
// Profile Actions
|
|
505
920
|
updateProfile,
|
|
506
921
|
deleteCurrentUser,
|
|
@@ -523,4 +938,4 @@ function useAuth() {
|
|
|
523
938
|
};
|
|
524
939
|
}
|
|
525
940
|
|
|
526
|
-
export { AuthApi, AuthState, accountToUser, initAuth, useAuth };
|
|
941
|
+
export { AuthApi, AuthState, PopupBlockedError, PopupClosedError, PopupTimeoutError, SSOError, StateMismatchError, accountToUser, getAllSSOProviders, getSSOProvider, handleOAuthCallback, initAuth, isSupportedProvider, setAuthContext, sso, ssoProviders, useAuth };
|