@alxia/react-router 0.41.0 → 0.42.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 +69 -1
- package/dist/chunks/index-1f16k1sj.js +186 -0
- package/dist/chunks/index-1f16k1sj.js.map +14 -0
- package/dist/chunks/index-atxcmy0f.js +47 -0
- package/dist/chunks/index-atxcmy0f.js.map +11 -0
- package/dist/chunks/index-gchzf3mc.js +30 -0
- package/dist/chunks/index-gchzf3mc.js.map +10 -0
- package/dist/chunks/{index-2swe47m6.js → index-hn6htqc2.js} +3 -46
- package/dist/chunks/{index-2swe47m6.js.map → index-hn6htqc2.js.map} +4 -6
- package/dist/chunks/index-vr82typq.js +23 -0
- package/dist/chunks/index-vr82typq.js.map +11 -0
- package/dist/chunks/index-zyg8amjc.js +23 -0
- package/dist/chunks/index-zyg8amjc.js.map +10 -0
- package/dist/errors/index.js +15 -30
- package/dist/errors/index.js.map +3 -4
- package/dist/fetcher/index.js +14 -28
- package/dist/fetcher/index.js.map +3 -4
- package/dist/oauth/answer.d.ts +24 -0
- package/dist/oauth/answer.d.ts.map +1 -0
- package/dist/oauth/consent.d.ts +88 -0
- package/dist/oauth/consent.d.ts.map +1 -0
- package/dist/oauth/index.d.ts +12 -0
- package/dist/oauth/index.d.ts.map +1 -0
- package/dist/oauth/index.js +106 -0
- package/dist/oauth/index.js.map +11 -0
- package/dist/oauth/model.d.ts +13 -0
- package/dist/oauth/model.d.ts.map +1 -0
- package/dist/oauth/react/index.d.ts +7 -0
- package/dist/oauth/react/index.d.ts.map +1 -0
- package/dist/oauth/react/index.js +87 -0
- package/dist/oauth/react/index.js.map +10 -0
- package/dist/oauth/react/use-consent.d.ts +47 -0
- package/dist/oauth/react/use-consent.d.ts.map +1 -0
- package/dist/session/index.js +30 -39
- package/dist/session/index.js.map +3 -5
- package/dist/session/react/index.js +31 -204
- package/dist/session/react/index.js.map +3 -8
- package/docs/README.md +2 -1
- package/docs/guide/oauth.md +132 -0
- package/docs/roadmap.md +17 -1
- package/docs/troubleshooting.md +39 -0
- package/package.json +14 -2
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isApiErrorLike2
|
|
3
|
+
} from "../chunks/index-hn6htqc2.js";
|
|
4
|
+
import {
|
|
5
|
+
fetcherRefusal
|
|
6
|
+
} from "../chunks/index-gchzf3mc.js";
|
|
7
|
+
import {
|
|
8
|
+
STEP_UP_REQUIRED2
|
|
9
|
+
} from "../chunks/index-vr82typq.js";
|
|
10
|
+
|
|
11
|
+
// src/oauth/consent.ts
|
|
12
|
+
import { data } from "react-router";
|
|
13
|
+
|
|
14
|
+
// src/oauth/model.ts
|
|
15
|
+
function requestIdOf(url) {
|
|
16
|
+
const id = url.searchParams.get("request_id") ?? url.searchParams.get("request");
|
|
17
|
+
const trimmed = id?.trim();
|
|
18
|
+
return trimmed ? trimmed : undefined;
|
|
19
|
+
}
|
|
20
|
+
function followableRedirect(value) {
|
|
21
|
+
if (typeof value !== "string")
|
|
22
|
+
return;
|
|
23
|
+
let url;
|
|
24
|
+
try {
|
|
25
|
+
url = new URL(value);
|
|
26
|
+
} catch {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
return url.protocol === "https:" || url.protocol === "http:" ? url.href : undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/oauth/consent.ts
|
|
33
|
+
var isStrings = (value) => Array.isArray(value) && value.every((item) => typeof item === "string" && item.length > 0);
|
|
34
|
+
function inputOf(body) {
|
|
35
|
+
const raw = typeof body === "object" && body !== null ? body : {};
|
|
36
|
+
const { intent, id, scopes } = raw;
|
|
37
|
+
if (intent !== "approve" && intent !== "deny" || typeof id !== "string" || id.length === 0 || scopes !== undefined && !isStrings(scopes)) {
|
|
38
|
+
throw data("Bad consent request", { status: 400 });
|
|
39
|
+
}
|
|
40
|
+
return { intent, id, scopes };
|
|
41
|
+
}
|
|
42
|
+
function oauthConsent(options) {
|
|
43
|
+
const notFound = options.notFound ?? ((error) => error.status === 404);
|
|
44
|
+
const stepUpCode = options.stepUpRequired ?? STEP_UP_REQUIRED2;
|
|
45
|
+
async function loader(request) {
|
|
46
|
+
const { viewer, headers: viewerHeaders } = await options.viewer(request);
|
|
47
|
+
const headers = new Headers(viewerHeaders);
|
|
48
|
+
headers.set("Cache-Control", "no-store");
|
|
49
|
+
const id = requestIdOf(new URL(request.url));
|
|
50
|
+
let pending = null;
|
|
51
|
+
if (id) {
|
|
52
|
+
try {
|
|
53
|
+
pending = await options.find(id, options.caller(request)) ?? null;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (!(isApiErrorLike2(error) && notFound(error)))
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return data({ pending, viewer }, pending ? { headers } : { status: 404, headers });
|
|
60
|
+
}
|
|
61
|
+
async function action(request, language) {
|
|
62
|
+
options.json.requireJsonPost(request);
|
|
63
|
+
const input = inputOf(await options.json.readJson(request));
|
|
64
|
+
const about = { intent: input.intent, id: input.id };
|
|
65
|
+
const caller = options.caller(request);
|
|
66
|
+
try {
|
|
67
|
+
const { redirectTo } = input.intent === "approve" ? await options.approve({
|
|
68
|
+
id: input.id,
|
|
69
|
+
...input.scopes && { scopes: input.scopes }
|
|
70
|
+
}, caller) : await options.deny({ id: input.id }, caller);
|
|
71
|
+
const next = followableRedirect(redirectTo);
|
|
72
|
+
if (!next) {
|
|
73
|
+
throw new Error("The API answered a redirect that is not an address");
|
|
74
|
+
}
|
|
75
|
+
return { ok: true, ...about, redirectTo: next };
|
|
76
|
+
} catch (error) {
|
|
77
|
+
const code = isApiErrorLike2(error) ? error.code : undefined;
|
|
78
|
+
const { message, status } = fetcherRefusal(options.errors, error, language);
|
|
79
|
+
const stepUp = code === stepUpCode;
|
|
80
|
+
const expired = isApiErrorLike2(error) && !stepUp && notFound(error);
|
|
81
|
+
return data({
|
|
82
|
+
ok: false,
|
|
83
|
+
...about,
|
|
84
|
+
stepUp,
|
|
85
|
+
expired,
|
|
86
|
+
error: stepUp || expired ? "" : message
|
|
87
|
+
}, { status });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { loader, action };
|
|
91
|
+
}
|
|
92
|
+
function consentShouldRevalidate({
|
|
93
|
+
formMethod,
|
|
94
|
+
defaultShouldRevalidate
|
|
95
|
+
}) {
|
|
96
|
+
return formMethod ? false : defaultShouldRevalidate;
|
|
97
|
+
}
|
|
98
|
+
export {
|
|
99
|
+
consentShouldRevalidate,
|
|
100
|
+
followableRedirect,
|
|
101
|
+
oauthConsent,
|
|
102
|
+
requestIdOf
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
//# debugId=DB3702F6926B73AE64756E2164756E21
|
|
106
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/oauth/consent.ts", "../src/oauth/model.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { data, type ShouldRevalidateFunctionArgs } from 'react-router';\nimport { type ApiErrorLike, isApiErrorLike } from '../errors/api-error';\nimport type { ApiErrors } from '../errors/api-errors';\nimport { fetcherRefusal } from '../errors/fetcher-result';\nimport type { JsonActions } from '../errors/json-actions';\nimport { STEP_UP_REQUIRED } from '../session/step-up-codes';\nimport type { ConsentAnswer, ConsentDecisionIntent } from './answer';\nimport { followableRedirect, requestIdOf } from './model';\n\n/** Where a decided request sends the browser, as `OAuthDecision` of `@nxgt/janus-oauth/graphql` has it. */\nexport interface ConsentRedirect {\n\treadonly redirectTo: string;\n}\n\n/** The decision a page posted, once checked. */\nexport interface ConsentInput {\n\treadonly intent: ConsentDecisionIntent;\n\treadonly id: string;\n\t/** The scopes allowed; all the asked ones when absent. */\n\treadonly scopes?: readonly string[] | undefined;\n}\n\nexport interface ConsentOptions<\n\tL extends string,\n\tCaller,\n\tViewer,\n\tPending extends object,\n> {\n\t/** The app's `jsonActions()`: the same-site and JSON checks of the action. */\n\treadonly json: Pick<JsonActions, 'readJson' | 'requireJsonPost'>;\n\t/** The app's `createApiErrors()`: words the refusals. */\n\treadonly errors: Pick<ApiErrors<L>, 'apiRefusal'>;\n\t/**\n\t * The signed-in viewer, or a thrown redirect to sign in that comes back to\n\t * this very address, request id included (`SessionFlow.signInRedirect`\n\t * does). `headers` are added to the answer (a refreshed session cookie).\n\t */\n\tviewer(\n\t\trequest: Request,\n\t): Promise<{ readonly viewer: Viewer; readonly headers?: HeadersInit }>;\n\t/** The caller that carries the request's session to the API. */\n\tcaller(request: Request): Caller;\n\t/** `oauthRequest(id)`: the pending request, or `null` when the API has none. */\n\tfind(id: string, caller: Caller): Promise<Pending | null | undefined>;\n\t/** `approveOAuthRequest(id, scopes)`. */\n\tapprove(\n\t\tinput: { readonly id: string; readonly scopes?: readonly string[] },\n\t\tcaller: Caller,\n\t): Promise<ConsentRedirect>;\n\t/** `denyOAuthRequest(id)`. */\n\tdeny(\n\t\tinput: { readonly id: string },\n\t\tcaller: Caller,\n\t): Promise<ConsentRedirect>;\n\t/**\n\t * Whether an API refusal means the request is unknown, decided, lapsed or\n\t * not the viewer's (the API cannot tell which). Default: a 404.\n\t */\n\tnotFound?(error: ApiErrorLike): boolean;\n\t/** The API's step-up code. Default `STEP_UP_REQUIRED`. */\n\tstepUpRequired?: string;\n}\n\nexport interface ConsentRoute<\n\tL extends string,\n\tViewer,\n\tPending extends object,\n> {\n\t/**\n\t * The route's loader: the viewer and the pending request (`null` for an\n\t * unknown one or none in the address, answered with a 404 so the page shows\n\t * its expired state). Never cached: it names a one-time request.\n\t */\n\tloader(\n\t\trequest: Request,\n\t): Promise<\n\t\tReturnType<typeof data<{ pending: Pending | null; viewer: Viewer }>>\n\t>;\n\t/**\n\t * The route's action: approves or denies the JSON `{ intent, id, scopes? }`\n\t * and answers a `ConsentAnswer` for the page's fetcher.\n\t */\n\taction(\n\t\trequest: Request,\n\t\tlanguage: L,\n\t): Promise<ConsentAnswer | ReturnType<typeof data<ConsentAnswer>>>;\n}\n\nconst isStrings = (value: unknown): value is string[] =>\n\tArray.isArray(value) &&\n\tvalue.every((item) => typeof item === 'string' && item.length > 0);\n\nfunction inputOf(body: unknown): ConsentInput {\n\tconst raw = (typeof body === 'object' && body !== null ? body : {}) as Record<\n\t\tstring,\n\t\tunknown\n\t>;\n\tconst { intent, id, scopes } = raw;\n\tif (\n\t\t(intent !== 'approve' && intent !== 'deny') ||\n\t\ttypeof id !== 'string' ||\n\t\tid.length === 0 ||\n\t\t(scopes !== undefined && !isStrings(scopes))\n\t) {\n\t\tthrow data('Bad consent request', { status: 400 });\n\t}\n\treturn { intent, id, scopes };\n}\n\n/**\n * The server side of the screen where a viewer allows or denies an OAuth\n * client (`oauthRequest`, `approveOAuthRequest` and `denyOAuthRequest` of\n * `@nxgt/janus-oauth/graphql`): a loader and an action for one route, the\n * GraphQL calls passed in. The page reads `useConsent`\n * (`@alxia/react-router/oauth/react`).\n *\n * @example\n * const consent = oauthConsent({ json, errors, viewer, caller, find, approve, deny });\n * export const loader = ({ request }) => consent.loader(request);\n * export const action = ({ request }) => consent.action(request, language);\n */\nexport function oauthConsent<\n\tL extends string,\n\tCaller,\n\tViewer,\n\tPending extends object,\n>(\n\toptions: ConsentOptions<L, Caller, Viewer, Pending>,\n): ConsentRoute<L, Viewer, Pending> {\n\tconst notFound =\n\t\toptions.notFound ?? ((error: ApiErrorLike) => error.status === 404);\n\tconst stepUpCode = options.stepUpRequired ?? STEP_UP_REQUIRED;\n\n\tasync function loader(request: Request) {\n\t\tconst { viewer, headers: viewerHeaders } = await options.viewer(request);\n\t\tconst headers = new Headers(viewerHeaders);\n\t\theaders.set('Cache-Control', 'no-store');\n\t\tconst id = requestIdOf(new URL(request.url));\n\t\tlet pending: Pending | null = null;\n\t\tif (id) {\n\t\t\ttry {\n\t\t\t\tpending = (await options.find(id, options.caller(request))) ?? null;\n\t\t\t} catch (error) {\n\t\t\t\tif (!(isApiErrorLike(error) && notFound(error))) throw error;\n\t\t\t}\n\t\t}\n\t\treturn data(\n\t\t\t{ pending, viewer },\n\t\t\tpending ? { headers } : { status: 404, headers },\n\t\t);\n\t}\n\n\tasync function action(request: Request, language: L) {\n\t\toptions.json.requireJsonPost(request);\n\t\tconst input = inputOf(await options.json.readJson(request));\n\t\tconst about = { intent: input.intent, id: input.id } as const;\n\t\tconst caller = options.caller(request);\n\t\ttry {\n\t\t\tconst { redirectTo } =\n\t\t\t\tinput.intent === 'approve'\n\t\t\t\t\t? await options.approve(\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tid: input.id,\n\t\t\t\t\t\t\t\t...(input.scopes && { scopes: input.scopes }),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tcaller,\n\t\t\t\t\t\t)\n\t\t\t\t\t: await options.deny({ id: input.id }, caller);\n\t\t\tconst next = followableRedirect(redirectTo);\n\t\t\tif (!next) {\n\t\t\t\tthrow new Error('The API answered a redirect that is not an address');\n\t\t\t}\n\t\t\treturn { ok: true as const, ...about, redirectTo: next };\n\t\t} catch (error) {\n\t\t\tconst code = isApiErrorLike(error) ? error.code : undefined;\n\t\t\tconst { message, status } = fetcherRefusal(\n\t\t\t\toptions.errors,\n\t\t\t\terror,\n\t\t\t\tlanguage,\n\t\t\t);\n\t\t\tconst stepUp = code === stepUpCode;\n\t\t\tconst expired = isApiErrorLike(error) && !stepUp && notFound(error);\n\t\t\treturn data(\n\t\t\t\t{\n\t\t\t\t\tok: false as const,\n\t\t\t\t\t...about,\n\t\t\t\t\tstepUp,\n\t\t\t\t\texpired,\n\t\t\t\t\terror: stepUp || expired ? '' : message,\n\t\t\t\t},\n\t\t\t\t{ status },\n\t\t\t);\n\t\t}\n\t}\n\n\treturn { loader, action };\n}\n\n/**\n * `shouldRevalidate` of the consent route: never reload after a decision.\n * The decision spends the request, so the loader would answer \"expired\"\n * while the browser is on its way to the client's callback. Any other change\n * (another `request_id`, a navigation) reloads as usual.\n */\nexport function consentShouldRevalidate({\n\tformMethod,\n\tdefaultShouldRevalidate,\n}: Pick<\n\tShouldRevalidateFunctionArgs,\n\t'formMethod' | 'defaultShouldRevalidate'\n>): boolean {\n\treturn formMethod ? false : defaultShouldRevalidate;\n}\n",
|
|
6
|
+
"/**\n * The request an authorization link names: `request_id` (what the API sends\n * the browser with), else `request`. Empty or absent: `undefined`.\n */\nexport function requestIdOf(url: URL): string | undefined {\n\tconst id =\n\t\turl.searchParams.get('request_id') ?? url.searchParams.get('request');\n\tconst trimmed = id?.trim();\n\treturn trimmed ? trimmed : undefined;\n}\n\n/**\n * Where the browser is sent once a request is decided: the client's own\n * callback, normalised. Only http(s) is followed (a loopback callback is\n * http), never a `javascript:` or `data:` address. Anything else:\n * `undefined`.\n */\nexport function followableRedirect(value: unknown): string | undefined {\n\tif (typeof value !== 'string') return undefined;\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(value);\n\t} catch {\n\t\treturn undefined;\n\t}\n\treturn url.protocol === 'https:' || url.protocol === 'http:'\n\t\t? url.href\n\t\t: undefined;\n}\n"
|
|
7
|
+
],
|
|
8
|
+
"mappings": ";;;;;;;;;;;AAAA;;;ACIO,SAAS,WAAW,CAAC,KAA8B;AAAA,EACzD,MAAM,KACL,IAAI,aAAa,IAAI,YAAY,KAAK,IAAI,aAAa,IAAI,SAAS;AAAA,EACrE,MAAM,UAAU,IAAI,KAAK;AAAA,EACzB,OAAO,UAAU,UAAU;AAAA;AASrB,SAAS,kBAAkB,CAAC,OAAoC;AAAA,EACtE,IAAI,OAAO,UAAU;AAAA,IAAU;AAAA,EAC/B,IAAI;AAAA,EACJ,IAAI;AAAA,IACH,MAAM,IAAI,IAAI,KAAK;AAAA,IAClB,MAAM;AAAA,IACP;AAAA;AAAA,EAED,OAAO,IAAI,aAAa,YAAY,IAAI,aAAa,UAClD,IAAI,OACJ;AAAA;;;AD6DJ,IAAM,YAAY,CAAC,UAClB,MAAM,QAAQ,KAAK,KACnB,MAAM,MAAM,CAAC,SAAS,OAAO,SAAS,YAAY,KAAK,SAAS,CAAC;AAElE,SAAS,OAAO,CAAC,MAA6B;AAAA,EAC7C,MAAM,MAAO,OAAO,SAAS,YAAY,SAAS,OAAO,OAAO,CAAC;AAAA,EAIjE,QAAQ,QAAQ,IAAI,WAAW;AAAA,EAC/B,IACE,WAAW,aAAa,WAAW,UACpC,OAAO,OAAO,YACd,GAAG,WAAW,KACb,WAAW,aAAa,CAAC,UAAU,MAAM,GACzC;AAAA,IACD,MAAM,KAAK,uBAAuB,EAAE,QAAQ,IAAI,CAAC;AAAA,EAClD;AAAA,EACA,OAAO,EAAE,QAAQ,IAAI,OAAO;AAAA;AAetB,SAAS,YAKf,CACA,SACmC;AAAA,EACnC,MAAM,WACL,QAAQ,aAAa,CAAC,UAAwB,MAAM,WAAW;AAAA,EAChE,MAAM,aAAa,QAAQ,kBAAkB;AAAA,EAE7C,eAAe,MAAM,CAAC,SAAkB;AAAA,IACvC,QAAQ,QAAQ,SAAS,kBAAkB,MAAM,QAAQ,OAAO,OAAO;AAAA,IACvE,MAAM,UAAU,IAAI,QAAQ,aAAa;AAAA,IACzC,QAAQ,IAAI,iBAAiB,UAAU;AAAA,IACvC,MAAM,KAAK,YAAY,IAAI,IAAI,QAAQ,GAAG,CAAC;AAAA,IAC3C,IAAI,UAA0B;AAAA,IAC9B,IAAI,IAAI;AAAA,MACP,IAAI;AAAA,QACH,UAAW,MAAM,QAAQ,KAAK,IAAI,QAAQ,OAAO,OAAO,CAAC,KAAM;AAAA,QAC9D,OAAO,OAAO;AAAA,QACf,IAAI,EAAE,gBAAe,KAAK,KAAK,SAAS,KAAK;AAAA,UAAI,MAAM;AAAA;AAAA,IAEzD;AAAA,IACA,OAAO,KACN,EAAE,SAAS,OAAO,GAClB,UAAU,EAAE,QAAQ,IAAI,EAAE,QAAQ,KAAK,QAAQ,CAChD;AAAA;AAAA,EAGD,eAAe,MAAM,CAAC,SAAkB,UAAa;AAAA,IACpD,QAAQ,KAAK,gBAAgB,OAAO;AAAA,IACpC,MAAM,QAAQ,QAAQ,MAAM,QAAQ,KAAK,SAAS,OAAO,CAAC;AAAA,IAC1D,MAAM,QAAQ,EAAE,QAAQ,MAAM,QAAQ,IAAI,MAAM,GAAG;AAAA,IACnD,MAAM,SAAS,QAAQ,OAAO,OAAO;AAAA,IACrC,IAAI;AAAA,MACH,QAAQ,eACP,MAAM,WAAW,YACd,MAAM,QAAQ,QACd;AAAA,QACC,IAAI,MAAM;AAAA,WACN,MAAM,UAAU,EAAE,QAAQ,MAAM,OAAO;AAAA,MAC5C,GACA,MACD,IACC,MAAM,QAAQ,KAAK,EAAE,IAAI,MAAM,GAAG,GAAG,MAAM;AAAA,MAC/C,MAAM,OAAO,mBAAmB,UAAU;AAAA,MAC1C,IAAI,CAAC,MAAM;AAAA,QACV,MAAM,IAAI,MAAM,oDAAoD;AAAA,MACrE;AAAA,MACA,OAAO,EAAE,IAAI,SAAkB,OAAO,YAAY,KAAK;AAAA,MACtD,OAAO,OAAO;AAAA,MACf,MAAM,OAAO,gBAAe,KAAK,IAAI,MAAM,OAAO;AAAA,MAClD,QAAQ,SAAS,WAAW,eAC3B,QAAQ,QACR,OACA,QACD;AAAA,MACA,MAAM,SAAS,SAAS;AAAA,MACxB,MAAM,UAAU,gBAAe,KAAK,KAAK,CAAC,UAAU,SAAS,KAAK;AAAA,MAClE,OAAO,KACN;AAAA,QACC,IAAI;AAAA,WACD;AAAA,QACH;AAAA,QACA;AAAA,QACA,OAAO,UAAU,UAAU,KAAK;AAAA,MACjC,GACA,EAAE,OAAO,CACV;AAAA;AAAA;AAAA,EAIF,OAAO,EAAE,QAAQ,OAAO;AAAA;AASlB,SAAS,uBAAuB;AAAA,EACtC;AAAA,EACA;AAAA,GAIW;AAAA,EACX,OAAO,aAAa,QAAQ;AAAA;",
|
|
9
|
+
"debugId": "DB3702F6926B73AE64756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The request an authorization link names: `request_id` (what the API sends
|
|
3
|
+
* the browser with), else `request`. Empty or absent: `undefined`.
|
|
4
|
+
*/
|
|
5
|
+
export declare function requestIdOf(url: URL): string | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* Where the browser is sent once a request is decided: the client's own
|
|
8
|
+
* callback, normalised. Only http(s) is followed (a loopback callback is
|
|
9
|
+
* http), never a `javascript:` or `data:` address. Anything else:
|
|
10
|
+
* `undefined`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function followableRedirect(value: unknown): string | undefined;
|
|
13
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../src/oauth/model.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,GAAG,SAAS,CAKxD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAWrE"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@alxia/react-router/oauth/react`: the hook of the screen where a viewer
|
|
3
|
+
* allows or denies an OAuth client. `react` and `react-router` are its only
|
|
4
|
+
* imports; it shares nothing with the server side of `/oauth`.
|
|
5
|
+
*/
|
|
6
|
+
export { type UseConsentOptions, useConsent } from './use-consent';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/oauth/react/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import {
|
|
2
|
+
useFetcherAnswer2
|
|
3
|
+
} from "../../chunks/index-zyg8amjc.js";
|
|
4
|
+
import {
|
|
5
|
+
useStepUpReplay2
|
|
6
|
+
} from "../../chunks/index-1f16k1sj.js";
|
|
7
|
+
|
|
8
|
+
// src/oauth/react/use-consent.ts
|
|
9
|
+
import { useRef, useState } from "react";
|
|
10
|
+
import { useFetcher } from "react-router";
|
|
11
|
+
function useConsent(requestId, { go, action, request, confirm }) {
|
|
12
|
+
const fetcher = useFetcher({
|
|
13
|
+
key: `oauth-consent-${requestId}`
|
|
14
|
+
});
|
|
15
|
+
const busy = fetcher.state !== "idle";
|
|
16
|
+
const last = useRef(undefined);
|
|
17
|
+
const [error, setError] = useState(undefined);
|
|
18
|
+
const [expired, setExpired] = useState(false);
|
|
19
|
+
const [leaving, setLeaving] = useState(false);
|
|
20
|
+
const [shownFor, setShownFor] = useState(requestId);
|
|
21
|
+
if (shownFor !== requestId) {
|
|
22
|
+
setShownFor(requestId);
|
|
23
|
+
setError(undefined);
|
|
24
|
+
setExpired(false);
|
|
25
|
+
setLeaving(false);
|
|
26
|
+
last.current = undefined;
|
|
27
|
+
}
|
|
28
|
+
const send = (intent) => fetcher.submit({ intent, id: requestId }, {
|
|
29
|
+
method: "post",
|
|
30
|
+
encType: "application/json",
|
|
31
|
+
...action ? { action } : {}
|
|
32
|
+
});
|
|
33
|
+
const decide = (intent) => {
|
|
34
|
+
if (busy || leaving)
|
|
35
|
+
return;
|
|
36
|
+
last.current = intent;
|
|
37
|
+
setError(undefined);
|
|
38
|
+
send(intent);
|
|
39
|
+
};
|
|
40
|
+
const latest = useFetcherAnswer2(fetcher, (answer) => {
|
|
41
|
+
if (!answer.ok) {
|
|
42
|
+
if (answer.stepUp)
|
|
43
|
+
return;
|
|
44
|
+
last.current = undefined;
|
|
45
|
+
if (answer.expired)
|
|
46
|
+
setExpired(true);
|
|
47
|
+
else
|
|
48
|
+
setError(answer.error);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
setLeaving(true);
|
|
52
|
+
(go ?? ((url) => window.location.assign(url)))(answer.redirectTo);
|
|
53
|
+
});
|
|
54
|
+
const stepUp = useStepUpReplay2({
|
|
55
|
+
answer: latest,
|
|
56
|
+
action: last.current,
|
|
57
|
+
replay() {
|
|
58
|
+
if (last.current)
|
|
59
|
+
send(last.current);
|
|
60
|
+
},
|
|
61
|
+
spendsCode: () => false,
|
|
62
|
+
request,
|
|
63
|
+
confirm
|
|
64
|
+
});
|
|
65
|
+
const { dialogProps } = stepUp;
|
|
66
|
+
const pending = busy || leaving || dialogProps.open;
|
|
67
|
+
return {
|
|
68
|
+
approve: () => decide("approve"),
|
|
69
|
+
deny: () => decide("deny"),
|
|
70
|
+
pending: pending ? last.current ?? true : false,
|
|
71
|
+
expired,
|
|
72
|
+
error,
|
|
73
|
+
dialogProps: {
|
|
74
|
+
...dialogProps,
|
|
75
|
+
onCancel() {
|
|
76
|
+
dialogProps.onCancel();
|
|
77
|
+
last.current = undefined;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
export {
|
|
83
|
+
useConsent
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
//# debugId=8F0D98662C6B0DFC64756E2164756E21
|
|
87
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/oauth/react/use-consent.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"import { useRef, useState } from 'react';\nimport { useFetcher } from 'react-router';\nimport { useFetcherAnswer } from '../../fetcher/use-fetcher-answer';\nimport {\n\ttype StepUpReplayOptions,\n\tuseStepUpReplay,\n} from '../../session/react/step-up';\nimport type { ConsentAnswer, ConsentDecisionIntent } from '../answer';\n\nexport interface UseConsentOptions\n\textends Pick<\n\t\tStepUpReplayOptions<ConsentDecisionIntent>,\n\t\t'request' | 'confirm'\n\t> {\n\t/**\n\t * Where the browser goes once a decision lands. A hard navigation by\n\t * default (`window.location.assign`): the callback is another site's.\n\t * A parameter so a test can watch it.\n\t */\n\tgo?(url: string): void;\n\t/** The route whose action decides, when it is not the page's own. */\n\taction?: string;\n}\n\n/**\n * The decision of a consent screen, posted as JSON to the page's action\n * (`oauthConsent().action`). A session not confirmed lately opens the shared\n * step-up (`useStepUpReplay`, nothing spent) and the decision goes again by\n * itself; a decision that lands sends the browser to the client's callback\n * (`go`), and the card stays pending until the page is left. A request the\n * API no longer knows is `expired`; any other refusal is the `error`.\n *\n * @example\n * const { approve, deny, pending, expired, error, dialogProps } =\n * \tuseConsent(request.id, { request: stepUpRequest, confirm: stepUpConfirm });\n */\nexport function useConsent(\n\trequestId: string,\n\t{ go, action, request, confirm }: UseConsentOptions,\n) {\n\tconst fetcher = useFetcher<ConsentAnswer>({\n\t\tkey: `oauth-consent-${requestId}`,\n\t});\n\tconst busy = fetcher.state !== 'idle';\n\tconst last = useRef<ConsentDecisionIntent | undefined>(undefined);\n\tconst [error, setError] = useState<string | undefined>(undefined);\n\tconst [expired, setExpired] = useState(false);\n\tconst [leaving, setLeaving] = useState(false);\n\n\t// Another request in the same mounted route starts from a clean card.\n\tconst [shownFor, setShownFor] = useState(requestId);\n\tif (shownFor !== requestId) {\n\t\tsetShownFor(requestId);\n\t\tsetError(undefined);\n\t\tsetExpired(false);\n\t\tsetLeaving(false);\n\t\tlast.current = undefined;\n\t}\n\n\tconst send = (intent: ConsentDecisionIntent) =>\n\t\tfetcher.submit(\n\t\t\t{ intent, id: requestId },\n\t\t\t{\n\t\t\t\tmethod: 'post',\n\t\t\t\tencType: 'application/json',\n\t\t\t\t...(action ? { action } : {}),\n\t\t\t},\n\t\t);\n\n\tconst decide = (intent: ConsentDecisionIntent) => {\n\t\tif (busy || leaving) return;\n\t\tlast.current = intent;\n\t\tsetError(undefined);\n\t\tvoid send(intent);\n\t};\n\n\tconst latest = useFetcherAnswer(fetcher, (answer) => {\n\t\tif (!answer.ok) {\n\t\t\tif (answer.stepUp) return; // the dialog asks; `replay` sends again\n\t\t\tlast.current = undefined;\n\t\t\tif (answer.expired) setExpired(true);\n\t\t\telse setError(answer.error);\n\t\t\treturn;\n\t\t}\n\t\tsetLeaving(true);\n\t\t(go ?? ((url) => window.location.assign(url)))(answer.redirectTo);\n\t});\n\n\tconst stepUp = useStepUpReplay<ConsentDecisionIntent>({\n\t\tanswer: latest,\n\t\taction: last.current,\n\t\treplay() {\n\t\t\tif (last.current) void send(last.current);\n\t\t},\n\t\t// A decision carries no one-time code: nothing was spent.\n\t\tspendsCode: () => false,\n\t\trequest,\n\t\tconfirm,\n\t});\n\n\tconst { dialogProps } = stepUp;\n\t// `last` is a ref, but each write sits beside a state change or a fetcher\n\t// transition, so a render follows it.\n\tconst pending = busy || leaving || dialogProps.open;\n\treturn {\n\t\tapprove: () => decide('approve'),\n\t\tdeny: () => decide('deny'),\n\t\t/** What the card spins: the decision in flight, held until the page is left. */\n\t\tpending: pending ? (last.current ?? true) : (false as const),\n\t\texpired,\n\t\terror,\n\t\t/** The step-up dialog's props; cancelling it frees the card. */\n\t\tdialogProps: {\n\t\t\t...dialogProps,\n\t\t\tonCancel() {\n\t\t\t\tdialogProps.onCancel();\n\t\t\t\tlast.current = undefined;\n\t\t\t},\n\t\t},\n\t};\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": ";;;;;;;;AAAA;AACA;AAmCO,SAAS,UAAU,CACzB,aACE,IAAI,QAAQ,SAAS,WACtB;AAAA,EACD,MAAM,UAAU,WAA0B;AAAA,IACzC,KAAK,iBAAiB;AAAA,EACvB,CAAC;AAAA,EACD,MAAM,OAAO,QAAQ,UAAU;AAAA,EAC/B,MAAM,OAAO,OAA0C,SAAS;AAAA,EAChE,OAAO,OAAO,YAAY,SAA6B,SAAS;AAAA,EAChE,OAAO,SAAS,cAAc,SAAS,KAAK;AAAA,EAC5C,OAAO,SAAS,cAAc,SAAS,KAAK;AAAA,EAG5C,OAAO,UAAU,eAAe,SAAS,SAAS;AAAA,EAClD,IAAI,aAAa,WAAW;AAAA,IAC3B,YAAY,SAAS;AAAA,IACrB,SAAS,SAAS;AAAA,IAClB,WAAW,KAAK;AAAA,IAChB,WAAW,KAAK;AAAA,IAChB,KAAK,UAAU;AAAA,EAChB;AAAA,EAEA,MAAM,OAAO,CAAC,WACb,QAAQ,OACP,EAAE,QAAQ,IAAI,UAAU,GACxB;AAAA,IACC,QAAQ;AAAA,IACR,SAAS;AAAA,OACL,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,EAC5B,CACD;AAAA,EAED,MAAM,SAAS,CAAC,WAAkC;AAAA,IACjD,IAAI,QAAQ;AAAA,MAAS;AAAA,IACrB,KAAK,UAAU;AAAA,IACf,SAAS,SAAS;AAAA,IACb,KAAK,MAAM;AAAA;AAAA,EAGjB,MAAM,SAAS,kBAAiB,SAAS,CAAC,WAAW;AAAA,IACpD,IAAI,CAAC,OAAO,IAAI;AAAA,MACf,IAAI,OAAO;AAAA,QAAQ;AAAA,MACnB,KAAK,UAAU;AAAA,MACf,IAAI,OAAO;AAAA,QAAS,WAAW,IAAI;AAAA,MACnC;AAAA,QAAK,SAAS,OAAO,KAAK;AAAA,MAC1B;AAAA,IACD;AAAA,IACA,WAAW,IAAI;AAAA,KACd,OAAO,CAAC,QAAQ,OAAO,SAAS,OAAO,GAAG,IAAI,OAAO,UAAU;AAAA,GAChE;AAAA,EAED,MAAM,SAAS,iBAAuC;AAAA,IACrD,QAAQ;AAAA,IACR,QAAQ,KAAK;AAAA,IACb,MAAM,GAAG;AAAA,MACR,IAAI,KAAK;AAAA,QAAc,KAAK,KAAK,OAAO;AAAA;AAAA,IAGzC,YAAY,MAAM;AAAA,IAClB;AAAA,IACA;AAAA,EACD,CAAC;AAAA,EAED,QAAQ,gBAAgB;AAAA,EAGxB,MAAM,UAAU,QAAQ,WAAW,YAAY;AAAA,EAC/C,OAAO;AAAA,IACN,SAAS,MAAM,OAAO,SAAS;AAAA,IAC/B,MAAM,MAAM,OAAO,MAAM;AAAA,IAEzB,SAAS,UAAW,KAAK,WAAW,OAAS;AAAA,IAC7C;AAAA,IACA;AAAA,IAEA,aAAa;AAAA,SACT;AAAA,MACH,QAAQ,GAAG;AAAA,QACV,YAAY,SAAS;AAAA,QACrB,KAAK,UAAU;AAAA;AAAA,IAEjB;AAAA,EACD;AAAA;",
|
|
8
|
+
"debugId": "8F0D98662C6B0DFC64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type StepUpReplayOptions } from '../../session/react/step-up';
|
|
2
|
+
import type { ConsentDecisionIntent } from '../answer';
|
|
3
|
+
export interface UseConsentOptions extends Pick<StepUpReplayOptions<ConsentDecisionIntent>, 'request' | 'confirm'> {
|
|
4
|
+
/**
|
|
5
|
+
* Where the browser goes once a decision lands. A hard navigation by
|
|
6
|
+
* default (`window.location.assign`): the callback is another site's.
|
|
7
|
+
* A parameter so a test can watch it.
|
|
8
|
+
*/
|
|
9
|
+
go?(url: string): void;
|
|
10
|
+
/** The route whose action decides, when it is not the page's own. */
|
|
11
|
+
action?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The decision of a consent screen, posted as JSON to the page's action
|
|
15
|
+
* (`oauthConsent().action`). A session not confirmed lately opens the shared
|
|
16
|
+
* step-up (`useStepUpReplay`, nothing spent) and the decision goes again by
|
|
17
|
+
* itself; a decision that lands sends the browser to the client's callback
|
|
18
|
+
* (`go`), and the card stays pending until the page is left. A request the
|
|
19
|
+
* API no longer knows is `expired`; any other refusal is the `error`.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* const { approve, deny, pending, expired, error, dialogProps } =
|
|
23
|
+
* useConsent(request.id, { request: stepUpRequest, confirm: stepUpConfirm });
|
|
24
|
+
*/
|
|
25
|
+
export declare function useConsent(requestId: string, { go, action, request, confirm }: UseConsentOptions): {
|
|
26
|
+
approve: () => void;
|
|
27
|
+
deny: () => void;
|
|
28
|
+
/** What the card spins: the decision in flight, held until the page is left. */
|
|
29
|
+
pending: boolean | ConsentDecisionIntent;
|
|
30
|
+
expired: boolean;
|
|
31
|
+
error: string | undefined;
|
|
32
|
+
/** The step-up dialog's props; cancelling it frees the card. */
|
|
33
|
+
dialogProps: {
|
|
34
|
+
onCancel(): void;
|
|
35
|
+
open: boolean;
|
|
36
|
+
phase: import("../../session/react").StepUpDialogPhase;
|
|
37
|
+
via: import("../../session/model").StepUpVia | undefined;
|
|
38
|
+
props: import("../../session/model").StepUpProps;
|
|
39
|
+
message: string | undefined;
|
|
40
|
+
busy: boolean;
|
|
41
|
+
confirming: boolean;
|
|
42
|
+
onRetry(): void;
|
|
43
|
+
onSubmit(code: string): void;
|
|
44
|
+
onResend: (() => void) | undefined;
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=use-consent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-consent.d.ts","sourceRoot":"","sources":["../../../src/oauth/react/use-consent.ts"],"names":[],"mappings":"AAGA,OAAO,EACN,KAAK,mBAAmB,EAExB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAiB,qBAAqB,EAAE,MAAM,WAAW,CAAC;AAEtE,MAAM,WAAW,iBAChB,SAAQ,IAAI,CACX,mBAAmB,CAAC,qBAAqB,CAAC,EAC1C,SAAS,GAAG,SAAS,CACrB;IACD;;;;OAIG;IACH,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CACzB,SAAS,EAAE,MAAM,EACjB,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,iBAAiB;;;IAqElD,gFAAgF;;;;IAIhF,gEAAgE;;;;;;;;;;;;;;EASjE"}
|
package/dist/session/index.js
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
isApiErrorLike2,
|
|
3
|
-
retryAfterOf2,
|
|
4
|
-
attemptsLeftOf2,
|
|
5
|
-
routeError2,
|
|
6
2
|
nextOf2,
|
|
7
3
|
hasSiteCheck,
|
|
8
4
|
refuseCrossSite
|
|
9
|
-
} from "../chunks/index-
|
|
5
|
+
} from "../chunks/index-atxcmy0f.js";
|
|
10
6
|
import {
|
|
11
7
|
HOME2,
|
|
12
8
|
safeNext2,
|
|
@@ -15,16 +11,20 @@ import {
|
|
|
15
11
|
signInPath2,
|
|
16
12
|
secondFactorPath2
|
|
17
13
|
} from "../chunks/index-xvx63t7n.js";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
14
|
+
import {
|
|
15
|
+
isApiErrorLike2,
|
|
16
|
+
retryAfterOf2,
|
|
17
|
+
attemptsLeftOf2,
|
|
18
|
+
routeError2
|
|
19
|
+
} from "../chunks/index-hn6htqc2.js";
|
|
20
|
+
import {
|
|
21
|
+
INVALID_CODE2,
|
|
22
|
+
CHALLENGE_OVER2,
|
|
23
|
+
MAIL_THROTTLED2,
|
|
24
|
+
UNAUTHENTICATED2,
|
|
25
|
+
STEP_UP_REQUIRED2,
|
|
26
|
+
STEP_UP_OVER2
|
|
27
|
+
} from "../chunks/index-vr82typq.js";
|
|
28
28
|
// src/session/serialize.ts
|
|
29
29
|
var SAME_SITE = { lax: "Lax", strict: "Strict", none: "None" };
|
|
30
30
|
function checkSameSite(caller, sameSite, secure) {
|
|
@@ -349,7 +349,7 @@ async function afterReset(options, resetDone, request, { context } = {}) {
|
|
|
349
349
|
var RESET_DONE = "/sign-in?password-reset=1";
|
|
350
350
|
var worded = ({ wording, errors }, error, language) => wording?.(error, language) ?? errors.apiErrorMessage(error, language);
|
|
351
351
|
var throttleCodes = (codes) => {
|
|
352
|
-
const configured = codes?.mailThrottled ??
|
|
352
|
+
const configured = codes?.mailThrottled ?? MAIL_THROTTLED2;
|
|
353
353
|
return typeof configured === "string" ? [configured] : configured;
|
|
354
354
|
};
|
|
355
355
|
async function requestLink(options, run, { request, language }) {
|
|
@@ -536,8 +536,8 @@ function resolve(options) {
|
|
|
536
536
|
if (!hasSiteCheck(options.json)) {
|
|
537
537
|
throw new TypeError("signInFlow(): json must come from jsonActions(), which carries the site check every step runs; this object has none (a hand-made double?). Pass the app's jsonActions({ origin }) instance, or a spread copy of it.");
|
|
538
538
|
}
|
|
539
|
-
const invalidCode = options.codes?.invalidCode ??
|
|
540
|
-
const challengeOver = options.codes?.challengeOver ??
|
|
539
|
+
const invalidCode = options.codes?.invalidCode ?? INVALID_CODE2;
|
|
540
|
+
const challengeOver = options.codes?.challengeOver ?? CHALLENGE_OVER2;
|
|
541
541
|
if (challengeOver.includes(invalidCode)) {
|
|
542
542
|
throw new TypeError(`signInFlow(): the code ${JSON.stringify(invalidCode)} is both the wrong-code code (codes.invalidCode) and in codes.challengeOver. A wrong code that leaves tries cannot end the challenge.`);
|
|
543
543
|
}
|
|
@@ -639,15 +639,6 @@ function signedSession(options) {
|
|
|
639
639
|
// src/session/step-up.ts
|
|
640
640
|
import { data as data6 } from "react-router";
|
|
641
641
|
|
|
642
|
-
// src/session/step-up-codes.ts
|
|
643
|
-
var STEP_UP_REQUIRED = "STEP_UP_REQUIRED";
|
|
644
|
-
var STEP_UP_OVER = [
|
|
645
|
-
...CHALLENGE_OVER,
|
|
646
|
-
"TOKEN_STALE",
|
|
647
|
-
"SECOND_FACTOR_ACTIVE",
|
|
648
|
-
"SECOND_FACTOR_NOT_ENROLLED"
|
|
649
|
-
];
|
|
650
|
-
|
|
651
642
|
// src/session/step-up-signed-out.ts
|
|
652
643
|
function isSignedOut(error, unauthenticated, from) {
|
|
653
644
|
if (error instanceof Response)
|
|
@@ -705,8 +696,8 @@ async function signingOut({
|
|
|
705
696
|
|
|
706
697
|
// src/session/step-up.ts
|
|
707
698
|
function resolve2(steps) {
|
|
708
|
-
const invalidCode = steps.codes?.invalidCode ??
|
|
709
|
-
const challengeOver = steps.codes?.challengeOver ??
|
|
699
|
+
const invalidCode = steps.codes?.invalidCode ?? INVALID_CODE2;
|
|
700
|
+
const challengeOver = steps.codes?.challengeOver ?? STEP_UP_OVER2;
|
|
710
701
|
if (challengeOver.includes(invalidCode)) {
|
|
711
702
|
throw new TypeError(`stepUpSteps(): the code ${JSON.stringify(invalidCode)} is both the wrong-code code (codes.invalidCode) and in codes.challengeOver. A wrong code that leaves tries cannot end the challenge.`);
|
|
712
703
|
}
|
|
@@ -720,9 +711,9 @@ function resolve2(steps) {
|
|
|
720
711
|
steps,
|
|
721
712
|
invalidCode,
|
|
722
713
|
challengeOver,
|
|
723
|
-
stepUpRequired: steps.codes?.stepUpRequired ??
|
|
724
|
-
mailThrottled: steps.codes?.mailThrottled ??
|
|
725
|
-
unauthenticated: steps.codes?.unauthenticated ??
|
|
714
|
+
stepUpRequired: steps.codes?.stepUpRequired ?? STEP_UP_REQUIRED2,
|
|
715
|
+
mailThrottled: steps.codes?.mailThrottled ?? MAIL_THROTTLED2,
|
|
716
|
+
unauthenticated: steps.codes?.unauthenticated ?? UNAUTHENTICATED2
|
|
726
717
|
};
|
|
727
718
|
}
|
|
728
719
|
function worded2({ steps }, error, language) {
|
|
@@ -963,13 +954,13 @@ function createSignInThrottle(options = {}) {
|
|
|
963
954
|
};
|
|
964
955
|
}
|
|
965
956
|
export {
|
|
966
|
-
CHALLENGE_OVER,
|
|
967
|
-
INVALID_CODE,
|
|
968
|
-
MAIL_THROTTLED,
|
|
969
|
-
STEP_UP_OVER,
|
|
970
|
-
STEP_UP_REQUIRED,
|
|
957
|
+
CHALLENGE_OVER2 as CHALLENGE_OVER,
|
|
958
|
+
INVALID_CODE2 as INVALID_CODE,
|
|
959
|
+
MAIL_THROTTLED2 as MAIL_THROTTLED,
|
|
960
|
+
STEP_UP_OVER2 as STEP_UP_OVER,
|
|
961
|
+
STEP_UP_REQUIRED2 as STEP_UP_REQUIRED,
|
|
971
962
|
TOKEN_PAGE_HEADERS,
|
|
972
|
-
UNAUTHENTICATED,
|
|
963
|
+
UNAUTHENTICATED2 as UNAUTHENTICATED,
|
|
973
964
|
authLinks,
|
|
974
965
|
createSignInThrottle,
|
|
975
966
|
readCookie,
|
|
@@ -981,5 +972,5 @@ export {
|
|
|
981
972
|
stepUpSteps
|
|
982
973
|
};
|
|
983
974
|
|
|
984
|
-
//# debugId=
|
|
975
|
+
//# debugId=5F046EFEF4108A2264756E2164756E21
|
|
985
976
|
//# sourceMappingURL=index.js.map
|