@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 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
@@ -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,2 @@
1
+ export { SessionManager } from './SessionManager'
2
+ export { useSession } from './hooks'
@@ -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>)
package/tsconfig.json ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src"
5
+ },
6
+ "include": ["src"]
7
+ }