@stack-spot/auth-react 1.6.0 → 2.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/CHANGELOG.md +11 -0
- package/out/index.d.ts +18 -2
- package/out/index.js +24 -6
- package/out/index.js.map +1 -1
- package/out/index.mjs +24 -6
- package/out/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/SessionManager.ts +38 -8
- package/src/utils/cookies.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.0.0] (2024-08-26)
|
|
4
|
+
|
|
5
|
+
* Support for login using tenant instead of email ([7f2585d](https://github.com/stack-spot/portal-auth-js/pull/42/commits/7f2585dd011a80e6bca1ac31d7736e3b6cc0dc5b))
|
|
6
|
+
|
|
7
|
+
## [1.7.0](https://github.com/stack-spot/portal-auth-js/compare/auth-react@v1.6.0...auth-react@v1.7.0) (2024-07-19)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* default values for url parameters ([d394d03](https://github.com/stack-spot/portal-auth-js/commit/d394d03c780a193fa6e9a656f4b7bd0e9b8b731b))
|
|
13
|
+
|
|
3
14
|
## [1.6.0](https://github.com/stack-spot/portal-auth-js/compare/auth-react-v1.5.0...auth-react@v1.6.0) (2024-07-03)
|
|
4
15
|
|
|
5
16
|
|
package/out/index.d.ts
CHANGED
|
@@ -26,9 +26,24 @@ type LoginProps = {
|
|
|
26
26
|
};
|
|
27
27
|
declare const Login: ({ onSubmit, initialValue, welcomeText, removeLoadingOnSuccess, className, style, banner, loginTypes }: LoginProps) => react_jsx_runtime.JSX.Element;
|
|
28
28
|
|
|
29
|
-
interface SessionManagerConfig extends Pick<AuthConfig, 'accountUrl' | 'authUrl' | 'clientId' | 'defaultTenant'
|
|
30
|
-
|
|
29
|
+
interface SessionManagerConfig extends Pick<AuthConfig, 'accountUrl' | 'authUrl' | 'clientId' | 'defaultTenant'> {
|
|
30
|
+
/**
|
|
31
|
+
* The URL to redirect to when the user logs out.
|
|
32
|
+
* @default location.origin
|
|
33
|
+
*/
|
|
34
|
+
loginUrl?: string;
|
|
35
|
+
/**
|
|
36
|
+
* The URL to redirect to when the login completes in the authentication app. If not provided, will be the same as `loginUrl`.
|
|
37
|
+
* @default loginUrl
|
|
38
|
+
*/
|
|
39
|
+
redirectUrl?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Forbidden authentication types to this Session Manager.
|
|
42
|
+
*/
|
|
31
43
|
blockedAuthTypes?: ThirdPartyAuthType[];
|
|
44
|
+
/**
|
|
45
|
+
* A URL to send login events to (observability).
|
|
46
|
+
*/
|
|
32
47
|
rdUrl?: string;
|
|
33
48
|
}
|
|
34
49
|
type ChangeListener = (session: Session | undefined) => void;
|
|
@@ -56,6 +71,7 @@ declare class SessionManager {
|
|
|
56
71
|
logout(): Promise<void>;
|
|
57
72
|
startThirdPartyLogin(data: ThirdPartyLoginParams): Promise<void>;
|
|
58
73
|
urlHasThirdPartyLoginData(): boolean;
|
|
74
|
+
startThirdPartyLoginUsingTenant(data: ThirdPartyLoginParams): Promise<void>;
|
|
59
75
|
completeThirdPartyLogin(): Promise<void>;
|
|
60
76
|
getEmailForLogin(): string | undefined;
|
|
61
77
|
switchAccount(accountId: string): Promise<void>;
|
package/out/index.js
CHANGED
|
@@ -174,8 +174,11 @@ const _SessionManager = class _SessionManager {
|
|
|
174
174
|
__publicField(this, "auth");
|
|
175
175
|
__publicField(this, "config");
|
|
176
176
|
__publicField(this, "changeListeners", []);
|
|
177
|
+
config.loginUrl || (config.loginUrl = location.origin);
|
|
178
|
+
const redirectUrl = (config.redirectUrl || config.loginUrl).replace(/([^/])$/, "$1/");
|
|
177
179
|
this.config = config;
|
|
178
180
|
this.auth = new auth.AuthManager(__spreadProps$1(__spreadValues$1({}, config), {
|
|
181
|
+
redirectUrl,
|
|
179
182
|
storage: localStorage,
|
|
180
183
|
sessionPersistence: {
|
|
181
184
|
load: () => localStorage.getItem(sessionKey),
|
|
@@ -214,7 +217,7 @@ const _SessionManager = class _SessionManager {
|
|
|
214
217
|
if (isSharedSessionTypeBlocked)
|
|
215
218
|
return false;
|
|
216
219
|
else if (isDifferentSessionActive || !session) {
|
|
217
|
-
await this.
|
|
220
|
+
await this.startThirdPartyLoginUsingTenant(sharedSessionCookie);
|
|
218
221
|
return false;
|
|
219
222
|
}
|
|
220
223
|
return true;
|
|
@@ -233,7 +236,7 @@ const _SessionManager = class _SessionManager {
|
|
|
233
236
|
this.current = void 0;
|
|
234
237
|
localStorage.removeItem(sessionKey);
|
|
235
238
|
sessionCookie.delete();
|
|
236
|
-
if (redirectToLogin)
|
|
239
|
+
if (redirectToLogin && this.config.loginUrl)
|
|
237
240
|
await redirect(this.config.loginUrl);
|
|
238
241
|
}
|
|
239
242
|
async logout() {
|
|
@@ -258,6 +261,21 @@ ${error}`);
|
|
|
258
261
|
const url = new URL(location.toString());
|
|
259
262
|
return url.searchParams.has("state") && !url.searchParams.has("error");
|
|
260
263
|
}
|
|
264
|
+
async startThirdPartyLoginUsingTenant(data) {
|
|
265
|
+
const params = new URLSearchParams(location.search);
|
|
266
|
+
const cookie = sessionCookie.get();
|
|
267
|
+
if (!cookie)
|
|
268
|
+
return;
|
|
269
|
+
const authUrl = await this.auth.getThirdPartyLoginFromTenant(
|
|
270
|
+
data,
|
|
271
|
+
cookie.tenant,
|
|
272
|
+
{
|
|
273
|
+
from: location.href,
|
|
274
|
+
finalRedirect: params.get("finalRedirect")
|
|
275
|
+
}
|
|
276
|
+
);
|
|
277
|
+
await redirect(authUrl);
|
|
278
|
+
}
|
|
261
279
|
async completeThirdPartyLogin() {
|
|
262
280
|
var _a, _b;
|
|
263
281
|
const url = new URL(location.toString());
|
|
@@ -288,14 +306,14 @@ ${error}`);
|
|
|
288
306
|
};
|
|
289
307
|
}
|
|
290
308
|
setSessionCookie(session) {
|
|
291
|
-
const { email, account_type, sub } = session.getTokenData();
|
|
292
|
-
if (!email || !sub)
|
|
309
|
+
const { email, account_type, sub, tenant } = session.getTokenData();
|
|
310
|
+
if (!email || !sub || !tenant)
|
|
293
311
|
return;
|
|
294
312
|
const isFreemium = account_type == "FREEMIUM";
|
|
295
313
|
if (isFreemium) {
|
|
296
|
-
sessionCookie.set({ type: "idp", provider: "external-idp:github", sub });
|
|
314
|
+
sessionCookie.set({ type: "idp", provider: "external-idp:github", sub, tenant });
|
|
297
315
|
} else {
|
|
298
|
-
sessionCookie.set({ email, type: "sso", sub });
|
|
316
|
+
sessionCookie.set({ email, type: "sso", sub, tenant });
|
|
299
317
|
}
|
|
300
318
|
}
|
|
301
319
|
async sendLoginEventRd(email, name) {
|
package/out/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/Login.tsx","../src/utils/cookies.ts","../src/utils/redirect.ts","../src/SessionManager.ts","../src/Authenticated.tsx","../src/hooks.ts"],"sourcesContent":["/* eslint-disable max-len */\nimport { Button, IconBox, Input, Text } from '@citric/core'\nimport { Github } from '@citric/icons'\nimport { LoadingCircular } from '@citric/ui'\nimport { BannerWarning } from '@stack-spot/portal-components'\nimport { MiniLogo } from '@stack-spot/portal-components/svg'\nimport { theme } from '@stack-spot/portal-theme'\nimport { Dictionary, useTranslate } from '@stack-spot/portal-translate'\nimport { useState } from 'react'\nimport { styled } from 'styled-components'\n\nexport type LoginType = 'sso' | 'idp'\n\ninterface BaseData {\n type: LoginType,\n}\n\ninterface SSOData extends BaseData {\n type: 'sso',\n email: string,\n}\n\ninterface IDPData extends BaseData {\n type: 'idp',\n provider: 'external-idp:github',\n}\n\ntype LoginData = SSOData | IDPData\n\nexport type LoginProps = {\n initialValue?: string,\n onSubmit: (data: LoginData) => Promise<void>,\n welcomeText?: string,\n removeLoadingOnSuccess?: boolean,\n className?: string,\n style?: React.CSSProperties,\n banner?: React.ReactNode,\n loginTypes?: LoginType[]\n}\n\nconst LoginBox = styled.form`\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: 24px;\n\n header {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 24px;\n }\n\n .title {\n font-size: 1rem;\n }\n\n .separator {\n padding: 0 8px;\n background-color: ${theme.color.light['300']};\n color: ${theme.color.light['700']};\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: center;\n gap: 20px;\n margin: 0;\n\n &:before, &:after {\n content: '';\n height: 1px;\n flex: 1;\n background-color: ${theme.color.light['600']};\n }\n }\n\n .error {\n color: ${theme.color.danger['500']};\n line-height: 1.5rem;\n }\n`\n\nexport const Login = ({ onSubmit, initialValue = '', welcomeText, removeLoadingOnSuccess, className, style, banner, loginTypes = ['idp', 'sso'] }: LoginProps) => {\n const t = useTranslate(dictionary)\n const searchParams = new URLSearchParams(location.search)\n const [error, setError] = useState(searchParams.get('error_description') || searchParams.get('error') || '')\n const [loading, setLoading] = useState(false)\n const [email, setEmail] = useState(initialValue)\n const disabled = !email.match(/\\w+@\\w+/)\n const idpLoginEnabled = loginTypes.includes('idp')\n const ssoLoginEnabled = loginTypes.includes('sso')\n\n async function login(type: LoginType) {\n setError('')\n setLoading(true)\n try {\n const data: LoginData = type === 'sso' ? { type: 'sso', email } : { type: 'idp', provider: 'external-idp:github' }\n await onSubmit(data)\n if (removeLoadingOnSuccess) setLoading(false)\n } catch (error: any) {\n setLoading(false)\n setError(error.message || error.toString())\n }\n }\n\n function submitForm(e: React.FormEvent<HTMLFormElement>) {\n e.preventDefault()\n if (disabled) return\n login('sso')\n }\n\n return (\n <>\n <LoginBox onSubmit={submitForm} className={className} style={style}>\n <header>\n <MiniLogo />\n <Text className=\"title\">{welcomeText || t.welcome}</Text>\n </header>\n {ssoLoginEnabled && <>\n <Input name=\"email\" value={email} onChange={e => setEmail(e.target.value)} placeholder={t.placeholder} />\n <Button colorScheme=\"primary\" disabled={disabled || loading}>\n {loading ? <LoadingCircular /> : <Text>{t.continue}</Text>}\n </Button>\n </>}\n {ssoLoginEnabled && idpLoginEnabled && <p className=\"separator\">{t.or}</p>}\n {idpLoginEnabled &&\n <Button colorScheme=\"light\" type=\"button\" onClick={() => login('idp')} disabled={loading}>\n {loading ? <LoadingCircular /> : (\n <>\n <IconBox>\n <Github />\n </IconBox>\n <Text>{t.loginWithGithub}</Text>\n </>\n )}\n </Button>}\n {error && <Text className=\"error\">{t.error}: {error}</Text>}\n </LoginBox>\n {banner ? <BannerWarning>\n {banner}\n </BannerWarning> : null}\n </>\n )\n}\n\nconst dictionary = {\n en: {\n welcome: 'Welcome to StackSpot',\n placeholder: 'your@email.com',\n continue: 'Continue',\n or: 'or',\n loginWithGithub: 'Login with Github',\n error: 'Error while attempting to login',\n },\n pt: {\n welcome: 'Bem vindo à StackSpot',\n placeholder: 'nome@email.com',\n continue: 'Continuar',\n or: 'ou',\n loginWithGithub: 'Logar com o GitHub',\n error: 'Erro ao fazer login',\n },\n} satisfies Dictionary\n","import { ThirdPartyLoginParams } from '@stack-spot/auth'\nimport { getCookie, getCookieDomain, removeCookie, setCookie } from '@stack-spot/portal-components'\n\nconst sessionKey = `stk-session${getCookieDomain()}`\n\ntype SessionCookie = ThirdPartyLoginParams & { sub: string }\n\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})\n","export const redirect = async (url: string) => {\n window.location.href = url\n /**\n * This is intentional. The promise bellow will never be fulfilled.\n * Once the set href is not instantaneous, this will guarantee no further code is executed until the user is really redirected.\n * Particularly useful to prevent flickering page renders on scenarios with redirects.\n */\n await new Promise(() => '')\n}","import { AuthConfig, AuthManager, Session, ThirdPartyAuthType, ThirdPartyLoginParams } from '@stack-spot/auth'\nimport { sessionCookie } from './utils/cookies'\nimport { redirect } from './utils/redirect'\n\nconst sessionKey = 'session'\n\ninterface SessionManagerConfig extends Pick<AuthConfig, 'accountUrl' | 'authUrl' | 'clientId' | 'defaultTenant' | 'redirectUrl'> {\n loginUrl: string,\n blockedAuthTypes?: ThirdPartyAuthType[]\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 private readonly auth: AuthManager<AuthExtraData>\n private config: SessionManagerConfig\n private changeListeners: ChangeListener[] = []\n static instance: SessionManager | undefined\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 this.changeListeners.forEach(l => l(session))\n if (session) this.setSessionCookie(session)\n }\n\n async restoreSession() {\n const session = await this.auth.restoreSession()\n const sessionValid = await this.validateSharedSession(session)\n this.setSession(sessionValid ? session : undefined)\n }\n\n async validateSharedSession(session: Session | undefined = this.current): Promise<boolean> {\n\n // skipping because authentication is in progress\n if (this.urlHasThirdPartyLoginData()) return false\n\n const sharedSessionCookie = sessionCookie.get()\n\n // It has been logged out on another portal, so logout on this one too\n if (!sharedSessionCookie) {\n session && await this.logout()\n return false\n }\n\n const isDifferentSessionActive = sharedSessionCookie.sub != session?.getTokenData().sub\n const isSharedSessionTypeBlocked = this.config.blockedAuthTypes?.includes(sharedSessionCookie.type)\n if (isSharedSessionTypeBlocked) return false\n else if (isDifferentSessionActive || !session) {\n await this.startThirdPartyLogin(sharedSessionCookie)\n return false\n }\n return true\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 async endSession(redirectToLogin = true) {\n this.current = undefined\n localStorage.removeItem(sessionKey)\n sessionCookie.delete()\n if (redirectToLogin) await redirect(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 await 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 await redirect(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 history.replaceState(null, '', from || location.toString().replace(/\\?.*$/, ''))\n this.sendLoginEventRd(this.current?.getTokenData().email, this.current?.getTokenData().name)\n if (finalRedirect) await redirect(finalRedirect)\n }\n\n getEmailForLogin() {\n const session = sessionCookie.get()\n return session?.type == 'sso' ? session.email : undefined\n }\n\n async switchAccount(accountId: string) {\n this.current && await this.auth.switchAccount(accountId, this.current)\n this.setSession(this.current)\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","/* eslint-disable max-len */\nimport { Flex } from '@citric/core'\nimport { useEffectOnce } from '@stack-spot/portal-components'\nimport { CSSToCitricAdapter } from '@stack-spot/portal-theme'\nimport '@stack-spot/portal-theme/dist/theme.css'\nimport { useLanguage } from '@stack-spot/portal-translate'\nimport { useState } from 'react'\nimport { Login, LoginProps } from './Login'\nimport { SessionManager } from './SessionManager'\n\ntype AuthStatus = 'unknown' | 'authenticated' | 'unauthenticated'\n\ninterface Props {\n children: React.ReactElement,\n onLogin?: () => void,\n onSession?: () => void,\n onChangeStatus?: (status: AuthStatus) => void,\n customLoginProps?: Omit<LoginProps, 'onSubmit' | 'initialValue'>,\n sessionManager?: SessionManager,\n}\n\nexport const Authenticated = ({ children, onLogin, onSession, customLoginProps, sessionManager, onChangeStatus }: Props) => {\n const [authStatus, setAuthStatus] = useState<AuthStatus>('unknown')\n const language = useLanguage()\n sessionManager ??= SessionManager.instance\n if (!sessionManager) throw new Error('Please, provide a sessionManager')\n\n useEffectOnce(() => {\n async function checkAuth() {\n if (!sessionManager) throw new Error('Please, provide a sessionManager')\n await sessionManager.restoreSession()\n if (sessionManager.urlHasThirdPartyLoginData()) {\n await sessionManager.completeThirdPartyLogin()\n onLogin?.()\n }\n if (sessionManager.hasSession()) {\n setAuthStatus('authenticated')\n onSession?.()\n onChangeStatus?.('authenticated')\n } else {\n setAuthStatus('unauthenticated')\n onChangeStatus?.('unauthenticated')\n }\n }\n\n checkAuth()\n })\n\n if (authStatus === 'unknown') return null\n if (authStatus === 'authenticated') return children\n\n return (\n <CSSToCitricAdapter>\n <Flex justifyContent=\"center\" alignItems=\"center\" flex={1} style={{ height: '100%' }}>\n <Login\n style={{ width: '360px' }}\n onSubmit={data => sessionManager.startThirdPartyLogin({ ...data, locale: language })}\n initialValue={sessionManager.getEmailForLogin()}\n {...(customLoginProps || {})}\n />\n </Flex>\n </CSSToCitricAdapter>\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":["styled","theme","useTranslate","useState","error","jsxs","Fragment","jsx","MiniLogo","Text","Input","Button","LoadingCircular","IconBox","Github","BannerWarning","sessionKey","getCookieDomain","setCookie","getCookie","removeCookie","AuthManager","__spreadProps","__spreadValues","useLanguage","useEffectOnce","CSSToCitricAdapter","Flex","useEffect"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/Login.tsx","../src/utils/cookies.ts","../src/utils/redirect.ts","../src/SessionManager.ts","../src/Authenticated.tsx","../src/hooks.ts"],"sourcesContent":["/* eslint-disable max-len */\nimport { Button, IconBox, Input, Text } from '@citric/core'\nimport { Github } from '@citric/icons'\nimport { LoadingCircular } from '@citric/ui'\nimport { BannerWarning } from '@stack-spot/portal-components'\nimport { MiniLogo } from '@stack-spot/portal-components/svg'\nimport { theme } from '@stack-spot/portal-theme'\nimport { Dictionary, useTranslate } from '@stack-spot/portal-translate'\nimport { useState } from 'react'\nimport { styled } from 'styled-components'\n\nexport type LoginType = 'sso' | 'idp'\n\ninterface BaseData {\n type: LoginType,\n}\n\ninterface SSOData extends BaseData {\n type: 'sso',\n email: string,\n}\n\ninterface IDPData extends BaseData {\n type: 'idp',\n provider: 'external-idp:github',\n}\n\ntype LoginData = SSOData | IDPData\n\nexport type LoginProps = {\n initialValue?: string,\n onSubmit: (data: LoginData) => Promise<void>,\n welcomeText?: string,\n removeLoadingOnSuccess?: boolean,\n className?: string,\n style?: React.CSSProperties,\n banner?: React.ReactNode,\n loginTypes?: LoginType[]\n}\n\nconst LoginBox = styled.form`\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: 24px;\n\n header {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 24px;\n }\n\n .title {\n font-size: 1rem;\n }\n\n .separator {\n padding: 0 8px;\n background-color: ${theme.color.light['300']};\n color: ${theme.color.light['700']};\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: center;\n gap: 20px;\n margin: 0;\n\n &:before, &:after {\n content: '';\n height: 1px;\n flex: 1;\n background-color: ${theme.color.light['600']};\n }\n }\n\n .error {\n color: ${theme.color.danger['500']};\n line-height: 1.5rem;\n }\n`\n\nexport const Login = ({ onSubmit, initialValue = '', welcomeText, removeLoadingOnSuccess, className, style, banner, loginTypes = ['idp', 'sso'] }: LoginProps) => {\n const t = useTranslate(dictionary)\n const searchParams = new URLSearchParams(location.search)\n const [error, setError] = useState(searchParams.get('error_description') || searchParams.get('error') || '')\n const [loading, setLoading] = useState(false)\n const [email, setEmail] = useState(initialValue)\n const disabled = !email.match(/\\w+@\\w+/)\n const idpLoginEnabled = loginTypes.includes('idp')\n const ssoLoginEnabled = loginTypes.includes('sso')\n\n async function login(type: LoginType) {\n setError('')\n setLoading(true)\n try {\n const data: LoginData = type === 'sso' ? { type: 'sso', email } : { type: 'idp', provider: 'external-idp:github' }\n await onSubmit(data)\n if (removeLoadingOnSuccess) setLoading(false)\n } catch (error: any) {\n setLoading(false)\n setError(error.message || error.toString())\n }\n }\n\n function submitForm(e: React.FormEvent<HTMLFormElement>) {\n e.preventDefault()\n if (disabled) return\n login('sso')\n }\n\n return (\n <>\n <LoginBox onSubmit={submitForm} className={className} style={style}>\n <header>\n <MiniLogo />\n <Text className=\"title\">{welcomeText || t.welcome}</Text>\n </header>\n {ssoLoginEnabled && <>\n <Input name=\"email\" value={email} onChange={e => setEmail(e.target.value)} placeholder={t.placeholder} />\n <Button colorScheme=\"primary\" disabled={disabled || loading}>\n {loading ? <LoadingCircular /> : <Text>{t.continue}</Text>}\n </Button>\n </>}\n {ssoLoginEnabled && idpLoginEnabled && <p className=\"separator\">{t.or}</p>}\n {idpLoginEnabled &&\n <Button colorScheme=\"light\" type=\"button\" onClick={() => login('idp')} disabled={loading}>\n {loading ? <LoadingCircular /> : (\n <>\n <IconBox>\n <Github />\n </IconBox>\n <Text>{t.loginWithGithub}</Text>\n </>\n )}\n </Button>}\n {error && <Text className=\"error\">{t.error}: {error}</Text>}\n </LoginBox>\n {banner ? <BannerWarning>\n {banner}\n </BannerWarning> : null}\n </>\n )\n}\n\nconst dictionary = {\n en: {\n welcome: 'Welcome to StackSpot',\n placeholder: 'your@email.com',\n continue: 'Continue',\n or: 'or',\n loginWithGithub: 'Login with Github',\n error: 'Error while attempting to login',\n },\n pt: {\n welcome: 'Bem vindo à StackSpot',\n placeholder: 'nome@email.com',\n continue: 'Continuar',\n or: 'ou',\n loginWithGithub: 'Logar com o GitHub',\n error: 'Erro ao fazer login',\n },\n} satisfies Dictionary\n","import { ThirdPartyLoginParams } from '@stack-spot/auth'\nimport { getCookie, getCookieDomain, removeCookie, setCookie } from '@stack-spot/portal-components'\n\nconst sessionKey = `stk-session${getCookieDomain()}`\n\ntype SessionCookie = ThirdPartyLoginParams & { sub: string, tenant: string }\n\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})\n","export const redirect = async (url: string) => {\n window.location.href = url\n /**\n * This is intentional. The promise bellow will never be fulfilled.\n * Once the set href is not instantaneous, this will guarantee no further code is executed until the user is really redirected.\n * Particularly useful to prevent flickering page renders on scenarios with redirects.\n */\n await new Promise(() => '')\n}","import { AuthConfig, AuthManager, Session, ThirdPartyAuthType, ThirdPartyLoginParams } from '@stack-spot/auth'\nimport { sessionCookie } from './utils/cookies'\nimport { redirect } from './utils/redirect'\n\nconst sessionKey = 'session'\n\ninterface SessionManagerConfig extends Pick<AuthConfig, 'accountUrl' | 'authUrl' | 'clientId' | 'defaultTenant'> {\n /**\n * The URL to redirect to when the user logs out.\n * @default location.origin\n */\n loginUrl?: string,\n /**\n * The URL to redirect to when the login completes in the authentication app. If not provided, will be the same as `loginUrl`.\n * @default loginUrl\n */\n redirectUrl?: string,\n /**\n * Forbidden authentication types to this Session Manager.\n */\n blockedAuthTypes?: ThirdPartyAuthType[]\n /**\n * A URL to send login events to (observability).\n */\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 private readonly auth: AuthManager<AuthExtraData>\n private config: SessionManagerConfig\n private changeListeners: ChangeListener[] = []\n static instance: SessionManager | undefined\n\n private constructor(config: SessionManagerConfig) {\n config.loginUrl ||= location.origin\n const redirectUrl = (config.redirectUrl || config.loginUrl).replace(/([^/])$/, '$1/') // the trailing \"/\" is required by Stackspot IAM.\n this.config = config\n this.auth = new AuthManager<AuthExtraData>({\n ...config,\n redirectUrl,\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 this.changeListeners.forEach(l => l(session))\n if (session) this.setSessionCookie(session)\n }\n\n async restoreSession() {\n const session = await this.auth.restoreSession()\n const sessionValid = await this.validateSharedSession(session)\n this.setSession(sessionValid ? session : undefined)\n }\n\n async validateSharedSession(session: Session | undefined = this.current): Promise<boolean> {\n\n // skipping because authentication is in progress\n if (this.urlHasThirdPartyLoginData()) return false\n\n const sharedSessionCookie = sessionCookie.get()\n\n // It has been logged out on another portal, so logout on this one too\n if (!sharedSessionCookie) {\n session && await this.logout()\n return false\n }\n\n const isDifferentSessionActive = sharedSessionCookie.sub != session?.getTokenData().sub\n const isSharedSessionTypeBlocked = this.config.blockedAuthTypes?.includes(sharedSessionCookie.type)\n if (isSharedSessionTypeBlocked) return false\n else if (isDifferentSessionActive || !session) {\n await this.startThirdPartyLoginUsingTenant(sharedSessionCookie)\n return false\n }\n return true\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 async endSession(redirectToLogin = true) {\n this.current = undefined\n localStorage.removeItem(sessionKey)\n sessionCookie.delete()\n if (redirectToLogin && this.config.loginUrl) await redirect(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 await 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 await redirect(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 startThirdPartyLoginUsingTenant(data: ThirdPartyLoginParams) {\n const params = new URLSearchParams(location.search)\n const cookie = sessionCookie.get()\n if (!cookie) return\n const authUrl = await this.auth.getThirdPartyLoginFromTenant(data, cookie.tenant, {\n from: location.href,\n finalRedirect: params.get('finalRedirect'),\n }\n )\n await redirect(authUrl)\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 history.replaceState(null, '', from || location.toString().replace(/\\?.*$/, ''))\n this.sendLoginEventRd(this.current?.getTokenData().email, this.current?.getTokenData().name)\n if (finalRedirect) await redirect(finalRedirect)\n }\n\n getEmailForLogin() {\n const session = sessionCookie.get()\n return session?.type == 'sso' ? session.email : undefined\n }\n\n async switchAccount(accountId: string) {\n this.current && await this.auth.switchAccount(accountId, this.current)\n this.setSession(this.current)\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, tenant } = session.getTokenData()\n if (!email || !sub || !tenant) return\n const isFreemium = account_type == 'FREEMIUM'\n if (isFreemium) {\n sessionCookie.set({ type: 'idp', provider: 'external-idp:github', sub, tenant })\n } else {\n sessionCookie.set({ email, type: 'sso', sub, tenant })\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","/* eslint-disable max-len */\nimport { Flex } from '@citric/core'\nimport { useEffectOnce } from '@stack-spot/portal-components'\nimport { CSSToCitricAdapter } from '@stack-spot/portal-theme'\nimport '@stack-spot/portal-theme/dist/theme.css'\nimport { useLanguage } from '@stack-spot/portal-translate'\nimport { useState } from 'react'\nimport { Login, LoginProps } from './Login'\nimport { SessionManager } from './SessionManager'\n\ntype AuthStatus = 'unknown' | 'authenticated' | 'unauthenticated'\n\ninterface Props {\n children: React.ReactElement,\n onLogin?: () => void,\n onSession?: () => void,\n onChangeStatus?: (status: AuthStatus) => void,\n customLoginProps?: Omit<LoginProps, 'onSubmit' | 'initialValue'>,\n sessionManager?: SessionManager,\n}\n\nexport const Authenticated = ({ children, onLogin, onSession, customLoginProps, sessionManager, onChangeStatus }: Props) => {\n const [authStatus, setAuthStatus] = useState<AuthStatus>('unknown')\n const language = useLanguage()\n sessionManager ??= SessionManager.instance\n if (!sessionManager) throw new Error('Please, provide a sessionManager')\n\n useEffectOnce(() => {\n async function checkAuth() {\n if (!sessionManager) throw new Error('Please, provide a sessionManager')\n await sessionManager.restoreSession()\n if (sessionManager.urlHasThirdPartyLoginData()) {\n await sessionManager.completeThirdPartyLogin()\n onLogin?.()\n }\n if (sessionManager.hasSession()) {\n setAuthStatus('authenticated')\n onSession?.()\n onChangeStatus?.('authenticated')\n } else {\n setAuthStatus('unauthenticated')\n onChangeStatus?.('unauthenticated')\n }\n }\n\n checkAuth()\n })\n\n if (authStatus === 'unknown') return null\n if (authStatus === 'authenticated') return children\n\n return (\n <CSSToCitricAdapter>\n <Flex justifyContent=\"center\" alignItems=\"center\" flex={1} style={{ height: '100%' }}>\n <Login\n style={{ width: '360px' }}\n onSubmit={data => sessionManager.startThirdPartyLogin({ ...data, locale: language })}\n initialValue={sessionManager.getEmailForLogin()}\n {...(customLoginProps || {})}\n />\n </Flex>\n </CSSToCitricAdapter>\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":["styled","theme","useTranslate","useState","error","jsxs","Fragment","jsx","MiniLogo","Text","Input","Button","LoadingCircular","IconBox","Github","BannerWarning","sessionKey","getCookieDomain","setCookie","getCookie","removeCookie","AuthManager","__spreadProps","__spreadValues","useLanguage","useEffectOnce","CSSToCitricAdapter","Flex","useEffect"],"mappings":"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"}
|
package/out/index.mjs
CHANGED
|
@@ -172,8 +172,11 @@ const _SessionManager = class _SessionManager {
|
|
|
172
172
|
__publicField(this, "auth");
|
|
173
173
|
__publicField(this, "config");
|
|
174
174
|
__publicField(this, "changeListeners", []);
|
|
175
|
+
config.loginUrl || (config.loginUrl = location.origin);
|
|
176
|
+
const redirectUrl = (config.redirectUrl || config.loginUrl).replace(/([^/])$/, "$1/");
|
|
175
177
|
this.config = config;
|
|
176
178
|
this.auth = new AuthManager(__spreadProps$1(__spreadValues$1({}, config), {
|
|
179
|
+
redirectUrl,
|
|
177
180
|
storage: localStorage,
|
|
178
181
|
sessionPersistence: {
|
|
179
182
|
load: () => localStorage.getItem(sessionKey),
|
|
@@ -212,7 +215,7 @@ const _SessionManager = class _SessionManager {
|
|
|
212
215
|
if (isSharedSessionTypeBlocked)
|
|
213
216
|
return false;
|
|
214
217
|
else if (isDifferentSessionActive || !session) {
|
|
215
|
-
await this.
|
|
218
|
+
await this.startThirdPartyLoginUsingTenant(sharedSessionCookie);
|
|
216
219
|
return false;
|
|
217
220
|
}
|
|
218
221
|
return true;
|
|
@@ -231,7 +234,7 @@ const _SessionManager = class _SessionManager {
|
|
|
231
234
|
this.current = void 0;
|
|
232
235
|
localStorage.removeItem(sessionKey);
|
|
233
236
|
sessionCookie.delete();
|
|
234
|
-
if (redirectToLogin)
|
|
237
|
+
if (redirectToLogin && this.config.loginUrl)
|
|
235
238
|
await redirect(this.config.loginUrl);
|
|
236
239
|
}
|
|
237
240
|
async logout() {
|
|
@@ -256,6 +259,21 @@ ${error}`);
|
|
|
256
259
|
const url = new URL(location.toString());
|
|
257
260
|
return url.searchParams.has("state") && !url.searchParams.has("error");
|
|
258
261
|
}
|
|
262
|
+
async startThirdPartyLoginUsingTenant(data) {
|
|
263
|
+
const params = new URLSearchParams(location.search);
|
|
264
|
+
const cookie = sessionCookie.get();
|
|
265
|
+
if (!cookie)
|
|
266
|
+
return;
|
|
267
|
+
const authUrl = await this.auth.getThirdPartyLoginFromTenant(
|
|
268
|
+
data,
|
|
269
|
+
cookie.tenant,
|
|
270
|
+
{
|
|
271
|
+
from: location.href,
|
|
272
|
+
finalRedirect: params.get("finalRedirect")
|
|
273
|
+
}
|
|
274
|
+
);
|
|
275
|
+
await redirect(authUrl);
|
|
276
|
+
}
|
|
259
277
|
async completeThirdPartyLogin() {
|
|
260
278
|
var _a, _b;
|
|
261
279
|
const url = new URL(location.toString());
|
|
@@ -286,14 +304,14 @@ ${error}`);
|
|
|
286
304
|
};
|
|
287
305
|
}
|
|
288
306
|
setSessionCookie(session) {
|
|
289
|
-
const { email, account_type, sub } = session.getTokenData();
|
|
290
|
-
if (!email || !sub)
|
|
307
|
+
const { email, account_type, sub, tenant } = session.getTokenData();
|
|
308
|
+
if (!email || !sub || !tenant)
|
|
291
309
|
return;
|
|
292
310
|
const isFreemium = account_type == "FREEMIUM";
|
|
293
311
|
if (isFreemium) {
|
|
294
|
-
sessionCookie.set({ type: "idp", provider: "external-idp:github", sub });
|
|
312
|
+
sessionCookie.set({ type: "idp", provider: "external-idp:github", sub, tenant });
|
|
295
313
|
} else {
|
|
296
|
-
sessionCookie.set({ email, type: "sso", sub });
|
|
314
|
+
sessionCookie.set({ email, type: "sso", sub, tenant });
|
|
297
315
|
}
|
|
298
316
|
}
|
|
299
317
|
async sendLoginEventRd(email, name) {
|
package/out/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/Login.tsx","../src/utils/cookies.ts","../src/utils/redirect.ts","../src/SessionManager.ts","../src/Authenticated.tsx","../src/hooks.ts"],"sourcesContent":["/* eslint-disable max-len */\nimport { Button, IconBox, Input, Text } from '@citric/core'\nimport { Github } from '@citric/icons'\nimport { LoadingCircular } from '@citric/ui'\nimport { BannerWarning } from '@stack-spot/portal-components'\nimport { MiniLogo } from '@stack-spot/portal-components/svg'\nimport { theme } from '@stack-spot/portal-theme'\nimport { Dictionary, useTranslate } from '@stack-spot/portal-translate'\nimport { useState } from 'react'\nimport { styled } from 'styled-components'\n\nexport type LoginType = 'sso' | 'idp'\n\ninterface BaseData {\n type: LoginType,\n}\n\ninterface SSOData extends BaseData {\n type: 'sso',\n email: string,\n}\n\ninterface IDPData extends BaseData {\n type: 'idp',\n provider: 'external-idp:github',\n}\n\ntype LoginData = SSOData | IDPData\n\nexport type LoginProps = {\n initialValue?: string,\n onSubmit: (data: LoginData) => Promise<void>,\n welcomeText?: string,\n removeLoadingOnSuccess?: boolean,\n className?: string,\n style?: React.CSSProperties,\n banner?: React.ReactNode,\n loginTypes?: LoginType[]\n}\n\nconst LoginBox = styled.form`\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: 24px;\n\n header {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 24px;\n }\n\n .title {\n font-size: 1rem;\n }\n\n .separator {\n padding: 0 8px;\n background-color: ${theme.color.light['300']};\n color: ${theme.color.light['700']};\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: center;\n gap: 20px;\n margin: 0;\n\n &:before, &:after {\n content: '';\n height: 1px;\n flex: 1;\n background-color: ${theme.color.light['600']};\n }\n }\n\n .error {\n color: ${theme.color.danger['500']};\n line-height: 1.5rem;\n }\n`\n\nexport const Login = ({ onSubmit, initialValue = '', welcomeText, removeLoadingOnSuccess, className, style, banner, loginTypes = ['idp', 'sso'] }: LoginProps) => {\n const t = useTranslate(dictionary)\n const searchParams = new URLSearchParams(location.search)\n const [error, setError] = useState(searchParams.get('error_description') || searchParams.get('error') || '')\n const [loading, setLoading] = useState(false)\n const [email, setEmail] = useState(initialValue)\n const disabled = !email.match(/\\w+@\\w+/)\n const idpLoginEnabled = loginTypes.includes('idp')\n const ssoLoginEnabled = loginTypes.includes('sso')\n\n async function login(type: LoginType) {\n setError('')\n setLoading(true)\n try {\n const data: LoginData = type === 'sso' ? { type: 'sso', email } : { type: 'idp', provider: 'external-idp:github' }\n await onSubmit(data)\n if (removeLoadingOnSuccess) setLoading(false)\n } catch (error: any) {\n setLoading(false)\n setError(error.message || error.toString())\n }\n }\n\n function submitForm(e: React.FormEvent<HTMLFormElement>) {\n e.preventDefault()\n if (disabled) return\n login('sso')\n }\n\n return (\n <>\n <LoginBox onSubmit={submitForm} className={className} style={style}>\n <header>\n <MiniLogo />\n <Text className=\"title\">{welcomeText || t.welcome}</Text>\n </header>\n {ssoLoginEnabled && <>\n <Input name=\"email\" value={email} onChange={e => setEmail(e.target.value)} placeholder={t.placeholder} />\n <Button colorScheme=\"primary\" disabled={disabled || loading}>\n {loading ? <LoadingCircular /> : <Text>{t.continue}</Text>}\n </Button>\n </>}\n {ssoLoginEnabled && idpLoginEnabled && <p className=\"separator\">{t.or}</p>}\n {idpLoginEnabled &&\n <Button colorScheme=\"light\" type=\"button\" onClick={() => login('idp')} disabled={loading}>\n {loading ? <LoadingCircular /> : (\n <>\n <IconBox>\n <Github />\n </IconBox>\n <Text>{t.loginWithGithub}</Text>\n </>\n )}\n </Button>}\n {error && <Text className=\"error\">{t.error}: {error}</Text>}\n </LoginBox>\n {banner ? <BannerWarning>\n {banner}\n </BannerWarning> : null}\n </>\n )\n}\n\nconst dictionary = {\n en: {\n welcome: 'Welcome to StackSpot',\n placeholder: 'your@email.com',\n continue: 'Continue',\n or: 'or',\n loginWithGithub: 'Login with Github',\n error: 'Error while attempting to login',\n },\n pt: {\n welcome: 'Bem vindo à StackSpot',\n placeholder: 'nome@email.com',\n continue: 'Continuar',\n or: 'ou',\n loginWithGithub: 'Logar com o GitHub',\n error: 'Erro ao fazer login',\n },\n} satisfies Dictionary\n","import { ThirdPartyLoginParams } from '@stack-spot/auth'\nimport { getCookie, getCookieDomain, removeCookie, setCookie } from '@stack-spot/portal-components'\n\nconst sessionKey = `stk-session${getCookieDomain()}`\n\ntype SessionCookie = ThirdPartyLoginParams & { sub: string }\n\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})\n","export const redirect = async (url: string) => {\n window.location.href = url\n /**\n * This is intentional. The promise bellow will never be fulfilled.\n * Once the set href is not instantaneous, this will guarantee no further code is executed until the user is really redirected.\n * Particularly useful to prevent flickering page renders on scenarios with redirects.\n */\n await new Promise(() => '')\n}","import { AuthConfig, AuthManager, Session, ThirdPartyAuthType, ThirdPartyLoginParams } from '@stack-spot/auth'\nimport { sessionCookie } from './utils/cookies'\nimport { redirect } from './utils/redirect'\n\nconst sessionKey = 'session'\n\ninterface SessionManagerConfig extends Pick<AuthConfig, 'accountUrl' | 'authUrl' | 'clientId' | 'defaultTenant' | 'redirectUrl'> {\n loginUrl: string,\n blockedAuthTypes?: ThirdPartyAuthType[]\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 private readonly auth: AuthManager<AuthExtraData>\n private config: SessionManagerConfig\n private changeListeners: ChangeListener[] = []\n static instance: SessionManager | undefined\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 this.changeListeners.forEach(l => l(session))\n if (session) this.setSessionCookie(session)\n }\n\n async restoreSession() {\n const session = await this.auth.restoreSession()\n const sessionValid = await this.validateSharedSession(session)\n this.setSession(sessionValid ? session : undefined)\n }\n\n async validateSharedSession(session: Session | undefined = this.current): Promise<boolean> {\n\n // skipping because authentication is in progress\n if (this.urlHasThirdPartyLoginData()) return false\n\n const sharedSessionCookie = sessionCookie.get()\n\n // It has been logged out on another portal, so logout on this one too\n if (!sharedSessionCookie) {\n session && await this.logout()\n return false\n }\n\n const isDifferentSessionActive = sharedSessionCookie.sub != session?.getTokenData().sub\n const isSharedSessionTypeBlocked = this.config.blockedAuthTypes?.includes(sharedSessionCookie.type)\n if (isSharedSessionTypeBlocked) return false\n else if (isDifferentSessionActive || !session) {\n await this.startThirdPartyLogin(sharedSessionCookie)\n return false\n }\n return true\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 async endSession(redirectToLogin = true) {\n this.current = undefined\n localStorage.removeItem(sessionKey)\n sessionCookie.delete()\n if (redirectToLogin) await redirect(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 await 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 await redirect(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 history.replaceState(null, '', from || location.toString().replace(/\\?.*$/, ''))\n this.sendLoginEventRd(this.current?.getTokenData().email, this.current?.getTokenData().name)\n if (finalRedirect) await redirect(finalRedirect)\n }\n\n getEmailForLogin() {\n const session = sessionCookie.get()\n return session?.type == 'sso' ? session.email : undefined\n }\n\n async switchAccount(accountId: string) {\n this.current && await this.auth.switchAccount(accountId, this.current)\n this.setSession(this.current)\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","/* eslint-disable max-len */\nimport { Flex } from '@citric/core'\nimport { useEffectOnce } from '@stack-spot/portal-components'\nimport { CSSToCitricAdapter } from '@stack-spot/portal-theme'\nimport '@stack-spot/portal-theme/dist/theme.css'\nimport { useLanguage } from '@stack-spot/portal-translate'\nimport { useState } from 'react'\nimport { Login, LoginProps } from './Login'\nimport { SessionManager } from './SessionManager'\n\ntype AuthStatus = 'unknown' | 'authenticated' | 'unauthenticated'\n\ninterface Props {\n children: React.ReactElement,\n onLogin?: () => void,\n onSession?: () => void,\n onChangeStatus?: (status: AuthStatus) => void,\n customLoginProps?: Omit<LoginProps, 'onSubmit' | 'initialValue'>,\n sessionManager?: SessionManager,\n}\n\nexport const Authenticated = ({ children, onLogin, onSession, customLoginProps, sessionManager, onChangeStatus }: Props) => {\n const [authStatus, setAuthStatus] = useState<AuthStatus>('unknown')\n const language = useLanguage()\n sessionManager ??= SessionManager.instance\n if (!sessionManager) throw new Error('Please, provide a sessionManager')\n\n useEffectOnce(() => {\n async function checkAuth() {\n if (!sessionManager) throw new Error('Please, provide a sessionManager')\n await sessionManager.restoreSession()\n if (sessionManager.urlHasThirdPartyLoginData()) {\n await sessionManager.completeThirdPartyLogin()\n onLogin?.()\n }\n if (sessionManager.hasSession()) {\n setAuthStatus('authenticated')\n onSession?.()\n onChangeStatus?.('authenticated')\n } else {\n setAuthStatus('unauthenticated')\n onChangeStatus?.('unauthenticated')\n }\n }\n\n checkAuth()\n })\n\n if (authStatus === 'unknown') return null\n if (authStatus === 'authenticated') return children\n\n return (\n <CSSToCitricAdapter>\n <Flex justifyContent=\"center\" alignItems=\"center\" flex={1} style={{ height: '100%' }}>\n <Login\n style={{ width: '360px' }}\n onSubmit={data => sessionManager.startThirdPartyLogin({ ...data, locale: language })}\n initialValue={sessionManager.getEmailForLogin()}\n {...(customLoginProps || {})}\n />\n </Flex>\n </CSSToCitricAdapter>\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":["error","sessionKey","__spreadProps","__spreadValues"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/Login.tsx","../src/utils/cookies.ts","../src/utils/redirect.ts","../src/SessionManager.ts","../src/Authenticated.tsx","../src/hooks.ts"],"sourcesContent":["/* eslint-disable max-len */\nimport { Button, IconBox, Input, Text } from '@citric/core'\nimport { Github } from '@citric/icons'\nimport { LoadingCircular } from '@citric/ui'\nimport { BannerWarning } from '@stack-spot/portal-components'\nimport { MiniLogo } from '@stack-spot/portal-components/svg'\nimport { theme } from '@stack-spot/portal-theme'\nimport { Dictionary, useTranslate } from '@stack-spot/portal-translate'\nimport { useState } from 'react'\nimport { styled } from 'styled-components'\n\nexport type LoginType = 'sso' | 'idp'\n\ninterface BaseData {\n type: LoginType,\n}\n\ninterface SSOData extends BaseData {\n type: 'sso',\n email: string,\n}\n\ninterface IDPData extends BaseData {\n type: 'idp',\n provider: 'external-idp:github',\n}\n\ntype LoginData = SSOData | IDPData\n\nexport type LoginProps = {\n initialValue?: string,\n onSubmit: (data: LoginData) => Promise<void>,\n welcomeText?: string,\n removeLoadingOnSuccess?: boolean,\n className?: string,\n style?: React.CSSProperties,\n banner?: React.ReactNode,\n loginTypes?: LoginType[]\n}\n\nconst LoginBox = styled.form`\n display: flex;\n flex-direction: column;\n justify-content: center;\n gap: 24px;\n\n header {\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 24px;\n }\n\n .title {\n font-size: 1rem;\n }\n\n .separator {\n padding: 0 8px;\n background-color: ${theme.color.light['300']};\n color: ${theme.color.light['700']};\n display: flex;\n flex-direction: row;\n align-items: center;\n justify-content: center;\n gap: 20px;\n margin: 0;\n\n &:before, &:after {\n content: '';\n height: 1px;\n flex: 1;\n background-color: ${theme.color.light['600']};\n }\n }\n\n .error {\n color: ${theme.color.danger['500']};\n line-height: 1.5rem;\n }\n`\n\nexport const Login = ({ onSubmit, initialValue = '', welcomeText, removeLoadingOnSuccess, className, style, banner, loginTypes = ['idp', 'sso'] }: LoginProps) => {\n const t = useTranslate(dictionary)\n const searchParams = new URLSearchParams(location.search)\n const [error, setError] = useState(searchParams.get('error_description') || searchParams.get('error') || '')\n const [loading, setLoading] = useState(false)\n const [email, setEmail] = useState(initialValue)\n const disabled = !email.match(/\\w+@\\w+/)\n const idpLoginEnabled = loginTypes.includes('idp')\n const ssoLoginEnabled = loginTypes.includes('sso')\n\n async function login(type: LoginType) {\n setError('')\n setLoading(true)\n try {\n const data: LoginData = type === 'sso' ? { type: 'sso', email } : { type: 'idp', provider: 'external-idp:github' }\n await onSubmit(data)\n if (removeLoadingOnSuccess) setLoading(false)\n } catch (error: any) {\n setLoading(false)\n setError(error.message || error.toString())\n }\n }\n\n function submitForm(e: React.FormEvent<HTMLFormElement>) {\n e.preventDefault()\n if (disabled) return\n login('sso')\n }\n\n return (\n <>\n <LoginBox onSubmit={submitForm} className={className} style={style}>\n <header>\n <MiniLogo />\n <Text className=\"title\">{welcomeText || t.welcome}</Text>\n </header>\n {ssoLoginEnabled && <>\n <Input name=\"email\" value={email} onChange={e => setEmail(e.target.value)} placeholder={t.placeholder} />\n <Button colorScheme=\"primary\" disabled={disabled || loading}>\n {loading ? <LoadingCircular /> : <Text>{t.continue}</Text>}\n </Button>\n </>}\n {ssoLoginEnabled && idpLoginEnabled && <p className=\"separator\">{t.or}</p>}\n {idpLoginEnabled &&\n <Button colorScheme=\"light\" type=\"button\" onClick={() => login('idp')} disabled={loading}>\n {loading ? <LoadingCircular /> : (\n <>\n <IconBox>\n <Github />\n </IconBox>\n <Text>{t.loginWithGithub}</Text>\n </>\n )}\n </Button>}\n {error && <Text className=\"error\">{t.error}: {error}</Text>}\n </LoginBox>\n {banner ? <BannerWarning>\n {banner}\n </BannerWarning> : null}\n </>\n )\n}\n\nconst dictionary = {\n en: {\n welcome: 'Welcome to StackSpot',\n placeholder: 'your@email.com',\n continue: 'Continue',\n or: 'or',\n loginWithGithub: 'Login with Github',\n error: 'Error while attempting to login',\n },\n pt: {\n welcome: 'Bem vindo à StackSpot',\n placeholder: 'nome@email.com',\n continue: 'Continuar',\n or: 'ou',\n loginWithGithub: 'Logar com o GitHub',\n error: 'Erro ao fazer login',\n },\n} satisfies Dictionary\n","import { ThirdPartyLoginParams } from '@stack-spot/auth'\nimport { getCookie, getCookieDomain, removeCookie, setCookie } from '@stack-spot/portal-components'\n\nconst sessionKey = `stk-session${getCookieDomain()}`\n\ntype SessionCookie = ThirdPartyLoginParams & { sub: string, tenant: string }\n\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})\n","export const redirect = async (url: string) => {\n window.location.href = url\n /**\n * This is intentional. The promise bellow will never be fulfilled.\n * Once the set href is not instantaneous, this will guarantee no further code is executed until the user is really redirected.\n * Particularly useful to prevent flickering page renders on scenarios with redirects.\n */\n await new Promise(() => '')\n}","import { AuthConfig, AuthManager, Session, ThirdPartyAuthType, ThirdPartyLoginParams } from '@stack-spot/auth'\nimport { sessionCookie } from './utils/cookies'\nimport { redirect } from './utils/redirect'\n\nconst sessionKey = 'session'\n\ninterface SessionManagerConfig extends Pick<AuthConfig, 'accountUrl' | 'authUrl' | 'clientId' | 'defaultTenant'> {\n /**\n * The URL to redirect to when the user logs out.\n * @default location.origin\n */\n loginUrl?: string,\n /**\n * The URL to redirect to when the login completes in the authentication app. If not provided, will be the same as `loginUrl`.\n * @default loginUrl\n */\n redirectUrl?: string,\n /**\n * Forbidden authentication types to this Session Manager.\n */\n blockedAuthTypes?: ThirdPartyAuthType[]\n /**\n * A URL to send login events to (observability).\n */\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 private readonly auth: AuthManager<AuthExtraData>\n private config: SessionManagerConfig\n private changeListeners: ChangeListener[] = []\n static instance: SessionManager | undefined\n\n private constructor(config: SessionManagerConfig) {\n config.loginUrl ||= location.origin\n const redirectUrl = (config.redirectUrl || config.loginUrl).replace(/([^/])$/, '$1/') // the trailing \"/\" is required by Stackspot IAM.\n this.config = config\n this.auth = new AuthManager<AuthExtraData>({\n ...config,\n redirectUrl,\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 this.changeListeners.forEach(l => l(session))\n if (session) this.setSessionCookie(session)\n }\n\n async restoreSession() {\n const session = await this.auth.restoreSession()\n const sessionValid = await this.validateSharedSession(session)\n this.setSession(sessionValid ? session : undefined)\n }\n\n async validateSharedSession(session: Session | undefined = this.current): Promise<boolean> {\n\n // skipping because authentication is in progress\n if (this.urlHasThirdPartyLoginData()) return false\n\n const sharedSessionCookie = sessionCookie.get()\n\n // It has been logged out on another portal, so logout on this one too\n if (!sharedSessionCookie) {\n session && await this.logout()\n return false\n }\n\n const isDifferentSessionActive = sharedSessionCookie.sub != session?.getTokenData().sub\n const isSharedSessionTypeBlocked = this.config.blockedAuthTypes?.includes(sharedSessionCookie.type)\n if (isSharedSessionTypeBlocked) return false\n else if (isDifferentSessionActive || !session) {\n await this.startThirdPartyLoginUsingTenant(sharedSessionCookie)\n return false\n }\n return true\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 async endSession(redirectToLogin = true) {\n this.current = undefined\n localStorage.removeItem(sessionKey)\n sessionCookie.delete()\n if (redirectToLogin && this.config.loginUrl) await redirect(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 await 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 await redirect(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 startThirdPartyLoginUsingTenant(data: ThirdPartyLoginParams) {\n const params = new URLSearchParams(location.search)\n const cookie = sessionCookie.get()\n if (!cookie) return\n const authUrl = await this.auth.getThirdPartyLoginFromTenant(data, cookie.tenant, {\n from: location.href,\n finalRedirect: params.get('finalRedirect'),\n }\n )\n await redirect(authUrl)\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 history.replaceState(null, '', from || location.toString().replace(/\\?.*$/, ''))\n this.sendLoginEventRd(this.current?.getTokenData().email, this.current?.getTokenData().name)\n if (finalRedirect) await redirect(finalRedirect)\n }\n\n getEmailForLogin() {\n const session = sessionCookie.get()\n return session?.type == 'sso' ? session.email : undefined\n }\n\n async switchAccount(accountId: string) {\n this.current && await this.auth.switchAccount(accountId, this.current)\n this.setSession(this.current)\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, tenant } = session.getTokenData()\n if (!email || !sub || !tenant) return\n const isFreemium = account_type == 'FREEMIUM'\n if (isFreemium) {\n sessionCookie.set({ type: 'idp', provider: 'external-idp:github', sub, tenant })\n } else {\n sessionCookie.set({ email, type: 'sso', sub, tenant })\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","/* eslint-disable max-len */\nimport { Flex } from '@citric/core'\nimport { useEffectOnce } from '@stack-spot/portal-components'\nimport { CSSToCitricAdapter } from '@stack-spot/portal-theme'\nimport '@stack-spot/portal-theme/dist/theme.css'\nimport { useLanguage } from '@stack-spot/portal-translate'\nimport { useState } from 'react'\nimport { Login, LoginProps } from './Login'\nimport { SessionManager } from './SessionManager'\n\ntype AuthStatus = 'unknown' | 'authenticated' | 'unauthenticated'\n\ninterface Props {\n children: React.ReactElement,\n onLogin?: () => void,\n onSession?: () => void,\n onChangeStatus?: (status: AuthStatus) => void,\n customLoginProps?: Omit<LoginProps, 'onSubmit' | 'initialValue'>,\n sessionManager?: SessionManager,\n}\n\nexport const Authenticated = ({ children, onLogin, onSession, customLoginProps, sessionManager, onChangeStatus }: Props) => {\n const [authStatus, setAuthStatus] = useState<AuthStatus>('unknown')\n const language = useLanguage()\n sessionManager ??= SessionManager.instance\n if (!sessionManager) throw new Error('Please, provide a sessionManager')\n\n useEffectOnce(() => {\n async function checkAuth() {\n if (!sessionManager) throw new Error('Please, provide a sessionManager')\n await sessionManager.restoreSession()\n if (sessionManager.urlHasThirdPartyLoginData()) {\n await sessionManager.completeThirdPartyLogin()\n onLogin?.()\n }\n if (sessionManager.hasSession()) {\n setAuthStatus('authenticated')\n onSession?.()\n onChangeStatus?.('authenticated')\n } else {\n setAuthStatus('unauthenticated')\n onChangeStatus?.('unauthenticated')\n }\n }\n\n checkAuth()\n })\n\n if (authStatus === 'unknown') return null\n if (authStatus === 'authenticated') return children\n\n return (\n <CSSToCitricAdapter>\n <Flex justifyContent=\"center\" alignItems=\"center\" flex={1} style={{ height: '100%' }}>\n <Login\n style={{ width: '360px' }}\n onSubmit={data => sessionManager.startThirdPartyLogin({ ...data, locale: language })}\n initialValue={sessionManager.getEmailForLogin()}\n {...(customLoginProps || {})}\n />\n </Flex>\n </CSSToCitricAdapter>\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":["error","sessionKey","__spreadProps","__spreadValues"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stack-spot/auth-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"main": "out/index.js",
|
|
5
5
|
"module": "out/index.mjs",
|
|
6
6
|
"typings": "out/index.d.ts",
|
|
@@ -29,12 +29,12 @@
|
|
|
29
29
|
"@citric/core": "^5.0.0 || ^6.0.0",
|
|
30
30
|
"@citric/icons": "^5.0.0",
|
|
31
31
|
"@citric/ui": "^5.0.0 || ^6.0.0",
|
|
32
|
-
"@stack-spot/auth": "^5.
|
|
32
|
+
"@stack-spot/auth": "^5.3.0",
|
|
33
33
|
"@stack-spot/portal-theme": "^1.0.0",
|
|
34
|
-
"@stack-spot/portal-components": "^1.0.0",
|
|
34
|
+
"@stack-spot/portal-components": "^1.0.0 || ^2.0.0",
|
|
35
35
|
"@stack-spot/portal-translate": "^1.0.0",
|
|
36
36
|
"react": "^18.2.0",
|
|
37
37
|
"react-dom": "^18.2.0",
|
|
38
38
|
"styled-components": "^6.1.10"
|
|
39
39
|
}
|
|
40
|
-
}
|
|
40
|
+
}
|
package/src/SessionManager.ts
CHANGED
|
@@ -4,9 +4,24 @@ import { redirect } from './utils/redirect'
|
|
|
4
4
|
|
|
5
5
|
const sessionKey = 'session'
|
|
6
6
|
|
|
7
|
-
interface SessionManagerConfig extends Pick<AuthConfig, 'accountUrl' | 'authUrl' | 'clientId' | 'defaultTenant'
|
|
8
|
-
|
|
7
|
+
interface SessionManagerConfig extends Pick<AuthConfig, 'accountUrl' | 'authUrl' | 'clientId' | 'defaultTenant'> {
|
|
8
|
+
/**
|
|
9
|
+
* The URL to redirect to when the user logs out.
|
|
10
|
+
* @default location.origin
|
|
11
|
+
*/
|
|
12
|
+
loginUrl?: string,
|
|
13
|
+
/**
|
|
14
|
+
* The URL to redirect to when the login completes in the authentication app. If not provided, will be the same as `loginUrl`.
|
|
15
|
+
* @default loginUrl
|
|
16
|
+
*/
|
|
17
|
+
redirectUrl?: string,
|
|
18
|
+
/**
|
|
19
|
+
* Forbidden authentication types to this Session Manager.
|
|
20
|
+
*/
|
|
9
21
|
blockedAuthTypes?: ThirdPartyAuthType[]
|
|
22
|
+
/**
|
|
23
|
+
* A URL to send login events to (observability).
|
|
24
|
+
*/
|
|
10
25
|
rdUrl?: string,
|
|
11
26
|
}
|
|
12
27
|
|
|
@@ -29,9 +44,12 @@ export class SessionManager {
|
|
|
29
44
|
static instance: SessionManager | undefined
|
|
30
45
|
|
|
31
46
|
private constructor(config: SessionManagerConfig) {
|
|
47
|
+
config.loginUrl ||= location.origin
|
|
48
|
+
const redirectUrl = (config.redirectUrl || config.loginUrl).replace(/([^/])$/, '$1/') // the trailing "/" is required by Stackspot IAM.
|
|
32
49
|
this.config = config
|
|
33
50
|
this.auth = new AuthManager<AuthExtraData>({
|
|
34
51
|
...config,
|
|
52
|
+
redirectUrl,
|
|
35
53
|
storage: localStorage,
|
|
36
54
|
sessionPersistence: {
|
|
37
55
|
load: () => localStorage.getItem(sessionKey),
|
|
@@ -77,7 +95,7 @@ export class SessionManager {
|
|
|
77
95
|
const isSharedSessionTypeBlocked = this.config.blockedAuthTypes?.includes(sharedSessionCookie.type)
|
|
78
96
|
if (isSharedSessionTypeBlocked) return false
|
|
79
97
|
else if (isDifferentSessionActive || !session) {
|
|
80
|
-
await this.
|
|
98
|
+
await this.startThirdPartyLoginUsingTenant(sharedSessionCookie)
|
|
81
99
|
return false
|
|
82
100
|
}
|
|
83
101
|
return true
|
|
@@ -99,7 +117,7 @@ export class SessionManager {
|
|
|
99
117
|
this.current = undefined
|
|
100
118
|
localStorage.removeItem(sessionKey)
|
|
101
119
|
sessionCookie.delete()
|
|
102
|
-
if (redirectToLogin) await redirect(this.config.loginUrl)
|
|
120
|
+
if (redirectToLogin && this.config.loginUrl) await redirect(this.config.loginUrl)
|
|
103
121
|
}
|
|
104
122
|
|
|
105
123
|
async logout() {
|
|
@@ -126,6 +144,18 @@ export class SessionManager {
|
|
|
126
144
|
return url.searchParams.has('state') && !url.searchParams.has('error')
|
|
127
145
|
}
|
|
128
146
|
|
|
147
|
+
async startThirdPartyLoginUsingTenant(data: ThirdPartyLoginParams) {
|
|
148
|
+
const params = new URLSearchParams(location.search)
|
|
149
|
+
const cookie = sessionCookie.get()
|
|
150
|
+
if (!cookie) return
|
|
151
|
+
const authUrl = await this.auth.getThirdPartyLoginFromTenant(data, cookie.tenant, {
|
|
152
|
+
from: location.href,
|
|
153
|
+
finalRedirect: params.get('finalRedirect'),
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
await redirect(authUrl)
|
|
157
|
+
}
|
|
158
|
+
|
|
129
159
|
async completeThirdPartyLogin() {
|
|
130
160
|
const url = new URL(location.toString())
|
|
131
161
|
if (url.searchParams.has('error')) {
|
|
@@ -157,13 +187,13 @@ export class SessionManager {
|
|
|
157
187
|
}
|
|
158
188
|
|
|
159
189
|
private setSessionCookie(session: Session) {
|
|
160
|
-
const { email, account_type, sub } = session.getTokenData()
|
|
161
|
-
if (!email || !sub) return
|
|
190
|
+
const { email, account_type, sub, tenant } = session.getTokenData()
|
|
191
|
+
if (!email || !sub || !tenant) return
|
|
162
192
|
const isFreemium = account_type == 'FREEMIUM'
|
|
163
193
|
if (isFreemium) {
|
|
164
|
-
sessionCookie.set({ type: 'idp', provider: 'external-idp:github', sub })
|
|
194
|
+
sessionCookie.set({ type: 'idp', provider: 'external-idp:github', sub, tenant })
|
|
165
195
|
} else {
|
|
166
|
-
sessionCookie.set({ email, type: 'sso', sub })
|
|
196
|
+
sessionCookie.set({ email, type: 'sso', sub, tenant })
|
|
167
197
|
}
|
|
168
198
|
}
|
|
169
199
|
|
package/src/utils/cookies.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { getCookie, getCookieDomain, removeCookie, setCookie } from '@stack-spot
|
|
|
3
3
|
|
|
4
4
|
const sessionKey = `stk-session${getCookieDomain()}`
|
|
5
5
|
|
|
6
|
-
type SessionCookie = ThirdPartyLoginParams & { sub: string }
|
|
6
|
+
type SessionCookie = ThirdPartyLoginParams & { sub: string, tenant: string }
|
|
7
7
|
|
|
8
8
|
export const sessionCookie = Object.freeze({
|
|
9
9
|
set: (data: SessionCookie) => setCookie(sessionKey, JSON.stringify(data)),
|