@ogcio/sag-client 0.2.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/README.md +533 -0
- package/dist/auth.d.ts +55 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +154 -0
- package/dist/auth.js.map +1 -0
- package/dist/client.d.ts +93 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +118 -0
- package/dist/client.js.map +1 -0
- package/dist/fetcher.d.ts +39 -0
- package/dist/fetcher.d.ts.map +1 -0
- package/dist/fetcher.js +141 -0
- package/dist/fetcher.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/onboarding.d.ts +68 -0
- package/dist/onboarding.d.ts.map +1 -0
- package/dist/onboarding.js +67 -0
- package/dist/onboarding.js.map +1 -0
- package/dist/react/index.d.ts +15 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +16 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/provider.d.ts +22 -0
- package/dist/react/provider.d.ts.map +1 -0
- package/dist/react/provider.js +31 -0
- package/dist/react/provider.js.map +1 -0
- package/dist/react/use-auth.d.ts +13 -0
- package/dist/react/use-auth.d.ts.map +1 -0
- package/dist/react/use-auth.js +84 -0
- package/dist/react/use-auth.js.map +1 -0
- package/dist/react/use-gateway-fetch.d.ts +29 -0
- package/dist/react/use-gateway-fetch.d.ts.map +1 -0
- package/dist/react/use-gateway-fetch.js +47 -0
- package/dist/react/use-gateway-fetch.js.map +1 -0
- package/dist/react/use-gateway-mutation.d.ts +32 -0
- package/dist/react/use-gateway-mutation.d.ts.map +1 -0
- package/dist/react/use-gateway-mutation.js +58 -0
- package/dist/react/use-gateway-mutation.js.map +1 -0
- package/dist/react/use-onboarding-guard.d.ts +91 -0
- package/dist/react/use-onboarding-guard.d.ts.map +1 -0
- package/dist/react/use-onboarding-guard.js +140 -0
- package/dist/react/use-onboarding-guard.js.map +1 -0
- package/dist/react/use-public-servant-guard.d.ts +70 -0
- package/dist/react/use-public-servant-guard.d.ts.map +1 -0
- package/dist/react/use-public-servant-guard.js +79 -0
- package/dist/react/use-public-servant-guard.js.map +1 -0
- package/dist/roles.d.ts +67 -0
- package/dist/roles.d.ts.map +1 -0
- package/dist/roles.js +93 -0
- package/dist/roles.js.map +1 -0
- package/dist/types.d.ts +131 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +28 -0
- package/dist/types.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-onboarding-guard.d.ts","sourceRoot":"","sources":["../../src/react/use-onboarding-guard.ts"],"names":[],"mappings":"AAuBA,iDAAiD;AACjD,MAAM,WAAW,yBAAyB;IACxC,qEAAqE;IACrE,UAAU,EAAE,MAAM,CAAA;IAElB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAA;IAElB;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAE7B;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,4CAA4C;AAC5C,MAAM,WAAW,wBAAwB;IACvC;;;;;;;;;;;OAWG;IACH,QAAQ,EAAE,OAAO,CAAA;CAClB;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,yBAAyB,GACjC,wBAAwB,CA6G1B"}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { buildOnboardingRedirectUrl, buildWrongLoginMethodRedirect, } from "../onboarding";
|
|
4
|
+
import { ALLOWED_SIGNIN_METHODS, DEFAULT_PUBLIC_SERVANT_ROLES, isCitizen, isCitizenOnboarded, } from "../roles";
|
|
5
|
+
import { useSagClient } from "./provider";
|
|
6
|
+
import { useAuth } from "./use-auth";
|
|
7
|
+
// ── Constants ───────────────────────────────────────────────
|
|
8
|
+
const ONBOARDING_STORAGE_KEY = "sag_onboarding_ts";
|
|
9
|
+
const DEFAULT_DEBOUNCE_MS = 30000; // 30 seconds
|
|
10
|
+
// ── Hook ────────────────────────────────────────────────────
|
|
11
|
+
/**
|
|
12
|
+
* Onboarding guard hook for citizen-facing applications.
|
|
13
|
+
*
|
|
14
|
+
* Must be used within a `SagClientProvider`. Internally calls
|
|
15
|
+
* `useAuth()` for auth state and `useSagClient()` for the gateway
|
|
16
|
+
* URL and app name.
|
|
17
|
+
*
|
|
18
|
+
* **Behaviour:**
|
|
19
|
+
*
|
|
20
|
+
* 1. If the user is not a citizen → resolved (let them through).
|
|
21
|
+
* 2. If the user signed in with a wrong method → redirect to the
|
|
22
|
+
* profile service's error page (checked for ALL citizens,
|
|
23
|
+
* including onboarded ones).
|
|
24
|
+
* 3. If the user is onboarded (`isCitizenOnboarded`) → resolved.
|
|
25
|
+
* 4. If a redirect happened less than `debounceMs` ago → resolved
|
|
26
|
+
* (prevents infinite loops).
|
|
27
|
+
* 5. If the user is a citizen who has not completed onboarding →
|
|
28
|
+
* invalidate the server session and redirect to the profile
|
|
29
|
+
* service's onboarding page.
|
|
30
|
+
*
|
|
31
|
+
* While the check is pending or a redirect is in flight, `resolved`
|
|
32
|
+
* remains `false`. The consuming component should gate children
|
|
33
|
+
* behind `resolved` to prevent `useGatewayFetch` from firing.
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```tsx
|
|
37
|
+
* function Shell({ children }) {
|
|
38
|
+
* // publicServantRoles defaults to DEFAULT_PUBLIC_SERVANT_ROLES
|
|
39
|
+
* // (["Organisation Admin", "Organisation Member"])
|
|
40
|
+
* const { resolved } = useOnboardingGuard({
|
|
41
|
+
* profileUrl: "http://localhost:3001",
|
|
42
|
+
* appBaseUrl: "http://localhost:3000",
|
|
43
|
+
* connector: CONNECTOR_MYGOVID,
|
|
44
|
+
* })
|
|
45
|
+
* const { user, signIn, signOut } = useAuth()
|
|
46
|
+
*
|
|
47
|
+
* if (!resolved) return <Loading />
|
|
48
|
+
* return user ? <App>{children}</App> : <SignInButton />
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export function useOnboardingGuard(options) {
|
|
53
|
+
const { profileUrl, appBaseUrl, publicServantRoles, connector, debounceMs = DEFAULT_DEBOUNCE_MS, } = options;
|
|
54
|
+
// Keep the roles in a ref so the effect dependency list stays stable.
|
|
55
|
+
// A new array reference from the caller (or the default spread) won't
|
|
56
|
+
// re-trigger the effect — only the ref's `.current` is read inside.
|
|
57
|
+
const rolesRef = useRef(publicServantRoles !== null && publicServantRoles !== void 0 ? publicServantRoles : [...DEFAULT_PUBLIC_SERVANT_ROLES]);
|
|
58
|
+
rolesRef.current = publicServantRoles !== null && publicServantRoles !== void 0 ? publicServantRoles : [...DEFAULT_PUBLIC_SERVANT_ROLES];
|
|
59
|
+
const client = useSagClient();
|
|
60
|
+
const { user, claims, loading, invalidateSession } = useAuth();
|
|
61
|
+
const [resolved, setResolved] = useState(false);
|
|
62
|
+
// Once we've started redirecting (invalidate + location change),
|
|
63
|
+
// never allow resolved to flip back to true — even if the effect
|
|
64
|
+
// re-fires before the browser finishes navigating.
|
|
65
|
+
const redirectingRef = useRef(false);
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
var _a;
|
|
68
|
+
if (loading)
|
|
69
|
+
return;
|
|
70
|
+
if (redirectingRef.current)
|
|
71
|
+
return;
|
|
72
|
+
// Not authenticated — no onboarding needed
|
|
73
|
+
if (!user || !claims) {
|
|
74
|
+
setResolved(true);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const orgRoles = (_a = claims.organization_roles) !== null && _a !== void 0 ? _a : [];
|
|
78
|
+
// 1. Not a citizen — let them through (public servants use different
|
|
79
|
+
// sign-in methods so the signinMethod check doesn't apply)
|
|
80
|
+
if (!isCitizen(orgRoles, rolesRef.current)) {
|
|
81
|
+
setResolved(true);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// 2. Wrong login method — checked for ALL citizens (including
|
|
85
|
+
// onboarded ones) to enforce that citizens always use MyGovID.
|
|
86
|
+
// This mirrors the original authorisation package's ordering.
|
|
87
|
+
const signinMethod = claims.signinMethod;
|
|
88
|
+
if (signinMethod &&
|
|
89
|
+
!ALLOWED_SIGNIN_METHODS.includes(signinMethod)) {
|
|
90
|
+
redirectingRef.current = true;
|
|
91
|
+
sessionStorage.setItem(ONBOARDING_STORAGE_KEY, String(Date.now()));
|
|
92
|
+
window.location.href = buildWrongLoginMethodRedirect({
|
|
93
|
+
profileUrl,
|
|
94
|
+
currentUrl: window.location.href,
|
|
95
|
+
});
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
// 3. Citizen who has completed onboarding — clear debounce, proceed
|
|
99
|
+
if (isCitizenOnboarded(claims.roles)) {
|
|
100
|
+
sessionStorage.removeItem(ONBOARDING_STORAGE_KEY);
|
|
101
|
+
setResolved(true);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// 4. Debounce — if we recently redirected, don't loop
|
|
105
|
+
const lastTs = Number(sessionStorage.getItem(ONBOARDING_STORAGE_KEY) || "0");
|
|
106
|
+
if (Date.now() - lastTs < debounceMs) {
|
|
107
|
+
setResolved(true);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
// 5. Citizen not onboarded — invalidate session and redirect.
|
|
111
|
+
// Lock the guard immediately so no subsequent effect run can
|
|
112
|
+
// set resolved=true and allow children to render.
|
|
113
|
+
redirectingRef.current = true;
|
|
114
|
+
sessionStorage.setItem(ONBOARDING_STORAGE_KEY, String(Date.now()));
|
|
115
|
+
const url = buildOnboardingRedirectUrl({
|
|
116
|
+
profileUrl,
|
|
117
|
+
currentPath: window.location.pathname,
|
|
118
|
+
gatewayUrl: client.gatewayUrl,
|
|
119
|
+
appName: client.appName,
|
|
120
|
+
appBaseUrl,
|
|
121
|
+
connector,
|
|
122
|
+
});
|
|
123
|
+
invalidateSession().then(() => {
|
|
124
|
+
window.location.href = url;
|
|
125
|
+
});
|
|
126
|
+
}, [
|
|
127
|
+
loading,
|
|
128
|
+
user,
|
|
129
|
+
claims,
|
|
130
|
+
invalidateSession,
|
|
131
|
+
profileUrl,
|
|
132
|
+
appBaseUrl,
|
|
133
|
+
connector,
|
|
134
|
+
debounceMs,
|
|
135
|
+
client.gatewayUrl,
|
|
136
|
+
client.appName,
|
|
137
|
+
]);
|
|
138
|
+
return { resolved };
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=use-onboarding-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-onboarding-guard.js","sourceRoot":"","sources":["../../src/react/use-onboarding-guard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/** Options for the `usePublicServantGuard` hook. */
|
|
2
|
+
export interface UsePublicServantGuardOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Role names that identify an active public servant.
|
|
5
|
+
*
|
|
6
|
+
* @default DEFAULT_PUBLIC_SERVANT_ROLES — `["Organisation Admin", "Organisation Member"]`
|
|
7
|
+
*/
|
|
8
|
+
publicServantRoles?: string[];
|
|
9
|
+
/**
|
|
10
|
+
* URL to redirect inactive public servants to.
|
|
11
|
+
* When omitted, inactive PS users are allowed through (resolved)
|
|
12
|
+
* with `isInactive` set to `true`.
|
|
13
|
+
*/
|
|
14
|
+
inactiveRedirectUrl?: string;
|
|
15
|
+
/**
|
|
16
|
+
* URL to redirect non-public-servant users to (e.g. citizens
|
|
17
|
+
* landing on an admin app). When omitted, non-PS users are
|
|
18
|
+
* allowed through with `authorized` set to `false`.
|
|
19
|
+
*/
|
|
20
|
+
unauthorizedRedirectUrl?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Return value of `usePublicServantGuard`. */
|
|
23
|
+
export interface UsePublicServantGuardResult {
|
|
24
|
+
/**
|
|
25
|
+
* `true` once the guard check has completed — meaning the user is
|
|
26
|
+
* a PS, is not authenticated, or a redirect is not configured.
|
|
27
|
+
*
|
|
28
|
+
* `false` while auth is loading or a redirect is in flight.
|
|
29
|
+
*/
|
|
30
|
+
resolved: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* `true` when the authenticated user is an active public servant.
|
|
33
|
+
* `false` for citizens, inactive PS, or unauthenticated users.
|
|
34
|
+
*/
|
|
35
|
+
authorized: boolean;
|
|
36
|
+
/**
|
|
37
|
+
* `true` when the user is an inactive public servant.
|
|
38
|
+
*/
|
|
39
|
+
isInactive: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Access guard hook for public-servant-facing applications.
|
|
43
|
+
*
|
|
44
|
+
* Must be used within a `SagClientProvider`. Internally calls
|
|
45
|
+
* `useAuth()` for auth state.
|
|
46
|
+
*
|
|
47
|
+
* **Behaviour:**
|
|
48
|
+
*
|
|
49
|
+
* 1. If the user is not authenticated -> resolved, not authorized.
|
|
50
|
+
* 2. If the user is an inactive public servant and `inactiveRedirectUrl`
|
|
51
|
+
* is set -> redirect. Otherwise resolved with `isInactive: true`.
|
|
52
|
+
* 3. If the user is an active public servant -> resolved, authorized.
|
|
53
|
+
* 4. If the user is not a PS (citizen) and `unauthorizedRedirectUrl`
|
|
54
|
+
* is set -> redirect. Otherwise resolved with `authorized: false`.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```tsx
|
|
58
|
+
* function AdminShell({ children }) {
|
|
59
|
+
* const { resolved, authorized } = usePublicServantGuard({
|
|
60
|
+
* unauthorizedRedirectUrl: "https://citizen-app.example.com",
|
|
61
|
+
* })
|
|
62
|
+
*
|
|
63
|
+
* if (!resolved) return <Loading />
|
|
64
|
+
* if (!authorized) return <AccessDenied />
|
|
65
|
+
* return <>{children}</>
|
|
66
|
+
* }
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
export declare function usePublicServantGuard(options?: UsePublicServantGuardOptions): UsePublicServantGuardResult;
|
|
70
|
+
//# sourceMappingURL=use-public-servant-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-public-servant-guard.d.ts","sourceRoot":"","sources":["../../src/react/use-public-servant-guard.ts"],"names":[],"mappings":"AAYA,oDAAoD;AACpD,MAAM,WAAW,4BAA4B;IAC3C;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;IAE7B;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAE5B;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAA;CACjC;AAED,+CAA+C;AAC/C,MAAM,WAAW,2BAA2B;IAC1C;;;;;OAKG;IACH,QAAQ,EAAE,OAAO,CAAA;IAEjB;;;OAGG;IACH,UAAU,EAAE,OAAO,CAAA;IAEnB;;OAEG;IACH,UAAU,EAAE,OAAO,CAAA;CACpB;AAID;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,4BAAiC,GACzC,2BAA2B,CAuD7B"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { DEFAULT_PUBLIC_SERVANT_ROLES, isInactivePublicServant, isPublicServant, } from "../roles";
|
|
4
|
+
import { useAuth } from "./use-auth";
|
|
5
|
+
// ── Hook ────────────────────────────────────────────────────
|
|
6
|
+
/**
|
|
7
|
+
* Access guard hook for public-servant-facing applications.
|
|
8
|
+
*
|
|
9
|
+
* Must be used within a `SagClientProvider`. Internally calls
|
|
10
|
+
* `useAuth()` for auth state.
|
|
11
|
+
*
|
|
12
|
+
* **Behaviour:**
|
|
13
|
+
*
|
|
14
|
+
* 1. If the user is not authenticated -> resolved, not authorized.
|
|
15
|
+
* 2. If the user is an inactive public servant and `inactiveRedirectUrl`
|
|
16
|
+
* is set -> redirect. Otherwise resolved with `isInactive: true`.
|
|
17
|
+
* 3. If the user is an active public servant -> resolved, authorized.
|
|
18
|
+
* 4. If the user is not a PS (citizen) and `unauthorizedRedirectUrl`
|
|
19
|
+
* is set -> redirect. Otherwise resolved with `authorized: false`.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```tsx
|
|
23
|
+
* function AdminShell({ children }) {
|
|
24
|
+
* const { resolved, authorized } = usePublicServantGuard({
|
|
25
|
+
* unauthorizedRedirectUrl: "https://citizen-app.example.com",
|
|
26
|
+
* })
|
|
27
|
+
*
|
|
28
|
+
* if (!resolved) return <Loading />
|
|
29
|
+
* if (!authorized) return <AccessDenied />
|
|
30
|
+
* return <>{children}</>
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function usePublicServantGuard(options = {}) {
|
|
35
|
+
const { publicServantRoles = [...DEFAULT_PUBLIC_SERVANT_ROLES], inactiveRedirectUrl, unauthorizedRedirectUrl, } = options;
|
|
36
|
+
const { user, claims, loading } = useAuth();
|
|
37
|
+
const [resolved, setResolved] = useState(false);
|
|
38
|
+
const [authorized, setAuthorized] = useState(false);
|
|
39
|
+
const [isInactive, setIsInactive] = useState(false);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
var _a;
|
|
42
|
+
if (loading)
|
|
43
|
+
return;
|
|
44
|
+
if (!user || !claims) {
|
|
45
|
+
setResolved(true);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const orgRoles = (_a = claims.organization_roles) !== null && _a !== void 0 ? _a : [];
|
|
49
|
+
if (isInactivePublicServant(orgRoles)) {
|
|
50
|
+
setIsInactive(true);
|
|
51
|
+
if (inactiveRedirectUrl) {
|
|
52
|
+
window.location.href = inactiveRedirectUrl;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
setResolved(true);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (isPublicServant(orgRoles, publicServantRoles)) {
|
|
59
|
+
setAuthorized(true);
|
|
60
|
+
setResolved(true);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// Not a public servant (citizen or unknown role)
|
|
64
|
+
if (unauthorizedRedirectUrl) {
|
|
65
|
+
window.location.href = unauthorizedRedirectUrl;
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
setResolved(true);
|
|
69
|
+
}, [
|
|
70
|
+
loading,
|
|
71
|
+
user,
|
|
72
|
+
claims,
|
|
73
|
+
publicServantRoles,
|
|
74
|
+
inactiveRedirectUrl,
|
|
75
|
+
unauthorizedRedirectUrl,
|
|
76
|
+
]);
|
|
77
|
+
return { resolved, authorized, isInactive };
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=use-public-servant-guard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-public-servant-guard.js","sourceRoot":"","sources":["../../src/react/use-public-servant-guard.ts"],"names":[],"mappings":"AAAA,YAAY,CAAA;AAEZ,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAC3C,OAAO,EACL,4BAA4B,EAC5B,uBAAuB,EACvB,eAAe,GAChB,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAkDpC,+DAA+D;AAE/D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,qBAAqB,CACnC,UAAwC,EAAE;IAE1C,MAAM,EACJ,kBAAkB,GAAG,CAAC,GAAG,4BAA4B,CAAC,EACtD,mBAAmB,EACnB,uBAAuB,GACxB,GAAG,OAAO,CAAA;IAEX,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAAA;IAE3C,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC/C,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IACnD,MAAM,CAAC,UAAU,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAEnD,SAAS,CAAC,GAAG,EAAE;;QACb,IAAI,OAAO;YAAE,OAAM;QAEnB,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACrB,WAAW,CAAC,IAAI,CAAC,CAAA;YACjB,OAAM;QACR,CAAC;QAED,MAAM,QAAQ,GAAG,MAAA,MAAM,CAAC,kBAAkB,mCAAI,EAAE,CAAA;QAEhD,IAAI,uBAAuB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACtC,aAAa,CAAC,IAAI,CAAC,CAAA;YACnB,IAAI,mBAAmB,EAAE,CAAC;gBACxB,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,mBAAmB,CAAA;gBAC1C,OAAM;YACR,CAAC;YACD,WAAW,CAAC,IAAI,CAAC,CAAA;YACjB,OAAM;QACR,CAAC;QAED,IAAI,eAAe,CAAC,QAAQ,EAAE,kBAAkB,CAAC,EAAE,CAAC;YAClD,aAAa,CAAC,IAAI,CAAC,CAAA;YACnB,WAAW,CAAC,IAAI,CAAC,CAAA;YACjB,OAAM;QACR,CAAC;QAED,iDAAiD;QACjD,IAAI,uBAAuB,EAAE,CAAC;YAC5B,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,uBAAuB,CAAA;YAC9C,OAAM;QACR,CAAC;QACD,WAAW,CAAC,IAAI,CAAC,CAAA;IACnB,CAAC,EAAE;QACD,OAAO;QACP,IAAI;QACJ,MAAM;QACN,kBAAkB;QAClB,mBAAmB;QACnB,uBAAuB;KACxB,CAAC,CAAA;IAEF,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,CAAA;AAC7C,CAAC"}
|
package/dist/roles.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role detection utilities for the Secure API Gateway.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions that derive citizen / public-servant / onboarding
|
|
5
|
+
* status from Logto ID-token claims. These mirror the logic in the
|
|
6
|
+
* reference `authorisation` package so consuming apps get identical
|
|
7
|
+
* behaviour without duplicating the rules.
|
|
8
|
+
*/
|
|
9
|
+
/** Default organization role string for inactive public servants. */
|
|
10
|
+
export declare const INACTIVE_PS_ORG_ROLE = "inactive-ps-org:Inactive Public Servant";
|
|
11
|
+
/** Default Logto role name assigned after citizen onboarding. */
|
|
12
|
+
export declare const ROLE_NAME_ONBOARDED_CITIZEN = "Onboarded citizen";
|
|
13
|
+
/** Logto directSignIn value for MyGovID social connector. */
|
|
14
|
+
export declare const CONNECTOR_MYGOVID = "social:mygovid";
|
|
15
|
+
/** Logto directSignIn value for Entra ID social connector. */
|
|
16
|
+
export declare const CONNECTOR_ENTRAID = "social:ogcio-entraid";
|
|
17
|
+
/** Sign-in methods that are considered valid for citizen flows. */
|
|
18
|
+
export declare const ALLOWED_SIGNIN_METHODS: readonly ["social:mygovid"];
|
|
19
|
+
/** Organisation role name for organisation administrators. */
|
|
20
|
+
export declare const ORG_ROLE_ADMIN = "Organisation Admin";
|
|
21
|
+
/** Organisation role name for regular organisation members. */
|
|
22
|
+
export declare const ORG_ROLE_MEMBER = "Organisation Member";
|
|
23
|
+
/**
|
|
24
|
+
* Default set of public-servant organisation role names.
|
|
25
|
+
*
|
|
26
|
+
* Citizen-facing apps typically use this as the `publicServantRoles`
|
|
27
|
+
* value — any user holding one of these roles is considered a public
|
|
28
|
+
* servant (not a citizen).
|
|
29
|
+
*
|
|
30
|
+
* Admin apps may use a different, service-specific list.
|
|
31
|
+
*/
|
|
32
|
+
export declare const DEFAULT_PUBLIC_SERVANT_ROLES: readonly ["Organisation Admin", "Organisation Member"];
|
|
33
|
+
/**
|
|
34
|
+
* Check whether the user is an *inactive* public servant.
|
|
35
|
+
*
|
|
36
|
+
* @param orgRoles `organization_roles` claim from the ID token
|
|
37
|
+
* (format: `"orgId:roleName"`).
|
|
38
|
+
* @param inactiveOrgRole Override the default inactive-PS org role string.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isInactivePublicServant(orgRoles: string[] | null | undefined, inactiveOrgRole?: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Check whether the user is an active public servant.
|
|
43
|
+
*
|
|
44
|
+
* A user is considered a public servant when they have at least one
|
|
45
|
+
* organisation role whose *role name* (the part after the colon) is
|
|
46
|
+
* listed in `expectedRoles`, **and** they are not an inactive PS.
|
|
47
|
+
*
|
|
48
|
+
* @param orgRoles `organization_roles` claim from the ID token.
|
|
49
|
+
* @param expectedRoles Role names that identify a public servant
|
|
50
|
+
* (e.g. `["Admin", "Editor"]`).
|
|
51
|
+
*/
|
|
52
|
+
export declare function isPublicServant(orgRoles: string[] | null | undefined, expectedRoles: string[]): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Check whether the user is a citizen (i.e. not a public servant).
|
|
55
|
+
*
|
|
56
|
+
* A user is a citizen when they are neither an inactive public
|
|
57
|
+
* servant nor an active public servant.
|
|
58
|
+
*/
|
|
59
|
+
export declare function isCitizen(orgRoles: string[] | null | undefined, expectedPublicServantRoles: string[]): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Check whether the citizen has completed onboarding.
|
|
62
|
+
*
|
|
63
|
+
* @param roles `roles` claim from the ID token.
|
|
64
|
+
* @param onboardedRoleName Override the default onboarded-citizen role name.
|
|
65
|
+
*/
|
|
66
|
+
export declare function isCitizenOnboarded(roles: string[] | null | undefined, onboardedRoleName?: string): boolean;
|
|
67
|
+
//# sourceMappingURL=roles.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roles.d.ts","sourceRoot":"","sources":["../src/roles.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,4CAA4C,CAAA;AAE7E,iEAAiE;AACjE,eAAO,MAAM,2BAA2B,sBAAsB,CAAA;AAE9D,6DAA6D;AAC7D,eAAO,MAAM,iBAAiB,mBAAmB,CAAA;AAEjD,8DAA8D;AAC9D,eAAO,MAAM,iBAAiB,yBAAyB,CAAA;AAEvD,mEAAmE;AACnE,eAAO,MAAM,sBAAsB,6BAA8B,CAAA;AAQjE,8DAA8D;AAC9D,eAAO,MAAM,cAAc,uBAAuB,CAAA;AAElD,+DAA+D;AAC/D,eAAO,MAAM,eAAe,wBAAwB,CAAA;AAEpD;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,wDAG/B,CAAA;AAIV;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,EACrC,eAAe,GAAE,MAA6B,GAC7C,OAAO,CAET;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,EACrC,aAAa,EAAE,MAAM,EAAE,GACtB,OAAO,CAMT;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,EACrC,0BAA0B,EAAE,MAAM,EAAE,GACnC,OAAO,CAKT;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,GAAG,SAAS,EAClC,iBAAiB,GAAE,MAAoC,GACtD,OAAO,CAET"}
|
package/dist/roles.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role detection utilities for the Secure API Gateway.
|
|
3
|
+
*
|
|
4
|
+
* Pure functions that derive citizen / public-servant / onboarding
|
|
5
|
+
* status from Logto ID-token claims. These mirror the logic in the
|
|
6
|
+
* reference `authorisation` package so consuming apps get identical
|
|
7
|
+
* behaviour without duplicating the rules.
|
|
8
|
+
*/
|
|
9
|
+
// ── Constants (configurable defaults) ───────────────────────
|
|
10
|
+
/** Default organization role string for inactive public servants. */
|
|
11
|
+
export const INACTIVE_PS_ORG_ROLE = "inactive-ps-org:Inactive Public Servant";
|
|
12
|
+
/** Default Logto role name assigned after citizen onboarding. */
|
|
13
|
+
export const ROLE_NAME_ONBOARDED_CITIZEN = "Onboarded citizen";
|
|
14
|
+
/** Logto directSignIn value for MyGovID social connector. */
|
|
15
|
+
export const CONNECTOR_MYGOVID = "social:mygovid";
|
|
16
|
+
/** Logto directSignIn value for Entra ID social connector. */
|
|
17
|
+
export const CONNECTOR_ENTRAID = "social:ogcio-entraid";
|
|
18
|
+
/** Sign-in methods that are considered valid for citizen flows. */
|
|
19
|
+
export const ALLOWED_SIGNIN_METHODS = ["social:mygovid"];
|
|
20
|
+
// ── Common public-servant organisation role names ───────────
|
|
21
|
+
//
|
|
22
|
+
// These are the standard Logto organisation role names used across
|
|
23
|
+
// the govie platform. Individual apps may use a subset or extend
|
|
24
|
+
// this list (e.g. "Messaging Public Servant" for admin apps).
|
|
25
|
+
/** Organisation role name for organisation administrators. */
|
|
26
|
+
export const ORG_ROLE_ADMIN = "Organisation Admin";
|
|
27
|
+
/** Organisation role name for regular organisation members. */
|
|
28
|
+
export const ORG_ROLE_MEMBER = "Organisation Member";
|
|
29
|
+
/**
|
|
30
|
+
* Default set of public-servant organisation role names.
|
|
31
|
+
*
|
|
32
|
+
* Citizen-facing apps typically use this as the `publicServantRoles`
|
|
33
|
+
* value — any user holding one of these roles is considered a public
|
|
34
|
+
* servant (not a citizen).
|
|
35
|
+
*
|
|
36
|
+
* Admin apps may use a different, service-specific list.
|
|
37
|
+
*/
|
|
38
|
+
export const DEFAULT_PUBLIC_SERVANT_ROLES = [
|
|
39
|
+
ORG_ROLE_ADMIN,
|
|
40
|
+
ORG_ROLE_MEMBER,
|
|
41
|
+
];
|
|
42
|
+
// ── Role detection ──────────────────────────────────────────
|
|
43
|
+
/**
|
|
44
|
+
* Check whether the user is an *inactive* public servant.
|
|
45
|
+
*
|
|
46
|
+
* @param orgRoles `organization_roles` claim from the ID token
|
|
47
|
+
* (format: `"orgId:roleName"`).
|
|
48
|
+
* @param inactiveOrgRole Override the default inactive-PS org role string.
|
|
49
|
+
*/
|
|
50
|
+
export function isInactivePublicServant(orgRoles, inactiveOrgRole = INACTIVE_PS_ORG_ROLE) {
|
|
51
|
+
var _a;
|
|
52
|
+
return (_a = orgRoles === null || orgRoles === void 0 ? void 0 : orgRoles.includes(inactiveOrgRole)) !== null && _a !== void 0 ? _a : false;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Check whether the user is an active public servant.
|
|
56
|
+
*
|
|
57
|
+
* A user is considered a public servant when they have at least one
|
|
58
|
+
* organisation role whose *role name* (the part after the colon) is
|
|
59
|
+
* listed in `expectedRoles`, **and** they are not an inactive PS.
|
|
60
|
+
*
|
|
61
|
+
* @param orgRoles `organization_roles` claim from the ID token.
|
|
62
|
+
* @param expectedRoles Role names that identify a public servant
|
|
63
|
+
* (e.g. `["Admin", "Editor"]`).
|
|
64
|
+
*/
|
|
65
|
+
export function isPublicServant(orgRoles, expectedRoles) {
|
|
66
|
+
if (isInactivePublicServant(orgRoles) || !orgRoles)
|
|
67
|
+
return false;
|
|
68
|
+
return orgRoles.some((orgRole) => {
|
|
69
|
+
const [, role] = orgRole.split(":");
|
|
70
|
+
return expectedRoles.includes(role);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Check whether the user is a citizen (i.e. not a public servant).
|
|
75
|
+
*
|
|
76
|
+
* A user is a citizen when they are neither an inactive public
|
|
77
|
+
* servant nor an active public servant.
|
|
78
|
+
*/
|
|
79
|
+
export function isCitizen(orgRoles, expectedPublicServantRoles) {
|
|
80
|
+
return !(isInactivePublicServant(orgRoles) ||
|
|
81
|
+
isPublicServant(orgRoles, expectedPublicServantRoles));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Check whether the citizen has completed onboarding.
|
|
85
|
+
*
|
|
86
|
+
* @param roles `roles` claim from the ID token.
|
|
87
|
+
* @param onboardedRoleName Override the default onboarded-citizen role name.
|
|
88
|
+
*/
|
|
89
|
+
export function isCitizenOnboarded(roles, onboardedRoleName = ROLE_NAME_ONBOARDED_CITIZEN) {
|
|
90
|
+
var _a;
|
|
91
|
+
return (_a = roles === null || roles === void 0 ? void 0 : roles.includes(onboardedRoleName)) !== null && _a !== void 0 ? _a : false;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=roles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roles.js","sourceRoot":"","sources":["../src/roles.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,+DAA+D;AAE/D,qEAAqE;AACrE,MAAM,CAAC,MAAM,oBAAoB,GAAG,yCAAyC,CAAA;AAE7E,iEAAiE;AACjE,MAAM,CAAC,MAAM,2BAA2B,GAAG,mBAAmB,CAAA;AAE9D,6DAA6D;AAC7D,MAAM,CAAC,MAAM,iBAAiB,GAAG,gBAAgB,CAAA;AAEjD,8DAA8D;AAC9D,MAAM,CAAC,MAAM,iBAAiB,GAAG,sBAAsB,CAAA;AAEvD,mEAAmE;AACnE,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,gBAAgB,CAAU,CAAA;AAEjE,+DAA+D;AAC/D,EAAE;AACF,mEAAmE;AACnE,kEAAkE;AAClE,8DAA8D;AAE9D,8DAA8D;AAC9D,MAAM,CAAC,MAAM,cAAc,GAAG,oBAAoB,CAAA;AAElD,+DAA+D;AAC/D,MAAM,CAAC,MAAM,eAAe,GAAG,qBAAqB,CAAA;AAEpD;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG;IAC1C,cAAc;IACd,eAAe;CACP,CAAA;AAEV,+DAA+D;AAE/D;;;;;;GAMG;AACH,MAAM,UAAU,uBAAuB,CACrC,QAAqC,EACrC,kBAA0B,oBAAoB;;IAE9C,OAAO,MAAA,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,QAAQ,CAAC,eAAe,CAAC,mCAAI,KAAK,CAAA;AACrD,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAqC,EACrC,aAAuB;IAEvB,IAAI,uBAAuB,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAQ;QAAE,OAAO,KAAK,CAAA;IAChE,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE;QAC/B,MAAM,CAAC,EAAE,IAAI,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;QACnC,OAAO,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAA;IACrC,CAAC,CAAC,CAAA;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,SAAS,CACvB,QAAqC,EACrC,0BAAoC;IAEpC,OAAO,CAAC,CACN,uBAAuB,CAAC,QAAQ,CAAC;QACjC,eAAe,CAAC,QAAQ,EAAE,0BAA0B,CAAC,CACtD,CAAA;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAAkC,EAClC,oBAA4B,2BAA2B;;IAEvD,OAAO,MAAA,KAAK,aAAL,KAAK,uBAAL,KAAK,CAAE,QAAQ,CAAC,iBAAiB,CAAC,mCAAI,KAAK,CAAA;AACpD,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export type AuthUser = {
|
|
2
|
+
sub: string;
|
|
3
|
+
email?: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
};
|
|
6
|
+
/** Claims returned from the gateway's enriched /auth/status endpoint. */
|
|
7
|
+
export type AuthClaims = {
|
|
8
|
+
roles: string[];
|
|
9
|
+
organizations: string[];
|
|
10
|
+
organization_roles: string[];
|
|
11
|
+
/** How the user signed in (e.g. "social:mygovid"). Logto custom claim. */
|
|
12
|
+
signinMethod?: string;
|
|
13
|
+
};
|
|
14
|
+
export type AuthStatus = {
|
|
15
|
+
authenticated: true;
|
|
16
|
+
app: string;
|
|
17
|
+
user: AuthUser;
|
|
18
|
+
claims: AuthClaims;
|
|
19
|
+
} | {
|
|
20
|
+
authenticated: false;
|
|
21
|
+
warning?: string;
|
|
22
|
+
};
|
|
23
|
+
/** Structured organization info returned by the gateway's /auth/organizations endpoint. */
|
|
24
|
+
export interface OrganizationInfo {
|
|
25
|
+
id: string;
|
|
26
|
+
name: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
roles: string[];
|
|
29
|
+
}
|
|
30
|
+
/** Options for the sign-in flow. */
|
|
31
|
+
export interface SignInOptions {
|
|
32
|
+
/**
|
|
33
|
+
* Logto `directSignIn` value — bypass the sign-in screen and go
|
|
34
|
+
* directly to a specific social connector or SSO provider.
|
|
35
|
+
*
|
|
36
|
+
* @example "social:mygovid"
|
|
37
|
+
* @example "social:ogcio-entraid"
|
|
38
|
+
* @example "sso:connector-id"
|
|
39
|
+
*/
|
|
40
|
+
connector?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Explicit post-login redirect URL.
|
|
43
|
+
* Validated against the gateway's allowed-origin patterns.
|
|
44
|
+
*/
|
|
45
|
+
redirectUrl?: string;
|
|
46
|
+
}
|
|
47
|
+
export interface SagClientConfig {
|
|
48
|
+
/** Secure API Gateway base URL (e.g. "http://localhost:3333") */
|
|
49
|
+
gatewayUrl: string;
|
|
50
|
+
/** Application name sent to the gateway (e.g. "cars", "weather") */
|
|
51
|
+
appName: string;
|
|
52
|
+
/**
|
|
53
|
+
* Called when the gateway returns 401 (session expired / invalid grant).
|
|
54
|
+
* Default: POST-redirect to the gateway's sign-in endpoint.
|
|
55
|
+
*/
|
|
56
|
+
onSessionExpired?: () => void;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Extended Error thrown by the gateway fetcher.
|
|
60
|
+
* Carries the HTTP status code and the gateway error code (if any)
|
|
61
|
+
* so consumers can branch on error type.
|
|
62
|
+
*/
|
|
63
|
+
export declare class SagFetchError extends Error {
|
|
64
|
+
status: number;
|
|
65
|
+
code?: string;
|
|
66
|
+
constructor(message: string, status: number, code?: string);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* HTTP header used to select user-token vs M2M-token flow on
|
|
70
|
+
* the Secure API Gateway.
|
|
71
|
+
*/
|
|
72
|
+
export declare const ACTOR_TYPE_HEADER = "X-Request-Actor-Type";
|
|
73
|
+
/**
|
|
74
|
+
* HTTP header used to request an organization-scoped token.
|
|
75
|
+
* When present, the gateway uses `getOrganizationToken(orgId)`
|
|
76
|
+
* instead of `getAccessToken(resource)` for the proxy request.
|
|
77
|
+
*/
|
|
78
|
+
export declare const ORGANIZATION_ID_HEADER = "X-Organization-Id";
|
|
79
|
+
/**
|
|
80
|
+
* Actor type sent to the gateway via `X-Request-Actor-Type`.
|
|
81
|
+
* - `"user"` (default) — proxy with the authenticated user's access token
|
|
82
|
+
* - `"m2m"` — proxy with the app's M2M client_credentials token
|
|
83
|
+
*/
|
|
84
|
+
export type ActorType = "user" | "m2m";
|
|
85
|
+
/** Options accepted by gateway fetch helpers. */
|
|
86
|
+
export interface GatewayFetchOptions {
|
|
87
|
+
/**
|
|
88
|
+
* Override the actor type for this request.
|
|
89
|
+
* When set to `"m2m"`, the fetcher sends `X-Request-Actor-Type: m2m`
|
|
90
|
+
* so the gateway uses client_credentials instead of the user token.
|
|
91
|
+
*/
|
|
92
|
+
actorType?: ActorType;
|
|
93
|
+
/**
|
|
94
|
+
* Organization ID for organization-scoped requests.
|
|
95
|
+
* When set, the fetcher sends `X-Organization-Id: <id>` so the
|
|
96
|
+
* gateway uses `getOrganizationToken(orgId)` instead of the
|
|
97
|
+
* default `getAccessToken(resource)`.
|
|
98
|
+
*/
|
|
99
|
+
organizationId?: string;
|
|
100
|
+
}
|
|
101
|
+
/** HTTP method for mutation requests. */
|
|
102
|
+
export type MutationMethod = "POST" | "PUT" | "PATCH" | "DELETE";
|
|
103
|
+
/** Options accepted by gateway mutation helpers. */
|
|
104
|
+
export interface GatewayMutationOptions extends GatewayFetchOptions {
|
|
105
|
+
/** HTTP method for the mutation (defaults to `"POST"`). */
|
|
106
|
+
method?: MutationMethod;
|
|
107
|
+
}
|
|
108
|
+
/** Options for the `useOnboardingGuard` hook. */
|
|
109
|
+
export type { UseOnboardingGuardOptions } from "./react/use-onboarding-guard";
|
|
110
|
+
/** Return value of the `useOnboardingGuard` hook. */
|
|
111
|
+
export type { UseOnboardingGuardResult } from "./react/use-onboarding-guard";
|
|
112
|
+
/** Options for the `usePublicServantGuard` hook. */
|
|
113
|
+
export type { UsePublicServantGuardOptions } from "./react/use-public-servant-guard";
|
|
114
|
+
/** Return value of the `usePublicServantGuard` hook. */
|
|
115
|
+
export type { UsePublicServantGuardResult } from "./react/use-public-servant-guard";
|
|
116
|
+
export type UseAuthResult = {
|
|
117
|
+
authenticated: boolean;
|
|
118
|
+
user?: AuthUser;
|
|
119
|
+
claims?: AuthClaims;
|
|
120
|
+
app?: string;
|
|
121
|
+
loading: boolean;
|
|
122
|
+
warning?: string;
|
|
123
|
+
/** When false, sign-in / sign-out are disabled (Logto unreachable) */
|
|
124
|
+
logtoAvailable: boolean;
|
|
125
|
+
signIn: (options?: SignInOptions) => void;
|
|
126
|
+
signOut: () => void;
|
|
127
|
+
/** Invalidate the server session so the next sign-in fetches fresh claims. */
|
|
128
|
+
invalidateSession: () => Promise<void>;
|
|
129
|
+
refresh: () => void;
|
|
130
|
+
};
|
|
131
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// ── Auth types ───────────────────────────────────────────────
|
|
2
|
+
// ── Fetch error ──────────────────────────────────────────────
|
|
3
|
+
/**
|
|
4
|
+
* Extended Error thrown by the gateway fetcher.
|
|
5
|
+
* Carries the HTTP status code and the gateway error code (if any)
|
|
6
|
+
* so consumers can branch on error type.
|
|
7
|
+
*/
|
|
8
|
+
export class SagFetchError extends Error {
|
|
9
|
+
constructor(message, status, code) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "SagFetchError";
|
|
12
|
+
this.status = status;
|
|
13
|
+
this.code = code;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
// ── M2M / Actor type ─────────────────────────────────────────
|
|
17
|
+
/**
|
|
18
|
+
* HTTP header used to select user-token vs M2M-token flow on
|
|
19
|
+
* the Secure API Gateway.
|
|
20
|
+
*/
|
|
21
|
+
export const ACTOR_TYPE_HEADER = "X-Request-Actor-Type";
|
|
22
|
+
/**
|
|
23
|
+
* HTTP header used to request an organization-scoped token.
|
|
24
|
+
* When present, the gateway uses `getOrganizationToken(orgId)`
|
|
25
|
+
* instead of `getAccessToken(resource)` for the proxy request.
|
|
26
|
+
*/
|
|
27
|
+
export const ORGANIZATION_ID_HEADER = "X-Organization-Id";
|
|
28
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,gEAAgE;AA6DhE,gEAAgE;AAEhE;;;;GAIG;AACH,MAAM,OAAO,aAAc,SAAQ,KAAK;IAItC,YAAY,OAAe,EAAE,MAAc,EAAE,IAAa;QACxD,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,eAAe,CAAA;QAC3B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;CACF;AAED,gEAAgE;AAEhE;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,sBAAsB,CAAA;AAEvD;;;;GAIG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,mBAAmB,CAAA"}
|