@stack-spot/auth-react 1.0.0 → 1.1.0
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/out/index.d.ts +3 -2
- package/out/index.js +43 -12
- package/out/index.js.map +1 -1
- package/out/index.mjs +43 -12
- package/out/index.mjs.map +1 -1
- package/package.json +6 -6
- package/src/SessionManager.ts +31 -11
- package/src/utils/cookies.ts +22 -4
package/out/index.d.ts
CHANGED
|
@@ -26,6 +26,7 @@ declare class SessionManager {
|
|
|
26
26
|
static create(config: SessionManagerConfig): SessionManager;
|
|
27
27
|
private setSession;
|
|
28
28
|
restoreSession(): Promise<void>;
|
|
29
|
+
validateSharedSession(session?: Session | undefined): Promise<void>;
|
|
29
30
|
hasSession(): boolean;
|
|
30
31
|
getSession(): Session;
|
|
31
32
|
endSession(redirectToLogin?: boolean): void;
|
|
@@ -33,9 +34,9 @@ declare class SessionManager {
|
|
|
33
34
|
startThirdPartyLogin(data: ThirdPartyLoginParams): Promise<void>;
|
|
34
35
|
urlHasThirdPartyLoginData(): boolean;
|
|
35
36
|
completeThirdPartyLogin(): Promise<void>;
|
|
36
|
-
getEmailForLogin(): string;
|
|
37
|
+
getEmailForLogin(): string | undefined;
|
|
37
38
|
onChange(listener: ChangeListener): () => void;
|
|
38
|
-
private
|
|
39
|
+
private setSessionCookie;
|
|
39
40
|
private sendLoginEventRd;
|
|
40
41
|
}
|
|
41
42
|
|
package/out/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
var auth = require('@stack-spot/auth');
|
|
4
4
|
var react = require('react');
|
|
5
5
|
|
|
6
|
+
const sessionKey$1 = "stk-session";
|
|
6
7
|
const portalUrl = new URL(location.href);
|
|
7
8
|
const cookieDomain = portalUrl.host.replace("app", "").replace("edp", "").split(":")[0];
|
|
8
9
|
const defaultCookieAttributes = `domain=${cookieDomain}; SameSite=Strict;`;
|
|
@@ -18,6 +19,18 @@ const getCookies = () => document.cookie.split("; ").reduce((prev, current) => {
|
|
|
18
19
|
prev[name] = value.join("=");
|
|
19
20
|
return prev;
|
|
20
21
|
}, {});
|
|
22
|
+
const sessionCookie = Object.freeze({
|
|
23
|
+
set: (data) => setCookie(sessionKey$1, JSON.stringify(data)),
|
|
24
|
+
get: () => {
|
|
25
|
+
try {
|
|
26
|
+
const cookie = getCookie(sessionKey$1);
|
|
27
|
+
return cookie ? JSON.parse(cookie) : void 0;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error(error);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
delete: () => removeCookie(sessionKey$1)
|
|
33
|
+
});
|
|
21
34
|
|
|
22
35
|
var __defProp = Object.defineProperty;
|
|
23
36
|
var __defProps = Object.defineProperties;
|
|
@@ -43,7 +56,6 @@ var __publicField = (obj, key, value) => {
|
|
|
43
56
|
return value;
|
|
44
57
|
};
|
|
45
58
|
const sessionKey = "session";
|
|
46
|
-
const loginEmailKey = "sessionEmail";
|
|
47
59
|
const _SessionManager = class _SessionManager {
|
|
48
60
|
constructor(config) {
|
|
49
61
|
__publicField(this, "current");
|
|
@@ -52,8 +64,6 @@ const _SessionManager = class _SessionManager {
|
|
|
52
64
|
__publicField(this, "changeListeners", []);
|
|
53
65
|
this.config = config;
|
|
54
66
|
this.auth = new auth.AuthManager(__spreadProps(__spreadValues({}, config), {
|
|
55
|
-
authMigrationUrl: config.authUrl,
|
|
56
|
-
migrationClientId: config.clientId,
|
|
57
67
|
storage: localStorage,
|
|
58
68
|
sessionPersistence: {
|
|
59
69
|
load: () => localStorage.getItem(sessionKey),
|
|
@@ -61,6 +71,7 @@ const _SessionManager = class _SessionManager {
|
|
|
61
71
|
}
|
|
62
72
|
}));
|
|
63
73
|
_SessionManager.instance = this;
|
|
74
|
+
addEventListener("focus", () => this.validateSharedSession());
|
|
64
75
|
}
|
|
65
76
|
static create(config) {
|
|
66
77
|
var _a;
|
|
@@ -68,13 +79,30 @@ const _SessionManager = class _SessionManager {
|
|
|
68
79
|
}
|
|
69
80
|
setSession(session) {
|
|
70
81
|
this.current = session;
|
|
82
|
+
if (session)
|
|
83
|
+
this.setSessionCookie(session);
|
|
84
|
+
else {
|
|
85
|
+
localStorage.removeItem(sessionKey);
|
|
86
|
+
sessionCookie.delete();
|
|
87
|
+
}
|
|
71
88
|
this.changeListeners.forEach((l) => l(session));
|
|
72
89
|
}
|
|
73
90
|
async restoreSession() {
|
|
74
91
|
const session = await this.auth.restoreSession();
|
|
92
|
+
await this.validateSharedSession(session);
|
|
75
93
|
if (session)
|
|
76
94
|
this.setSession(session);
|
|
77
95
|
}
|
|
96
|
+
async validateSharedSession(session = this.current) {
|
|
97
|
+
const sharedSessionCookie = sessionCookie.get();
|
|
98
|
+
if (sharedSessionCookie) {
|
|
99
|
+
const isDifferentSessionActive = sharedSessionCookie.sub != (session == null ? void 0 : session.getTokenData().sub);
|
|
100
|
+
const noSessionActive = !session && !this.urlHasThirdPartyLoginData();
|
|
101
|
+
if (isDifferentSessionActive || noSessionActive)
|
|
102
|
+
this.startThirdPartyLogin(sharedSessionCookie);
|
|
103
|
+
} else if (session)
|
|
104
|
+
await this.logout();
|
|
105
|
+
}
|
|
78
106
|
hasSession() {
|
|
79
107
|
return !!this.current && !this.current.isExpired();
|
|
80
108
|
}
|
|
@@ -87,7 +115,6 @@ const _SessionManager = class _SessionManager {
|
|
|
87
115
|
}
|
|
88
116
|
endSession(redirectToLogin = true) {
|
|
89
117
|
this.setSession(void 0);
|
|
90
|
-
localStorage.removeItem(sessionKey);
|
|
91
118
|
if (redirectToLogin)
|
|
92
119
|
window.location.href = this.config.loginUrl;
|
|
93
120
|
}
|
|
@@ -99,7 +126,6 @@ const _SessionManager = class _SessionManager {
|
|
|
99
126
|
console.error(`Could not logout from IDM.
|
|
100
127
|
${error}`);
|
|
101
128
|
}
|
|
102
|
-
removeCookie(loginEmailKey);
|
|
103
129
|
this.endSession();
|
|
104
130
|
}
|
|
105
131
|
async startThirdPartyLogin(data) {
|
|
@@ -122,15 +148,14 @@ ${error}`);
|
|
|
122
148
|
}
|
|
123
149
|
const { session, data: { from, finalRedirect } } = await this.auth.completeThirdPartyLogin(location.search);
|
|
124
150
|
this.setSession(session);
|
|
125
|
-
this.persistSessionEmailCookie();
|
|
126
151
|
if (finalRedirect)
|
|
127
152
|
location.href = finalRedirect;
|
|
128
153
|
history.replaceState(null, "", from || location.toString().replace(/\?.*$/, ""));
|
|
129
154
|
this.sendLoginEventRd((_a = this.current) == null ? void 0 : _a.getTokenData().email, (_b = this.current) == null ? void 0 : _b.getTokenData().name);
|
|
130
155
|
}
|
|
131
156
|
getEmailForLogin() {
|
|
132
|
-
|
|
133
|
-
return (
|
|
157
|
+
const session = sessionCookie.get();
|
|
158
|
+
return (session == null ? void 0 : session.type) == "sso" ? session.email : void 0;
|
|
134
159
|
}
|
|
135
160
|
onChange(listener) {
|
|
136
161
|
this.changeListeners.push(listener);
|
|
@@ -140,10 +165,16 @@ ${error}`);
|
|
|
140
165
|
this.changeListeners.splice(index, 1);
|
|
141
166
|
};
|
|
142
167
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
168
|
+
setSessionCookie(session) {
|
|
169
|
+
const { email, account_type, sub } = session.getTokenData();
|
|
170
|
+
if (!email || !sub)
|
|
171
|
+
return;
|
|
172
|
+
const isFreemium = account_type == "FREEMIUM";
|
|
173
|
+
if (isFreemium) {
|
|
174
|
+
sessionCookie.set({ type: "idp", provider: "external-idp:github", sub });
|
|
175
|
+
} else {
|
|
176
|
+
sessionCookie.set({ email, type: "sso", sub });
|
|
177
|
+
}
|
|
147
178
|
}
|
|
148
179
|
async sendLoginEventRd(email, name) {
|
|
149
180
|
if (!this.config.rdUrl)
|
package/out/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/utils/cookies.ts","../src/SessionManager.ts","../src/hooks.ts"],"sourcesContent":["const portalUrl = new URL(location.href)\nconst cookieDomain = portalUrl.host.replace('app', '').replace('edp', '').split(':')[0]\nconst defaultCookieAttributes = `domain=${cookieDomain}; SameSite=Strict;`\n\nexport const setCookie = (key: string, value: string) => {\n document.cookie = `${key}=${value}; ${defaultCookieAttributes}`\n}\n\nexport const removeCookie = (key: string) => {\n document.cookie = `${key}=; max-age=0; ${defaultCookieAttributes}`\n}\n\nexport const getCookie = (key: string) => getCookies()[key]\n\nexport const getCookies = (): Record<string, string> => document.cookie.split('; ').reduce((prev, current) => {\n const [name, ...value] = current.split('=')\n prev[name] = value.join('=')\n return prev\n}, {} as Record<string, string>)\n","import { AuthConfig, AuthManager, Session, ThirdPartyLoginParams } from '@stack-spot/auth'\nimport { getCookie, removeCookie, setCookie } from \"./utils/cookies\"\n\nconst sessionKey = 'session'\nconst loginEmailKey = 'sessionEmail'\n\ninterface SessionManagerConfig extends Pick<AuthConfig, 'accountUrl' | 'authUrl' | 'clientId' | 'defaultTenant' | 'redirectUrl'> {\n loginUrl: string,\n rdUrl?: string,\n}\n\ntype AuthExtraData = { from?: string | null, finalRedirect?: string | null }\n\ntype ChangeListener = (session: Session | undefined) => void\n\n/**\n * Controls the current session in a browser.\n * \n * This should not be used under a Node.JS environment.\n * \n * This is a singleton. To create the first instance or recover the current one, use `SessionManager.create`.\n */\nexport class SessionManager {\n private current: Session | undefined\n readonly auth: AuthManager<AuthExtraData>\n private config: SessionManagerConfig\n private changeListeners: ChangeListener[] = []\n static instance: SessionManager\n\n private constructor(config: SessionManagerConfig) {\n this.config = config\n this.auth = new AuthManager<AuthExtraData>({\n ...config,\n authMigrationUrl: config.authUrl,\n migrationClientId: config.clientId,\n storage: localStorage,\n sessionPersistence: {\n load: () => localStorage.getItem(sessionKey),\n save: (session) => localStorage.setItem(sessionKey, session),\n },\n })\n SessionManager.instance = this\n }\n\n static create(config: SessionManagerConfig) {\n return SessionManager.instance ?? new SessionManager(config)\n }\n\n private setSession(session: Session | undefined) {\n this.current = session\n this.changeListeners.forEach(l => l(session))\n }\n\n async restoreSession() {\n const session = await this.auth.restoreSession()\n if (session) this.setSession(session)\n }\n\n hasSession() {\n return !!this.current && !this.current.isExpired()\n }\n\n getSession() {\n if (!this.hasSession()) {\n this.endSession()\n throw new Error('Session is not available, redirecting to login.')\n }\n return this.current!\n }\n\n endSession(redirectToLogin = true) {\n this.setSession(undefined)\n localStorage.removeItem(sessionKey)\n if (redirectToLogin) window.location.href = this.config.loginUrl\n }\n\n async logout() {\n try {\n await this.current?.logout()\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(`Could not logout from IDM.\\n${error}`)\n }\n removeCookie(loginEmailKey)\n this.endSession()\n }\n\n async startThirdPartyLogin(data: ThirdPartyLoginParams) {\n const params = new URLSearchParams(location.search)\n const authUrl = await this.auth.startThirdPartyLogin(data, {\n from: location.href,\n finalRedirect: params.get('finalRedirect'),\n })\n location.href = authUrl\n }\n\n urlHasThirdPartyLoginData() {\n const url = new URL(location.toString())\n return url.searchParams.has('state') && !url.searchParams.has('error')\n }\n\n async completeThirdPartyLogin() {\n const url = new URL(location.toString())\n if (url.searchParams.has('error')) {\n throw new Error(`Error while signing in: ${url.searchParams.get('error_description')}`)\n }\n const { session, data: { from, finalRedirect } } = await this.auth.completeThirdPartyLogin(location.search)\n this.setSession(session)\n this.persistSessionEmailCookie()\n if (finalRedirect) location.href = finalRedirect\n history.replaceState(null, '', from || location.toString().replace(/\\?.*$/, ''))\n this.sendLoginEventRd(this.current?.getTokenData().email, this.current?.getTokenData().name)\n }\n\n getEmailForLogin() {\n return getCookie(loginEmailKey) ?? ''\n }\n\n onChange(listener: ChangeListener) {\n this.changeListeners.push(listener)\n return () => {\n const index = this.changeListeners.indexOf(listener)\n if (index != -1) this.changeListeners.splice(index, 1)\n }\n }\n\n private persistSessionEmailCookie() {\n const sessionEmail = this.current?.getTokenData().email ?? ''\n setCookie(loginEmailKey, sessionEmail)\n }\n\n private async sendLoginEventRd(email?: string, name?: string) {\n if (!this.config.rdUrl) return\n if (!email && !name) {\n // eslint-disable-next-line no-console\n console.error('Unable to trigger login hook. No sessionEmail or name identified.')\n return\n }\n\n const rdObject = {\n event_type: 'CONVERSION',\n event_family: 'CDP',\n payload: {\n email,\n name,\n conversion_identifier: 'login-v1',\n },\n }\n\n const response = await fetch(this.config.rdUrl, {\n method: 'POST',\n body: JSON.stringify(rdObject),\n headers: {\n 'content-type': 'application/json',\n },\n })\n const data = await response.json()\n\n if (!response.ok) {\n // eslint-disable-next-line no-console\n console.error('Error while sending event to RD Station', data)\n }\n }\n}\n","import { Session } from '@stack-spot/auth'\nimport { useEffect, useState } from 'react'\nimport { SessionManager } from './SessionManager'\n\nexport function useSession() {\n const manager = SessionManager.instance\n const [session, setSession] = useState<Session | undefined>(manager?.hasSession() ? manager.getSession() : undefined)\n useEffect(() => {\n return manager?.onChange(setSession)\n }, [])\n return session\n}\n"],"names":["AuthManager","useState","useEffect"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/utils/cookies.ts","../src/SessionManager.ts","../src/hooks.ts"],"sourcesContent":["import { ThirdPartyLoginParams } from \"@stack-spot/auth\"\n\nconst sessionKey = 'stk-session'\nconst portalUrl = new URL(location.href)\nconst cookieDomain = portalUrl.host.replace('app', '').replace('edp', '').split(':')[0]\nconst defaultCookieAttributes = `domain=${cookieDomain}; SameSite=Strict;`\n\nconst setCookie = (key: string, value: string) => {\n document.cookie = `${key}=${value}; ${defaultCookieAttributes}`\n}\n\nconst removeCookie = (key: string) => {\n document.cookie = `${key}=; max-age=0; ${defaultCookieAttributes}`\n}\n\nconst getCookie = (key: string) => getCookies()[key]\n\nconst getCookies = (): Record<string, string> => document.cookie.split('; ').reduce((prev, current) => {\n const [name, ...value] = current.split('=')\n prev[name] = value.join('=')\n return prev\n}, {} as Record<string, string>)\n\n\ntype SessionCookie = ThirdPartyLoginParams & { sub: string }\nexport const sessionCookie = Object.freeze({\n set: (data: SessionCookie) => setCookie(sessionKey, JSON.stringify(data)),\n get: (): SessionCookie | undefined => {\n try {\n const cookie = getCookie(sessionKey)\n return cookie ? JSON.parse(cookie) : undefined\n } catch (error) {\n console.error(error)\n }\n },\n delete: () => removeCookie(sessionKey)\n})","import { AuthConfig, AuthManager, Session, ThirdPartyLoginParams } from '@stack-spot/auth'\nimport { sessionCookie } from './utils/cookies'\n\nconst sessionKey = 'session'\n\ninterface SessionManagerConfig extends Pick<AuthConfig, 'accountUrl' | 'authUrl' | 'clientId' | 'defaultTenant' | 'redirectUrl'> {\n loginUrl: string,\n rdUrl?: string,\n}\n\ntype AuthExtraData = { from?: string | null, finalRedirect?: string | null }\n\ntype ChangeListener = (session: Session | undefined) => void\n\n/**\n * Controls the current session in a browser.\n * \n * This should not be used under a Node.JS environment.\n * \n * This is a singleton. To create the first instance or recover the current one, use `SessionManager.create`.\n */\nexport class SessionManager {\n private current: Session | undefined\n readonly auth: AuthManager<AuthExtraData>\n private config: SessionManagerConfig\n private changeListeners: ChangeListener[] = []\n static instance: SessionManager\n\n private constructor(config: SessionManagerConfig) {\n this.config = config\n this.auth = new AuthManager<AuthExtraData>({\n ...config,\n storage: localStorage,\n sessionPersistence: {\n load: () => localStorage.getItem(sessionKey),\n save: (session) => localStorage.setItem(sessionKey, session),\n },\n })\n SessionManager.instance = this\n\n // Keep session in sync with other app's session\n addEventListener('focus', () => this.validateSharedSession())\n }\n\n static create(config: SessionManagerConfig) {\n return SessionManager.instance ?? new SessionManager(config)\n }\n\n private setSession(session: Session | undefined) {\n this.current = session\n if (session) this.setSessionCookie(session)\n else {\n localStorage.removeItem(sessionKey)\n sessionCookie.delete()\n }\n this.changeListeners.forEach(l => l(session))\n }\n\n async restoreSession() {\n const session = await this.auth.restoreSession()\n await this.validateSharedSession(session)\n if (session) this.setSession(session)\n }\n\n async validateSharedSession(session: Session | undefined = this.current) {\n const sharedSessionCookie = sessionCookie.get()\n if (sharedSessionCookie) {\n const isDifferentSessionActive = sharedSessionCookie.sub != session?.getTokenData().sub\n const noSessionActive = !session && !this.urlHasThirdPartyLoginData()\n if (isDifferentSessionActive || noSessionActive) this.startThirdPartyLogin(sharedSessionCookie)\n }\n else if (session) await this.logout()\n }\n\n hasSession() {\n return !!this.current && !this.current.isExpired()\n }\n\n getSession() {\n if (!this.hasSession()) {\n this.endSession()\n throw new Error('Session is not available, redirecting to login.')\n }\n return this.current!\n }\n\n endSession(redirectToLogin = true) {\n this.setSession(undefined)\n if (redirectToLogin) window.location.href = this.config.loginUrl\n }\n\n async logout() {\n try {\n await this.current?.logout()\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(`Could not logout from IDM.\\n${error}`)\n }\n this.endSession()\n }\n\n async startThirdPartyLogin(data: ThirdPartyLoginParams) {\n const params = new URLSearchParams(location.search)\n const authUrl = await this.auth.startThirdPartyLogin(data, {\n from: location.href,\n finalRedirect: params.get('finalRedirect'),\n })\n location.href = authUrl\n }\n\n urlHasThirdPartyLoginData() {\n const url = new URL(location.toString())\n return url.searchParams.has('state') && !url.searchParams.has('error')\n }\n\n async completeThirdPartyLogin() {\n const url = new URL(location.toString())\n if (url.searchParams.has('error')) {\n throw new Error(`Error while signing in: ${url.searchParams.get('error_description')}`)\n }\n const { session, data: { from, finalRedirect } } = await this.auth.completeThirdPartyLogin(location.search)\n this.setSession(session)\n if (finalRedirect) location.href = finalRedirect\n history.replaceState(null, '', from || location.toString().replace(/\\?.*$/, ''))\n this.sendLoginEventRd(this.current?.getTokenData().email, this.current?.getTokenData().name)\n }\n\n getEmailForLogin() {\n const session = sessionCookie.get()\n return session?.type == 'sso' ? session.email : undefined\n }\n\n onChange(listener: ChangeListener) {\n this.changeListeners.push(listener)\n return () => {\n const index = this.changeListeners.indexOf(listener)\n if (index != -1) this.changeListeners.splice(index, 1)\n }\n }\n\n private setSessionCookie(session: Session) {\n const { email, account_type, sub } = session.getTokenData()\n if (!email || !sub) return\n const isFreemium = account_type == 'FREEMIUM'\n if (isFreemium) {\n sessionCookie.set({ type: 'idp', provider: 'external-idp:github', sub })\n } else {\n sessionCookie.set({ email, type: 'sso', sub })\n }\n }\n\n private async sendLoginEventRd(email?: string, name?: string) {\n if (!this.config.rdUrl) return\n if (!email && !name) {\n // eslint-disable-next-line no-console\n console.error('Unable to trigger login hook. No sessionEmail or name identified.')\n return\n }\n\n const rdObject = {\n event_type: 'CONVERSION',\n event_family: 'CDP',\n payload: {\n email,\n name,\n conversion_identifier: 'login-v1',\n },\n }\n\n const response = await fetch(this.config.rdUrl, {\n method: 'POST',\n body: JSON.stringify(rdObject),\n headers: {\n 'content-type': 'application/json',\n },\n })\n const data = await response.json()\n\n if (!response.ok) {\n // eslint-disable-next-line no-console\n console.error('Error while sending event to RD Station', data)\n }\n }\n}\n","import { Session } from '@stack-spot/auth'\nimport { useEffect, useState } from 'react'\nimport { SessionManager } from './SessionManager'\n\nexport function useSession() {\n const manager = SessionManager.instance\n const [session, setSession] = useState<Session | undefined>(manager?.hasSession() ? manager.getSession() : undefined)\n useEffect(() => {\n return manager?.onChange(setSession)\n }, [])\n return session\n}\n"],"names":["sessionKey","AuthManager","useState","useEffect"],"mappings":"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"}
|
package/out/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AuthManager } from '@stack-spot/auth';
|
|
2
2
|
import { useState, useEffect } from 'react';
|
|
3
3
|
|
|
4
|
+
const sessionKey$1 = "stk-session";
|
|
4
5
|
const portalUrl = new URL(location.href);
|
|
5
6
|
const cookieDomain = portalUrl.host.replace("app", "").replace("edp", "").split(":")[0];
|
|
6
7
|
const defaultCookieAttributes = `domain=${cookieDomain}; SameSite=Strict;`;
|
|
@@ -16,6 +17,18 @@ const getCookies = () => document.cookie.split("; ").reduce((prev, current) => {
|
|
|
16
17
|
prev[name] = value.join("=");
|
|
17
18
|
return prev;
|
|
18
19
|
}, {});
|
|
20
|
+
const sessionCookie = Object.freeze({
|
|
21
|
+
set: (data) => setCookie(sessionKey$1, JSON.stringify(data)),
|
|
22
|
+
get: () => {
|
|
23
|
+
try {
|
|
24
|
+
const cookie = getCookie(sessionKey$1);
|
|
25
|
+
return cookie ? JSON.parse(cookie) : void 0;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error(error);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
delete: () => removeCookie(sessionKey$1)
|
|
31
|
+
});
|
|
19
32
|
|
|
20
33
|
var __defProp = Object.defineProperty;
|
|
21
34
|
var __defProps = Object.defineProperties;
|
|
@@ -41,7 +54,6 @@ var __publicField = (obj, key, value) => {
|
|
|
41
54
|
return value;
|
|
42
55
|
};
|
|
43
56
|
const sessionKey = "session";
|
|
44
|
-
const loginEmailKey = "sessionEmail";
|
|
45
57
|
const _SessionManager = class _SessionManager {
|
|
46
58
|
constructor(config) {
|
|
47
59
|
__publicField(this, "current");
|
|
@@ -50,8 +62,6 @@ const _SessionManager = class _SessionManager {
|
|
|
50
62
|
__publicField(this, "changeListeners", []);
|
|
51
63
|
this.config = config;
|
|
52
64
|
this.auth = new AuthManager(__spreadProps(__spreadValues({}, config), {
|
|
53
|
-
authMigrationUrl: config.authUrl,
|
|
54
|
-
migrationClientId: config.clientId,
|
|
55
65
|
storage: localStorage,
|
|
56
66
|
sessionPersistence: {
|
|
57
67
|
load: () => localStorage.getItem(sessionKey),
|
|
@@ -59,6 +69,7 @@ const _SessionManager = class _SessionManager {
|
|
|
59
69
|
}
|
|
60
70
|
}));
|
|
61
71
|
_SessionManager.instance = this;
|
|
72
|
+
addEventListener("focus", () => this.validateSharedSession());
|
|
62
73
|
}
|
|
63
74
|
static create(config) {
|
|
64
75
|
var _a;
|
|
@@ -66,13 +77,30 @@ const _SessionManager = class _SessionManager {
|
|
|
66
77
|
}
|
|
67
78
|
setSession(session) {
|
|
68
79
|
this.current = session;
|
|
80
|
+
if (session)
|
|
81
|
+
this.setSessionCookie(session);
|
|
82
|
+
else {
|
|
83
|
+
localStorage.removeItem(sessionKey);
|
|
84
|
+
sessionCookie.delete();
|
|
85
|
+
}
|
|
69
86
|
this.changeListeners.forEach((l) => l(session));
|
|
70
87
|
}
|
|
71
88
|
async restoreSession() {
|
|
72
89
|
const session = await this.auth.restoreSession();
|
|
90
|
+
await this.validateSharedSession(session);
|
|
73
91
|
if (session)
|
|
74
92
|
this.setSession(session);
|
|
75
93
|
}
|
|
94
|
+
async validateSharedSession(session = this.current) {
|
|
95
|
+
const sharedSessionCookie = sessionCookie.get();
|
|
96
|
+
if (sharedSessionCookie) {
|
|
97
|
+
const isDifferentSessionActive = sharedSessionCookie.sub != (session == null ? void 0 : session.getTokenData().sub);
|
|
98
|
+
const noSessionActive = !session && !this.urlHasThirdPartyLoginData();
|
|
99
|
+
if (isDifferentSessionActive || noSessionActive)
|
|
100
|
+
this.startThirdPartyLogin(sharedSessionCookie);
|
|
101
|
+
} else if (session)
|
|
102
|
+
await this.logout();
|
|
103
|
+
}
|
|
76
104
|
hasSession() {
|
|
77
105
|
return !!this.current && !this.current.isExpired();
|
|
78
106
|
}
|
|
@@ -85,7 +113,6 @@ const _SessionManager = class _SessionManager {
|
|
|
85
113
|
}
|
|
86
114
|
endSession(redirectToLogin = true) {
|
|
87
115
|
this.setSession(void 0);
|
|
88
|
-
localStorage.removeItem(sessionKey);
|
|
89
116
|
if (redirectToLogin)
|
|
90
117
|
window.location.href = this.config.loginUrl;
|
|
91
118
|
}
|
|
@@ -97,7 +124,6 @@ const _SessionManager = class _SessionManager {
|
|
|
97
124
|
console.error(`Could not logout from IDM.
|
|
98
125
|
${error}`);
|
|
99
126
|
}
|
|
100
|
-
removeCookie(loginEmailKey);
|
|
101
127
|
this.endSession();
|
|
102
128
|
}
|
|
103
129
|
async startThirdPartyLogin(data) {
|
|
@@ -120,15 +146,14 @@ ${error}`);
|
|
|
120
146
|
}
|
|
121
147
|
const { session, data: { from, finalRedirect } } = await this.auth.completeThirdPartyLogin(location.search);
|
|
122
148
|
this.setSession(session);
|
|
123
|
-
this.persistSessionEmailCookie();
|
|
124
149
|
if (finalRedirect)
|
|
125
150
|
location.href = finalRedirect;
|
|
126
151
|
history.replaceState(null, "", from || location.toString().replace(/\?.*$/, ""));
|
|
127
152
|
this.sendLoginEventRd((_a = this.current) == null ? void 0 : _a.getTokenData().email, (_b = this.current) == null ? void 0 : _b.getTokenData().name);
|
|
128
153
|
}
|
|
129
154
|
getEmailForLogin() {
|
|
130
|
-
|
|
131
|
-
return (
|
|
155
|
+
const session = sessionCookie.get();
|
|
156
|
+
return (session == null ? void 0 : session.type) == "sso" ? session.email : void 0;
|
|
132
157
|
}
|
|
133
158
|
onChange(listener) {
|
|
134
159
|
this.changeListeners.push(listener);
|
|
@@ -138,10 +163,16 @@ ${error}`);
|
|
|
138
163
|
this.changeListeners.splice(index, 1);
|
|
139
164
|
};
|
|
140
165
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
166
|
+
setSessionCookie(session) {
|
|
167
|
+
const { email, account_type, sub } = session.getTokenData();
|
|
168
|
+
if (!email || !sub)
|
|
169
|
+
return;
|
|
170
|
+
const isFreemium = account_type == "FREEMIUM";
|
|
171
|
+
if (isFreemium) {
|
|
172
|
+
sessionCookie.set({ type: "idp", provider: "external-idp:github", sub });
|
|
173
|
+
} else {
|
|
174
|
+
sessionCookie.set({ email, type: "sso", sub });
|
|
175
|
+
}
|
|
145
176
|
}
|
|
146
177
|
async sendLoginEventRd(email, name) {
|
|
147
178
|
if (!this.config.rdUrl)
|
package/out/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/utils/cookies.ts","../src/SessionManager.ts","../src/hooks.ts"],"sourcesContent":["const portalUrl = new URL(location.href)\nconst cookieDomain = portalUrl.host.replace('app', '').replace('edp', '').split(':')[0]\nconst defaultCookieAttributes = `domain=${cookieDomain}; SameSite=Strict;`\n\nexport const setCookie = (key: string, value: string) => {\n document.cookie = `${key}=${value}; ${defaultCookieAttributes}`\n}\n\nexport const removeCookie = (key: string) => {\n document.cookie = `${key}=; max-age=0; ${defaultCookieAttributes}`\n}\n\nexport const getCookie = (key: string) => getCookies()[key]\n\nexport const getCookies = (): Record<string, string> => document.cookie.split('; ').reduce((prev, current) => {\n const [name, ...value] = current.split('=')\n prev[name] = value.join('=')\n return prev\n}, {} as Record<string, string>)\n","import { AuthConfig, AuthManager, Session, ThirdPartyLoginParams } from '@stack-spot/auth'\nimport { getCookie, removeCookie, setCookie } from \"./utils/cookies\"\n\nconst sessionKey = 'session'\nconst loginEmailKey = 'sessionEmail'\n\ninterface SessionManagerConfig extends Pick<AuthConfig, 'accountUrl' | 'authUrl' | 'clientId' | 'defaultTenant' | 'redirectUrl'> {\n loginUrl: string,\n rdUrl?: string,\n}\n\ntype AuthExtraData = { from?: string | null, finalRedirect?: string | null }\n\ntype ChangeListener = (session: Session | undefined) => void\n\n/**\n * Controls the current session in a browser.\n * \n * This should not be used under a Node.JS environment.\n * \n * This is a singleton. To create the first instance or recover the current one, use `SessionManager.create`.\n */\nexport class SessionManager {\n private current: Session | undefined\n readonly auth: AuthManager<AuthExtraData>\n private config: SessionManagerConfig\n private changeListeners: ChangeListener[] = []\n static instance: SessionManager\n\n private constructor(config: SessionManagerConfig) {\n this.config = config\n this.auth = new AuthManager<AuthExtraData>({\n ...config,\n authMigrationUrl: config.authUrl,\n migrationClientId: config.clientId,\n storage: localStorage,\n sessionPersistence: {\n load: () => localStorage.getItem(sessionKey),\n save: (session) => localStorage.setItem(sessionKey, session),\n },\n })\n SessionManager.instance = this\n }\n\n static create(config: SessionManagerConfig) {\n return SessionManager.instance ?? new SessionManager(config)\n }\n\n private setSession(session: Session | undefined) {\n this.current = session\n this.changeListeners.forEach(l => l(session))\n }\n\n async restoreSession() {\n const session = await this.auth.restoreSession()\n if (session) this.setSession(session)\n }\n\n hasSession() {\n return !!this.current && !this.current.isExpired()\n }\n\n getSession() {\n if (!this.hasSession()) {\n this.endSession()\n throw new Error('Session is not available, redirecting to login.')\n }\n return this.current!\n }\n\n endSession(redirectToLogin = true) {\n this.setSession(undefined)\n localStorage.removeItem(sessionKey)\n if (redirectToLogin) window.location.href = this.config.loginUrl\n }\n\n async logout() {\n try {\n await this.current?.logout()\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(`Could not logout from IDM.\\n${error}`)\n }\n removeCookie(loginEmailKey)\n this.endSession()\n }\n\n async startThirdPartyLogin(data: ThirdPartyLoginParams) {\n const params = new URLSearchParams(location.search)\n const authUrl = await this.auth.startThirdPartyLogin(data, {\n from: location.href,\n finalRedirect: params.get('finalRedirect'),\n })\n location.href = authUrl\n }\n\n urlHasThirdPartyLoginData() {\n const url = new URL(location.toString())\n return url.searchParams.has('state') && !url.searchParams.has('error')\n }\n\n async completeThirdPartyLogin() {\n const url = new URL(location.toString())\n if (url.searchParams.has('error')) {\n throw new Error(`Error while signing in: ${url.searchParams.get('error_description')}`)\n }\n const { session, data: { from, finalRedirect } } = await this.auth.completeThirdPartyLogin(location.search)\n this.setSession(session)\n this.persistSessionEmailCookie()\n if (finalRedirect) location.href = finalRedirect\n history.replaceState(null, '', from || location.toString().replace(/\\?.*$/, ''))\n this.sendLoginEventRd(this.current?.getTokenData().email, this.current?.getTokenData().name)\n }\n\n getEmailForLogin() {\n return getCookie(loginEmailKey) ?? ''\n }\n\n onChange(listener: ChangeListener) {\n this.changeListeners.push(listener)\n return () => {\n const index = this.changeListeners.indexOf(listener)\n if (index != -1) this.changeListeners.splice(index, 1)\n }\n }\n\n private persistSessionEmailCookie() {\n const sessionEmail = this.current?.getTokenData().email ?? ''\n setCookie(loginEmailKey, sessionEmail)\n }\n\n private async sendLoginEventRd(email?: string, name?: string) {\n if (!this.config.rdUrl) return\n if (!email && !name) {\n // eslint-disable-next-line no-console\n console.error('Unable to trigger login hook. No sessionEmail or name identified.')\n return\n }\n\n const rdObject = {\n event_type: 'CONVERSION',\n event_family: 'CDP',\n payload: {\n email,\n name,\n conversion_identifier: 'login-v1',\n },\n }\n\n const response = await fetch(this.config.rdUrl, {\n method: 'POST',\n body: JSON.stringify(rdObject),\n headers: {\n 'content-type': 'application/json',\n },\n })\n const data = await response.json()\n\n if (!response.ok) {\n // eslint-disable-next-line no-console\n console.error('Error while sending event to RD Station', data)\n }\n }\n}\n","import { Session } from '@stack-spot/auth'\nimport { useEffect, useState } from 'react'\nimport { SessionManager } from './SessionManager'\n\nexport function useSession() {\n const manager = SessionManager.instance\n const [session, setSession] = useState<Session | undefined>(manager?.hasSession() ? manager.getSession() : undefined)\n useEffect(() => {\n return manager?.onChange(setSession)\n }, [])\n return session\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/utils/cookies.ts","../src/SessionManager.ts","../src/hooks.ts"],"sourcesContent":["import { ThirdPartyLoginParams } from \"@stack-spot/auth\"\n\nconst sessionKey = 'stk-session'\nconst portalUrl = new URL(location.href)\nconst cookieDomain = portalUrl.host.replace('app', '').replace('edp', '').split(':')[0]\nconst defaultCookieAttributes = `domain=${cookieDomain}; SameSite=Strict;`\n\nconst setCookie = (key: string, value: string) => {\n document.cookie = `${key}=${value}; ${defaultCookieAttributes}`\n}\n\nconst removeCookie = (key: string) => {\n document.cookie = `${key}=; max-age=0; ${defaultCookieAttributes}`\n}\n\nconst getCookie = (key: string) => getCookies()[key]\n\nconst getCookies = (): Record<string, string> => document.cookie.split('; ').reduce((prev, current) => {\n const [name, ...value] = current.split('=')\n prev[name] = value.join('=')\n return prev\n}, {} as Record<string, string>)\n\n\ntype SessionCookie = ThirdPartyLoginParams & { sub: string }\nexport const sessionCookie = Object.freeze({\n set: (data: SessionCookie) => setCookie(sessionKey, JSON.stringify(data)),\n get: (): SessionCookie | undefined => {\n try {\n const cookie = getCookie(sessionKey)\n return cookie ? JSON.parse(cookie) : undefined\n } catch (error) {\n console.error(error)\n }\n },\n delete: () => removeCookie(sessionKey)\n})","import { AuthConfig, AuthManager, Session, ThirdPartyLoginParams } from '@stack-spot/auth'\nimport { sessionCookie } from './utils/cookies'\n\nconst sessionKey = 'session'\n\ninterface SessionManagerConfig extends Pick<AuthConfig, 'accountUrl' | 'authUrl' | 'clientId' | 'defaultTenant' | 'redirectUrl'> {\n loginUrl: string,\n rdUrl?: string,\n}\n\ntype AuthExtraData = { from?: string | null, finalRedirect?: string | null }\n\ntype ChangeListener = (session: Session | undefined) => void\n\n/**\n * Controls the current session in a browser.\n * \n * This should not be used under a Node.JS environment.\n * \n * This is a singleton. To create the first instance or recover the current one, use `SessionManager.create`.\n */\nexport class SessionManager {\n private current: Session | undefined\n readonly auth: AuthManager<AuthExtraData>\n private config: SessionManagerConfig\n private changeListeners: ChangeListener[] = []\n static instance: SessionManager\n\n private constructor(config: SessionManagerConfig) {\n this.config = config\n this.auth = new AuthManager<AuthExtraData>({\n ...config,\n storage: localStorage,\n sessionPersistence: {\n load: () => localStorage.getItem(sessionKey),\n save: (session) => localStorage.setItem(sessionKey, session),\n },\n })\n SessionManager.instance = this\n\n // Keep session in sync with other app's session\n addEventListener('focus', () => this.validateSharedSession())\n }\n\n static create(config: SessionManagerConfig) {\n return SessionManager.instance ?? new SessionManager(config)\n }\n\n private setSession(session: Session | undefined) {\n this.current = session\n if (session) this.setSessionCookie(session)\n else {\n localStorage.removeItem(sessionKey)\n sessionCookie.delete()\n }\n this.changeListeners.forEach(l => l(session))\n }\n\n async restoreSession() {\n const session = await this.auth.restoreSession()\n await this.validateSharedSession(session)\n if (session) this.setSession(session)\n }\n\n async validateSharedSession(session: Session | undefined = this.current) {\n const sharedSessionCookie = sessionCookie.get()\n if (sharedSessionCookie) {\n const isDifferentSessionActive = sharedSessionCookie.sub != session?.getTokenData().sub\n const noSessionActive = !session && !this.urlHasThirdPartyLoginData()\n if (isDifferentSessionActive || noSessionActive) this.startThirdPartyLogin(sharedSessionCookie)\n }\n else if (session) await this.logout()\n }\n\n hasSession() {\n return !!this.current && !this.current.isExpired()\n }\n\n getSession() {\n if (!this.hasSession()) {\n this.endSession()\n throw new Error('Session is not available, redirecting to login.')\n }\n return this.current!\n }\n\n endSession(redirectToLogin = true) {\n this.setSession(undefined)\n if (redirectToLogin) window.location.href = this.config.loginUrl\n }\n\n async logout() {\n try {\n await this.current?.logout()\n } catch (error) {\n // eslint-disable-next-line no-console\n console.error(`Could not logout from IDM.\\n${error}`)\n }\n this.endSession()\n }\n\n async startThirdPartyLogin(data: ThirdPartyLoginParams) {\n const params = new URLSearchParams(location.search)\n const authUrl = await this.auth.startThirdPartyLogin(data, {\n from: location.href,\n finalRedirect: params.get('finalRedirect'),\n })\n location.href = authUrl\n }\n\n urlHasThirdPartyLoginData() {\n const url = new URL(location.toString())\n return url.searchParams.has('state') && !url.searchParams.has('error')\n }\n\n async completeThirdPartyLogin() {\n const url = new URL(location.toString())\n if (url.searchParams.has('error')) {\n throw new Error(`Error while signing in: ${url.searchParams.get('error_description')}`)\n }\n const { session, data: { from, finalRedirect } } = await this.auth.completeThirdPartyLogin(location.search)\n this.setSession(session)\n if (finalRedirect) location.href = finalRedirect\n history.replaceState(null, '', from || location.toString().replace(/\\?.*$/, ''))\n this.sendLoginEventRd(this.current?.getTokenData().email, this.current?.getTokenData().name)\n }\n\n getEmailForLogin() {\n const session = sessionCookie.get()\n return session?.type == 'sso' ? session.email : undefined\n }\n\n onChange(listener: ChangeListener) {\n this.changeListeners.push(listener)\n return () => {\n const index = this.changeListeners.indexOf(listener)\n if (index != -1) this.changeListeners.splice(index, 1)\n }\n }\n\n private setSessionCookie(session: Session) {\n const { email, account_type, sub } = session.getTokenData()\n if (!email || !sub) return\n const isFreemium = account_type == 'FREEMIUM'\n if (isFreemium) {\n sessionCookie.set({ type: 'idp', provider: 'external-idp:github', sub })\n } else {\n sessionCookie.set({ email, type: 'sso', sub })\n }\n }\n\n private async sendLoginEventRd(email?: string, name?: string) {\n if (!this.config.rdUrl) return\n if (!email && !name) {\n // eslint-disable-next-line no-console\n console.error('Unable to trigger login hook. No sessionEmail or name identified.')\n return\n }\n\n const rdObject = {\n event_type: 'CONVERSION',\n event_family: 'CDP',\n payload: {\n email,\n name,\n conversion_identifier: 'login-v1',\n },\n }\n\n const response = await fetch(this.config.rdUrl, {\n method: 'POST',\n body: JSON.stringify(rdObject),\n headers: {\n 'content-type': 'application/json',\n },\n })\n const data = await response.json()\n\n if (!response.ok) {\n // eslint-disable-next-line no-console\n console.error('Error while sending event to RD Station', data)\n }\n }\n}\n","import { Session } from '@stack-spot/auth'\nimport { useEffect, useState } from 'react'\nimport { SessionManager } from './SessionManager'\n\nexport function useSession() {\n const manager = SessionManager.instance\n const [session, setSession] = useState<Session | undefined>(manager?.hasSession() ? manager.getSession() : undefined)\n useEffect(() => {\n return manager?.onChange(setSession)\n }, [])\n return session\n}\n"],"names":["sessionKey"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stack-spot/auth-react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"main": "out/index.js",
|
|
5
5
|
"module": "out/index.mjs",
|
|
6
6
|
"typings": "out/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"compile": "rollup -c",
|
|
9
|
+
"build": "pnpm compile"
|
|
10
|
+
},
|
|
7
11
|
"devDependencies": {
|
|
8
12
|
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
|
9
13
|
"@typescript-eslint/parser": "^6.21.0",
|
|
@@ -19,10 +23,6 @@
|
|
|
19
23
|
"peerDependencies": {
|
|
20
24
|
"react": ">=18.2.0",
|
|
21
25
|
"react-dom": ">=18.2.0",
|
|
22
|
-
"@stack-spot/auth": ">=
|
|
23
|
-
},
|
|
24
|
-
"scripts": {
|
|
25
|
-
"compile": "rollup -c",
|
|
26
|
-
"build": "pnpm compile"
|
|
26
|
+
"@stack-spot/auth": ">=5.0.0"
|
|
27
27
|
}
|
|
28
28
|
}
|
package/src/SessionManager.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { AuthConfig, AuthManager, Session, ThirdPartyLoginParams } from '@stack-spot/auth'
|
|
2
|
-
import {
|
|
2
|
+
import { sessionCookie } from './utils/cookies'
|
|
3
3
|
|
|
4
4
|
const sessionKey = 'session'
|
|
5
|
-
const loginEmailKey = 'sessionEmail'
|
|
6
5
|
|
|
7
6
|
interface SessionManagerConfig extends Pick<AuthConfig, 'accountUrl' | 'authUrl' | 'clientId' | 'defaultTenant' | 'redirectUrl'> {
|
|
8
7
|
loginUrl: string,
|
|
@@ -31,8 +30,6 @@ export class SessionManager {
|
|
|
31
30
|
this.config = config
|
|
32
31
|
this.auth = new AuthManager<AuthExtraData>({
|
|
33
32
|
...config,
|
|
34
|
-
authMigrationUrl: config.authUrl,
|
|
35
|
-
migrationClientId: config.clientId,
|
|
36
33
|
storage: localStorage,
|
|
37
34
|
sessionPersistence: {
|
|
38
35
|
load: () => localStorage.getItem(sessionKey),
|
|
@@ -40,6 +37,9 @@ export class SessionManager {
|
|
|
40
37
|
},
|
|
41
38
|
})
|
|
42
39
|
SessionManager.instance = this
|
|
40
|
+
|
|
41
|
+
// Keep session in sync with other app's session
|
|
42
|
+
addEventListener('focus', () => this.validateSharedSession())
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
static create(config: SessionManagerConfig) {
|
|
@@ -48,14 +48,30 @@ export class SessionManager {
|
|
|
48
48
|
|
|
49
49
|
private setSession(session: Session | undefined) {
|
|
50
50
|
this.current = session
|
|
51
|
+
if (session) this.setSessionCookie(session)
|
|
52
|
+
else {
|
|
53
|
+
localStorage.removeItem(sessionKey)
|
|
54
|
+
sessionCookie.delete()
|
|
55
|
+
}
|
|
51
56
|
this.changeListeners.forEach(l => l(session))
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
async restoreSession() {
|
|
55
60
|
const session = await this.auth.restoreSession()
|
|
61
|
+
await this.validateSharedSession(session)
|
|
56
62
|
if (session) this.setSession(session)
|
|
57
63
|
}
|
|
58
64
|
|
|
65
|
+
async validateSharedSession(session: Session | undefined = this.current) {
|
|
66
|
+
const sharedSessionCookie = sessionCookie.get()
|
|
67
|
+
if (sharedSessionCookie) {
|
|
68
|
+
const isDifferentSessionActive = sharedSessionCookie.sub != session?.getTokenData().sub
|
|
69
|
+
const noSessionActive = !session && !this.urlHasThirdPartyLoginData()
|
|
70
|
+
if (isDifferentSessionActive || noSessionActive) this.startThirdPartyLogin(sharedSessionCookie)
|
|
71
|
+
}
|
|
72
|
+
else if (session) await this.logout()
|
|
73
|
+
}
|
|
74
|
+
|
|
59
75
|
hasSession() {
|
|
60
76
|
return !!this.current && !this.current.isExpired()
|
|
61
77
|
}
|
|
@@ -70,7 +86,6 @@ export class SessionManager {
|
|
|
70
86
|
|
|
71
87
|
endSession(redirectToLogin = true) {
|
|
72
88
|
this.setSession(undefined)
|
|
73
|
-
localStorage.removeItem(sessionKey)
|
|
74
89
|
if (redirectToLogin) window.location.href = this.config.loginUrl
|
|
75
90
|
}
|
|
76
91
|
|
|
@@ -81,7 +96,6 @@ export class SessionManager {
|
|
|
81
96
|
// eslint-disable-next-line no-console
|
|
82
97
|
console.error(`Could not logout from IDM.\n${error}`)
|
|
83
98
|
}
|
|
84
|
-
removeCookie(loginEmailKey)
|
|
85
99
|
this.endSession()
|
|
86
100
|
}
|
|
87
101
|
|
|
@@ -106,14 +120,14 @@ export class SessionManager {
|
|
|
106
120
|
}
|
|
107
121
|
const { session, data: { from, finalRedirect } } = await this.auth.completeThirdPartyLogin(location.search)
|
|
108
122
|
this.setSession(session)
|
|
109
|
-
this.persistSessionEmailCookie()
|
|
110
123
|
if (finalRedirect) location.href = finalRedirect
|
|
111
124
|
history.replaceState(null, '', from || location.toString().replace(/\?.*$/, ''))
|
|
112
125
|
this.sendLoginEventRd(this.current?.getTokenData().email, this.current?.getTokenData().name)
|
|
113
126
|
}
|
|
114
127
|
|
|
115
128
|
getEmailForLogin() {
|
|
116
|
-
|
|
129
|
+
const session = sessionCookie.get()
|
|
130
|
+
return session?.type == 'sso' ? session.email : undefined
|
|
117
131
|
}
|
|
118
132
|
|
|
119
133
|
onChange(listener: ChangeListener) {
|
|
@@ -124,9 +138,15 @@ export class SessionManager {
|
|
|
124
138
|
}
|
|
125
139
|
}
|
|
126
140
|
|
|
127
|
-
private
|
|
128
|
-
const
|
|
129
|
-
|
|
141
|
+
private setSessionCookie(session: Session) {
|
|
142
|
+
const { email, account_type, sub } = session.getTokenData()
|
|
143
|
+
if (!email || !sub) return
|
|
144
|
+
const isFreemium = account_type == 'FREEMIUM'
|
|
145
|
+
if (isFreemium) {
|
|
146
|
+
sessionCookie.set({ type: 'idp', provider: 'external-idp:github', sub })
|
|
147
|
+
} else {
|
|
148
|
+
sessionCookie.set({ email, type: 'sso', sub })
|
|
149
|
+
}
|
|
130
150
|
}
|
|
131
151
|
|
|
132
152
|
private async sendLoginEventRd(email?: string, name?: string) {
|
package/src/utils/cookies.ts
CHANGED
|
@@ -1,19 +1,37 @@
|
|
|
1
|
+
import { ThirdPartyLoginParams } from "@stack-spot/auth"
|
|
2
|
+
|
|
3
|
+
const sessionKey = 'stk-session'
|
|
1
4
|
const portalUrl = new URL(location.href)
|
|
2
5
|
const cookieDomain = portalUrl.host.replace('app', '').replace('edp', '').split(':')[0]
|
|
3
6
|
const defaultCookieAttributes = `domain=${cookieDomain}; SameSite=Strict;`
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
const setCookie = (key: string, value: string) => {
|
|
6
9
|
document.cookie = `${key}=${value}; ${defaultCookieAttributes}`
|
|
7
10
|
}
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
const removeCookie = (key: string) => {
|
|
10
13
|
document.cookie = `${key}=; max-age=0; ${defaultCookieAttributes}`
|
|
11
14
|
}
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
const getCookie = (key: string) => getCookies()[key]
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
const getCookies = (): Record<string, string> => document.cookie.split('; ').reduce((prev, current) => {
|
|
16
19
|
const [name, ...value] = current.split('=')
|
|
17
20
|
prev[name] = value.join('=')
|
|
18
21
|
return prev
|
|
19
22
|
}, {} as Record<string, string>)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
type SessionCookie = ThirdPartyLoginParams & { sub: string }
|
|
26
|
+
export const sessionCookie = Object.freeze({
|
|
27
|
+
set: (data: SessionCookie) => setCookie(sessionKey, JSON.stringify(data)),
|
|
28
|
+
get: (): SessionCookie | undefined => {
|
|
29
|
+
try {
|
|
30
|
+
const cookie = getCookie(sessionKey)
|
|
31
|
+
return cookie ? JSON.parse(cookie) : undefined
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(error)
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
delete: () => removeCookie(sessionKey)
|
|
37
|
+
})
|