@dofe/sso-browser 0.1.42
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/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/sso-session.d.ts +64 -0
- package/dist/sso-session.d.ts.map +1 -0
- package/dist/sso-session.js +115 -0
- package/dist/sso-session.js.map +1 -0
- package/package.json +33 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../packages/sso-browser/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./sso-session"), exports);
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../packages/sso-browser/src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,gDAA8B"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSO cross-subdomain session detection utilities.
|
|
3
|
+
*
|
|
4
|
+
* On page load, subdomain apps can check if the user
|
|
5
|
+
* already has a session at the central SSO via:
|
|
6
|
+
* Layer 1: fetch API to /auth/session (same-site, cookie sent automatically)
|
|
7
|
+
* Layer 2: hidden iframe + postMessage (fallback for strict browser policies)
|
|
8
|
+
* Layer 3: full OIDC redirect (existing flow, always works)
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* User information from SSO session.
|
|
12
|
+
* Projects should extend this interface or provide their own.
|
|
13
|
+
*/
|
|
14
|
+
export interface SsoUserInfo {
|
|
15
|
+
id: string;
|
|
16
|
+
email?: string;
|
|
17
|
+
nickname?: string;
|
|
18
|
+
avatar?: string;
|
|
19
|
+
tenantId?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Session data returned from SSO.
|
|
23
|
+
*/
|
|
24
|
+
export interface SsoSessionData {
|
|
25
|
+
user: SsoUserInfo;
|
|
26
|
+
access: string;
|
|
27
|
+
refresh: string;
|
|
28
|
+
accessExpire: number;
|
|
29
|
+
expire: number;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Configuration for SSO session detection.
|
|
33
|
+
*/
|
|
34
|
+
export interface SsoSessionConfig {
|
|
35
|
+
/** SSO base URL (e.g., 'https://sso.dofe.ai') */
|
|
36
|
+
ssoBaseUrl: string;
|
|
37
|
+
/** Timeout in milliseconds for silent check (default: 5000) */
|
|
38
|
+
timeoutMs?: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Layer 1: Check SSO session via direct fetch API call.
|
|
42
|
+
* Since all subdomains share the same domain, the access_token cookie
|
|
43
|
+
* is sent automatically with SameSite=Lax + credentials:'include'.
|
|
44
|
+
*
|
|
45
|
+
* @param config - SSO configuration with base URL
|
|
46
|
+
* @returns Session data if authenticated, null otherwise
|
|
47
|
+
*/
|
|
48
|
+
export declare function checkSsoSession(config: SsoSessionConfig): Promise<SsoSessionData | null>;
|
|
49
|
+
/**
|
|
50
|
+
* Layer 2: Check SSO session via hidden iframe + postMessage.
|
|
51
|
+
* Fallback for browsers that block third-party cookies or CORS requests.
|
|
52
|
+
*
|
|
53
|
+
* @param config - SSO configuration with base URL
|
|
54
|
+
* @returns Session data if authenticated, null otherwise
|
|
55
|
+
*/
|
|
56
|
+
export declare function checkSsoSessionViaIframe(config: SsoSessionConfig): Promise<SsoSessionData | null>;
|
|
57
|
+
/**
|
|
58
|
+
* Combined session check - tries Layer 1 first, then Layer 2 if needed.
|
|
59
|
+
*
|
|
60
|
+
* @param config - SSO configuration with base URL
|
|
61
|
+
* @returns Session data if authenticated, null otherwise
|
|
62
|
+
*/
|
|
63
|
+
export declare function checkSsoSessionCombined(config: SsoSessionConfig): Promise<SsoSessionData | null>;
|
|
64
|
+
//# sourceMappingURL=sso-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sso-session.d.ts","sourceRoot":"","sources":["../../../packages/sso-browser/src/sso-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAqBhC;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAkDhC;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CAShC"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SSO cross-subdomain session detection utilities.
|
|
4
|
+
*
|
|
5
|
+
* On page load, subdomain apps can check if the user
|
|
6
|
+
* already has a session at the central SSO via:
|
|
7
|
+
* Layer 1: fetch API to /auth/session (same-site, cookie sent automatically)
|
|
8
|
+
* Layer 2: hidden iframe + postMessage (fallback for strict browser policies)
|
|
9
|
+
* Layer 3: full OIDC redirect (existing flow, always works)
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.checkSsoSession = checkSsoSession;
|
|
13
|
+
exports.checkSsoSessionViaIframe = checkSsoSessionViaIframe;
|
|
14
|
+
exports.checkSsoSessionCombined = checkSsoSessionCombined;
|
|
15
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
16
|
+
/**
|
|
17
|
+
* Layer 1: Check SSO session via direct fetch API call.
|
|
18
|
+
* Since all subdomains share the same domain, the access_token cookie
|
|
19
|
+
* is sent automatically with SameSite=Lax + credentials:'include'.
|
|
20
|
+
*
|
|
21
|
+
* @param config - SSO configuration with base URL
|
|
22
|
+
* @returns Session data if authenticated, null otherwise
|
|
23
|
+
*/
|
|
24
|
+
async function checkSsoSession(config) {
|
|
25
|
+
const { ssoBaseUrl } = config;
|
|
26
|
+
try {
|
|
27
|
+
const res = await fetch(`${ssoBaseUrl}/auth/session`, {
|
|
28
|
+
method: 'GET',
|
|
29
|
+
credentials: 'include',
|
|
30
|
+
cache: 'no-store',
|
|
31
|
+
mode: 'cors',
|
|
32
|
+
});
|
|
33
|
+
if (!res.ok)
|
|
34
|
+
return null;
|
|
35
|
+
const body = await res.json();
|
|
36
|
+
if (body.code !== 0 || !body.data?.access)
|
|
37
|
+
return null;
|
|
38
|
+
return body.data;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Network error or CORS blocked — fall through to next layer
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Layer 2: Check SSO session via hidden iframe + postMessage.
|
|
47
|
+
* Fallback for browsers that block third-party cookies or CORS requests.
|
|
48
|
+
*
|
|
49
|
+
* @param config - SSO configuration with base URL
|
|
50
|
+
* @returns Session data if authenticated, null otherwise
|
|
51
|
+
*/
|
|
52
|
+
function checkSsoSessionViaIframe(config) {
|
|
53
|
+
const { ssoBaseUrl, timeoutMs = DEFAULT_TIMEOUT_MS } = config;
|
|
54
|
+
return new Promise((resolve) => {
|
|
55
|
+
let settled = false;
|
|
56
|
+
let iframe = null;
|
|
57
|
+
const finish = (result) => {
|
|
58
|
+
if (settled)
|
|
59
|
+
return;
|
|
60
|
+
settled = true;
|
|
61
|
+
clearTimeout(timeout);
|
|
62
|
+
window.removeEventListener('message', handler);
|
|
63
|
+
if (iframe) {
|
|
64
|
+
iframe.onerror = null;
|
|
65
|
+
iframe.onload = null;
|
|
66
|
+
}
|
|
67
|
+
try {
|
|
68
|
+
if (iframe) {
|
|
69
|
+
document.body.removeChild(iframe);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
/* already removed */
|
|
74
|
+
}
|
|
75
|
+
resolve(result);
|
|
76
|
+
};
|
|
77
|
+
const timeout = setTimeout(() => finish(null), timeoutMs);
|
|
78
|
+
const handler = (event) => {
|
|
79
|
+
// Exact origin match to prevent spoofing via sibling subdomains
|
|
80
|
+
if (event.origin !== ssoBaseUrl)
|
|
81
|
+
return;
|
|
82
|
+
if (event.data?.type === 'SSO_SILENT_CHECK_RESULT') {
|
|
83
|
+
if (event.data.authenticated && event.data.data) {
|
|
84
|
+
finish(event.data.data);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
finish(null);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
window.addEventListener('message', handler);
|
|
92
|
+
iframe = document.createElement('iframe');
|
|
93
|
+
iframe.src = `${ssoBaseUrl}/auth/silent-check?parent=${encodeURIComponent(window.location.origin)}`;
|
|
94
|
+
iframe.style.display = 'none';
|
|
95
|
+
iframe.setAttribute('aria-hidden', 'true');
|
|
96
|
+
iframe.onerror = () => finish(null);
|
|
97
|
+
document.body.appendChild(iframe);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Combined session check - tries Layer 1 first, then Layer 2 if needed.
|
|
102
|
+
*
|
|
103
|
+
* @param config - SSO configuration with base URL
|
|
104
|
+
* @returns Session data if authenticated, null otherwise
|
|
105
|
+
*/
|
|
106
|
+
async function checkSsoSessionCombined(config) {
|
|
107
|
+
// Try Layer 1 first (direct fetch)
|
|
108
|
+
const sessionData = await checkSsoSession(config);
|
|
109
|
+
if (sessionData) {
|
|
110
|
+
return sessionData;
|
|
111
|
+
}
|
|
112
|
+
// Fall back to Layer 2 (iframe + postMessage)
|
|
113
|
+
return checkSsoSessionViaIframe(config);
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=sso-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sso-session.js","sourceRoot":"","sources":["../../../packages/sso-browser/src/sso-session.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dofe/sso-browser",
|
|
3
|
+
"version": "0.1.42",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"default": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./sso-session": {
|
|
12
|
+
"types": "./dist/sso-session.d.ts",
|
|
13
|
+
"default": "./dist/sso-session.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"rimraf": "^6.1.3",
|
|
18
|
+
"typescript": "^6.0.3"
|
|
19
|
+
},
|
|
20
|
+
"type": "commonjs",
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist"
|
|
26
|
+
],
|
|
27
|
+
"license": "UNLICENSED",
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc",
|
|
30
|
+
"typecheck": "tsc --noEmit",
|
|
31
|
+
"clean": "rimraf dist"
|
|
32
|
+
}
|
|
33
|
+
}
|