@snokam/auth 3.3.9 → 3.3.11
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 +10 -0
- package/package.json +5 -4
- package/src/app-router/index.tsx +1 -0
- package/src/components/EmployeeGate/index.tsx +3 -1
- package/src/components/NotProvisioned/AccessHelp.tsx +51 -0
- package/src/components/NotProvisioned/index.tsx +35 -14
- package/src/components/NotProvisioned/styles.tsx +97 -0
- package/src/employee-query/retry.ts +9 -0
- package/src/index.ts +1 -0
- package/src/theme/employee-theme-sync.tsx +8 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [3.3.11](https://github.com/snokam/components/compare/@snokam/auth@3.3.10...@snokam/auth@3.3.11) (2026-10-05)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- no employee auto-provisioning at login + explain how to get access ([#2675](https://github.com/snokam/components/issues/2675)) ([b30d34a](https://github.com/snokam/components/commit/b30d34abce4bcfabe6326a7dc5876312e603efdc))
|
|
11
|
+
|
|
12
|
+
## [3.3.10](https://github.com/snokam/components/compare/@snokam/auth@3.3.9...@snokam/auth@3.3.10) (2026-10-05)
|
|
13
|
+
|
|
14
|
+
**Note:** Version bump only for package @snokam/auth
|
|
15
|
+
|
|
6
16
|
## [3.3.9](https://github.com/snokam/components/compare/@snokam/auth@3.3.8...@snokam/auth@3.3.9) (2026-10-05)
|
|
7
17
|
|
|
8
18
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@snokam/auth",
|
|
3
3
|
"description": "Snøkam auth components",
|
|
4
|
-
"version": "3.3.
|
|
4
|
+
"version": "3.3.11",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
7
7
|
"files": [
|
|
@@ -26,10 +26,11 @@
|
|
|
26
26
|
"next-auth": "4.24.15",
|
|
27
27
|
"path-to-regexp": "^8.4.2",
|
|
28
28
|
"@snokam/identity": "1.1.0",
|
|
29
|
-
"@snokam/platform-client": "3.5.0",
|
|
30
29
|
"@snokam/react-query": "1.1.0",
|
|
31
|
-
"@snokam/
|
|
32
|
-
"@snokam/tenant": "2.1.
|
|
30
|
+
"@snokam/platform-client": "3.5.1",
|
|
31
|
+
"@snokam/tenant": "2.1.12",
|
|
32
|
+
"@snokam/sanity-page-content": "4.4.1",
|
|
33
|
+
"@snokam/ui": "4.0.0"
|
|
33
34
|
},
|
|
34
35
|
"peerDependencies": {
|
|
35
36
|
"next": "16.3.3",
|
package/src/app-router/index.tsx
CHANGED
|
@@ -12,6 +12,7 @@ export { Unauthenticated } from "../components/Unauthenticated";
|
|
|
12
12
|
export { Authenticated } from "../components/Authenticated";
|
|
13
13
|
export { useAuth } from "../hooks";
|
|
14
14
|
export { useSession } from "../session/useSession";
|
|
15
|
+
export { retryUnlessMissing } from "../employee-query/retry";
|
|
15
16
|
export { EmployeeThemeSync } from "../theme/employee-theme-sync";
|
|
16
17
|
export { ScopesProvider, useScopes, useHasScope } from "../scopes";
|
|
17
18
|
export {
|
|
@@ -8,6 +8,7 @@ import { NotProvisioned } from "../NotProvisioned";
|
|
|
8
8
|
import { InactiveEmployee } from "../InactiveEmployee";
|
|
9
9
|
|
|
10
10
|
interface Props {
|
|
11
|
+
appKey: string;
|
|
11
12
|
employee: unknown;
|
|
12
13
|
isLoading: boolean;
|
|
13
14
|
allowAnonymous?: boolean;
|
|
@@ -15,6 +16,7 @@ interface Props {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export const EmployeeGate = ({
|
|
19
|
+
appKey,
|
|
18
20
|
employee,
|
|
19
21
|
isLoading,
|
|
20
22
|
allowAnonymous = false,
|
|
@@ -42,7 +44,7 @@ export const EmployeeGate = ({
|
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
if (isAuthenticated && !employee) {
|
|
45
|
-
return <NotProvisioned />;
|
|
47
|
+
return <NotProvisioned appKey={appKey} />;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
if (isAuthenticated && (employee as { active?: boolean }).active === false) {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { usePageContent } from "@snokam/sanity-page-content";
|
|
4
|
+
import { PersonOffOutlined } from "@snokam/ui/icons";
|
|
5
|
+
import { SystemPage, SystemPageAction } from "@snokam/ui/system-page";
|
|
6
|
+
import { useAuth } from "../../hooks";
|
|
7
|
+
import Styled from "./styles";
|
|
8
|
+
|
|
9
|
+
const SIGN_IN_AGAIN_URL = "/auth/login?callbackUrl=/&prompt=select_account";
|
|
10
|
+
|
|
11
|
+
export const AccessHelp = () => {
|
|
12
|
+
const { getText } = usePageContent();
|
|
13
|
+
const { session, signOut } = useAuth();
|
|
14
|
+
const email = session?.user?.email ?? "";
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<SystemPage>
|
|
18
|
+
<Styled.Content>
|
|
19
|
+
<Styled.IconBadge>
|
|
20
|
+
<PersonOffOutlined />
|
|
21
|
+
</Styled.IconBadge>
|
|
22
|
+
<Styled.Title>{getText("accessGateTitle")}</Styled.Title>
|
|
23
|
+
<Styled.SignedInAs>
|
|
24
|
+
{getText("accessGateSignedInAs", { email })}
|
|
25
|
+
</Styled.SignedInAs>
|
|
26
|
+
<Styled.Message>{getText("accessGateMessage")}</Styled.Message>
|
|
27
|
+
|
|
28
|
+
<Styled.Steps>
|
|
29
|
+
<Styled.StepsTitle>
|
|
30
|
+
{getText("accessGateStepsTitle")}
|
|
31
|
+
</Styled.StepsTitle>
|
|
32
|
+
<Styled.StepList>
|
|
33
|
+
<li>{getText("accessGateStepAskAdmin")}</li>
|
|
34
|
+
<li>{getText("accessGateStepEmail", { email })}</li>
|
|
35
|
+
<li>{getText("accessGateStepSignInAgain")}</li>
|
|
36
|
+
</Styled.StepList>
|
|
37
|
+
</Styled.Steps>
|
|
38
|
+
|
|
39
|
+
<Styled.Hint>{getText("accessGateWrongAccount")}</Styled.Hint>
|
|
40
|
+
<SystemPageAction.PrimaryButton
|
|
41
|
+
type="button"
|
|
42
|
+
onClick={() => signOut({ callbackUrl: SIGN_IN_AGAIN_URL })}
|
|
43
|
+
>
|
|
44
|
+
{getText("accessGateSignOut")}
|
|
45
|
+
</SystemPageAction.PrimaryButton>
|
|
46
|
+
</Styled.Content>
|
|
47
|
+
</SystemPage>
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default AccessHelp;
|
|
@@ -1,19 +1,40 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { useQuery } from "@snokam/react-query";
|
|
4
|
+
import { PageLoader } from "@snokam/ui";
|
|
5
|
+
import { loadAppContent } from "@snokam/sanity-page-content/app-router/actions";
|
|
6
|
+
import {
|
|
7
|
+
AppContentProvider,
|
|
8
|
+
PageContentProvider,
|
|
9
|
+
} from "@snokam/sanity-page-content/app-router/providers";
|
|
10
|
+
import { AccessHelp } from "./AccessHelp";
|
|
4
11
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
)
|
|
12
|
+
const ACCESS_GATE_PAGE_KEY = "access-gate";
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
appKey: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const NotProvisioned = ({ appKey }: Props) => {
|
|
19
|
+
const { data, isPending } = useQuery({
|
|
20
|
+
queryKey: ["access-gate-content", appKey],
|
|
21
|
+
queryFn: () => loadAppContent(appKey),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
if (isPending) {
|
|
25
|
+
return <PageLoader />;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<AppContentProvider appKey={appKey} initialContent={data ?? null}>
|
|
30
|
+
<PageContentProvider
|
|
31
|
+
pageKey={ACCESS_GATE_PAGE_KEY}
|
|
32
|
+
initialPageContent={null}
|
|
33
|
+
>
|
|
34
|
+
<AccessHelp />
|
|
35
|
+
</PageContentProvider>
|
|
36
|
+
</AppContentProvider>
|
|
37
|
+
);
|
|
38
|
+
};
|
|
18
39
|
|
|
19
40
|
export default NotProvisioned;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import styled from "@emotion/styled";
|
|
2
|
+
|
|
3
|
+
const Content = styled.div`
|
|
4
|
+
width: 100%;
|
|
5
|
+
max-width: 36rem;
|
|
6
|
+
display: flex;
|
|
7
|
+
flex-direction: column;
|
|
8
|
+
align-items: center;
|
|
9
|
+
text-align: center;
|
|
10
|
+
color: var(--color-light-blue);
|
|
11
|
+
`;
|
|
12
|
+
|
|
13
|
+
const IconBadge = styled.div`
|
|
14
|
+
display: flex;
|
|
15
|
+
align-items: center;
|
|
16
|
+
justify-content: center;
|
|
17
|
+
width: 5rem;
|
|
18
|
+
height: 5rem;
|
|
19
|
+
margin-bottom: 1.5rem;
|
|
20
|
+
border-radius: 50%;
|
|
21
|
+
border: 1px solid rgba(225, 248, 255, 0.25);
|
|
22
|
+
background: rgba(225, 248, 255, 0.06);
|
|
23
|
+
color: var(--color-blue);
|
|
24
|
+
|
|
25
|
+
svg {
|
|
26
|
+
width: 2.25rem;
|
|
27
|
+
height: 2.25rem;
|
|
28
|
+
}
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const Title = styled.h1`
|
|
32
|
+
margin: 0;
|
|
33
|
+
font-family: Cocogoose-Light;
|
|
34
|
+
font-size: clamp(1.75rem, 5vw, 2.5rem);
|
|
35
|
+
color: inherit;
|
|
36
|
+
`;
|
|
37
|
+
|
|
38
|
+
const SignedInAs = styled.p`
|
|
39
|
+
max-width: 100%;
|
|
40
|
+
margin: 1rem 0 0;
|
|
41
|
+
padding: 0.3rem 0.9rem;
|
|
42
|
+
border-radius: 999px;
|
|
43
|
+
background: rgba(225, 248, 255, 0.08);
|
|
44
|
+
font-size: 0.9rem;
|
|
45
|
+
overflow-wrap: anywhere;
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
const Message = styled.p`
|
|
49
|
+
margin: 1.25rem 0 0;
|
|
50
|
+
font-size: 1.05rem;
|
|
51
|
+
line-height: 1.6;
|
|
52
|
+
color: rgba(225, 248, 255, 0.8);
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
const Steps = styled.section`
|
|
56
|
+
width: 100%;
|
|
57
|
+
margin-top: 2rem;
|
|
58
|
+
padding: 1.25rem 1.5rem;
|
|
59
|
+
border-radius: 1rem;
|
|
60
|
+
border: 1px solid rgba(225, 248, 255, 0.18);
|
|
61
|
+
background: rgba(225, 248, 255, 0.05);
|
|
62
|
+
text-align: left;
|
|
63
|
+
`;
|
|
64
|
+
|
|
65
|
+
const StepsTitle = styled.h2`
|
|
66
|
+
margin: 0 0 0.75rem;
|
|
67
|
+
font-size: 1rem;
|
|
68
|
+
font-weight: 600;
|
|
69
|
+
color: var(--color-blue);
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
const StepList = styled.ol`
|
|
73
|
+
margin: 0;
|
|
74
|
+
padding-left: 1.25rem;
|
|
75
|
+
display: flex;
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
gap: 0.6rem;
|
|
78
|
+
line-height: 1.5;
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
const Hint = styled.p`
|
|
82
|
+
margin: 1.5rem 0 1rem;
|
|
83
|
+
font-size: 0.9rem;
|
|
84
|
+
color: rgba(225, 248, 255, 0.65);
|
|
85
|
+
`;
|
|
86
|
+
|
|
87
|
+
export default {
|
|
88
|
+
Content,
|
|
89
|
+
IconBadge,
|
|
90
|
+
Title,
|
|
91
|
+
SignedInAs,
|
|
92
|
+
Message,
|
|
93
|
+
Steps,
|
|
94
|
+
StepsTitle,
|
|
95
|
+
StepList,
|
|
96
|
+
Hint,
|
|
97
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
const MAX_RETRIES = 3;
|
|
2
|
+
|
|
3
|
+
function responseStatus(error: unknown): number | undefined {
|
|
4
|
+
return (error as { response?: { status?: number } } | null)?.response?.status;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function retryUnlessMissing(failureCount: number, error: unknown) {
|
|
8
|
+
return responseStatus(error) !== 404 && failureCount < MAX_RETRIES;
|
|
9
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./scopes";
|
|
|
3
3
|
|
|
4
4
|
export { useAuth, clearAuthTokenCache } from "./hooks";
|
|
5
5
|
export { useSession } from "./session/useSession";
|
|
6
|
+
export { retryUnlessMissing } from "./employee-query/retry";
|
|
6
7
|
|
|
7
8
|
export { buildAdminConsentUrl } from "./consent";
|
|
8
9
|
export type { AdminConsentParams } from "./consent";
|
|
@@ -7,6 +7,7 @@ import { useClientQuery } from "@snokam/react-query";
|
|
|
7
7
|
import { useAdminTheme } from "@snokam/ui/theme/admin";
|
|
8
8
|
import type { ColorSchemeMode } from "@snokam/ui/theme/admin";
|
|
9
9
|
import { useAuth } from "../hooks";
|
|
10
|
+
import { retryUnlessMissing } from "../employee-query/retry";
|
|
10
11
|
|
|
11
12
|
const MODES: ColorSchemeMode[] = ["system", "light", "dark"];
|
|
12
13
|
|
|
@@ -41,7 +42,13 @@ export function EmployeeThemeSync() {
|
|
|
41
42
|
const { data } = useClientQuery({
|
|
42
43
|
name: "Employees",
|
|
43
44
|
client,
|
|
44
|
-
}).useGetMeProtectedQuery(
|
|
45
|
+
}).useGetMeProtectedQuery(
|
|
46
|
+
{},
|
|
47
|
+
{
|
|
48
|
+
enabled: isAuthenticated && !!basePath,
|
|
49
|
+
retry: retryUnlessMissing,
|
|
50
|
+
}
|
|
51
|
+
);
|
|
45
52
|
|
|
46
53
|
const saved = data?.settings?.colorScheme;
|
|
47
54
|
|