@foir/sdk 0.20.0 → 0.22.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 +135 -1
- package/README.md +9 -9
- package/dist/app/client.d.ts +90 -84
- package/dist/app/contract/proto/apps/v1/apps_pb.d.ts +14 -13
- package/dist/app/dispatch.d.ts +5 -5
- package/dist/app/errors.d.ts +11 -11
- package/dist/app/index.d.ts +4 -4
- package/dist/app/naming.d.ts +1 -1
- package/dist/app/ticket.d.ts +2 -2
- package/dist/app/vocabulary.generated.d.ts +2 -2
- package/dist/app-contract.js +3 -3
- package/dist/app.js +92 -90
- package/dist/auth/access.d.ts +19 -19
- package/dist/auth/auth.d.ts +216 -114
- package/dist/auth/hosted.d.ts +14 -14
- package/dist/auth/index.d.ts +7 -7
- package/dist/auth/operations.d.ts +128 -32
- package/dist/auth/ops.d.ts +39 -21
- package/dist/auth/session-client.d.ts +3 -3
- package/dist/auth-session.js +3 -3
- package/dist/auth.js +291 -146
- package/dist/client/platform-client.d.ts +1 -1
- package/dist/client.js +32 -5
- package/dist/codegen/index.js +1 -1
- package/dist/data/builtins.d.ts +41 -82
- package/dist/data/client.d.ts +2 -2
- package/dist/data/decimal.d.ts +1 -1
- package/dist/data/document.d.ts +14 -0
- 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/transport.d.ts +33 -26
- package/dist/data/ws-transport.d.ts +1 -1
- package/dist/data.js +97 -19
- package/dist/graphql/content-client.d.ts +13 -13
- package/dist/index.js +24 -0
- package/dist/live/drift.d.ts +1 -1
- package/dist/media/screen-capture.d.ts +1 -1
- package/dist/next/index.d.ts +2 -2
- package/dist/next/session.d.ts +10 -10
- package/dist/next.js +121 -61
- package/dist/placement/index.d.ts +15 -32
- package/dist/placement.js +18 -20
- package/dist/push.js +113 -46
- package/dist/react/FoirFeedback.d.ts +2 -2
- package/dist/react/FoirProvider.d.ts +23 -23
- package/dist/react/SignIn.d.ts +4 -4
- package/dist/react/UserButton.d.ts +1 -1
- package/dist/react/feedback-notes.d.ts +4 -4
- package/dist/react/index.d.ts +3 -3
- package/dist/react.js +168 -105
- package/dist/realtime/index.d.ts +1 -1
- package/dist/server/callback-client.d.ts +4 -4
- package/dist/server/events-client.d.ts +18 -18
- package/dist/server/operation-types.d.ts +8 -8
- package/dist/server/require-scoped-token.d.ts +3 -3
- package/dist/server/server-client.d.ts +1 -1
- package/dist/server/types.d.ts +1 -1
- package/dist/server/verify-scoped-token.d.ts +4 -28
- package/dist/server.js +67 -112
- package/dist/transport/foir-http.d.ts +57 -18
- package/dist/transport/key-kind.d.ts +40 -0
- package/dist/types/index.d.ts +2 -2
- package/package.json +3 -3
- package/dist/server/older-claims.generated.d.ts +0 -8
package/dist/auth.js
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
// src/graphql/foir-request.ts
|
|
2
2
|
import { print as print2 } from "graphql";
|
|
3
3
|
|
|
4
|
+
// src/transport/key-kind.ts
|
|
5
|
+
var KEY_PREFIXES = [
|
|
6
|
+
["foir_pub_", "publishable"],
|
|
7
|
+
["foir_srv_", "server"],
|
|
8
|
+
["foir_adm_", "admin"],
|
|
9
|
+
["pk_", "publishable"],
|
|
10
|
+
["sk_", "older"],
|
|
11
|
+
["tc_", "older"]
|
|
12
|
+
];
|
|
13
|
+
var FOIR_CREDENTIALS_DOC = "https://docs.foir.dev/credentials";
|
|
14
|
+
function foirKeyKind(apiKey) {
|
|
15
|
+
if (typeof apiKey !== "string") return void 0;
|
|
16
|
+
for (const [prefix, kind] of KEY_PREFIXES) {
|
|
17
|
+
if (apiKey.startsWith(prefix)) return kind;
|
|
18
|
+
}
|
|
19
|
+
return void 0;
|
|
20
|
+
}
|
|
21
|
+
function adminKeyRefusal(client) {
|
|
22
|
+
return `[foir] ${client} was given an admin key. An admin key is a machine's own: nobody signs in through it, and an app never holds one. Build this client from your publishable key, or from your server key on the server that renders your pages. A machine that acts for itself uses createServerClient from @foir/sdk/server. See ${FOIR_CREDENTIALS_DOC}`;
|
|
23
|
+
}
|
|
24
|
+
function isPublishableFoirKey(apiKey) {
|
|
25
|
+
return foirKeyKind(apiKey) === "publishable";
|
|
26
|
+
}
|
|
27
|
+
|
|
4
28
|
// src/graphql/cdn-route.ts
|
|
5
29
|
import { print } from "graphql";
|
|
6
30
|
|
|
@@ -125,6 +149,28 @@ function refuseCredentialHeaders(extra) {
|
|
|
125
149
|
}
|
|
126
150
|
}
|
|
127
151
|
var FOIR_VISITOR_IP_HEADER = "X-Foir-Visitor-IP";
|
|
152
|
+
function sentCoordinate(value) {
|
|
153
|
+
return Math.round(value * 100) / 100;
|
|
154
|
+
}
|
|
155
|
+
async function resolveFoirVisitor(source) {
|
|
156
|
+
const value = typeof source === "function" ? await source() : source;
|
|
157
|
+
if (value === null || value === void 0) return { place: {} };
|
|
158
|
+
if (typeof value === "string") {
|
|
159
|
+
const address2 = value.trim();
|
|
160
|
+
return { address: address2 || void 0, place: {} };
|
|
161
|
+
}
|
|
162
|
+
const address = value.address?.trim();
|
|
163
|
+
const country = value.country?.trim();
|
|
164
|
+
const place = {};
|
|
165
|
+
if (country) place.country = country;
|
|
166
|
+
if (Number.isFinite(value.latitude) && Number.isFinite(value.longitude)) {
|
|
167
|
+
place.location = {
|
|
168
|
+
latitude: sentCoordinate(value.latitude),
|
|
169
|
+
longitude: sentCoordinate(value.longitude)
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return { address: address || void 0, place };
|
|
173
|
+
}
|
|
128
174
|
function buildFoirHeaders(input) {
|
|
129
175
|
const headers = {
|
|
130
176
|
"Content-Type": "application/json"
|
|
@@ -171,8 +217,8 @@ var FoirAuthDowngradedError = class extends FoirRequestError {
|
|
|
171
217
|
downgrade;
|
|
172
218
|
constructor(downgrade) {
|
|
173
219
|
super(
|
|
174
|
-
`[foir] your
|
|
175
|
-
{ graphQLErrors: [{ message: "
|
|
220
|
+
`[foir] your user token was ${downgrade.userToken} (${downgrade.reason}), so the request was served as ${downgrade.servedAs}`,
|
|
221
|
+
{ graphQLErrors: [{ message: "user token rejected", extensions: { code: "AUTH_DOWNGRADED" } }] }
|
|
176
222
|
);
|
|
177
223
|
this.name = "FoirAuthDowngradedError";
|
|
178
224
|
this.downgrade = downgrade;
|
|
@@ -180,7 +226,7 @@ var FoirAuthDowngradedError = class extends FoirRequestError {
|
|
|
180
226
|
};
|
|
181
227
|
var FoirSessionExpiredError = class extends FoirRequestError {
|
|
182
228
|
constructor(options) {
|
|
183
|
-
super("[foir] the
|
|
229
|
+
super("[foir] the user session has expired and could not be refreshed: sign in again", {
|
|
184
230
|
cause: options?.cause,
|
|
185
231
|
graphQLErrors: [{ message: "session expired", extensions: { code: "SESSION_EXPIRED" } }]
|
|
186
232
|
});
|
|
@@ -197,7 +243,7 @@ function reportAuthDowngrade(envelope) {
|
|
|
197
243
|
if (warnedDowngrade === downgrade.reason) return;
|
|
198
244
|
warnedDowngrade = downgrade.reason;
|
|
199
245
|
console.warn(
|
|
200
|
-
`[foir] your
|
|
246
|
+
`[foir] your user token was ${downgrade.userToken} (${downgrade.reason}) \u2014 this request was served as ${downgrade.servedAs}, so auth-targeted content resolved to its anonymous variant. ` + (downgrade.reason === "expired" ? "Re-exchange the token and retry." : "Check which credential you are sending.")
|
|
201
247
|
);
|
|
202
248
|
}
|
|
203
249
|
function refusePrunedDocument(json, options) {
|
|
@@ -277,7 +323,7 @@ function isCdnEligible(env, documentText, bearerToken, orgId) {
|
|
|
277
323
|
if (orgId) return false;
|
|
278
324
|
if (isFoirPreview(env)) return false;
|
|
279
325
|
const apiKey = resolveFoirApiKey(env);
|
|
280
|
-
if (!apiKey
|
|
326
|
+
if (!isPublishableFoirKey(apiKey)) return false;
|
|
281
327
|
return !/\bmutation\b/.test(documentText);
|
|
282
328
|
}
|
|
283
329
|
|
|
@@ -290,6 +336,9 @@ async function foirRequest(env, document, variables, options) {
|
|
|
290
336
|
"[foir] PLATFORM_API_URL or PLATFORM_API_KEY is not set \u2014 add them to .env"
|
|
291
337
|
);
|
|
292
338
|
}
|
|
339
|
+
if (foirKeyKind(apiKey) === "admin") {
|
|
340
|
+
throw new FoirRequestError(adminKeyRefusal("foirRequest"));
|
|
341
|
+
}
|
|
293
342
|
const headers = buildFoirHeaders({
|
|
294
343
|
apiKey,
|
|
295
344
|
draftChannel: isFoirPreview(env),
|
|
@@ -852,10 +901,7 @@ function foirDataEndpoint(apiUrl, path) {
|
|
|
852
901
|
return foirApiEndpoint(apiUrl, path);
|
|
853
902
|
}
|
|
854
903
|
async function resolveVisitorAddress(config) {
|
|
855
|
-
|
|
856
|
-
const value = typeof source === "function" ? await source() : source;
|
|
857
|
-
const address = value?.trim();
|
|
858
|
-
return address ? address : void 0;
|
|
904
|
+
return (await resolveFoirVisitor(config.visitor)).address;
|
|
859
905
|
}
|
|
860
906
|
async function resolveBearerToken(config) {
|
|
861
907
|
const source = config.bearerToken;
|
|
@@ -896,7 +942,7 @@ async function resolveFoirAuth(config) {
|
|
|
896
942
|
};
|
|
897
943
|
}
|
|
898
944
|
function foirRoute(input) {
|
|
899
|
-
if (!input.apiKey
|
|
945
|
+
if (!isPublishableFoirKey(input.apiKey)) return { lane: "origin", reason: "key" };
|
|
900
946
|
if (input.bearerToken) return { lane: "origin", reason: "bearer" };
|
|
901
947
|
if (input.orgId || input.orgUrl) return { lane: "origin", reason: "org" };
|
|
902
948
|
if (input.mutation) return { lane: "origin", reason: "operation" };
|
|
@@ -1030,42 +1076,63 @@ var AUTH_OPS = {
|
|
|
1030
1076
|
authConfig: "authConfig",
|
|
1031
1077
|
authProviders: "authProviders",
|
|
1032
1078
|
defaultAuthProvider: "defaultAuthProvider",
|
|
1033
|
-
|
|
1079
|
+
signupContext: "signupContext",
|
|
1034
1080
|
// Post-credential self reads.
|
|
1035
1081
|
currentUser: "currentUser",
|
|
1036
1082
|
myProviderToken: "myProviderToken",
|
|
1037
|
-
// What the calling credential may do, and for a signed-in
|
|
1083
|
+
// What the calling credential may do, and for a signed-in user the roles
|
|
1038
1084
|
// they hold. `./access` reads it.
|
|
1039
1085
|
whoami: "whoami",
|
|
1040
1086
|
// Methods.
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1087
|
+
login: "login",
|
|
1088
|
+
register: "register",
|
|
1089
|
+
requestOTP: "requestOTP",
|
|
1090
|
+
loginWithOTP: "loginWithOTP",
|
|
1045
1091
|
// Session lifecycle.
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
// Password management and account lifecycle
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1092
|
+
refreshToken: "refreshToken",
|
|
1093
|
+
logout: "logout",
|
|
1094
|
+
joinWaitlist: "joinWaitlist",
|
|
1095
|
+
// Password management and account lifecycle.
|
|
1096
|
+
requestPasswordReset: "requestPasswordReset",
|
|
1097
|
+
resetPassword: "resetPassword",
|
|
1098
|
+
updatePassword: "updatePassword",
|
|
1099
|
+
verifyEmail: "verifyEmail",
|
|
1054
1100
|
deleteMyAccount: "deleteMyAccount",
|
|
1055
|
-
// The
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
//
|
|
1060
|
-
//
|
|
1101
|
+
// The user's own profile, and the apps they have agreed may act for them.
|
|
1102
|
+
updateMyProfile: "updateMyProfile",
|
|
1103
|
+
myAuthorizedApps: "myAuthorizedApps",
|
|
1104
|
+
revokeAuthorizedApp: "revokeAuthorizedApp",
|
|
1105
|
+
// Where they are signed in, their personal access tokens, and the
|
|
1106
|
+
// providers they sign in with.
|
|
1107
|
+
mySessions: "mySessions",
|
|
1108
|
+
endMySession: "endMySession",
|
|
1109
|
+
signOutEverywhere: "signOutEverywhere",
|
|
1110
|
+
createPersonalAccessToken: "createPersonalAccessToken",
|
|
1111
|
+
mySignInMethods: "mySignInMethods",
|
|
1112
|
+
connectSignInMethod: "connectSignInMethod",
|
|
1113
|
+
disconnectSignInMethod: "disconnectSignInMethod",
|
|
1114
|
+
// Their second factors. A change needs one proven first, by a step-up.
|
|
1115
|
+
myFactors: "myFactors",
|
|
1116
|
+
beginStepUp: "beginStepUp",
|
|
1117
|
+
completeStepUp: "completeStepUp",
|
|
1118
|
+
beginFactorEnrollment: "beginFactorEnrollment",
|
|
1119
|
+
completeFactorEnrollment: "completeFactorEnrollment",
|
|
1120
|
+
removeFactor: "removeFactor",
|
|
1121
|
+
makeRecoveryCodes: "makeRecoveryCodes",
|
|
1122
|
+
// The organizations the caller may stand in. Nothing selects one: a request
|
|
1123
|
+
// names the organization it acts in (`orgId` or `orgUrl` below), and the
|
|
1124
|
+
// platform checks the caller stands there every time.
|
|
1061
1125
|
myOrganizations: "myOrganizations",
|
|
1062
|
-
selectMyOrganization: "selectMyOrganization",
|
|
1063
1126
|
// Founding one. Present in a manifest only when the project opted in.
|
|
1064
|
-
|
|
1127
|
+
registerOrganization: "registerOrganization",
|
|
1065
1128
|
// External identity providers.
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1129
|
+
loginWithProvider: "loginWithProvider",
|
|
1130
|
+
providerCallback: "providerCallback",
|
|
1131
|
+
providerVerifyOTP: "providerVerifyOTP",
|
|
1132
|
+
// A provider sign-in that names an address somebody already holds, waiting
|
|
1133
|
+
// for the code sent to it.
|
|
1134
|
+
confirmProviderLink: "confirmProviderLink",
|
|
1135
|
+
resendProviderLinkCode: "resendProviderLinkCode"
|
|
1069
1136
|
};
|
|
1070
1137
|
function authClientConfig(env, options) {
|
|
1071
1138
|
const apiKey = resolveFoirApiKey(env);
|
|
@@ -1128,31 +1195,48 @@ function stripUndefined(args) {
|
|
|
1128
1195
|
// src/auth/auth.ts
|
|
1129
1196
|
var TOKEN_KEY = "foir:token";
|
|
1130
1197
|
var REFRESH_KEY = "foir:refreshToken";
|
|
1131
|
-
var
|
|
1198
|
+
var USER_ID_KEY = "foir:userId";
|
|
1132
1199
|
var EMAIL_KEY = "foir:email";
|
|
1133
|
-
async function
|
|
1134
|
-
return authOp(env, AUTH_OPS.
|
|
1200
|
+
async function login(env, vars) {
|
|
1201
|
+
return authOp(env, AUTH_OPS.login, vars);
|
|
1202
|
+
}
|
|
1203
|
+
async function register(env, vars) {
|
|
1204
|
+
return authOp(env, AUTH_OPS.register, vars);
|
|
1205
|
+
}
|
|
1206
|
+
async function requestOTP(env, email) {
|
|
1207
|
+
return authOp(env, AUTH_OPS.requestOTP, { email });
|
|
1135
1208
|
}
|
|
1136
|
-
async function
|
|
1137
|
-
return authOp(env, AUTH_OPS.
|
|
1209
|
+
async function loginWithOTP(env, vars) {
|
|
1210
|
+
return authOp(env, AUTH_OPS.loginWithOTP, vars);
|
|
1138
1211
|
}
|
|
1139
|
-
async function
|
|
1140
|
-
return authOp(env, AUTH_OPS.
|
|
1212
|
+
async function refreshTokens(env, refreshToken) {
|
|
1213
|
+
return authOp(env, AUTH_OPS.refreshToken, { refreshToken });
|
|
1141
1214
|
}
|
|
1142
|
-
async function
|
|
1143
|
-
return authOp(env, AUTH_OPS.
|
|
1215
|
+
async function loginWithProvider(env, input) {
|
|
1216
|
+
return authOp(env, AUTH_OPS.loginWithProvider, { input });
|
|
1144
1217
|
}
|
|
1145
|
-
async function
|
|
1146
|
-
return authOp(env, AUTH_OPS.
|
|
1218
|
+
async function providerCallback(env, input) {
|
|
1219
|
+
return authOp(env, AUTH_OPS.providerCallback, { input });
|
|
1147
1220
|
}
|
|
1148
|
-
async function
|
|
1149
|
-
return authOp(env, AUTH_OPS.
|
|
1221
|
+
async function providerVerifyOTP(env, input) {
|
|
1222
|
+
return authOp(env, AUTH_OPS.providerVerifyOTP, { input });
|
|
1150
1223
|
}
|
|
1151
|
-
|
|
1152
|
-
|
|
1224
|
+
function providerSignInRefusalOf(error) {
|
|
1225
|
+
if (!(error instanceof FoirRequestError)) return null;
|
|
1226
|
+
for (const entry of error.graphQLErrors ?? []) {
|
|
1227
|
+
const value = entry.extensions?.providerSignIn;
|
|
1228
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
1229
|
+
const { code, pending } = value;
|
|
1230
|
+
if (typeof code !== "string") continue;
|
|
1231
|
+
return typeof pending === "string" && pending !== "" ? { code, pending } : { code };
|
|
1232
|
+
}
|
|
1233
|
+
return null;
|
|
1234
|
+
}
|
|
1235
|
+
async function confirmProviderLink(env, input) {
|
|
1236
|
+
return authOp(env, AUTH_OPS.confirmProviderLink, { input });
|
|
1153
1237
|
}
|
|
1154
|
-
async function
|
|
1155
|
-
return authOp(env, AUTH_OPS.
|
|
1238
|
+
async function resendProviderLinkCode(env, input) {
|
|
1239
|
+
return authOp(env, AUTH_OPS.resendProviderLinkCode, { input });
|
|
1156
1240
|
}
|
|
1157
1241
|
async function getAuthProviders(env) {
|
|
1158
1242
|
return authOp(env, AUTH_OPS.authProviders);
|
|
@@ -1163,23 +1247,23 @@ async function getAuthConfig(env) {
|
|
|
1163
1247
|
async function getDefaultAuthProvider(env) {
|
|
1164
1248
|
return authOp(env, AUTH_OPS.defaultAuthProvider);
|
|
1165
1249
|
}
|
|
1166
|
-
async function
|
|
1167
|
-
return authOp(env, AUTH_OPS.
|
|
1250
|
+
async function signupContext(env) {
|
|
1251
|
+
return authOp(env, AUTH_OPS.signupContext);
|
|
1168
1252
|
}
|
|
1169
|
-
async function
|
|
1170
|
-
return authOp(env, AUTH_OPS.
|
|
1253
|
+
async function joinWaitlist(env, email) {
|
|
1254
|
+
return authOp(env, AUTH_OPS.joinWaitlist, { email });
|
|
1171
1255
|
}
|
|
1172
|
-
async function
|
|
1173
|
-
return authOp(env, AUTH_OPS.
|
|
1256
|
+
async function requestPasswordReset(env, email) {
|
|
1257
|
+
return authOp(env, AUTH_OPS.requestPasswordReset, { email });
|
|
1174
1258
|
}
|
|
1175
|
-
async function
|
|
1176
|
-
return authOp(env, AUTH_OPS.
|
|
1259
|
+
async function resetPassword(env, vars) {
|
|
1260
|
+
return authOp(env, AUTH_OPS.resetPassword, vars);
|
|
1177
1261
|
}
|
|
1178
|
-
async function
|
|
1179
|
-
return authOp(env, AUTH_OPS.
|
|
1262
|
+
async function verifyEmail(env, token) {
|
|
1263
|
+
return authOp(env, AUTH_OPS.verifyEmail, { token });
|
|
1180
1264
|
}
|
|
1181
|
-
async function
|
|
1182
|
-
return authOp(env, AUTH_OPS.
|
|
1265
|
+
async function updatePassword(env, vars, options) {
|
|
1266
|
+
return authOp(env, AUTH_OPS.updatePassword, vars, options);
|
|
1183
1267
|
}
|
|
1184
1268
|
async function currentUser(env, options) {
|
|
1185
1269
|
return authOp(env, AUTH_OPS.currentUser, void 0, options);
|
|
@@ -1187,63 +1271,111 @@ async function currentUser(env, options) {
|
|
|
1187
1271
|
async function myProviderToken(env, providerKey, options) {
|
|
1188
1272
|
return authOp(env, AUTH_OPS.myProviderToken, { providerKey }, options);
|
|
1189
1273
|
}
|
|
1190
|
-
async function
|
|
1191
|
-
return authOp(env, AUTH_OPS.
|
|
1274
|
+
async function logout(env, options) {
|
|
1275
|
+
return authOp(env, AUTH_OPS.logout, void 0, options);
|
|
1192
1276
|
}
|
|
1193
1277
|
async function deleteMyAccount(env, vars, options) {
|
|
1194
1278
|
return authOp(env, AUTH_OPS.deleteMyAccount, vars, options);
|
|
1195
1279
|
}
|
|
1280
|
+
async function updateMyProfile(env, change, options) {
|
|
1281
|
+
return authOp(env, AUTH_OPS.updateMyProfile, change, options);
|
|
1282
|
+
}
|
|
1283
|
+
async function myAuthorizedApps(env, options) {
|
|
1284
|
+
return authOp(env, AUTH_OPS.myAuthorizedApps, void 0, options);
|
|
1285
|
+
}
|
|
1286
|
+
async function revokeAuthorizedApp(env, id, options) {
|
|
1287
|
+
return authOp(env, AUTH_OPS.revokeAuthorizedApp, { id }, options);
|
|
1288
|
+
}
|
|
1289
|
+
async function mySessions(env, options) {
|
|
1290
|
+
return authOp(env, AUTH_OPS.mySessions, void 0, options);
|
|
1291
|
+
}
|
|
1292
|
+
async function endMySession(env, id, options) {
|
|
1293
|
+
return authOp(env, AUTH_OPS.endMySession, { id }, options);
|
|
1294
|
+
}
|
|
1295
|
+
async function signOutEverywhere(env, vars = {}, options) {
|
|
1296
|
+
return authOp(env, AUTH_OPS.signOutEverywhere, vars, options);
|
|
1297
|
+
}
|
|
1298
|
+
async function createPersonalAccessToken(env, vars, options) {
|
|
1299
|
+
return authOp(env, AUTH_OPS.createPersonalAccessToken, vars, options);
|
|
1300
|
+
}
|
|
1301
|
+
async function mySignInMethods(env, options) {
|
|
1302
|
+
return authOp(env, AUTH_OPS.mySignInMethods, void 0, options);
|
|
1303
|
+
}
|
|
1304
|
+
async function connectSignInMethod(env, vars, options) {
|
|
1305
|
+
return authOp(env, AUTH_OPS.connectSignInMethod, vars, options);
|
|
1306
|
+
}
|
|
1307
|
+
async function disconnectSignInMethod(env, provider, options) {
|
|
1308
|
+
return authOp(env, AUTH_OPS.disconnectSignInMethod, { provider }, options);
|
|
1309
|
+
}
|
|
1310
|
+
async function myFactors(env, options) {
|
|
1311
|
+
return authOp(env, AUTH_OPS.myFactors, void 0, options);
|
|
1312
|
+
}
|
|
1313
|
+
async function beginStepUp(env, password, options) {
|
|
1314
|
+
return authOp(env, AUTH_OPS.beginStepUp, { password }, options);
|
|
1315
|
+
}
|
|
1316
|
+
async function completeStepUp(env, vars, options) {
|
|
1317
|
+
return authOp(env, AUTH_OPS.completeStepUp, vars, options);
|
|
1318
|
+
}
|
|
1319
|
+
async function beginFactorEnrollment(env, vars, options) {
|
|
1320
|
+
return authOp(env, AUTH_OPS.beginFactorEnrollment, vars, options);
|
|
1321
|
+
}
|
|
1322
|
+
async function completeFactorEnrollment(env, vars, options) {
|
|
1323
|
+
return authOp(env, AUTH_OPS.completeFactorEnrollment, vars, options);
|
|
1324
|
+
}
|
|
1325
|
+
async function removeFactor(env, vars, options) {
|
|
1326
|
+
return authOp(env, AUTH_OPS.removeFactor, vars, options);
|
|
1327
|
+
}
|
|
1328
|
+
async function makeRecoveryCodes(env, elevatedHandle, options) {
|
|
1329
|
+
return authOp(env, AUTH_OPS.makeRecoveryCodes, { elevatedHandle }, options);
|
|
1330
|
+
}
|
|
1196
1331
|
async function myOrganizations(env, vars, options) {
|
|
1197
1332
|
return authOp(env, AUTH_OPS.myOrganizations, vars ?? {}, options);
|
|
1198
1333
|
}
|
|
1199
|
-
async function selectOrganization(env, vars, options) {
|
|
1200
|
-
return authOp(env, AUTH_OPS.selectMyOrganization, vars, options);
|
|
1201
|
-
}
|
|
1202
1334
|
async function registerOrganization(env, vars, options) {
|
|
1203
|
-
return authOp(env, AUTH_OPS.
|
|
1335
|
+
return authOp(env, AUTH_OPS.registerOrganization, vars, options);
|
|
1204
1336
|
}
|
|
1205
1337
|
async function saveSession(adapter, result) {
|
|
1206
1338
|
await adapter.set(TOKEN_KEY, result.token);
|
|
1207
1339
|
await adapter.set(REFRESH_KEY, result.refreshToken);
|
|
1208
|
-
if (!result.
|
|
1209
|
-
return await
|
|
1340
|
+
if (!result.userId) {
|
|
1341
|
+
return await getSessionUser(adapter) ?? { id: "", email: "" };
|
|
1210
1342
|
}
|
|
1211
|
-
await adapter.set(
|
|
1343
|
+
await adapter.set(USER_ID_KEY, result.userId);
|
|
1212
1344
|
await adapter.set(EMAIL_KEY, result.email);
|
|
1213
|
-
return { id: result.
|
|
1345
|
+
return { id: result.userId, email: result.email };
|
|
1214
1346
|
}
|
|
1215
1347
|
async function clearSession(adapter) {
|
|
1216
1348
|
await adapter.unset(TOKEN_KEY);
|
|
1217
1349
|
await adapter.unset(REFRESH_KEY);
|
|
1218
|
-
await adapter.unset(
|
|
1350
|
+
await adapter.unset(USER_ID_KEY);
|
|
1219
1351
|
await adapter.unset(EMAIL_KEY);
|
|
1220
1352
|
}
|
|
1221
1353
|
async function getSessionToken(adapter) {
|
|
1222
1354
|
return await adapter.get(TOKEN_KEY) ?? null;
|
|
1223
1355
|
}
|
|
1224
|
-
async function
|
|
1225
|
-
const id = await adapter.get(
|
|
1356
|
+
async function getSessionUser(adapter) {
|
|
1357
|
+
const id = await adapter.get(USER_ID_KEY) ?? null;
|
|
1226
1358
|
const email = await adapter.get(EMAIL_KEY) ?? null;
|
|
1227
1359
|
if (!id) return null;
|
|
1228
1360
|
return { id, email: email ?? "" };
|
|
1229
1361
|
}
|
|
1230
|
-
async function
|
|
1231
|
-
return saveSession(adapter, await
|
|
1362
|
+
async function signInWithPassword(adapter, env, vars) {
|
|
1363
|
+
return saveSession(adapter, await login(env, vars));
|
|
1232
1364
|
}
|
|
1233
|
-
async function
|
|
1234
|
-
return saveSession(adapter, await
|
|
1365
|
+
async function signUpWithPassword(adapter, env, vars) {
|
|
1366
|
+
return saveSession(adapter, await register(env, vars));
|
|
1235
1367
|
}
|
|
1236
|
-
async function
|
|
1237
|
-
return saveSession(adapter, await
|
|
1368
|
+
async function signInWithOTP(adapter, env, vars) {
|
|
1369
|
+
return saveSession(adapter, await loginWithOTP(env, vars));
|
|
1238
1370
|
}
|
|
1239
1371
|
async function resetPasswordAndSignIn(adapter, env, vars) {
|
|
1240
|
-
return saveSession(adapter, await
|
|
1372
|
+
return saveSession(adapter, await resetPassword(env, vars));
|
|
1241
1373
|
}
|
|
1242
1374
|
async function signOut(adapter, env) {
|
|
1243
1375
|
const token = await getSessionToken(adapter);
|
|
1244
1376
|
if (token) {
|
|
1245
1377
|
try {
|
|
1246
|
-
await
|
|
1378
|
+
await logout(env, { bearerToken: token });
|
|
1247
1379
|
} catch {
|
|
1248
1380
|
}
|
|
1249
1381
|
}
|
|
@@ -1253,7 +1385,7 @@ async function refreshSession(adapter, env) {
|
|
|
1253
1385
|
const refreshToken = await adapter.get(REFRESH_KEY) ?? null;
|
|
1254
1386
|
if (!refreshToken) return null;
|
|
1255
1387
|
try {
|
|
1256
|
-
const result = await
|
|
1388
|
+
const result = await refreshTokens(env, refreshToken);
|
|
1257
1389
|
await saveSession(adapter, result);
|
|
1258
1390
|
return { token: result.token, refreshToken: result.refreshToken };
|
|
1259
1391
|
} catch {
|
|
@@ -1326,20 +1458,20 @@ async function authedOp(adapter, env, op, args) {
|
|
|
1326
1458
|
}
|
|
1327
1459
|
async function requireAuth(adapter, options) {
|
|
1328
1460
|
const token = await getSessionToken(adapter);
|
|
1329
|
-
let
|
|
1330
|
-
if (token && !
|
|
1331
|
-
|
|
1461
|
+
let user = await getSessionUser(adapter);
|
|
1462
|
+
if (token && !user && options.env) {
|
|
1463
|
+
user = await recoverSessionUser(adapter, options.env, token);
|
|
1332
1464
|
}
|
|
1333
|
-
if (!token || !
|
|
1465
|
+
if (!token || !user) {
|
|
1334
1466
|
await options.onUnauthenticated();
|
|
1335
1467
|
throw new Error("[foir] onUnauthenticated must not return");
|
|
1336
1468
|
}
|
|
1337
1469
|
if (options.resolveUser) {
|
|
1338
|
-
return options.resolveUser({ token,
|
|
1470
|
+
return options.resolveUser({ token, user });
|
|
1339
1471
|
}
|
|
1340
|
-
return
|
|
1472
|
+
return user;
|
|
1341
1473
|
}
|
|
1342
|
-
async function
|
|
1474
|
+
async function recoverSessionUser(adapter, env, token) {
|
|
1343
1475
|
let me;
|
|
1344
1476
|
try {
|
|
1345
1477
|
me = await currentUser(env, { bearerToken: token });
|
|
@@ -1347,25 +1479,17 @@ async function recoverSessionCustomer(adapter, env, token) {
|
|
|
1347
1479
|
return null;
|
|
1348
1480
|
}
|
|
1349
1481
|
if (!me?.id) return null;
|
|
1350
|
-
await adapter.set(
|
|
1482
|
+
await adapter.set(USER_ID_KEY, me.id);
|
|
1351
1483
|
await adapter.set(EMAIL_KEY, me.email);
|
|
1352
1484
|
return { id: me.id, email: me.email };
|
|
1353
1485
|
}
|
|
1354
|
-
async function requireAdmin(adapter, options) {
|
|
1355
|
-
const user = await requireAuth(adapter, options);
|
|
1356
|
-
if (!user.isAdmin) {
|
|
1357
|
-
await options.onForbidden();
|
|
1358
|
-
throw new Error("[foir] onForbidden must not return");
|
|
1359
|
-
}
|
|
1360
|
-
return user;
|
|
1361
|
-
}
|
|
1362
1486
|
|
|
1363
1487
|
// src/auth/grant-table.generated.ts
|
|
1364
1488
|
var GRANT_TABLE = [
|
|
1365
|
-
["records:read", true, ["own", "
|
|
1489
|
+
["records:read", true, ["own", "all"], []],
|
|
1366
1490
|
["records:create", true, [], []],
|
|
1367
|
-
["records:write", true, ["own", "
|
|
1368
|
-
["records:delete", true, ["own", "
|
|
1491
|
+
["records:write", true, ["own", "all"], []],
|
|
1492
|
+
["records:delete", true, ["own", "all"], []],
|
|
1369
1493
|
["records:publish", true, ["own", "all"], []],
|
|
1370
1494
|
["records:attribute", true, [], []],
|
|
1371
1495
|
["search:read", true, [], []],
|
|
@@ -1381,8 +1505,8 @@ var GRANT_TABLE = [
|
|
|
1381
1505
|
["notifications:consent", false, [], []],
|
|
1382
1506
|
["pushcredentials:read", false, [], []],
|
|
1383
1507
|
["pushcredentials:write", false, [], []],
|
|
1384
|
-
["notes:read", false, [], []],
|
|
1385
|
-
["notes:write", false, [], []],
|
|
1508
|
+
["notes:read", false, ["own", "all"], []],
|
|
1509
|
+
["notes:write", false, ["own", "all"], []],
|
|
1386
1510
|
["notes:create", false, [], []],
|
|
1387
1511
|
["approvals:read", false, [], []],
|
|
1388
1512
|
["approvals:write", false, [], []],
|
|
@@ -1439,7 +1563,8 @@ var GRANT_TABLE = [
|
|
|
1439
1563
|
["roles:write", false, [], []],
|
|
1440
1564
|
["projects:read", false, [], []],
|
|
1441
1565
|
["projects:write", false, [], []],
|
|
1442
|
-
["project:
|
|
1566
|
+
["project:stand", false, [], []],
|
|
1567
|
+
["project:manage", false, [], ["project:stand", "records:read.all", "records:create", "records:write.all", "records:delete.all", "records:publish.all", "records:attribute", "search:read", "embeddings:read", "embeddings:write", "schemas:read", "drafts:read", "files:read.all", "files:write.all", "files:delete.all", "users:read.all", "identities:read", "users:create", "identities:delete", "users:write.all", "users:setup", "users:delete.all", "notifications:read.all", "notifications:write.all", "notifications:consent", "pushcredentials:read", "pushcredentials:write", "notes:read.all", "notes:write.all", "notes:create", "approvals:read", "approvals:write", "rooms:read", "rooms:write", "presence:read", "operations:read", "operations:execute", "operations:write", "appconfig:read", "schedules:read", "schedules:write", "hooks:read", "hooks:write", "auth:login_with_foir", "relyingparties:read", "relyingparties:write", "domains:read", "domains:write", "secrets:read", "secrets:list", "secrets:put", "secrets:delete", "appsecrets:read", "appsecrets:list", "appsecrets:put", "appsecrets:delete", "usersecrets:read", "usersecrets:list", "usersecrets:put", "usersecrets:delete", "opsecrets:read", "opsecrets:write", "settings:read", "settings:write", "designtokens:read", "designtokens:write", "models:read", "models:write", "configs:read", "configs:write", "signinproviders:read", "signinproviders:write", "apps:read", "apps:write", "events:read", "targeting:debug", "visitors:forward", "apikeys:read", "apikeys:write", "organizations:read", "organizations:create", "organizations:delete", "organizations:write", "organizations:reach", "members:read", "members:write", "roles:read", "roles:write", "projects:read", "projects:write", "audit:read"]],
|
|
1443
1568
|
["projects:create", false, [], []],
|
|
1444
1569
|
["projects:delete", false, [], []],
|
|
1445
1570
|
["tenants:read", false, [], []],
|
|
@@ -1452,12 +1577,14 @@ var GRANT_TABLE = [
|
|
|
1452
1577
|
["usage:read", false, [], []],
|
|
1453
1578
|
["controlkeys:read", false, [], []],
|
|
1454
1579
|
["controlkeys:write", false, [], []],
|
|
1580
|
+
["tenantsigninproviders:read", false, [], []],
|
|
1581
|
+
["tenantsigninproviders:write", false, [], []],
|
|
1455
1582
|
["tenanthooks:read", false, [], []],
|
|
1456
1583
|
["tenanthooks:write", false, [], []],
|
|
1457
1584
|
["apps:install", false, [], []],
|
|
1458
|
-
["tenant:manage", false, [], ["project:manage", "projects:create", "projects:delete", "tenants:read", "tenants:write", "tenants:delete", "
|
|
1585
|
+
["tenant:manage", false, [], ["project:manage", "projects:create", "projects:delete", "tenants:read", "tenants:write", "tenants:delete", "export:run", "billing:read", "billing:manage", "usage:read", "controlkeys:read", "controlkeys:write", "tenanthooks:read", "tenanthooks:write", "tenantsigninproviders:read", "tenantsigninproviders:write", "apps:install"]],
|
|
1459
1586
|
["platform:read", false, [], []],
|
|
1460
|
-
["platform:manage", false, [], ["tenant:manage", "platform:read", "platform:create_workspace"
|
|
1587
|
+
["platform:manage", false, [], ["tenant:manage", "platform:read", "platform:create_workspace"]],
|
|
1461
1588
|
["platform:create_workspace", false, [], []],
|
|
1462
1589
|
["users:read", false, ["own", "named", "all"], []],
|
|
1463
1590
|
["users:write", false, ["own", "all"], []],
|
|
@@ -1556,15 +1683,15 @@ function holds(held, required) {
|
|
|
1556
1683
|
|
|
1557
1684
|
// src/auth/access.ts
|
|
1558
1685
|
function accessFrom(who) {
|
|
1559
|
-
const
|
|
1560
|
-
if (!
|
|
1686
|
+
const user = who?.credential?.user;
|
|
1687
|
+
if (!user?.id) return null;
|
|
1561
1688
|
return {
|
|
1562
|
-
|
|
1563
|
-
roles: Array.isArray(
|
|
1689
|
+
userId: user.id,
|
|
1690
|
+
roles: Array.isArray(user.roles) ? [...user.roles] : null,
|
|
1564
1691
|
scopes: [...who?.authority?.effective ?? []]
|
|
1565
1692
|
};
|
|
1566
1693
|
}
|
|
1567
|
-
async function
|
|
1694
|
+
async function userAccess(env, options) {
|
|
1568
1695
|
return accessFrom(await authOp(env, AUTH_OPS.whoami, void 0, options));
|
|
1569
1696
|
}
|
|
1570
1697
|
async function getSessionAccess(adapter, env) {
|
|
@@ -1597,11 +1724,11 @@ function denialMessage(reason, required) {
|
|
|
1597
1724
|
case "signed_out":
|
|
1598
1725
|
return "[foir] nobody is signed in";
|
|
1599
1726
|
case "role_not_held":
|
|
1600
|
-
return `[foir] the signed-in
|
|
1727
|
+
return `[foir] the signed-in user does not hold the role "${required.role}"`;
|
|
1601
1728
|
case "roles_unknown":
|
|
1602
|
-
return `[foir] the platform did not say which roles the signed-in
|
|
1729
|
+
return `[foir] the platform did not say which roles the signed-in user holds, so "${required.role}" could not be checked`;
|
|
1603
1730
|
case "scope_not_held":
|
|
1604
|
-
return `[foir] the signed-in
|
|
1731
|
+
return `[foir] the signed-in user's scopes do not cover "${required.scope}"`;
|
|
1605
1732
|
}
|
|
1606
1733
|
}
|
|
1607
1734
|
async function requireRole(adapter, env, key) {
|
|
@@ -1619,7 +1746,7 @@ async function requireScope(adapter, env, scope) {
|
|
|
1619
1746
|
}
|
|
1620
1747
|
|
|
1621
1748
|
// src/auth/hosted.ts
|
|
1622
|
-
var DEFAULT_COOKIE_NAME = "
|
|
1749
|
+
var DEFAULT_COOKIE_NAME = "foir_session";
|
|
1623
1750
|
var DEFAULT_SESSION_PATH = "/session/token";
|
|
1624
1751
|
var DEFAULT_END_SESSION_PATH = "/session/end-session";
|
|
1625
1752
|
function hostedLoginUrl(config, returnTo) {
|
|
@@ -1675,14 +1802,14 @@ async function fetchHostedSession(request, config) {
|
|
|
1675
1802
|
} catch {
|
|
1676
1803
|
return { outcome: "unavailable", reason: "malformed", httpStatus: res.status };
|
|
1677
1804
|
}
|
|
1678
|
-
if (!json.access_token || !json.
|
|
1805
|
+
if (!json.access_token || !json.user_id) {
|
|
1679
1806
|
return { outcome: "unavailable", reason: "malformed", httpStatus: res.status };
|
|
1680
1807
|
}
|
|
1681
1808
|
return {
|
|
1682
1809
|
outcome: "active",
|
|
1683
1810
|
session: {
|
|
1684
1811
|
token: json.access_token,
|
|
1685
|
-
|
|
1812
|
+
userId: json.user_id,
|
|
1686
1813
|
email: json.email ?? "",
|
|
1687
1814
|
expiresIn: typeof json.expires_in === "number" ? json.expires_in : 0
|
|
1688
1815
|
}
|
|
@@ -1758,14 +1885,14 @@ function createFoirSessionClient(config) {
|
|
|
1758
1885
|
} catch {
|
|
1759
1886
|
return cached;
|
|
1760
1887
|
}
|
|
1761
|
-
if (!json.access_token || !json.
|
|
1888
|
+
if (!json.access_token || !json.user_id) return cached;
|
|
1762
1889
|
const expiresIn = typeof json.expires_in === "number" ? json.expires_in : 0;
|
|
1763
1890
|
const next = {
|
|
1764
1891
|
token: json.access_token,
|
|
1765
1892
|
expiresAt: Date.now() + expiresIn * 1e3,
|
|
1766
|
-
user: {
|
|
1893
|
+
user: { userId: json.user_id, email: json.email ?? "" }
|
|
1767
1894
|
};
|
|
1768
|
-
const changed = cached?.user.
|
|
1895
|
+
const changed = cached?.user.userId !== next.user.userId;
|
|
1769
1896
|
cached = next;
|
|
1770
1897
|
scheduleRefresh(next.expiresAt);
|
|
1771
1898
|
if (changed) emit(next.user);
|
|
@@ -1833,51 +1960,69 @@ export {
|
|
|
1833
1960
|
FoirSessionExpiredError,
|
|
1834
1961
|
authedOp,
|
|
1835
1962
|
authedRequest,
|
|
1963
|
+
beginFactorEnrollment,
|
|
1964
|
+
beginStepUp,
|
|
1836
1965
|
clearSession,
|
|
1966
|
+
completeFactorEnrollment,
|
|
1967
|
+
completeStepUp,
|
|
1968
|
+
confirmProviderLink,
|
|
1969
|
+
connectSignInMethod,
|
|
1837
1970
|
createFoirSessionClient,
|
|
1971
|
+
createPersonalAccessToken,
|
|
1838
1972
|
currentUser,
|
|
1839
|
-
customerAccess,
|
|
1840
|
-
customerJoinWaitlist,
|
|
1841
|
-
customerLogin,
|
|
1842
|
-
customerLoginOTP,
|
|
1843
|
-
customerLoginWithProvider,
|
|
1844
|
-
customerLogout,
|
|
1845
|
-
customerProviderCallback,
|
|
1846
|
-
customerProviderVerifyOTP,
|
|
1847
|
-
customerRefreshToken,
|
|
1848
|
-
customerRegister,
|
|
1849
|
-
customerRequestOTP,
|
|
1850
|
-
customerRequestPasswordReset,
|
|
1851
|
-
customerResetPassword,
|
|
1852
|
-
customerSignupContext,
|
|
1853
|
-
customerUpdatePassword,
|
|
1854
|
-
customerVerifyEmail,
|
|
1855
1973
|
deleteMyAccount,
|
|
1974
|
+
disconnectSignInMethod,
|
|
1975
|
+
endMySession,
|
|
1856
1976
|
fetchHostedSession,
|
|
1857
1977
|
getAuthConfig,
|
|
1858
1978
|
getAuthProviders,
|
|
1859
1979
|
getDefaultAuthProvider,
|
|
1860
1980
|
getSessionAccess,
|
|
1861
|
-
getSessionCustomer,
|
|
1862
1981
|
getSessionToken,
|
|
1982
|
+
getSessionUser,
|
|
1863
1983
|
hasRole,
|
|
1864
1984
|
hasScope,
|
|
1865
1985
|
hostedLoginUrl,
|
|
1866
1986
|
hostedLogoutUrl,
|
|
1987
|
+
joinWaitlist,
|
|
1988
|
+
login,
|
|
1867
1989
|
loginWithOTP,
|
|
1868
|
-
|
|
1990
|
+
loginWithProvider,
|
|
1991
|
+
logout,
|
|
1992
|
+
makeRecoveryCodes,
|
|
1993
|
+
myAuthorizedApps,
|
|
1994
|
+
myFactors,
|
|
1869
1995
|
myOrganizations,
|
|
1870
1996
|
myProviderToken,
|
|
1997
|
+
mySessions,
|
|
1998
|
+
mySignInMethods,
|
|
1999
|
+
providerCallback,
|
|
2000
|
+
providerSignInRefusalOf,
|
|
2001
|
+
providerVerifyOTP,
|
|
1871
2002
|
refreshSession,
|
|
1872
2003
|
refreshSessionOnce,
|
|
2004
|
+
refreshTokens,
|
|
2005
|
+
register,
|
|
1873
2006
|
registerOrganization,
|
|
1874
|
-
|
|
1875
|
-
|
|
2007
|
+
removeFactor,
|
|
2008
|
+
requestOTP,
|
|
2009
|
+
requestPasswordReset,
|
|
1876
2010
|
requireAuth,
|
|
1877
2011
|
requireRole,
|
|
1878
2012
|
requireScope,
|
|
2013
|
+
resendProviderLinkCode,
|
|
2014
|
+
resetPassword,
|
|
1879
2015
|
resetPasswordAndSignIn,
|
|
2016
|
+
revokeAuthorizedApp,
|
|
1880
2017
|
saveSession,
|
|
1881
|
-
|
|
1882
|
-
|
|
2018
|
+
signInWithOTP,
|
|
2019
|
+
signInWithPassword,
|
|
2020
|
+
signOut,
|
|
2021
|
+
signOutEverywhere,
|
|
2022
|
+
signUpWithPassword,
|
|
2023
|
+
signupContext,
|
|
2024
|
+
updateMyProfile,
|
|
2025
|
+
updatePassword,
|
|
2026
|
+
userAccess,
|
|
2027
|
+
verifyEmail
|
|
1883
2028
|
};
|