@stack-spot/auth-react 1.2.4 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/index.d.ts +35 -1
- package/out/index.js +219 -18
- package/out/index.js.map +1 -1
- package/out/index.mjs +218 -19
- package/out/index.mjs.map +1 -1
- package/package.json +10 -2
- package/src/Authenticated.tsx +58 -0
- package/src/Login.tsx +163 -0
- package/src/index.ts +3 -0
- package/src/utils/hooks/use-effect-once.tsx +43 -0
- package/tsconfig.json +5 -1
package/out/index.d.ts
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
1
2
|
import { Session, ThirdPartyLoginParams, AuthConfig, ThirdPartyAuthType } from '@stack-spot/auth';
|
|
2
3
|
|
|
4
|
+
type LoginType = 'sso' | 'idp';
|
|
5
|
+
interface BaseData {
|
|
6
|
+
type: LoginType;
|
|
7
|
+
}
|
|
8
|
+
interface SSOData extends BaseData {
|
|
9
|
+
type: 'sso';
|
|
10
|
+
email: string;
|
|
11
|
+
}
|
|
12
|
+
interface IDPData extends BaseData {
|
|
13
|
+
type: 'idp';
|
|
14
|
+
provider: 'external-idp:github';
|
|
15
|
+
}
|
|
16
|
+
type LoginData = SSOData | IDPData;
|
|
17
|
+
type LoginProps = {
|
|
18
|
+
initialValue?: string;
|
|
19
|
+
onSubmit: (data: LoginData) => Promise<void>;
|
|
20
|
+
welcomeText?: string;
|
|
21
|
+
removeLoadingOnSuccess?: boolean;
|
|
22
|
+
className?: string;
|
|
23
|
+
style?: React.CSSProperties;
|
|
24
|
+
banner?: React.ReactNode;
|
|
25
|
+
loginTypes?: LoginType[];
|
|
26
|
+
};
|
|
27
|
+
declare const Login: ({ onSubmit, initialValue, welcomeText, removeLoadingOnSuccess, className, style, banner, loginTypes }: LoginProps) => react_jsx_runtime.JSX.Element;
|
|
28
|
+
|
|
29
|
+
interface Props {
|
|
30
|
+
children: React.ReactElement;
|
|
31
|
+
onLogin?: () => void;
|
|
32
|
+
onSession?: () => void;
|
|
33
|
+
customLoginProps?: Omit<LoginProps, 'onSubmit' | 'initialValue'>;
|
|
34
|
+
}
|
|
35
|
+
declare const Authenticated: ({ children, onLogin, onSession, customLoginProps }: Props) => react_jsx_runtime.JSX.Element | null;
|
|
36
|
+
|
|
3
37
|
interface SessionManagerConfig extends Pick<AuthConfig, 'accountUrl' | 'authUrl' | 'clientId' | 'defaultTenant' | 'redirectUrl'> {
|
|
4
38
|
loginUrl: string;
|
|
5
39
|
blockedAuthTypes?: ThirdPartyAuthType[];
|
|
@@ -40,4 +74,4 @@ declare class SessionManager {
|
|
|
40
74
|
|
|
41
75
|
declare function useSession(): Session | undefined;
|
|
42
76
|
|
|
43
|
-
export { SessionManager, useSession };
|
|
77
|
+
export { Authenticated, Login, SessionManager, useSession };
|
package/out/index.js
CHANGED
|
@@ -1,7 +1,129 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
var core = require('@citric/core');
|
|
5
|
+
var portalTheme = require('@stack-spot/portal-theme');
|
|
6
|
+
require('@stack-spot/portal-theme/dist/theme.css');
|
|
7
|
+
var portalTranslate = require('@stack-spot/portal-translate');
|
|
4
8
|
var react = require('react');
|
|
9
|
+
var icons = require('@citric/icons');
|
|
10
|
+
var ui = require('@citric/ui');
|
|
11
|
+
var portalComponents = require('@stack-spot/portal-components');
|
|
12
|
+
var MiniLogo = require('@stack-spot/portal-components/dist/components/MiniLogo');
|
|
13
|
+
var styledComponents = require('styled-components');
|
|
14
|
+
var auth = require('@stack-spot/auth');
|
|
15
|
+
|
|
16
|
+
const LoginBox = styledComponents.styled.form`
|
|
17
|
+
display: flex;
|
|
18
|
+
flex-direction: column;
|
|
19
|
+
justify-content: center;
|
|
20
|
+
gap: 24px;
|
|
21
|
+
|
|
22
|
+
header {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
align-items: center;
|
|
26
|
+
gap: 24px;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.title {
|
|
30
|
+
font-size: 1rem;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.separator {
|
|
34
|
+
padding: 0 8px;
|
|
35
|
+
background-color: ${portalTheme.theme.color.light["400"]};
|
|
36
|
+
color: ${portalTheme.theme.color.light["700"]};
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-direction: row;
|
|
39
|
+
align-items: center;
|
|
40
|
+
justify-content: center;
|
|
41
|
+
gap: 20px;
|
|
42
|
+
margin: 0;
|
|
43
|
+
|
|
44
|
+
&:before, &:after {
|
|
45
|
+
content: '';
|
|
46
|
+
height: 1px;
|
|
47
|
+
flex: 1;
|
|
48
|
+
background-color: ${portalTheme.theme.color.light["600"]};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.error {
|
|
53
|
+
color: ${portalTheme.theme.color.danger["500"]};
|
|
54
|
+
line-height: 1.5rem;
|
|
55
|
+
}
|
|
56
|
+
`;
|
|
57
|
+
const Login = ({ onSubmit, initialValue = "", welcomeText, removeLoadingOnSuccess, className, style, banner, loginTypes = ["idp", "sso"] }) => {
|
|
58
|
+
const t = portalTranslate.useTranslate(dictionary);
|
|
59
|
+
const searchParams = new URLSearchParams(location.search);
|
|
60
|
+
const [error, setError] = react.useState(searchParams.get("error_description") || searchParams.get("error") || "");
|
|
61
|
+
const [loading, setLoading] = react.useState(false);
|
|
62
|
+
const [email, setEmail] = react.useState(initialValue);
|
|
63
|
+
const disabled = !email.match(/\w+@\w+/);
|
|
64
|
+
const idpLoginEnabled = loginTypes.includes("idp");
|
|
65
|
+
const ssoLoginEnabled = loginTypes.includes("sso");
|
|
66
|
+
async function login(type) {
|
|
67
|
+
setError("");
|
|
68
|
+
setLoading(true);
|
|
69
|
+
try {
|
|
70
|
+
const data = type === "sso" ? { type: "sso", email } : { type: "idp", provider: "external-idp:github" };
|
|
71
|
+
await onSubmit(data);
|
|
72
|
+
if (removeLoadingOnSuccess)
|
|
73
|
+
setLoading(false);
|
|
74
|
+
} catch (error2) {
|
|
75
|
+
setLoading(false);
|
|
76
|
+
setError(error2.message || error2.toString());
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function submitForm(e) {
|
|
80
|
+
e.preventDefault();
|
|
81
|
+
if (disabled)
|
|
82
|
+
return;
|
|
83
|
+
login("sso");
|
|
84
|
+
}
|
|
85
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
86
|
+
/* @__PURE__ */ jsxRuntime.jsxs(LoginBox, { onSubmit: submitForm, className, style, children: [
|
|
87
|
+
/* @__PURE__ */ jsxRuntime.jsxs("header", { children: [
|
|
88
|
+
/* @__PURE__ */ jsxRuntime.jsx(MiniLogo.MiniLogo, {}),
|
|
89
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.Text, { className: "title", children: welcomeText || t.welcome })
|
|
90
|
+
] }),
|
|
91
|
+
ssoLoginEnabled && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
92
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.Input, { name: "email", value: email, onChange: (e) => setEmail(e.target.value), placeholder: t.placeholder }),
|
|
93
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.Button, { colorScheme: "primary", disabled: disabled || loading, children: loading ? /* @__PURE__ */ jsxRuntime.jsx(ui.LoadingCircular, {}) : /* @__PURE__ */ jsxRuntime.jsx(core.Text, { children: t.continue }) })
|
|
94
|
+
] }),
|
|
95
|
+
ssoLoginEnabled && idpLoginEnabled && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "separator", children: t.or }),
|
|
96
|
+
idpLoginEnabled && /* @__PURE__ */ jsxRuntime.jsx(core.Button, { colorScheme: "light", type: "button", onClick: () => login("idp"), disabled: loading, children: loading ? /* @__PURE__ */ jsxRuntime.jsx(ui.LoadingCircular, {}) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
97
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.IconBox, { children: /* @__PURE__ */ jsxRuntime.jsx(icons.Github, {}) }),
|
|
98
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.Text, { children: t.loginWithGithub })
|
|
99
|
+
] }) }),
|
|
100
|
+
error && /* @__PURE__ */ jsxRuntime.jsxs(core.Text, { className: "error", children: [
|
|
101
|
+
t.error,
|
|
102
|
+
": ",
|
|
103
|
+
error
|
|
104
|
+
] })
|
|
105
|
+
] }),
|
|
106
|
+
banner ? /* @__PURE__ */ jsxRuntime.jsx(portalComponents.BannerWarning, { children: banner }) : null
|
|
107
|
+
] });
|
|
108
|
+
};
|
|
109
|
+
const dictionary = {
|
|
110
|
+
en: {
|
|
111
|
+
welcome: "Welcome to StackSpot",
|
|
112
|
+
placeholder: "your@email.com",
|
|
113
|
+
continue: "Continue",
|
|
114
|
+
or: "or",
|
|
115
|
+
loginWithGithub: "Login with Github",
|
|
116
|
+
error: "Error while attempting to login"
|
|
117
|
+
},
|
|
118
|
+
pt: {
|
|
119
|
+
welcome: "Bem vindo \xE0 StackSpot",
|
|
120
|
+
placeholder: "nome@email.com",
|
|
121
|
+
continue: "Continuar",
|
|
122
|
+
or: "ou",
|
|
123
|
+
loginWithGithub: "Logar com o GitHub",
|
|
124
|
+
error: "Erro ao fazer login"
|
|
125
|
+
}
|
|
126
|
+
};
|
|
5
127
|
|
|
6
128
|
const DOMAIN_REGEX = new RegExp(/(\.*(prd|stg|dev)*.stackspot.com)|localhost/);
|
|
7
129
|
|
|
@@ -40,27 +162,27 @@ const redirect = async (url) => {
|
|
|
40
162
|
await new Promise(() => "");
|
|
41
163
|
};
|
|
42
164
|
|
|
43
|
-
var __defProp = Object.defineProperty;
|
|
44
|
-
var __defProps = Object.defineProperties;
|
|
45
|
-
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
46
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
47
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
48
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
49
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
50
|
-
var __spreadValues = (a, b) => {
|
|
165
|
+
var __defProp$1 = Object.defineProperty;
|
|
166
|
+
var __defProps$1 = Object.defineProperties;
|
|
167
|
+
var __getOwnPropDescs$1 = Object.getOwnPropertyDescriptors;
|
|
168
|
+
var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols;
|
|
169
|
+
var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
|
|
170
|
+
var __propIsEnum$1 = Object.prototype.propertyIsEnumerable;
|
|
171
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
172
|
+
var __spreadValues$1 = (a, b) => {
|
|
51
173
|
for (var prop in b || (b = {}))
|
|
52
|
-
if (__hasOwnProp.call(b, prop))
|
|
53
|
-
__defNormalProp(a, prop, b[prop]);
|
|
54
|
-
if (__getOwnPropSymbols)
|
|
55
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
56
|
-
if (__propIsEnum.call(b, prop))
|
|
57
|
-
__defNormalProp(a, prop, b[prop]);
|
|
174
|
+
if (__hasOwnProp$1.call(b, prop))
|
|
175
|
+
__defNormalProp$1(a, prop, b[prop]);
|
|
176
|
+
if (__getOwnPropSymbols$1)
|
|
177
|
+
for (var prop of __getOwnPropSymbols$1(b)) {
|
|
178
|
+
if (__propIsEnum$1.call(b, prop))
|
|
179
|
+
__defNormalProp$1(a, prop, b[prop]);
|
|
58
180
|
}
|
|
59
181
|
return a;
|
|
60
182
|
};
|
|
61
|
-
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
183
|
+
var __spreadProps$1 = (a, b) => __defProps$1(a, __getOwnPropDescs$1(b));
|
|
62
184
|
var __publicField = (obj, key, value) => {
|
|
63
|
-
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
185
|
+
__defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
64
186
|
return value;
|
|
65
187
|
};
|
|
66
188
|
const sessionKey = "session";
|
|
@@ -71,7 +193,7 @@ const _SessionManager = class _SessionManager {
|
|
|
71
193
|
__publicField(this, "config");
|
|
72
194
|
__publicField(this, "changeListeners", []);
|
|
73
195
|
this.config = config;
|
|
74
|
-
this.auth = new auth.AuthManager(__spreadProps(__spreadValues({}, config), {
|
|
196
|
+
this.auth = new auth.AuthManager(__spreadProps$1(__spreadValues$1({}, config), {
|
|
75
197
|
storage: localStorage,
|
|
76
198
|
sessionPersistence: {
|
|
77
199
|
load: () => localStorage.getItem(sessionKey),
|
|
@@ -226,6 +348,83 @@ ${error}`);
|
|
|
226
348
|
__publicField(_SessionManager, "instance");
|
|
227
349
|
let SessionManager = _SessionManager;
|
|
228
350
|
|
|
351
|
+
const useEffectOnce = (effect) => {
|
|
352
|
+
const effectFn = react.useRef(effect);
|
|
353
|
+
const destroyFn = react.useRef();
|
|
354
|
+
const effectCalled = react.useRef(false);
|
|
355
|
+
const rendered = react.useRef(false);
|
|
356
|
+
const [, setVal] = react.useState(0);
|
|
357
|
+
if (effectCalled.current) {
|
|
358
|
+
rendered.current = true;
|
|
359
|
+
}
|
|
360
|
+
react.useEffect(() => {
|
|
361
|
+
if (!effectCalled.current) {
|
|
362
|
+
destroyFn.current = effectFn.current();
|
|
363
|
+
effectCalled.current = true;
|
|
364
|
+
}
|
|
365
|
+
setVal((val) => val + 1);
|
|
366
|
+
return () => {
|
|
367
|
+
if (!rendered.current)
|
|
368
|
+
return;
|
|
369
|
+
if (destroyFn.current)
|
|
370
|
+
destroyFn.current();
|
|
371
|
+
};
|
|
372
|
+
}, []);
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
var __defProp = Object.defineProperty;
|
|
376
|
+
var __defProps = Object.defineProperties;
|
|
377
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
378
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
379
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
380
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
381
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
382
|
+
var __spreadValues = (a, b) => {
|
|
383
|
+
for (var prop in b || (b = {}))
|
|
384
|
+
if (__hasOwnProp.call(b, prop))
|
|
385
|
+
__defNormalProp(a, prop, b[prop]);
|
|
386
|
+
if (__getOwnPropSymbols)
|
|
387
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
388
|
+
if (__propIsEnum.call(b, prop))
|
|
389
|
+
__defNormalProp(a, prop, b[prop]);
|
|
390
|
+
}
|
|
391
|
+
return a;
|
|
392
|
+
};
|
|
393
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
394
|
+
const Authenticated = ({ children, onLogin, onSession, customLoginProps }) => {
|
|
395
|
+
const [authStatus, setAuthStatus] = react.useState("unknown");
|
|
396
|
+
const language = portalTranslate.useLanguage();
|
|
397
|
+
const sessionManager = SessionManager.instance;
|
|
398
|
+
useEffectOnce(() => {
|
|
399
|
+
async function checkAuth() {
|
|
400
|
+
await sessionManager.restoreSession();
|
|
401
|
+
if (sessionManager.urlHasThirdPartyLoginData()) {
|
|
402
|
+
await sessionManager.completeThirdPartyLogin();
|
|
403
|
+
onLogin == null ? void 0 : onLogin();
|
|
404
|
+
}
|
|
405
|
+
if (sessionManager.hasSession()) {
|
|
406
|
+
setAuthStatus("authenticated");
|
|
407
|
+
onSession == null ? void 0 : onSession();
|
|
408
|
+
} else {
|
|
409
|
+
setAuthStatus("unauthenticated");
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
checkAuth();
|
|
413
|
+
});
|
|
414
|
+
if (authStatus === "unknown")
|
|
415
|
+
return null;
|
|
416
|
+
if (authStatus === "authenticated")
|
|
417
|
+
return children;
|
|
418
|
+
return /* @__PURE__ */ jsxRuntime.jsx(portalTheme.CSSToCitricAdapter, { children: /* @__PURE__ */ jsxRuntime.jsx(core.Flex, { justifyContent: "center", alignItems: "center", flex: 1, style: { height: "100%" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
419
|
+
Login,
|
|
420
|
+
__spreadValues({
|
|
421
|
+
style: { width: "360px" },
|
|
422
|
+
onSubmit: (data) => sessionManager.startThirdPartyLogin(__spreadProps(__spreadValues({}, data), { locale: language })),
|
|
423
|
+
initialValue: sessionManager.getEmailForLogin()
|
|
424
|
+
}, customLoginProps || {})
|
|
425
|
+
) }) });
|
|
426
|
+
};
|
|
427
|
+
|
|
229
428
|
function useSession() {
|
|
230
429
|
const manager = SessionManager.instance;
|
|
231
430
|
const [session, setSession] = react.useState((manager == null ? void 0 : manager.hasSession()) ? manager.getSession() : void 0);
|
|
@@ -235,6 +434,8 @@ function useSession() {
|
|
|
235
434
|
return session;
|
|
236
435
|
}
|
|
237
436
|
|
|
437
|
+
exports.Authenticated = Authenticated;
|
|
438
|
+
exports.Login = Login;
|
|
238
439
|
exports.SessionManager = SessionManager;
|
|
239
440
|
exports.useSession = useSession;
|
|
240
441
|
//# sourceMappingURL=index.js.map
|
package/out/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/utils/regex.ts","../src/utils/cookies.ts","../src/utils/redirect.ts","../src/SessionManager.ts","../src/hooks.ts"],"sourcesContent":["export const DOMAIN_REGEX = new RegExp(/(\\.*(prd|stg|dev)*.stackspot.com)|localhost/)","import { ThirdPartyLoginParams } from \"@stack-spot/auth\"\nimport { DOMAIN_REGEX } from './regex'\n\nconst portalUrl = new URL(location.href)\nconst cookieDomain = DOMAIN_REGEX.exec(portalUrl.host)?.[0]\nconst defaultCookieAttributes = `domain=${cookieDomain}; SameSite=Strict;`\nconst sessionKey = `stk-session${cookieDomain}`\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})","\n\nexport 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\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","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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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/Login.tsx","../src/utils/regex.ts","../src/utils/cookies.ts","../src/utils/redirect.ts","../src/SessionManager.ts","../src/utils/hooks/use-effect-once.tsx","../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/dist/components/MiniLogo'\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['400']};\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","export const DOMAIN_REGEX = new RegExp(/(\\.*(prd|stg|dev)*.stackspot.com)|localhost/)","import { ThirdPartyLoginParams } from \"@stack-spot/auth\"\nimport { DOMAIN_REGEX } from './regex'\n\nconst portalUrl = new URL(location.href)\nconst cookieDomain = DOMAIN_REGEX.exec(portalUrl.host)?.[0]\nconst defaultCookieAttributes = `domain=${cookieDomain}; SameSite=Strict;`\nconst sessionKey = `stk-session${cookieDomain}`\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})","\n\nexport 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\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","import { useEffect, useRef, useState } from 'react'\n\n/**\n * Code taken from https://blog.ag-grid.com/avoiding-react-18-double-mount/\n * \n * Attention: don't use this hook unless you really have to!\n * \n * This hook fixes the React 18 behavior of calling useEffect hooks twice in strict/development mode, which ruins some mounting/unmounting\n * behaviors.\n * \n * @param effect refer to React's useEffect.\n */\nexport const useEffectOnce = (effect: () => void | (() => void)) => {\n const effectFn = useRef<() => void | (() => void)>(effect)\n const destroyFn = useRef<void | (() => void)>()\n const effectCalled = useRef(false)\n const rendered = useRef(false)\n const [, setVal] = useState<number>(0)\n\n if (effectCalled.current) {\n rendered.current = true\n }\n\n useEffect(() => {\n // only execute the effect first time around\n if (!effectCalled.current) {\n destroyFn.current = effectFn.current()\n effectCalled.current = true\n }\n\n // this forces one render after the effect is run\n setVal((val) => val + 1)\n\n return () => {\n // if the comp didn't render since the useEffect was called,\n // we know it's the dummy React cycle\n if (!rendered.current) return\n\n // otherwise this is not a dummy destroy, so call the destroy func\n if (destroyFn.current) destroyFn.current()\n }\n }, [])\n}\n","/* eslint-disable max-len */\nimport { Flex } from '@citric/core'\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'\nimport { useEffectOnce } from './utils/hooks/use-effect-once'\n\ntype AuthStatus = 'unknown' | 'authenticated' | 'unauthenticated'\n\ninterface Props {\n children: React.ReactElement\n onLogin?: () => void\n onSession?: () => void\n customLoginProps?: Omit<LoginProps, 'onSubmit' | 'initialValue'>\n}\n\nexport const Authenticated = ({ children, onLogin, onSession, customLoginProps }: Props) => {\n const [authStatus, setAuthStatus] = useState<AuthStatus>('unknown')\n const language = useLanguage()\n const sessionManager = SessionManager.instance\n\n useEffectOnce(() => {\n async function checkAuth() {\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 } else {\n setAuthStatus('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","AuthManager","__spreadProps","__spreadValues","useRef","useEffect","useLanguage","CSSToCitricAdapter","Flex"],"mappings":"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"}
|
package/out/index.mjs
CHANGED
|
@@ -1,5 +1,127 @@
|
|
|
1
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { Text, Input, Button, IconBox, Flex } from '@citric/core';
|
|
3
|
+
import { theme, CSSToCitricAdapter } from '@stack-spot/portal-theme';
|
|
4
|
+
import '@stack-spot/portal-theme/dist/theme.css';
|
|
5
|
+
import { useTranslate, useLanguage } from '@stack-spot/portal-translate';
|
|
6
|
+
import { useState, useRef, useEffect } from 'react';
|
|
7
|
+
import { Github } from '@citric/icons';
|
|
8
|
+
import { LoadingCircular } from '@citric/ui';
|
|
9
|
+
import { BannerWarning } from '@stack-spot/portal-components';
|
|
10
|
+
import { MiniLogo } from '@stack-spot/portal-components/dist/components/MiniLogo';
|
|
11
|
+
import { styled } from 'styled-components';
|
|
1
12
|
import { AuthManager } from '@stack-spot/auth';
|
|
2
|
-
|
|
13
|
+
|
|
14
|
+
const LoginBox = styled.form`
|
|
15
|
+
display: flex;
|
|
16
|
+
flex-direction: column;
|
|
17
|
+
justify-content: center;
|
|
18
|
+
gap: 24px;
|
|
19
|
+
|
|
20
|
+
header {
|
|
21
|
+
display: flex;
|
|
22
|
+
flex-direction: column;
|
|
23
|
+
align-items: center;
|
|
24
|
+
gap: 24px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.title {
|
|
28
|
+
font-size: 1rem;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.separator {
|
|
32
|
+
padding: 0 8px;
|
|
33
|
+
background-color: ${theme.color.light["400"]};
|
|
34
|
+
color: ${theme.color.light["700"]};
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: row;
|
|
37
|
+
align-items: center;
|
|
38
|
+
justify-content: center;
|
|
39
|
+
gap: 20px;
|
|
40
|
+
margin: 0;
|
|
41
|
+
|
|
42
|
+
&:before, &:after {
|
|
43
|
+
content: '';
|
|
44
|
+
height: 1px;
|
|
45
|
+
flex: 1;
|
|
46
|
+
background-color: ${theme.color.light["600"]};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.error {
|
|
51
|
+
color: ${theme.color.danger["500"]};
|
|
52
|
+
line-height: 1.5rem;
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
const Login = ({ onSubmit, initialValue = "", welcomeText, removeLoadingOnSuccess, className, style, banner, loginTypes = ["idp", "sso"] }) => {
|
|
56
|
+
const t = useTranslate(dictionary);
|
|
57
|
+
const searchParams = new URLSearchParams(location.search);
|
|
58
|
+
const [error, setError] = useState(searchParams.get("error_description") || searchParams.get("error") || "");
|
|
59
|
+
const [loading, setLoading] = useState(false);
|
|
60
|
+
const [email, setEmail] = useState(initialValue);
|
|
61
|
+
const disabled = !email.match(/\w+@\w+/);
|
|
62
|
+
const idpLoginEnabled = loginTypes.includes("idp");
|
|
63
|
+
const ssoLoginEnabled = loginTypes.includes("sso");
|
|
64
|
+
async function login(type) {
|
|
65
|
+
setError("");
|
|
66
|
+
setLoading(true);
|
|
67
|
+
try {
|
|
68
|
+
const data = type === "sso" ? { type: "sso", email } : { type: "idp", provider: "external-idp:github" };
|
|
69
|
+
await onSubmit(data);
|
|
70
|
+
if (removeLoadingOnSuccess)
|
|
71
|
+
setLoading(false);
|
|
72
|
+
} catch (error2) {
|
|
73
|
+
setLoading(false);
|
|
74
|
+
setError(error2.message || error2.toString());
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function submitForm(e) {
|
|
78
|
+
e.preventDefault();
|
|
79
|
+
if (disabled)
|
|
80
|
+
return;
|
|
81
|
+
login("sso");
|
|
82
|
+
}
|
|
83
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
84
|
+
/* @__PURE__ */ jsxs(LoginBox, { onSubmit: submitForm, className, style, children: [
|
|
85
|
+
/* @__PURE__ */ jsxs("header", { children: [
|
|
86
|
+
/* @__PURE__ */ jsx(MiniLogo, {}),
|
|
87
|
+
/* @__PURE__ */ jsx(Text, { className: "title", children: welcomeText || t.welcome })
|
|
88
|
+
] }),
|
|
89
|
+
ssoLoginEnabled && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
90
|
+
/* @__PURE__ */ jsx(Input, { name: "email", value: email, onChange: (e) => setEmail(e.target.value), placeholder: t.placeholder }),
|
|
91
|
+
/* @__PURE__ */ jsx(Button, { colorScheme: "primary", disabled: disabled || loading, children: loading ? /* @__PURE__ */ jsx(LoadingCircular, {}) : /* @__PURE__ */ jsx(Text, { children: t.continue }) })
|
|
92
|
+
] }),
|
|
93
|
+
ssoLoginEnabled && idpLoginEnabled && /* @__PURE__ */ jsx("p", { className: "separator", children: t.or }),
|
|
94
|
+
idpLoginEnabled && /* @__PURE__ */ jsx(Button, { colorScheme: "light", type: "button", onClick: () => login("idp"), disabled: loading, children: loading ? /* @__PURE__ */ jsx(LoadingCircular, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
95
|
+
/* @__PURE__ */ jsx(IconBox, { children: /* @__PURE__ */ jsx(Github, {}) }),
|
|
96
|
+
/* @__PURE__ */ jsx(Text, { children: t.loginWithGithub })
|
|
97
|
+
] }) }),
|
|
98
|
+
error && /* @__PURE__ */ jsxs(Text, { className: "error", children: [
|
|
99
|
+
t.error,
|
|
100
|
+
": ",
|
|
101
|
+
error
|
|
102
|
+
] })
|
|
103
|
+
] }),
|
|
104
|
+
banner ? /* @__PURE__ */ jsx(BannerWarning, { children: banner }) : null
|
|
105
|
+
] });
|
|
106
|
+
};
|
|
107
|
+
const dictionary = {
|
|
108
|
+
en: {
|
|
109
|
+
welcome: "Welcome to StackSpot",
|
|
110
|
+
placeholder: "your@email.com",
|
|
111
|
+
continue: "Continue",
|
|
112
|
+
or: "or",
|
|
113
|
+
loginWithGithub: "Login with Github",
|
|
114
|
+
error: "Error while attempting to login"
|
|
115
|
+
},
|
|
116
|
+
pt: {
|
|
117
|
+
welcome: "Bem vindo \xE0 StackSpot",
|
|
118
|
+
placeholder: "nome@email.com",
|
|
119
|
+
continue: "Continuar",
|
|
120
|
+
or: "ou",
|
|
121
|
+
loginWithGithub: "Logar com o GitHub",
|
|
122
|
+
error: "Erro ao fazer login"
|
|
123
|
+
}
|
|
124
|
+
};
|
|
3
125
|
|
|
4
126
|
const DOMAIN_REGEX = new RegExp(/(\.*(prd|stg|dev)*.stackspot.com)|localhost/);
|
|
5
127
|
|
|
@@ -38,27 +160,27 @@ const redirect = async (url) => {
|
|
|
38
160
|
await new Promise(() => "");
|
|
39
161
|
};
|
|
40
162
|
|
|
41
|
-
var __defProp = Object.defineProperty;
|
|
42
|
-
var __defProps = Object.defineProperties;
|
|
43
|
-
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
44
|
-
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
45
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
46
|
-
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
47
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
48
|
-
var __spreadValues = (a, b) => {
|
|
163
|
+
var __defProp$1 = Object.defineProperty;
|
|
164
|
+
var __defProps$1 = Object.defineProperties;
|
|
165
|
+
var __getOwnPropDescs$1 = Object.getOwnPropertyDescriptors;
|
|
166
|
+
var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols;
|
|
167
|
+
var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
|
|
168
|
+
var __propIsEnum$1 = Object.prototype.propertyIsEnumerable;
|
|
169
|
+
var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
170
|
+
var __spreadValues$1 = (a, b) => {
|
|
49
171
|
for (var prop in b || (b = {}))
|
|
50
|
-
if (__hasOwnProp.call(b, prop))
|
|
51
|
-
__defNormalProp(a, prop, b[prop]);
|
|
52
|
-
if (__getOwnPropSymbols)
|
|
53
|
-
for (var prop of __getOwnPropSymbols(b)) {
|
|
54
|
-
if (__propIsEnum.call(b, prop))
|
|
55
|
-
__defNormalProp(a, prop, b[prop]);
|
|
172
|
+
if (__hasOwnProp$1.call(b, prop))
|
|
173
|
+
__defNormalProp$1(a, prop, b[prop]);
|
|
174
|
+
if (__getOwnPropSymbols$1)
|
|
175
|
+
for (var prop of __getOwnPropSymbols$1(b)) {
|
|
176
|
+
if (__propIsEnum$1.call(b, prop))
|
|
177
|
+
__defNormalProp$1(a, prop, b[prop]);
|
|
56
178
|
}
|
|
57
179
|
return a;
|
|
58
180
|
};
|
|
59
|
-
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
181
|
+
var __spreadProps$1 = (a, b) => __defProps$1(a, __getOwnPropDescs$1(b));
|
|
60
182
|
var __publicField = (obj, key, value) => {
|
|
61
|
-
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
183
|
+
__defNormalProp$1(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
62
184
|
return value;
|
|
63
185
|
};
|
|
64
186
|
const sessionKey = "session";
|
|
@@ -69,7 +191,7 @@ const _SessionManager = class _SessionManager {
|
|
|
69
191
|
__publicField(this, "config");
|
|
70
192
|
__publicField(this, "changeListeners", []);
|
|
71
193
|
this.config = config;
|
|
72
|
-
this.auth = new AuthManager(__spreadProps(__spreadValues({}, config), {
|
|
194
|
+
this.auth = new AuthManager(__spreadProps$1(__spreadValues$1({}, config), {
|
|
73
195
|
storage: localStorage,
|
|
74
196
|
sessionPersistence: {
|
|
75
197
|
load: () => localStorage.getItem(sessionKey),
|
|
@@ -224,6 +346,83 @@ ${error}`);
|
|
|
224
346
|
__publicField(_SessionManager, "instance");
|
|
225
347
|
let SessionManager = _SessionManager;
|
|
226
348
|
|
|
349
|
+
const useEffectOnce = (effect) => {
|
|
350
|
+
const effectFn = useRef(effect);
|
|
351
|
+
const destroyFn = useRef();
|
|
352
|
+
const effectCalled = useRef(false);
|
|
353
|
+
const rendered = useRef(false);
|
|
354
|
+
const [, setVal] = useState(0);
|
|
355
|
+
if (effectCalled.current) {
|
|
356
|
+
rendered.current = true;
|
|
357
|
+
}
|
|
358
|
+
useEffect(() => {
|
|
359
|
+
if (!effectCalled.current) {
|
|
360
|
+
destroyFn.current = effectFn.current();
|
|
361
|
+
effectCalled.current = true;
|
|
362
|
+
}
|
|
363
|
+
setVal((val) => val + 1);
|
|
364
|
+
return () => {
|
|
365
|
+
if (!rendered.current)
|
|
366
|
+
return;
|
|
367
|
+
if (destroyFn.current)
|
|
368
|
+
destroyFn.current();
|
|
369
|
+
};
|
|
370
|
+
}, []);
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
var __defProp = Object.defineProperty;
|
|
374
|
+
var __defProps = Object.defineProperties;
|
|
375
|
+
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
376
|
+
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
377
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
378
|
+
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
379
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
380
|
+
var __spreadValues = (a, b) => {
|
|
381
|
+
for (var prop in b || (b = {}))
|
|
382
|
+
if (__hasOwnProp.call(b, prop))
|
|
383
|
+
__defNormalProp(a, prop, b[prop]);
|
|
384
|
+
if (__getOwnPropSymbols)
|
|
385
|
+
for (var prop of __getOwnPropSymbols(b)) {
|
|
386
|
+
if (__propIsEnum.call(b, prop))
|
|
387
|
+
__defNormalProp(a, prop, b[prop]);
|
|
388
|
+
}
|
|
389
|
+
return a;
|
|
390
|
+
};
|
|
391
|
+
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
392
|
+
const Authenticated = ({ children, onLogin, onSession, customLoginProps }) => {
|
|
393
|
+
const [authStatus, setAuthStatus] = useState("unknown");
|
|
394
|
+
const language = useLanguage();
|
|
395
|
+
const sessionManager = SessionManager.instance;
|
|
396
|
+
useEffectOnce(() => {
|
|
397
|
+
async function checkAuth() {
|
|
398
|
+
await sessionManager.restoreSession();
|
|
399
|
+
if (sessionManager.urlHasThirdPartyLoginData()) {
|
|
400
|
+
await sessionManager.completeThirdPartyLogin();
|
|
401
|
+
onLogin == null ? void 0 : onLogin();
|
|
402
|
+
}
|
|
403
|
+
if (sessionManager.hasSession()) {
|
|
404
|
+
setAuthStatus("authenticated");
|
|
405
|
+
onSession == null ? void 0 : onSession();
|
|
406
|
+
} else {
|
|
407
|
+
setAuthStatus("unauthenticated");
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
checkAuth();
|
|
411
|
+
});
|
|
412
|
+
if (authStatus === "unknown")
|
|
413
|
+
return null;
|
|
414
|
+
if (authStatus === "authenticated")
|
|
415
|
+
return children;
|
|
416
|
+
return /* @__PURE__ */ jsx(CSSToCitricAdapter, { children: /* @__PURE__ */ jsx(Flex, { justifyContent: "center", alignItems: "center", flex: 1, style: { height: "100%" }, children: /* @__PURE__ */ jsx(
|
|
417
|
+
Login,
|
|
418
|
+
__spreadValues({
|
|
419
|
+
style: { width: "360px" },
|
|
420
|
+
onSubmit: (data) => sessionManager.startThirdPartyLogin(__spreadProps(__spreadValues({}, data), { locale: language })),
|
|
421
|
+
initialValue: sessionManager.getEmailForLogin()
|
|
422
|
+
}, customLoginProps || {})
|
|
423
|
+
) }) });
|
|
424
|
+
};
|
|
425
|
+
|
|
227
426
|
function useSession() {
|
|
228
427
|
const manager = SessionManager.instance;
|
|
229
428
|
const [session, setSession] = useState((manager == null ? void 0 : manager.hasSession()) ? manager.getSession() : void 0);
|
|
@@ -233,5 +432,5 @@ function useSession() {
|
|
|
233
432
|
return session;
|
|
234
433
|
}
|
|
235
434
|
|
|
236
|
-
export { SessionManager, useSession };
|
|
435
|
+
export { Authenticated, Login, SessionManager, useSession };
|
|
237
436
|
//# sourceMappingURL=index.mjs.map
|
package/out/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../src/utils/regex.ts","../src/utils/cookies.ts","../src/utils/redirect.ts","../src/SessionManager.ts","../src/hooks.ts"],"sourcesContent":["export const DOMAIN_REGEX = new RegExp(/(\\.*(prd|stg|dev)*.stackspot.com)|localhost/)","import { ThirdPartyLoginParams } from \"@stack-spot/auth\"\nimport { DOMAIN_REGEX } from './regex'\n\nconst portalUrl = new URL(location.href)\nconst cookieDomain = DOMAIN_REGEX.exec(portalUrl.host)?.[0]\nconst defaultCookieAttributes = `domain=${cookieDomain}; SameSite=Strict;`\nconst sessionKey = `stk-session${cookieDomain}`\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})","\n\nexport 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\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","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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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../src/Login.tsx","../src/utils/regex.ts","../src/utils/cookies.ts","../src/utils/redirect.ts","../src/SessionManager.ts","../src/utils/hooks/use-effect-once.tsx","../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/dist/components/MiniLogo'\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['400']};\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","export const DOMAIN_REGEX = new RegExp(/(\\.*(prd|stg|dev)*.stackspot.com)|localhost/)","import { ThirdPartyLoginParams } from \"@stack-spot/auth\"\nimport { DOMAIN_REGEX } from './regex'\n\nconst portalUrl = new URL(location.href)\nconst cookieDomain = DOMAIN_REGEX.exec(portalUrl.host)?.[0]\nconst defaultCookieAttributes = `domain=${cookieDomain}; SameSite=Strict;`\nconst sessionKey = `stk-session${cookieDomain}`\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})","\n\nexport 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\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","import { useEffect, useRef, useState } from 'react'\n\n/**\n * Code taken from https://blog.ag-grid.com/avoiding-react-18-double-mount/\n * \n * Attention: don't use this hook unless you really have to!\n * \n * This hook fixes the React 18 behavior of calling useEffect hooks twice in strict/development mode, which ruins some mounting/unmounting\n * behaviors.\n * \n * @param effect refer to React's useEffect.\n */\nexport const useEffectOnce = (effect: () => void | (() => void)) => {\n const effectFn = useRef<() => void | (() => void)>(effect)\n const destroyFn = useRef<void | (() => void)>()\n const effectCalled = useRef(false)\n const rendered = useRef(false)\n const [, setVal] = useState<number>(0)\n\n if (effectCalled.current) {\n rendered.current = true\n }\n\n useEffect(() => {\n // only execute the effect first time around\n if (!effectCalled.current) {\n destroyFn.current = effectFn.current()\n effectCalled.current = true\n }\n\n // this forces one render after the effect is run\n setVal((val) => val + 1)\n\n return () => {\n // if the comp didn't render since the useEffect was called,\n // we know it's the dummy React cycle\n if (!rendered.current) return\n\n // otherwise this is not a dummy destroy, so call the destroy func\n if (destroyFn.current) destroyFn.current()\n }\n }, [])\n}\n","/* eslint-disable max-len */\nimport { Flex } from '@citric/core'\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'\nimport { useEffectOnce } from './utils/hooks/use-effect-once'\n\ntype AuthStatus = 'unknown' | 'authenticated' | 'unauthenticated'\n\ninterface Props {\n children: React.ReactElement\n onLogin?: () => void\n onSession?: () => void\n customLoginProps?: Omit<LoginProps, 'onSubmit' | 'initialValue'>\n}\n\nexport const Authenticated = ({ children, onLogin, onSession, customLoginProps }: Props) => {\n const [authStatus, setAuthStatus] = useState<AuthStatus>('unknown')\n const language = useLanguage()\n const sessionManager = SessionManager.instance\n\n useEffectOnce(() => {\n async function checkAuth() {\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 } else {\n setAuthStatus('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": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"main": "out/index.js",
|
|
5
5
|
"module": "out/index.mjs",
|
|
6
6
|
"typings": "out/index.d.ts",
|
|
@@ -21,8 +21,16 @@
|
|
|
21
21
|
"@types/react-dom": "^18.2.15"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
|
+
"@citric/core": ">=5.0.0",
|
|
25
|
+
"@citric/icons": ">=5.0.0",
|
|
26
|
+
"@citric/ui": ">=5.0.0",
|
|
27
|
+
"@stack-spot/auth": ">=5.0.0",
|
|
28
|
+
"@stack-spot/portal-theme": ">=0.0.11",
|
|
29
|
+
"@stack-spot/portal-components": ">=0.0.12",
|
|
30
|
+
"@stack-spot/portal-translate": ">=0.0.6",
|
|
31
|
+
"@stack-spot/portal-layout": ">=0.0.47",
|
|
24
32
|
"react": ">=18.2.0",
|
|
25
33
|
"react-dom": ">=18.2.0",
|
|
26
|
-
"
|
|
34
|
+
"styled-components": ">=6.1.1"
|
|
27
35
|
}
|
|
28
36
|
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
import { Flex } from '@citric/core'
|
|
3
|
+
import { CSSToCitricAdapter } from '@stack-spot/portal-theme'
|
|
4
|
+
import '@stack-spot/portal-theme/dist/theme.css'
|
|
5
|
+
import { useLanguage } from '@stack-spot/portal-translate'
|
|
6
|
+
import { useState } from 'react'
|
|
7
|
+
import { Login, LoginProps } from './Login'
|
|
8
|
+
import { SessionManager } from './SessionManager'
|
|
9
|
+
import { useEffectOnce } from './utils/hooks/use-effect-once'
|
|
10
|
+
|
|
11
|
+
type AuthStatus = 'unknown' | 'authenticated' | 'unauthenticated'
|
|
12
|
+
|
|
13
|
+
interface Props {
|
|
14
|
+
children: React.ReactElement
|
|
15
|
+
onLogin?: () => void
|
|
16
|
+
onSession?: () => void
|
|
17
|
+
customLoginProps?: Omit<LoginProps, 'onSubmit' | 'initialValue'>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const Authenticated = ({ children, onLogin, onSession, customLoginProps }: Props) => {
|
|
21
|
+
const [authStatus, setAuthStatus] = useState<AuthStatus>('unknown')
|
|
22
|
+
const language = useLanguage()
|
|
23
|
+
const sessionManager = SessionManager.instance
|
|
24
|
+
|
|
25
|
+
useEffectOnce(() => {
|
|
26
|
+
async function checkAuth() {
|
|
27
|
+
await sessionManager.restoreSession()
|
|
28
|
+
if (sessionManager.urlHasThirdPartyLoginData()) {
|
|
29
|
+
await sessionManager.completeThirdPartyLogin()
|
|
30
|
+
onLogin?.()
|
|
31
|
+
}
|
|
32
|
+
if (sessionManager.hasSession()) {
|
|
33
|
+
setAuthStatus('authenticated')
|
|
34
|
+
onSession?.()
|
|
35
|
+
} else {
|
|
36
|
+
setAuthStatus('unauthenticated')
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
checkAuth()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
if (authStatus === 'unknown') return null
|
|
44
|
+
if (authStatus === 'authenticated') return children
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<CSSToCitricAdapter>
|
|
48
|
+
<Flex justifyContent="center" alignItems="center" flex={1} style={{ height: '100%' }}>
|
|
49
|
+
<Login
|
|
50
|
+
style={{ width: '360px' }}
|
|
51
|
+
onSubmit={data => sessionManager.startThirdPartyLogin({ ...data, locale: language })}
|
|
52
|
+
initialValue={sessionManager.getEmailForLogin()}
|
|
53
|
+
{...(customLoginProps || {})}
|
|
54
|
+
/>
|
|
55
|
+
</Flex>
|
|
56
|
+
</CSSToCitricAdapter>
|
|
57
|
+
)
|
|
58
|
+
}
|
package/src/Login.tsx
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/* eslint-disable max-len */
|
|
2
|
+
import { Button, IconBox, Input, Text } from '@citric/core'
|
|
3
|
+
import { Github } from '@citric/icons'
|
|
4
|
+
import { LoadingCircular } from '@citric/ui'
|
|
5
|
+
import { BannerWarning } from '@stack-spot/portal-components'
|
|
6
|
+
import { MiniLogo } from '@stack-spot/portal-components/dist/components/MiniLogo'
|
|
7
|
+
import { theme } from '@stack-spot/portal-theme'
|
|
8
|
+
import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
|
|
9
|
+
import { useState } from 'react'
|
|
10
|
+
import { styled } from 'styled-components'
|
|
11
|
+
|
|
12
|
+
export type LoginType = 'sso' | 'idp'
|
|
13
|
+
|
|
14
|
+
interface BaseData {
|
|
15
|
+
type: LoginType,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface SSOData extends BaseData {
|
|
19
|
+
type: 'sso',
|
|
20
|
+
email: string,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface IDPData extends BaseData {
|
|
24
|
+
type: 'idp',
|
|
25
|
+
provider: 'external-idp:github',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type LoginData = SSOData | IDPData
|
|
29
|
+
|
|
30
|
+
export type LoginProps = {
|
|
31
|
+
initialValue?: string,
|
|
32
|
+
onSubmit: (data: LoginData) => Promise<void>,
|
|
33
|
+
welcomeText?: string,
|
|
34
|
+
removeLoadingOnSuccess?: boolean,
|
|
35
|
+
className?: string,
|
|
36
|
+
style?: React.CSSProperties,
|
|
37
|
+
banner?: React.ReactNode,
|
|
38
|
+
loginTypes?: LoginType[]
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const LoginBox = styled.form`
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
gap: 24px;
|
|
46
|
+
|
|
47
|
+
header {
|
|
48
|
+
display: flex;
|
|
49
|
+
flex-direction: column;
|
|
50
|
+
align-items: center;
|
|
51
|
+
gap: 24px;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.title {
|
|
55
|
+
font-size: 1rem;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.separator {
|
|
59
|
+
padding: 0 8px;
|
|
60
|
+
background-color: ${theme.color.light['400']};
|
|
61
|
+
color: ${theme.color.light['700']};
|
|
62
|
+
display: flex;
|
|
63
|
+
flex-direction: row;
|
|
64
|
+
align-items: center;
|
|
65
|
+
justify-content: center;
|
|
66
|
+
gap: 20px;
|
|
67
|
+
margin: 0;
|
|
68
|
+
|
|
69
|
+
&:before, &:after {
|
|
70
|
+
content: '';
|
|
71
|
+
height: 1px;
|
|
72
|
+
flex: 1;
|
|
73
|
+
background-color: ${theme.color.light['600']};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.error {
|
|
78
|
+
color: ${theme.color.danger['500']};
|
|
79
|
+
line-height: 1.5rem;
|
|
80
|
+
}
|
|
81
|
+
`
|
|
82
|
+
|
|
83
|
+
export const Login = ({ onSubmit, initialValue = '', welcomeText, removeLoadingOnSuccess, className, style, banner, loginTypes = ['idp', 'sso'] }: LoginProps) => {
|
|
84
|
+
const t = useTranslate(dictionary)
|
|
85
|
+
const searchParams = new URLSearchParams(location.search)
|
|
86
|
+
const [error, setError] = useState(searchParams.get('error_description') || searchParams.get('error') || '')
|
|
87
|
+
const [loading, setLoading] = useState(false)
|
|
88
|
+
const [email, setEmail] = useState(initialValue)
|
|
89
|
+
const disabled = !email.match(/\w+@\w+/)
|
|
90
|
+
const idpLoginEnabled = loginTypes.includes('idp')
|
|
91
|
+
const ssoLoginEnabled = loginTypes.includes('sso')
|
|
92
|
+
|
|
93
|
+
async function login(type: LoginType) {
|
|
94
|
+
setError('')
|
|
95
|
+
setLoading(true)
|
|
96
|
+
try {
|
|
97
|
+
const data: LoginData = type === 'sso' ? { type: 'sso', email } : { type: 'idp', provider: 'external-idp:github' }
|
|
98
|
+
await onSubmit(data)
|
|
99
|
+
if (removeLoadingOnSuccess) setLoading(false)
|
|
100
|
+
} catch (error: any) {
|
|
101
|
+
setLoading(false)
|
|
102
|
+
setError(error.message || error.toString())
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function submitForm(e: React.FormEvent<HTMLFormElement>) {
|
|
107
|
+
e.preventDefault()
|
|
108
|
+
if (disabled) return
|
|
109
|
+
login('sso')
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<>
|
|
114
|
+
<LoginBox onSubmit={submitForm} className={className} style={style}>
|
|
115
|
+
<header>
|
|
116
|
+
<MiniLogo />
|
|
117
|
+
<Text className="title">{welcomeText || t.welcome}</Text>
|
|
118
|
+
</header>
|
|
119
|
+
{ssoLoginEnabled && <>
|
|
120
|
+
<Input name="email" value={email} onChange={e => setEmail(e.target.value)} placeholder={t.placeholder} />
|
|
121
|
+
<Button colorScheme="primary" disabled={disabled || loading}>
|
|
122
|
+
{loading ? <LoadingCircular /> : <Text>{t.continue}</Text>}
|
|
123
|
+
</Button>
|
|
124
|
+
</>}
|
|
125
|
+
{ssoLoginEnabled && idpLoginEnabled && <p className="separator">{t.or}</p>}
|
|
126
|
+
{idpLoginEnabled &&
|
|
127
|
+
<Button colorScheme="light" type="button" onClick={() => login('idp')} disabled={loading}>
|
|
128
|
+
{loading ? <LoadingCircular /> : (
|
|
129
|
+
<>
|
|
130
|
+
<IconBox>
|
|
131
|
+
<Github />
|
|
132
|
+
</IconBox>
|
|
133
|
+
<Text>{t.loginWithGithub}</Text>
|
|
134
|
+
</>
|
|
135
|
+
)}
|
|
136
|
+
</Button>}
|
|
137
|
+
{error && <Text className="error">{t.error}: {error}</Text>}
|
|
138
|
+
</LoginBox>
|
|
139
|
+
{banner ? <BannerWarning>
|
|
140
|
+
{banner}
|
|
141
|
+
</BannerWarning> : null}
|
|
142
|
+
</>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const dictionary = {
|
|
147
|
+
en: {
|
|
148
|
+
welcome: 'Welcome to StackSpot',
|
|
149
|
+
placeholder: 'your@email.com',
|
|
150
|
+
continue: 'Continue',
|
|
151
|
+
or: 'or',
|
|
152
|
+
loginWithGithub: 'Login with Github',
|
|
153
|
+
error: 'Error while attempting to login',
|
|
154
|
+
},
|
|
155
|
+
pt: {
|
|
156
|
+
welcome: 'Bem vindo à StackSpot',
|
|
157
|
+
placeholder: 'nome@email.com',
|
|
158
|
+
continue: 'Continuar',
|
|
159
|
+
or: 'ou',
|
|
160
|
+
loginWithGithub: 'Logar com o GitHub',
|
|
161
|
+
error: 'Erro ao fazer login',
|
|
162
|
+
},
|
|
163
|
+
} satisfies Dictionary
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Code taken from https://blog.ag-grid.com/avoiding-react-18-double-mount/
|
|
5
|
+
*
|
|
6
|
+
* Attention: don't use this hook unless you really have to!
|
|
7
|
+
*
|
|
8
|
+
* This hook fixes the React 18 behavior of calling useEffect hooks twice in strict/development mode, which ruins some mounting/unmounting
|
|
9
|
+
* behaviors.
|
|
10
|
+
*
|
|
11
|
+
* @param effect refer to React's useEffect.
|
|
12
|
+
*/
|
|
13
|
+
export const useEffectOnce = (effect: () => void | (() => void)) => {
|
|
14
|
+
const effectFn = useRef<() => void | (() => void)>(effect)
|
|
15
|
+
const destroyFn = useRef<void | (() => void)>()
|
|
16
|
+
const effectCalled = useRef(false)
|
|
17
|
+
const rendered = useRef(false)
|
|
18
|
+
const [, setVal] = useState<number>(0)
|
|
19
|
+
|
|
20
|
+
if (effectCalled.current) {
|
|
21
|
+
rendered.current = true
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
// only execute the effect first time around
|
|
26
|
+
if (!effectCalled.current) {
|
|
27
|
+
destroyFn.current = effectFn.current()
|
|
28
|
+
effectCalled.current = true
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// this forces one render after the effect is run
|
|
32
|
+
setVal((val) => val + 1)
|
|
33
|
+
|
|
34
|
+
return () => {
|
|
35
|
+
// if the comp didn't render since the useEffect was called,
|
|
36
|
+
// we know it's the dummy React cycle
|
|
37
|
+
if (!rendered.current) return
|
|
38
|
+
|
|
39
|
+
// otherwise this is not a dummy destroy, so call the destroy func
|
|
40
|
+
if (destroyFn.current) destroyFn.current()
|
|
41
|
+
}
|
|
42
|
+
}, [])
|
|
43
|
+
}
|