@foir/sdk 0.30.0 → 0.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +1 -1
- package/dist/ai/index.d.ts +10 -15
- package/dist/ai.js +185 -8
- package/dist/app/client.d.ts +9 -5
- package/dist/app.js +12 -13
- package/dist/auth/auth.d.ts +80 -101
- package/dist/auth/hosted.d.ts +12 -12
- package/dist/auth/index.d.ts +11 -7
- package/dist/auth/operations.d.ts +17 -32
- package/dist/auth/ops.d.ts +4 -13
- package/dist/auth/session-client.d.ts +4 -4
- package/dist/auth/steps.d.ts +50 -0
- package/dist/auth-session.js +3 -3
- package/dist/auth.js +157 -75
- package/dist/content/content-renderer.d.ts +2 -2
- package/dist/content/richtext.d.ts +2 -2
- package/dist/content.js +7 -7
- package/dist/data/builtins.d.ts +64 -33
- package/dist/data/index.d.ts +2 -2
- package/dist/data/manifest.d.ts +1 -1
- package/dist/data/schema.d.ts +1 -1
- package/dist/data/selection.d.ts +3 -3
- package/dist/data.js +5 -2
- package/dist/next/index.d.ts +1 -1
- package/dist/next/session.d.ts +27 -11
- package/dist/next.js +98 -40
- package/dist/push.js +9 -19
- package/dist/react/FoirProvider.d.ts +18 -16
- package/dist/react/feedback-notes.d.ts +1 -2
- package/dist/react.js +167 -84
- package/dist/runs/step-reply.d.ts +81 -0
- package/dist/server/callback-client.d.ts +11 -16
- package/dist/server/index.d.ts +2 -0
- package/dist/server/operation-types.d.ts +6 -13
- package/dist/server.js +76 -107
- package/dist/transport/foir-http.d.ts +20 -6
- package/package.json +3 -3
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The finishing halves of signing in, and of a person's own account steps,
|
|
3
|
+
* each on the data API's own route.
|
|
4
|
+
*
|
|
5
|
+
* A step is begun by an auth op (asking for a password reset, for one) and
|
|
6
|
+
* finished with the proof the person brings back: the token the reset link
|
|
7
|
+
* carried. The finish is no op of its own. The data API takes it on the step's
|
|
8
|
+
* route for somebody who holds no session yet, and the proof decides whose
|
|
9
|
+
* step it finishes, so it is sent with no key and no session.
|
|
10
|
+
*/
|
|
11
|
+
import { type FoirEnv } from '../transport/foir-http';
|
|
12
|
+
/**
|
|
13
|
+
* A code sign-in begun: {@link requestOTP} and {@link signUpWithPassword} answer one.
|
|
14
|
+
* The code the address was sent is brought back to it with
|
|
15
|
+
* {@link signInWithOTP}. Every address is answered a step alike, so having one
|
|
16
|
+
* says nothing of whether the address is anybody's.
|
|
17
|
+
*/
|
|
18
|
+
export interface FoirSignInStep {
|
|
19
|
+
id: string;
|
|
20
|
+
/** Minted for this step alone; it is the credential the code goes back with. */
|
|
21
|
+
token: string;
|
|
22
|
+
/** The step's own route. */
|
|
23
|
+
endpoint: string;
|
|
24
|
+
/** When the step, and its code, lapse (RFC 3339). */
|
|
25
|
+
expiresAt: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A change to the calling user's own account begun: {@link addMyAddress}
|
|
29
|
+
* answers one. What it asks for (the code the address was sent) is brought
|
|
30
|
+
* back to it with its token in the body, with {@link proveMyAddress}. It
|
|
31
|
+
* finishes once, for the person who began it, and not once the session that
|
|
32
|
+
* began it has ended.
|
|
33
|
+
*/
|
|
34
|
+
export interface FoirAccountStep {
|
|
35
|
+
id: string;
|
|
36
|
+
/** Minted for this step alone; it goes back in the body. */
|
|
37
|
+
token: string;
|
|
38
|
+
/** The step's own route. */
|
|
39
|
+
endpoint: string;
|
|
40
|
+
/** When the step lapses (RFC 3339). */
|
|
41
|
+
expiresAt: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Posts a step's proof to its route and answers the step as the platform gave
|
|
45
|
+
* it, with the step's token where the step has one. A refusal is thrown as a
|
|
46
|
+
* {@link FoirRequestError} carrying the HTTP status and the same error code a
|
|
47
|
+
* GraphQL refusal carries, so `isFoirUnauthenticated` and the rest read it
|
|
48
|
+
* alike.
|
|
49
|
+
*/
|
|
50
|
+
export declare function postSignInStep<T>(env: FoirEnv, path: `/v1/${string}`, body: Record<string, unknown>, stepToken?: string): Promise<T>;
|
package/dist/auth-session.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// src/auth/session-client.ts
|
|
2
2
|
var DEFAULT_SESSION_PATH = "/session/token";
|
|
3
|
-
var
|
|
3
|
+
var DEFAULT_SIGN_OUT_PATH = "/session/logout";
|
|
4
4
|
var DEFAULT_REFRESH_SKEW_SECONDS = 60;
|
|
5
5
|
var MIN_REFRESH_DELAY_MS = 5e3;
|
|
6
6
|
var CSRF_HEADER = { "X-Foir-Session": "1" };
|
|
7
7
|
function createFoirSessionClient(config) {
|
|
8
8
|
const forApp = config.clientId ? `?client_id=${encodeURIComponent(config.clientId)}` : "";
|
|
9
9
|
const sessionUrl = `${trimSlash(config.loginOrigin)}${config.sessionPath ?? DEFAULT_SESSION_PATH}${forApp}`;
|
|
10
|
-
const
|
|
10
|
+
const signOutUrl = `${trimSlash(config.loginOrigin)}${config.signOutPath ?? DEFAULT_SIGN_OUT_PATH}${forApp}`;
|
|
11
11
|
const skewMs = (config.refreshSkewSeconds ?? DEFAULT_REFRESH_SKEW_SECONDS) * 1e3;
|
|
12
12
|
let cached = null;
|
|
13
13
|
let inflight = null;
|
|
@@ -103,7 +103,7 @@ function createFoirSessionClient(config) {
|
|
|
103
103
|
},
|
|
104
104
|
async signOut() {
|
|
105
105
|
try {
|
|
106
|
-
await fetch(
|
|
106
|
+
await fetch(signOutUrl, {
|
|
107
107
|
method: "POST",
|
|
108
108
|
credentials: "include",
|
|
109
109
|
headers: { ...CSRF_HEADER }
|
package/dist/auth.js
CHANGED
|
@@ -37,6 +37,18 @@ var MALFORMED_PRUNE_REASONS = /* @__PURE__ */ new Set([
|
|
|
37
37
|
"subselections_on_scalar",
|
|
38
38
|
"missing_subselections"
|
|
39
39
|
]);
|
|
40
|
+
function stepUpRequired(error) {
|
|
41
|
+
if (!(error instanceof FoirRequestError)) return null;
|
|
42
|
+
for (const item of error.graphQLErrors ?? []) {
|
|
43
|
+
if (item.extensions?.code !== "STEP_UP_REQUIRED") continue;
|
|
44
|
+
const kind = item.extensions.stepUp;
|
|
45
|
+
if (kind === "proof" || kind === "second_factor" || kind === "sign_in_method") {
|
|
46
|
+
const url = item.extensions.stepUpUrl;
|
|
47
|
+
return { kind, url: typeof url === "string" && url !== "" ? url : null };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
40
52
|
var FoirRequestError = class extends Error {
|
|
41
53
|
/** HTTP status code, when the request failed at the HTTP layer. */
|
|
42
54
|
status;
|
|
@@ -1069,7 +1081,7 @@ async function fetchFoirManifest(config, options = {}) {
|
|
|
1069
1081
|
|
|
1070
1082
|
// src/auth/ops.ts
|
|
1071
1083
|
var AUTH_OPS = {
|
|
1072
|
-
// Pre-credential reads a
|
|
1084
|
+
// Pre-credential reads a sign-in screen renders itself from.
|
|
1073
1085
|
authConfig: "authConfig",
|
|
1074
1086
|
authProviders: "authProviders",
|
|
1075
1087
|
defaultAuthProvider: "defaultAuthProvider",
|
|
@@ -1081,17 +1093,15 @@ var AUTH_OPS = {
|
|
|
1081
1093
|
// they hold. `./access` reads it.
|
|
1082
1094
|
whoami: "whoami",
|
|
1083
1095
|
// Methods.
|
|
1084
|
-
|
|
1096
|
+
signIn: "signIn",
|
|
1085
1097
|
register: "register",
|
|
1086
1098
|
requestOTP: "requestOTP",
|
|
1087
|
-
loginWithOTP: "loginWithOTP",
|
|
1088
1099
|
// Session lifecycle.
|
|
1089
1100
|
refreshToken: "refreshToken",
|
|
1090
|
-
|
|
1101
|
+
signOut: "signOut",
|
|
1091
1102
|
joinWaitlist: "joinWaitlist",
|
|
1092
1103
|
// Password management and account lifecycle.
|
|
1093
1104
|
requestPasswordReset: "requestPasswordReset",
|
|
1094
|
-
resetPassword: "resetPassword",
|
|
1095
1105
|
updatePassword: "updatePassword",
|
|
1096
1106
|
deleteMyAccount: "deleteMyAccount",
|
|
1097
1107
|
// Ending their own membership where the request stands.
|
|
@@ -1105,10 +1115,10 @@ var AUTH_OPS = {
|
|
|
1105
1115
|
mySessions: "mySessions",
|
|
1106
1116
|
endMySession: "endMySession",
|
|
1107
1117
|
signOutEverywhere: "signOutEverywhere",
|
|
1108
|
-
// Their email addresses: one added is kept once the code it was sent
|
|
1118
|
+
// Their email addresses: one added is kept once the code it was sent comes
|
|
1119
|
+
// back on the step addMyAddress answers, on its own route (proveMyAddress).
|
|
1109
1120
|
myAddresses: "myAddresses",
|
|
1110
1121
|
addMyAddress: "addMyAddress",
|
|
1111
|
-
proveMyAddress: "proveMyAddress",
|
|
1112
1122
|
makeMyAddressPrimary: "makeMyAddressPrimary",
|
|
1113
1123
|
removeMyAddress: "removeMyAddress",
|
|
1114
1124
|
// A copy of their own data.
|
|
@@ -1119,10 +1129,8 @@ var AUTH_OPS = {
|
|
|
1119
1129
|
disconnectSignInMethod: "disconnectSignInMethod",
|
|
1120
1130
|
// Their second factors. A change needs one proven first, by a step-up.
|
|
1121
1131
|
myFactors: "myFactors",
|
|
1122
|
-
|
|
1123
|
-
completeStepUp: "completeStepUp",
|
|
1132
|
+
stepUp: "stepUp",
|
|
1124
1133
|
beginFactorEnrollment: "beginFactorEnrollment",
|
|
1125
|
-
completeFactorEnrollment: "completeFactorEnrollment",
|
|
1126
1134
|
removeFactor: "removeFactor",
|
|
1127
1135
|
renameFactor: "renameFactor",
|
|
1128
1136
|
makeRecoveryCodes: "makeRecoveryCodes",
|
|
@@ -1136,13 +1144,7 @@ var AUTH_OPS = {
|
|
|
1136
1144
|
// Founding one. Present in a manifest only when the project opted in.
|
|
1137
1145
|
registerOrganization: "registerOrganization",
|
|
1138
1146
|
// External identity providers.
|
|
1139
|
-
|
|
1140
|
-
providerCallback: "providerCallback",
|
|
1141
|
-
providerVerifyOTP: "providerVerifyOTP",
|
|
1142
|
-
// A provider sign-in that names an address somebody already holds, waiting
|
|
1143
|
-
// for the code sent to it.
|
|
1144
|
-
confirmProviderLink: "confirmProviderLink",
|
|
1145
|
-
resendProviderLinkCode: "resendProviderLinkCode"
|
|
1147
|
+
signInWithProvider: "signInWithProvider"
|
|
1146
1148
|
};
|
|
1147
1149
|
function authClientConfig(env, options) {
|
|
1148
1150
|
const apiKey = resolveFoirApiKey(env);
|
|
@@ -1202,34 +1204,94 @@ function stripUndefined(args) {
|
|
|
1202
1204
|
return out;
|
|
1203
1205
|
}
|
|
1204
1206
|
|
|
1207
|
+
// src/auth/steps.ts
|
|
1208
|
+
async function postSignInStep(env, path, body, stepToken) {
|
|
1209
|
+
const apiUrl = env.PLATFORM_API_URL;
|
|
1210
|
+
if (!apiUrl) {
|
|
1211
|
+
throw new FoirRequestError("[foir] no api url configured: set PLATFORM_API_URL");
|
|
1212
|
+
}
|
|
1213
|
+
const headers = { "Content-Type": "application/json" };
|
|
1214
|
+
if (stepToken) headers["X-Foir-Token"] = stepToken;
|
|
1215
|
+
const res = await fetch(foirApiEndpoint(apiUrl, path), {
|
|
1216
|
+
method: "POST",
|
|
1217
|
+
headers,
|
|
1218
|
+
body: JSON.stringify(body)
|
|
1219
|
+
});
|
|
1220
|
+
const data = await res.json().catch(() => ({}));
|
|
1221
|
+
if (!res.ok) {
|
|
1222
|
+
const message = typeof data.error === "string" ? data.error : `[foir] POST ${path} failed: HTTP ${res.status}`;
|
|
1223
|
+
const code = typeof data.code === "string" ? data.code : void 0;
|
|
1224
|
+
const extensions = {};
|
|
1225
|
+
if (code) extensions.code = code;
|
|
1226
|
+
if (data.signIn && typeof data.signIn === "object") extensions.signIn = data.signIn;
|
|
1227
|
+
if (data.providerSignIn && typeof data.providerSignIn === "object") extensions.providerSignIn = data.providerSignIn;
|
|
1228
|
+
throw new FoirRequestError(message, {
|
|
1229
|
+
status: res.status,
|
|
1230
|
+
graphQLErrors: Object.keys(extensions).length > 0 ? [{ message, extensions }] : void 0
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
return data;
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1205
1236
|
// src/auth/auth.ts
|
|
1206
1237
|
var TOKEN_KEY = "foir:token";
|
|
1207
1238
|
var REFRESH_KEY = "foir:refreshToken";
|
|
1208
1239
|
var USER_ID_KEY = "foir:userId";
|
|
1209
1240
|
var EMAIL_KEY = "foir:email";
|
|
1210
|
-
async function login(env, vars) {
|
|
1211
|
-
return authOp(env, AUTH_OPS.login, vars);
|
|
1212
|
-
}
|
|
1213
|
-
async function register(env, vars) {
|
|
1214
|
-
return authOp(env, AUTH_OPS.register, vars);
|
|
1215
|
-
}
|
|
1216
1241
|
async function requestOTP(env, email) {
|
|
1217
1242
|
return authOp(env, AUTH_OPS.requestOTP, { email });
|
|
1218
1243
|
}
|
|
1219
|
-
async function
|
|
1220
|
-
|
|
1244
|
+
async function codeSignIn(env, vars) {
|
|
1245
|
+
const signedIn = await postSignInStep(env, `/v1/sign-ins/${encodeURIComponent(vars.step.id)}/code`, { code: vars.otp }, vars.step.token);
|
|
1246
|
+
return {
|
|
1247
|
+
token: signedIn.accessToken ?? "",
|
|
1248
|
+
refreshToken: signedIn.refreshToken ?? "",
|
|
1249
|
+
userId: signedIn.user?.id ?? "",
|
|
1250
|
+
email: signedIn.user?.email ?? ""
|
|
1251
|
+
};
|
|
1221
1252
|
}
|
|
1222
|
-
async function
|
|
1253
|
+
async function rotateTokens(env, refreshToken) {
|
|
1223
1254
|
return authOp(env, AUTH_OPS.refreshToken, { refreshToken });
|
|
1224
1255
|
}
|
|
1225
|
-
async function
|
|
1226
|
-
|
|
1256
|
+
async function signInWithProvider(adapter, env, input) {
|
|
1257
|
+
const started = await authOp(
|
|
1258
|
+
env,
|
|
1259
|
+
AUTH_OPS.signInWithProvider,
|
|
1260
|
+
{ input }
|
|
1261
|
+
);
|
|
1262
|
+
if (started.step) await adapter.set(PROVIDER_STEP_KEY, JSON.stringify(started.step));
|
|
1263
|
+
else await adapter.unset(PROVIDER_STEP_KEY);
|
|
1264
|
+
return { redirectUrl: started.redirectUrl ?? null };
|
|
1265
|
+
}
|
|
1266
|
+
async function finishProviderSignIn(adapter, env, input) {
|
|
1267
|
+
const step = await keptProviderStep(adapter, "no provider sign-in was started here: start one before finishing it");
|
|
1268
|
+
return signedInOnStep(adapter, env, step, "provider", {
|
|
1269
|
+
providerKey: input.providerKey,
|
|
1270
|
+
code: input.code,
|
|
1271
|
+
state: input.state
|
|
1272
|
+
});
|
|
1227
1273
|
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1274
|
+
var PROVIDER_STEP_KEY = "foir:providerStep";
|
|
1275
|
+
async function keptProviderStep(adapter, missing) {
|
|
1276
|
+
const raw = await adapter.get(PROVIDER_STEP_KEY);
|
|
1277
|
+
if (raw) {
|
|
1278
|
+
try {
|
|
1279
|
+
const step = JSON.parse(raw);
|
|
1280
|
+
if (typeof step.id === "string" && typeof step.token === "string") return step;
|
|
1281
|
+
} catch {
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
throw new Error(`[foir] ${missing}`);
|
|
1230
1285
|
}
|
|
1231
|
-
async function
|
|
1232
|
-
|
|
1286
|
+
async function signedInOnStep(adapter, env, step, route, body) {
|
|
1287
|
+
const signedIn = await postSignInStep(env, `/v1/sign-ins/${encodeURIComponent(step.id)}/${route}`, body, step.token);
|
|
1288
|
+
await adapter.unset(PROVIDER_STEP_KEY);
|
|
1289
|
+
return saveSession(adapter, {
|
|
1290
|
+
token: signedIn.accessToken ?? "",
|
|
1291
|
+
refreshToken: signedIn.refreshToken ?? "",
|
|
1292
|
+
userId: signedIn.userId ?? "",
|
|
1293
|
+
email: signedIn.email ?? ""
|
|
1294
|
+
});
|
|
1233
1295
|
}
|
|
1234
1296
|
function providerSignInRefusalOf(error) {
|
|
1235
1297
|
if (!(error instanceof FoirRequestError)) return null;
|
|
@@ -1252,11 +1314,19 @@ function signInRefusalOf(error) {
|
|
|
1252
1314
|
}
|
|
1253
1315
|
return null;
|
|
1254
1316
|
}
|
|
1255
|
-
async function confirmProviderLink(env, input) {
|
|
1256
|
-
|
|
1317
|
+
async function confirmProviderLink(adapter, env, input) {
|
|
1318
|
+
const step = await keptProviderStep(adapter, "no provider sign-in is waiting for a code here");
|
|
1319
|
+
return signedInOnStep(adapter, env, step, "link", { pending: input.pending, code: input.code });
|
|
1257
1320
|
}
|
|
1258
|
-
async function resendProviderLinkCode(env, input) {
|
|
1259
|
-
|
|
1321
|
+
async function resendProviderLinkCode(adapter, env, input) {
|
|
1322
|
+
const step = await keptProviderStep(adapter, "no provider sign-in is waiting for a code here");
|
|
1323
|
+
const resent = await postSignInStep(
|
|
1324
|
+
env,
|
|
1325
|
+
`/v1/sign-ins/${encodeURIComponent(step.id)}/link/resend`,
|
|
1326
|
+
{ pending: input.pending },
|
|
1327
|
+
step.token
|
|
1328
|
+
);
|
|
1329
|
+
return { pending: resent.pending ?? "" };
|
|
1260
1330
|
}
|
|
1261
1331
|
async function getAuthProviders(env) {
|
|
1262
1332
|
return authOp(env, AUTH_OPS.authProviders);
|
|
@@ -1276,8 +1346,14 @@ async function joinWaitlist(env, email) {
|
|
|
1276
1346
|
async function requestPasswordReset(env, email, options) {
|
|
1277
1347
|
return authOp(env, AUTH_OPS.requestPasswordReset, { email, clientId: options?.clientId });
|
|
1278
1348
|
}
|
|
1279
|
-
async function
|
|
1280
|
-
|
|
1349
|
+
async function redeemPasswordReset(env, vars) {
|
|
1350
|
+
const reset = await postSignInStep(env, "/v1/password-resets/redeem", { token: vars.token, newPassword: vars.newPassword });
|
|
1351
|
+
return {
|
|
1352
|
+
token: reset.accessToken ?? "",
|
|
1353
|
+
refreshToken: reset.refreshToken ?? "",
|
|
1354
|
+
userId: reset.user?.id ?? "",
|
|
1355
|
+
email: reset.user?.email ?? ""
|
|
1356
|
+
};
|
|
1281
1357
|
}
|
|
1282
1358
|
async function updatePassword(env, vars, options) {
|
|
1283
1359
|
return authOp(env, AUTH_OPS.updatePassword, vars, options);
|
|
@@ -1290,8 +1366,8 @@ async function myProviderToken(env, providerKey, options) {
|
|
|
1290
1366
|
const vars = forceRefresh ? { providerKey, forceRefresh: true } : { providerKey };
|
|
1291
1367
|
return authOp(env, AUTH_OPS.myProviderToken, vars, rest);
|
|
1292
1368
|
}
|
|
1293
|
-
async function
|
|
1294
|
-
return authOp(env, AUTH_OPS.
|
|
1369
|
+
async function endSignIn(env, options) {
|
|
1370
|
+
return authOp(env, AUTH_OPS.signOut, void 0, options);
|
|
1295
1371
|
}
|
|
1296
1372
|
async function deleteMyAccount(env, vars, options) {
|
|
1297
1373
|
return authOp(env, AUTH_OPS.deleteMyAccount, vars, options);
|
|
@@ -1326,8 +1402,14 @@ async function myAddresses(env, options) {
|
|
|
1326
1402
|
async function addMyAddress(env, vars, options) {
|
|
1327
1403
|
return authOp(env, AUTH_OPS.addMyAddress, vars, options);
|
|
1328
1404
|
}
|
|
1329
|
-
async function proveMyAddress(env, vars
|
|
1330
|
-
|
|
1405
|
+
async function proveMyAddress(env, vars) {
|
|
1406
|
+
const kept = await postSignInStep(env, `/v1/address-proofs/${encodeURIComponent(vars.step.id)}`, { token: vars.step.token, code: vars.code });
|
|
1407
|
+
return {
|
|
1408
|
+
email: kept.address?.email ?? "",
|
|
1409
|
+
primary: kept.address?.primary ?? false,
|
|
1410
|
+
proven: kept.address?.proven ?? false,
|
|
1411
|
+
addedAt: kept.address?.addedAt ?? null
|
|
1412
|
+
};
|
|
1331
1413
|
}
|
|
1332
1414
|
async function makeMyAddressPrimary(env, email, options) {
|
|
1333
1415
|
return authOp(env, AUTH_OPS.makeMyAddressPrimary, { email }, options);
|
|
@@ -1350,17 +1432,24 @@ async function disconnectSignInMethod(env, provider, options) {
|
|
|
1350
1432
|
async function myFactors(env, options) {
|
|
1351
1433
|
return authOp(env, AUTH_OPS.myFactors, void 0, options);
|
|
1352
1434
|
}
|
|
1353
|
-
async function
|
|
1354
|
-
return authOp(env, AUTH_OPS.
|
|
1355
|
-
}
|
|
1356
|
-
async function completeStepUp(env, vars, options) {
|
|
1357
|
-
return authOp(env, AUTH_OPS.completeStepUp, vars, options);
|
|
1435
|
+
async function stepUp(env, options) {
|
|
1436
|
+
return authOp(env, AUTH_OPS.stepUp, void 0, options);
|
|
1358
1437
|
}
|
|
1359
1438
|
async function beginFactorEnrollment(env, vars, options) {
|
|
1360
1439
|
return authOp(env, AUTH_OPS.beginFactorEnrollment, vars, options);
|
|
1361
1440
|
}
|
|
1362
|
-
async function completeFactorEnrollment(env, vars
|
|
1363
|
-
|
|
1441
|
+
async function completeFactorEnrollment(env, vars) {
|
|
1442
|
+
const done = await postSignInStep(
|
|
1443
|
+
env,
|
|
1444
|
+
`/v1/factor-enrollments/${encodeURIComponent(vars.step.id)}`,
|
|
1445
|
+
{
|
|
1446
|
+
token: vars.step.token,
|
|
1447
|
+
webauthnAttestationJson: vars.webauthnAttestationJson,
|
|
1448
|
+
totpCode: vars.totpCode,
|
|
1449
|
+
displayName: vars.name
|
|
1450
|
+
}
|
|
1451
|
+
);
|
|
1452
|
+
return done.enrolled === true;
|
|
1364
1453
|
}
|
|
1365
1454
|
async function removeFactor(env, vars, options) {
|
|
1366
1455
|
return authOp(env, AUTH_OPS.removeFactor, vars, options);
|
|
@@ -1368,8 +1457,8 @@ async function removeFactor(env, vars, options) {
|
|
|
1368
1457
|
async function renameFactor(env, vars, options) {
|
|
1369
1458
|
return authOp(env, AUTH_OPS.renameFactor, vars, options);
|
|
1370
1459
|
}
|
|
1371
|
-
async function makeRecoveryCodes(env,
|
|
1372
|
-
return authOp(env, AUTH_OPS.makeRecoveryCodes,
|
|
1460
|
+
async function makeRecoveryCodes(env, options) {
|
|
1461
|
+
return authOp(env, AUTH_OPS.makeRecoveryCodes, void 0, options);
|
|
1373
1462
|
}
|
|
1374
1463
|
async function myOrganizations(env, vars, options) {
|
|
1375
1464
|
return authOp(env, AUTH_OPS.myOrganizations, vars ?? {}, options);
|
|
@@ -1406,16 +1495,16 @@ async function getSessionUser(adapter) {
|
|
|
1406
1495
|
return { id, email: email ?? "" };
|
|
1407
1496
|
}
|
|
1408
1497
|
async function signInWithPassword(adapter, env, vars) {
|
|
1409
|
-
return saveSession(adapter, await
|
|
1498
|
+
return saveSession(adapter, await authOp(env, AUTH_OPS.signIn, vars));
|
|
1410
1499
|
}
|
|
1411
1500
|
async function signUpWithPassword(env, vars) {
|
|
1412
|
-
|
|
1501
|
+
return authOp(env, AUTH_OPS.register, vars);
|
|
1413
1502
|
}
|
|
1414
1503
|
async function signInWithOTP(adapter, env, vars) {
|
|
1415
|
-
return saveSession(adapter, await
|
|
1504
|
+
return saveSession(adapter, await codeSignIn(env, vars));
|
|
1416
1505
|
}
|
|
1417
1506
|
async function resetPasswordAndSignIn(adapter, env, vars) {
|
|
1418
|
-
return saveSession(adapter, await
|
|
1507
|
+
return saveSession(adapter, await redeemPasswordReset(env, vars));
|
|
1419
1508
|
}
|
|
1420
1509
|
async function deleteMyAccountAndSignOut(adapter, env, vars) {
|
|
1421
1510
|
const result = await authedOp(adapter, env, AUTH_OPS.deleteMyAccount, vars);
|
|
@@ -1426,7 +1515,7 @@ async function signOut(adapter, env) {
|
|
|
1426
1515
|
const token = await getSessionToken(adapter);
|
|
1427
1516
|
if (token) {
|
|
1428
1517
|
try {
|
|
1429
|
-
await
|
|
1518
|
+
await endSignIn(env, { bearerToken: token });
|
|
1430
1519
|
} catch {
|
|
1431
1520
|
}
|
|
1432
1521
|
}
|
|
@@ -1436,7 +1525,7 @@ async function refreshSession(adapter, env) {
|
|
|
1436
1525
|
const refreshToken = await adapter.get(REFRESH_KEY) ?? null;
|
|
1437
1526
|
if (!refreshToken) return null;
|
|
1438
1527
|
try {
|
|
1439
|
-
const result = await
|
|
1528
|
+
const result = await rotateTokens(env, refreshToken);
|
|
1440
1529
|
await saveSession(adapter, result);
|
|
1441
1530
|
return { token: result.token, refreshToken: result.refreshToken };
|
|
1442
1531
|
} catch {
|
|
@@ -1816,7 +1905,7 @@ async function requireScope(adapter, env, scope) {
|
|
|
1816
1905
|
var DEFAULT_COOKIE_NAME = "foir_session";
|
|
1817
1906
|
var DEFAULT_SESSION_PATH = "/session/token";
|
|
1818
1907
|
var DEFAULT_END_SESSION_PATH = "/session/end-session";
|
|
1819
|
-
function
|
|
1908
|
+
function hostedSignInUrl(config, returnTo, options = {}) {
|
|
1820
1909
|
const dest = new URL(config.loginOrigin);
|
|
1821
1910
|
if (options.signUp) {
|
|
1822
1911
|
dest.pathname = `${dest.pathname.replace(/\/+$/, "")}/signup`;
|
|
@@ -1829,7 +1918,7 @@ function hostedLoginUrl(config, returnTo, options = {}) {
|
|
|
1829
1918
|
}
|
|
1830
1919
|
return dest.toString();
|
|
1831
1920
|
}
|
|
1832
|
-
function
|
|
1921
|
+
function hostedSignOutUrl(config, clientId, returnTo) {
|
|
1833
1922
|
const path = config.endSessionPath ?? DEFAULT_END_SESSION_PATH;
|
|
1834
1923
|
const dest = new URL(path, config.loginOrigin);
|
|
1835
1924
|
dest.searchParams.set("client_id", clientId);
|
|
@@ -2092,14 +2181,14 @@ async function completeSignIn(config, callback, pending) {
|
|
|
2092
2181
|
|
|
2093
2182
|
// src/auth/session-client.ts
|
|
2094
2183
|
var DEFAULT_SESSION_PATH2 = "/session/token";
|
|
2095
|
-
var
|
|
2184
|
+
var DEFAULT_SIGN_OUT_PATH = "/session/logout";
|
|
2096
2185
|
var DEFAULT_REFRESH_SKEW_SECONDS = 60;
|
|
2097
2186
|
var MIN_REFRESH_DELAY_MS = 5e3;
|
|
2098
2187
|
var CSRF_HEADER = { "X-Foir-Session": "1" };
|
|
2099
2188
|
function createFoirSessionClient(config) {
|
|
2100
2189
|
const forApp2 = config.clientId ? `?client_id=${encodeURIComponent(config.clientId)}` : "";
|
|
2101
2190
|
const sessionUrl = `${trimSlash2(config.loginOrigin)}${config.sessionPath ?? DEFAULT_SESSION_PATH2}${forApp2}`;
|
|
2102
|
-
const
|
|
2191
|
+
const signOutUrl = `${trimSlash2(config.loginOrigin)}${config.signOutPath ?? DEFAULT_SIGN_OUT_PATH}${forApp2}`;
|
|
2103
2192
|
const skewMs = (config.refreshSkewSeconds ?? DEFAULT_REFRESH_SKEW_SECONDS) * 1e3;
|
|
2104
2193
|
let cached = null;
|
|
2105
2194
|
let inflight = null;
|
|
@@ -2195,7 +2284,7 @@ function createFoirSessionClient(config) {
|
|
|
2195
2284
|
},
|
|
2196
2285
|
async signOut() {
|
|
2197
2286
|
try {
|
|
2198
|
-
await fetch(
|
|
2287
|
+
await fetch(signOutUrl, {
|
|
2199
2288
|
method: "POST",
|
|
2200
2289
|
credentials: "include",
|
|
2201
2290
|
headers: { ...CSRF_HEADER }
|
|
@@ -2237,11 +2326,9 @@ export {
|
|
|
2237
2326
|
authedRequest,
|
|
2238
2327
|
beginFactorEnrollment,
|
|
2239
2328
|
beginSignIn,
|
|
2240
|
-
beginStepUp,
|
|
2241
2329
|
clearSession,
|
|
2242
2330
|
completeFactorEnrollment,
|
|
2243
2331
|
completeSignIn,
|
|
2244
|
-
completeStepUp,
|
|
2245
2332
|
confirmProviderLink,
|
|
2246
2333
|
connectSignInMethod,
|
|
2247
2334
|
createFoirSessionClient,
|
|
@@ -2253,6 +2340,7 @@ export {
|
|
|
2253
2340
|
endMySession,
|
|
2254
2341
|
exportMyData,
|
|
2255
2342
|
fetchHostedSession,
|
|
2343
|
+
finishProviderSignIn,
|
|
2256
2344
|
getAuthConfig,
|
|
2257
2345
|
getAuthProviders,
|
|
2258
2346
|
getDefaultAuthProvider,
|
|
@@ -2261,15 +2349,11 @@ export {
|
|
|
2261
2349
|
getSessionUser,
|
|
2262
2350
|
hasRole,
|
|
2263
2351
|
hasScope,
|
|
2264
|
-
hostedLoginUrl,
|
|
2265
|
-
hostedLogoutUrl,
|
|
2266
2352
|
hostedSignInNoticeOf,
|
|
2353
|
+
hostedSignInUrl,
|
|
2354
|
+
hostedSignOutUrl,
|
|
2267
2355
|
joinWaitlist,
|
|
2268
2356
|
leaveMembership,
|
|
2269
|
-
login,
|
|
2270
|
-
loginWithOTP,
|
|
2271
|
-
loginWithProvider,
|
|
2272
|
-
logout,
|
|
2273
2357
|
makeMyAddressPrimary,
|
|
2274
2358
|
makeRecoveryCodes,
|
|
2275
2359
|
myAddresses,
|
|
@@ -2281,13 +2365,9 @@ export {
|
|
|
2281
2365
|
mySessions,
|
|
2282
2366
|
mySignInMethods,
|
|
2283
2367
|
proveMyAddress,
|
|
2284
|
-
providerCallback,
|
|
2285
2368
|
providerSignInRefusalOf,
|
|
2286
|
-
providerVerifyOTP,
|
|
2287
2369
|
refreshSession,
|
|
2288
2370
|
refreshSessionOnce,
|
|
2289
|
-
refreshTokens,
|
|
2290
|
-
register,
|
|
2291
2371
|
registerOrganization,
|
|
2292
2372
|
removeFactor,
|
|
2293
2373
|
removeMyAddress,
|
|
@@ -2298,17 +2378,19 @@ export {
|
|
|
2298
2378
|
requireRole,
|
|
2299
2379
|
requireScope,
|
|
2300
2380
|
resendProviderLinkCode,
|
|
2301
|
-
resetPassword,
|
|
2302
2381
|
resetPasswordAndSignIn,
|
|
2303
2382
|
revokeAuthorizedApp,
|
|
2304
2383
|
saveSession,
|
|
2305
2384
|
signInRefusalOf,
|
|
2306
2385
|
signInWithOTP,
|
|
2307
2386
|
signInWithPassword,
|
|
2387
|
+
signInWithProvider,
|
|
2308
2388
|
signOut,
|
|
2309
2389
|
signOutEverywhere,
|
|
2310
2390
|
signUpWithPassword,
|
|
2311
2391
|
signupContext,
|
|
2392
|
+
stepUp,
|
|
2393
|
+
stepUpRequired,
|
|
2312
2394
|
updateMyProfile,
|
|
2313
2395
|
updatePassword,
|
|
2314
2396
|
userAccess
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* It does NOT know any concrete item types. Given a registry that maps each
|
|
5
5
|
* entry `__typename` to a component, it renders a prose flow's entries (or a
|
|
6
6
|
* plain item array): looks each entry up by `__typename`, renders unknown
|
|
7
|
-
* types as null, keys by the entry `
|
|
7
|
+
* types as null, keys by the entry `_id` where one is present, assigns
|
|
8
8
|
* LCP `priority` to the first entry that actually renders, and threads a
|
|
9
9
|
* consumer-defined `Ctx` to every component.
|
|
10
10
|
*
|
|
@@ -31,8 +31,8 @@ import { type PassageLike } from './richtext';
|
|
|
31
31
|
/** Minimal shape every entry in a prose flow carries. */
|
|
32
32
|
export interface ItemLike {
|
|
33
33
|
__typename: string;
|
|
34
|
+
/** The entry's own id. */
|
|
34
35
|
_id?: string | null;
|
|
35
|
-
_key?: string | null;
|
|
36
36
|
}
|
|
37
37
|
/** Props an item component receives from the content renderer. */
|
|
38
38
|
export interface ItemComponentProps<TItem, TCtx = unknown> {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* - `list` + `level` flatten list membership onto the passage; consecutive
|
|
18
18
|
* list passages regroup into <ul>/<ol> with nesting rebuilt from `level`.
|
|
19
19
|
* - Span marks are the eight decorations (bold, italic, underline,
|
|
20
|
-
* strikethrough, code, subscript, superscript, highlight) or the `
|
|
20
|
+
* strikethrough, code, subscript, superscript, highlight) or the `_id`
|
|
21
21
|
* of a markDef on the same passage (links, scheme-checked through
|
|
22
22
|
* `safeHref`).
|
|
23
23
|
* - A soft line break is `"\n"` inside a span's text.
|
|
@@ -64,7 +64,7 @@ export interface ProseSpanLike {
|
|
|
64
64
|
export interface PassageLike {
|
|
65
65
|
__typename?: string;
|
|
66
66
|
_type?: string;
|
|
67
|
-
|
|
67
|
+
_id?: string | null;
|
|
68
68
|
kind?: string | null;
|
|
69
69
|
token?: string | null;
|
|
70
70
|
align?: string | null;
|
package/dist/content.js
CHANGED
|
@@ -108,12 +108,12 @@ function PassageList({
|
|
|
108
108
|
level: minLevel(run),
|
|
109
109
|
tokens: tokenMap
|
|
110
110
|
},
|
|
111
|
-
run[0].
|
|
111
|
+
run[0]._id ?? `list-${out.length}`
|
|
112
112
|
)
|
|
113
113
|
);
|
|
114
114
|
continue;
|
|
115
115
|
}
|
|
116
|
-
out.push(renderPassage(passage, passage.
|
|
116
|
+
out.push(renderPassage(passage, passage._id ?? i, tokenMap, fallbackTag));
|
|
117
117
|
i++;
|
|
118
118
|
}
|
|
119
119
|
return /* @__PURE__ */ jsx(Fragment, { children: out });
|
|
@@ -144,7 +144,7 @@ function ListRun({
|
|
|
144
144
|
prev.nested = nested;
|
|
145
145
|
} else {
|
|
146
146
|
items.push({
|
|
147
|
-
key: deep[0].
|
|
147
|
+
key: deep[0]._id ?? `d${items.length}`,
|
|
148
148
|
content: null,
|
|
149
149
|
nested,
|
|
150
150
|
type: deep[0].list ?? "bullet"
|
|
@@ -154,7 +154,7 @@ function ListRun({
|
|
|
154
154
|
}
|
|
155
155
|
const token = passage.token ? tokens.get(passage.token) ?? null : null;
|
|
156
156
|
items.push({
|
|
157
|
-
key: passage.
|
|
157
|
+
key: passage._id ?? `li${items.length}`,
|
|
158
158
|
content: /* @__PURE__ */ jsx("span", { style: tokenToCss(token, passage.align), children: renderSpans(passage) }),
|
|
159
159
|
nested: null,
|
|
160
160
|
type: passage.list ?? "bullet"
|
|
@@ -216,7 +216,7 @@ function renderSpan(span, key, markDefs) {
|
|
|
216
216
|
el = createElement(decorationTag, null, el);
|
|
217
217
|
continue;
|
|
218
218
|
}
|
|
219
|
-
const def = markDefs.find((d) => d.
|
|
219
|
+
const def = markDefs.find((d) => d._id === mark);
|
|
220
220
|
if (def?.type === "link" && def.href) link = def;
|
|
221
221
|
}
|
|
222
222
|
if (link?.href) {
|
|
@@ -273,7 +273,7 @@ function createContentRenderer(registry, options) {
|
|
|
273
273
|
const priority = !priorityAssigned;
|
|
274
274
|
priorityAssigned = true;
|
|
275
275
|
out.push(
|
|
276
|
-
/* @__PURE__ */ jsx2(Fragment2, { children: /* @__PURE__ */ jsx2(PassageRun, { passages: run, ctx, priority }) }, run[0].
|
|
276
|
+
/* @__PURE__ */ jsx2(Fragment2, { children: /* @__PURE__ */ jsx2(PassageRun, { passages: run, ctx, priority }) }, run[0]._id ?? `run-${out.length}`)
|
|
277
277
|
);
|
|
278
278
|
run = [];
|
|
279
279
|
};
|
|
@@ -289,7 +289,7 @@ function createContentRenderer(registry, options) {
|
|
|
289
289
|
const priority = !priorityAssigned;
|
|
290
290
|
priorityAssigned = true;
|
|
291
291
|
out.push(
|
|
292
|
-
/* @__PURE__ */ jsx2(Fragment2, { children: /* @__PURE__ */ jsx2(Component, { item, block: item, ctx, priority }) }, item.
|
|
292
|
+
/* @__PURE__ */ jsx2(Fragment2, { children: /* @__PURE__ */ jsx2(Component, { item, block: item, ctx, priority }) }, item._id ?? i)
|
|
293
293
|
);
|
|
294
294
|
});
|
|
295
295
|
flushRun();
|