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