@stack-spot/auth-react 1.0.0 → 1.1.1

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 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 persistSessionEmailCookie;
39
+ private setSessionCookie;
39
40
  private sendLoginEventRd;
40
41
  }
41
42
 
package/out/index.js CHANGED
@@ -3,8 +3,11 @@
3
3
  var auth = require('@stack-spot/auth');
4
4
  var react = require('react');
5
5
 
6
+ var _a;
7
+ const sessionKey$1 = "stk-session";
6
8
  const portalUrl = new URL(location.href);
7
- const cookieDomain = portalUrl.host.replace("app", "").replace("edp", "").split(":")[0];
9
+ const domainRegex = new RegExp(/(\.*(prd|stg|dev)*.stackspot.com)|localhost/);
10
+ const cookieDomain = (_a = domainRegex.exec(portalUrl.host)) == null ? void 0 : _a[0];
8
11
  const defaultCookieAttributes = `domain=${cookieDomain}; SameSite=Strict;`;
9
12
  const setCookie = (key, value) => {
10
13
  document.cookie = `${key}=${value}; ${defaultCookieAttributes}`;
@@ -18,6 +21,18 @@ const getCookies = () => document.cookie.split("; ").reduce((prev, current) => {
18
21
  prev[name] = value.join("=");
19
22
  return prev;
20
23
  }, {});
24
+ const sessionCookie = Object.freeze({
25
+ set: (data) => setCookie(sessionKey$1, JSON.stringify(data)),
26
+ get: () => {
27
+ try {
28
+ const cookie = getCookie(sessionKey$1);
29
+ return cookie ? JSON.parse(cookie) : void 0;
30
+ } catch (error) {
31
+ console.error(error);
32
+ }
33
+ },
34
+ delete: () => removeCookie(sessionKey$1)
35
+ });
21
36
 
22
37
  var __defProp = Object.defineProperty;
23
38
  var __defProps = Object.defineProperties;
@@ -43,7 +58,6 @@ var __publicField = (obj, key, value) => {
43
58
  return value;
44
59
  };
45
60
  const sessionKey = "session";
46
- const loginEmailKey = "sessionEmail";
47
61
  const _SessionManager = class _SessionManager {
48
62
  constructor(config) {
49
63
  __publicField(this, "current");
@@ -52,8 +66,6 @@ const _SessionManager = class _SessionManager {
52
66
  __publicField(this, "changeListeners", []);
53
67
  this.config = config;
54
68
  this.auth = new auth.AuthManager(__spreadProps(__spreadValues({}, config), {
55
- authMigrationUrl: config.authUrl,
56
- migrationClientId: config.clientId,
57
69
  storage: localStorage,
58
70
  sessionPersistence: {
59
71
  load: () => localStorage.getItem(sessionKey),
@@ -61,6 +73,7 @@ const _SessionManager = class _SessionManager {
61
73
  }
62
74
  }));
63
75
  _SessionManager.instance = this;
76
+ addEventListener("focus", () => this.validateSharedSession());
64
77
  }
65
78
  static create(config) {
66
79
  var _a;
@@ -68,13 +81,30 @@ const _SessionManager = class _SessionManager {
68
81
  }
69
82
  setSession(session) {
70
83
  this.current = session;
84
+ if (session)
85
+ this.setSessionCookie(session);
86
+ else {
87
+ localStorage.removeItem(sessionKey);
88
+ sessionCookie.delete();
89
+ }
71
90
  this.changeListeners.forEach((l) => l(session));
72
91
  }
73
92
  async restoreSession() {
74
93
  const session = await this.auth.restoreSession();
94
+ await this.validateSharedSession(session);
75
95
  if (session)
76
96
  this.setSession(session);
77
97
  }
98
+ async validateSharedSession(session = this.current) {
99
+ const sharedSessionCookie = sessionCookie.get();
100
+ if (sharedSessionCookie) {
101
+ const isDifferentSessionActive = sharedSessionCookie.sub != (session == null ? void 0 : session.getTokenData().sub);
102
+ const noSessionActive = !session && !this.urlHasThirdPartyLoginData();
103
+ if (isDifferentSessionActive || noSessionActive)
104
+ this.startThirdPartyLogin(sharedSessionCookie);
105
+ } else if (session)
106
+ await this.logout();
107
+ }
78
108
  hasSession() {
79
109
  return !!this.current && !this.current.isExpired();
80
110
  }
@@ -87,7 +117,6 @@ const _SessionManager = class _SessionManager {
87
117
  }
88
118
  endSession(redirectToLogin = true) {
89
119
  this.setSession(void 0);
90
- localStorage.removeItem(sessionKey);
91
120
  if (redirectToLogin)
92
121
  window.location.href = this.config.loginUrl;
93
122
  }
@@ -99,7 +128,6 @@ const _SessionManager = class _SessionManager {
99
128
  console.error(`Could not logout from IDM.
100
129
  ${error}`);
101
130
  }
102
- removeCookie(loginEmailKey);
103
131
  this.endSession();
104
132
  }
105
133
  async startThirdPartyLogin(data) {
@@ -122,15 +150,14 @@ ${error}`);
122
150
  }
123
151
  const { session, data: { from, finalRedirect } } = await this.auth.completeThirdPartyLogin(location.search);
124
152
  this.setSession(session);
125
- this.persistSessionEmailCookie();
126
153
  if (finalRedirect)
127
154
  location.href = finalRedirect;
128
155
  history.replaceState(null, "", from || location.toString().replace(/\?.*$/, ""));
129
156
  this.sendLoginEventRd((_a = this.current) == null ? void 0 : _a.getTokenData().email, (_b = this.current) == null ? void 0 : _b.getTokenData().name);
130
157
  }
131
158
  getEmailForLogin() {
132
- var _a;
133
- return (_a = getCookie(loginEmailKey)) != null ? _a : "";
159
+ const session = sessionCookie.get();
160
+ return (session == null ? void 0 : session.type) == "sso" ? session.email : void 0;
134
161
  }
135
162
  onChange(listener) {
136
163
  this.changeListeners.push(listener);
@@ -140,10 +167,16 @@ ${error}`);
140
167
  this.changeListeners.splice(index, 1);
141
168
  };
142
169
  }
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);
170
+ setSessionCookie(session) {
171
+ const { email, account_type, sub } = session.getTokenData();
172
+ if (!email || !sub)
173
+ return;
174
+ const isFreemium = account_type == "FREEMIUM";
175
+ if (isFreemium) {
176
+ sessionCookie.set({ type: "idp", provider: "external-idp:github", sub });
177
+ } else {
178
+ sessionCookie.set({ email, type: "sso", sub });
179
+ }
147
180
  }
148
181
  async sendLoginEventRd(email, name) {
149
182
  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 domainRegex = new RegExp(/(\\.*(prd|stg|dev)*.stackspot.com)|localhost/)\nconst cookieDomain = domainRegex.exec(portalUrl.host)?.[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,8 +1,11 @@
1
1
  import { AuthManager } from '@stack-spot/auth';
2
2
  import { useState, useEffect } from 'react';
3
3
 
4
+ var _a;
5
+ const sessionKey$1 = "stk-session";
4
6
  const portalUrl = new URL(location.href);
5
- const cookieDomain = portalUrl.host.replace("app", "").replace("edp", "").split(":")[0];
7
+ const domainRegex = new RegExp(/(\.*(prd|stg|dev)*.stackspot.com)|localhost/);
8
+ const cookieDomain = (_a = domainRegex.exec(portalUrl.host)) == null ? void 0 : _a[0];
6
9
  const defaultCookieAttributes = `domain=${cookieDomain}; SameSite=Strict;`;
7
10
  const setCookie = (key, value) => {
8
11
  document.cookie = `${key}=${value}; ${defaultCookieAttributes}`;
@@ -16,6 +19,18 @@ const getCookies = () => document.cookie.split("; ").reduce((prev, current) => {
16
19
  prev[name] = value.join("=");
17
20
  return prev;
18
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
+ });
19
34
 
20
35
  var __defProp = Object.defineProperty;
21
36
  var __defProps = Object.defineProperties;
@@ -41,7 +56,6 @@ var __publicField = (obj, key, value) => {
41
56
  return value;
42
57
  };
43
58
  const sessionKey = "session";
44
- const loginEmailKey = "sessionEmail";
45
59
  const _SessionManager = class _SessionManager {
46
60
  constructor(config) {
47
61
  __publicField(this, "current");
@@ -50,8 +64,6 @@ const _SessionManager = class _SessionManager {
50
64
  __publicField(this, "changeListeners", []);
51
65
  this.config = config;
52
66
  this.auth = new AuthManager(__spreadProps(__spreadValues({}, config), {
53
- authMigrationUrl: config.authUrl,
54
- migrationClientId: config.clientId,
55
67
  storage: localStorage,
56
68
  sessionPersistence: {
57
69
  load: () => localStorage.getItem(sessionKey),
@@ -59,6 +71,7 @@ const _SessionManager = class _SessionManager {
59
71
  }
60
72
  }));
61
73
  _SessionManager.instance = this;
74
+ addEventListener("focus", () => this.validateSharedSession());
62
75
  }
63
76
  static create(config) {
64
77
  var _a;
@@ -66,13 +79,30 @@ const _SessionManager = class _SessionManager {
66
79
  }
67
80
  setSession(session) {
68
81
  this.current = session;
82
+ if (session)
83
+ this.setSessionCookie(session);
84
+ else {
85
+ localStorage.removeItem(sessionKey);
86
+ sessionCookie.delete();
87
+ }
69
88
  this.changeListeners.forEach((l) => l(session));
70
89
  }
71
90
  async restoreSession() {
72
91
  const session = await this.auth.restoreSession();
92
+ await this.validateSharedSession(session);
73
93
  if (session)
74
94
  this.setSession(session);
75
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
+ }
76
106
  hasSession() {
77
107
  return !!this.current && !this.current.isExpired();
78
108
  }
@@ -85,7 +115,6 @@ const _SessionManager = class _SessionManager {
85
115
  }
86
116
  endSession(redirectToLogin = true) {
87
117
  this.setSession(void 0);
88
- localStorage.removeItem(sessionKey);
89
118
  if (redirectToLogin)
90
119
  window.location.href = this.config.loginUrl;
91
120
  }
@@ -97,7 +126,6 @@ const _SessionManager = class _SessionManager {
97
126
  console.error(`Could not logout from IDM.
98
127
  ${error}`);
99
128
  }
100
- removeCookie(loginEmailKey);
101
129
  this.endSession();
102
130
  }
103
131
  async startThirdPartyLogin(data) {
@@ -120,15 +148,14 @@ ${error}`);
120
148
  }
121
149
  const { session, data: { from, finalRedirect } } = await this.auth.completeThirdPartyLogin(location.search);
122
150
  this.setSession(session);
123
- this.persistSessionEmailCookie();
124
151
  if (finalRedirect)
125
152
  location.href = finalRedirect;
126
153
  history.replaceState(null, "", from || location.toString().replace(/\?.*$/, ""));
127
154
  this.sendLoginEventRd((_a = this.current) == null ? void 0 : _a.getTokenData().email, (_b = this.current) == null ? void 0 : _b.getTokenData().name);
128
155
  }
129
156
  getEmailForLogin() {
130
- var _a;
131
- return (_a = getCookie(loginEmailKey)) != null ? _a : "";
157
+ const session = sessionCookie.get();
158
+ return (session == null ? void 0 : session.type) == "sso" ? session.email : void 0;
132
159
  }
133
160
  onChange(listener) {
134
161
  this.changeListeners.push(listener);
@@ -138,10 +165,16 @@ ${error}`);
138
165
  this.changeListeners.splice(index, 1);
139
166
  };
140
167
  }
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);
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
+ }
145
178
  }
146
179
  async sendLoginEventRd(email, name) {
147
180
  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 domainRegex = new RegExp(/(\\.*(prd|stg|dev)*.stackspot.com)|localhost/)\nconst cookieDomain = domainRegex.exec(portalUrl.host)?.[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.0.0",
3
+ "version": "1.1.1",
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": ">=4.2.0"
23
- },
24
- "scripts": {
25
- "compile": "rollup -c",
26
- "build": "pnpm compile"
26
+ "@stack-spot/auth": ">=5.0.0"
27
27
  }
28
28
  }
@@ -1,8 +1,7 @@
1
1
  import { AuthConfig, AuthManager, Session, ThirdPartyLoginParams } from '@stack-spot/auth'
2
- import { getCookie, removeCookie, setCookie } from "./utils/cookies"
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
- return getCookie(loginEmailKey) ?? ''
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 persistSessionEmailCookie() {
128
- const sessionEmail = this.current?.getTokenData().email ?? ''
129
- setCookie(loginEmailKey, sessionEmail)
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) {
@@ -1,19 +1,38 @@
1
+ import { ThirdPartyLoginParams } from "@stack-spot/auth"
2
+
3
+ const sessionKey = 'stk-session'
1
4
  const portalUrl = new URL(location.href)
2
- const cookieDomain = portalUrl.host.replace('app', '').replace('edp', '').split(':')[0]
5
+ const domainRegex = new RegExp(/(\.*(prd|stg|dev)*.stackspot.com)|localhost/)
6
+ const cookieDomain = domainRegex.exec(portalUrl.host)?.[0]
3
7
  const defaultCookieAttributes = `domain=${cookieDomain}; SameSite=Strict;`
4
8
 
5
- export const setCookie = (key: string, value: string) => {
9
+ const setCookie = (key: string, value: string) => {
6
10
  document.cookie = `${key}=${value}; ${defaultCookieAttributes}`
7
11
  }
8
12
 
9
- export const removeCookie = (key: string) => {
13
+ const removeCookie = (key: string) => {
10
14
  document.cookie = `${key}=; max-age=0; ${defaultCookieAttributes}`
11
15
  }
12
16
 
13
- export const getCookie = (key: string) => getCookies()[key]
17
+ const getCookie = (key: string) => getCookies()[key]
14
18
 
15
- export const getCookies = (): Record<string, string> => document.cookie.split('; ').reduce((prev, current) => {
19
+ const getCookies = (): Record<string, string> => document.cookie.split('; ').reduce((prev, current) => {
16
20
  const [name, ...value] = current.split('=')
17
21
  prev[name] = value.join('=')
18
22
  return prev
19
23
  }, {} as Record<string, string>)
24
+
25
+
26
+ type SessionCookie = ThirdPartyLoginParams & { sub: string }
27
+ export const sessionCookie = Object.freeze({
28
+ set: (data: SessionCookie) => setCookie(sessionKey, JSON.stringify(data)),
29
+ get: (): SessionCookie | undefined => {
30
+ try {
31
+ const cookie = getCookie(sessionKey)
32
+ return cookie ? JSON.parse(cookie) : undefined
33
+ } catch (error) {
34
+ console.error(error)
35
+ }
36
+ },
37
+ delete: () => removeCookie(sessionKey)
38
+ })