@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/react.js
CHANGED
|
@@ -14,6 +14,26 @@ import {
|
|
|
14
14
|
// src/graphql/foir-request.ts
|
|
15
15
|
import { print as print2 } from "graphql";
|
|
16
16
|
|
|
17
|
+
// src/transport/key-kind.ts
|
|
18
|
+
var KEY_PREFIXES = [
|
|
19
|
+
["foir_pub_", "publishable"],
|
|
20
|
+
["foir_srv_", "server"],
|
|
21
|
+
["foir_adm_", "admin"],
|
|
22
|
+
["pk_", "publishable"],
|
|
23
|
+
["sk_", "older"],
|
|
24
|
+
["tc_", "older"]
|
|
25
|
+
];
|
|
26
|
+
function foirKeyKind(apiKey) {
|
|
27
|
+
if (typeof apiKey !== "string") return void 0;
|
|
28
|
+
for (const [prefix, kind] of KEY_PREFIXES) {
|
|
29
|
+
if (apiKey.startsWith(prefix)) return kind;
|
|
30
|
+
}
|
|
31
|
+
return void 0;
|
|
32
|
+
}
|
|
33
|
+
function isPublishableFoirKey(apiKey) {
|
|
34
|
+
return foirKeyKind(apiKey) === "publishable";
|
|
35
|
+
}
|
|
36
|
+
|
|
17
37
|
// src/graphql/cdn-route.ts
|
|
18
38
|
import { print } from "graphql";
|
|
19
39
|
|
|
@@ -117,6 +137,28 @@ function refuseCredentialHeaders(extra) {
|
|
|
117
137
|
}
|
|
118
138
|
}
|
|
119
139
|
var FOIR_VISITOR_IP_HEADER = "X-Foir-Visitor-IP";
|
|
140
|
+
function sentCoordinate(value) {
|
|
141
|
+
return Math.round(value * 100) / 100;
|
|
142
|
+
}
|
|
143
|
+
async function resolveFoirVisitor(source) {
|
|
144
|
+
const value = typeof source === "function" ? await source() : source;
|
|
145
|
+
if (value === null || value === void 0) return { place: {} };
|
|
146
|
+
if (typeof value === "string") {
|
|
147
|
+
const address2 = value.trim();
|
|
148
|
+
return { address: address2 || void 0, place: {} };
|
|
149
|
+
}
|
|
150
|
+
const address = value.address?.trim();
|
|
151
|
+
const country = value.country?.trim();
|
|
152
|
+
const place = {};
|
|
153
|
+
if (country) place.country = country;
|
|
154
|
+
if (Number.isFinite(value.latitude) && Number.isFinite(value.longitude)) {
|
|
155
|
+
place.location = {
|
|
156
|
+
latitude: sentCoordinate(value.latitude),
|
|
157
|
+
longitude: sentCoordinate(value.longitude)
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return { address: address || void 0, place };
|
|
161
|
+
}
|
|
120
162
|
function buildFoirHeaders(input) {
|
|
121
163
|
const headers = {
|
|
122
164
|
"Content-Type": "application/json"
|
|
@@ -163,8 +205,8 @@ var FoirAuthDowngradedError = class extends FoirRequestError {
|
|
|
163
205
|
downgrade;
|
|
164
206
|
constructor(downgrade) {
|
|
165
207
|
super(
|
|
166
|
-
`[foir] your
|
|
167
|
-
{ graphQLErrors: [{ message: "
|
|
208
|
+
`[foir] your user token was ${downgrade.userToken} (${downgrade.reason}), so the request was served as ${downgrade.servedAs}`,
|
|
209
|
+
{ graphQLErrors: [{ message: "user token rejected", extensions: { code: "AUTH_DOWNGRADED" } }] }
|
|
168
210
|
);
|
|
169
211
|
this.name = "FoirAuthDowngradedError";
|
|
170
212
|
this.downgrade = downgrade;
|
|
@@ -172,7 +214,7 @@ var FoirAuthDowngradedError = class extends FoirRequestError {
|
|
|
172
214
|
};
|
|
173
215
|
var FoirSessionExpiredError = class extends FoirRequestError {
|
|
174
216
|
constructor(options) {
|
|
175
|
-
super("[foir] the
|
|
217
|
+
super("[foir] the user session has expired and could not be refreshed: sign in again", {
|
|
176
218
|
cause: options?.cause,
|
|
177
219
|
graphQLErrors: [{ message: "session expired", extensions: { code: "SESSION_EXPIRED" } }]
|
|
178
220
|
});
|
|
@@ -189,7 +231,7 @@ function reportAuthDowngrade(envelope) {
|
|
|
189
231
|
if (warnedDowngrade === downgrade.reason) return;
|
|
190
232
|
warnedDowngrade = downgrade.reason;
|
|
191
233
|
console.warn(
|
|
192
|
-
`[foir] your
|
|
234
|
+
`[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.")
|
|
193
235
|
);
|
|
194
236
|
}
|
|
195
237
|
function refusePrunedDocument(json, options) {
|
|
@@ -733,10 +775,7 @@ function foirDataEndpoint(apiUrl, path) {
|
|
|
733
775
|
return foirApiEndpoint(apiUrl, path);
|
|
734
776
|
}
|
|
735
777
|
async function resolveVisitorAddress(config) {
|
|
736
|
-
|
|
737
|
-
const value = typeof source === "function" ? await source() : source;
|
|
738
|
-
const address = value?.trim();
|
|
739
|
-
return address ? address : void 0;
|
|
778
|
+
return (await resolveFoirVisitor(config.visitor)).address;
|
|
740
779
|
}
|
|
741
780
|
async function resolveBearerToken(config) {
|
|
742
781
|
const source = config.bearerToken;
|
|
@@ -777,7 +816,7 @@ async function resolveFoirAuth(config) {
|
|
|
777
816
|
};
|
|
778
817
|
}
|
|
779
818
|
function foirRoute(input) {
|
|
780
|
-
if (!input.apiKey
|
|
819
|
+
if (!isPublishableFoirKey(input.apiKey)) return { lane: "origin", reason: "key" };
|
|
781
820
|
if (input.bearerToken) return { lane: "origin", reason: "bearer" };
|
|
782
821
|
if (input.orgId || input.orgUrl) return { lane: "origin", reason: "org" };
|
|
783
822
|
if (input.mutation) return { lane: "origin", reason: "operation" };
|
|
@@ -911,42 +950,63 @@ var AUTH_OPS = {
|
|
|
911
950
|
authConfig: "authConfig",
|
|
912
951
|
authProviders: "authProviders",
|
|
913
952
|
defaultAuthProvider: "defaultAuthProvider",
|
|
914
|
-
|
|
953
|
+
signupContext: "signupContext",
|
|
915
954
|
// Post-credential self reads.
|
|
916
955
|
currentUser: "currentUser",
|
|
917
956
|
myProviderToken: "myProviderToken",
|
|
918
|
-
// What the calling credential may do, and for a signed-in
|
|
957
|
+
// What the calling credential may do, and for a signed-in user the roles
|
|
919
958
|
// they hold. `./access` reads it.
|
|
920
959
|
whoami: "whoami",
|
|
921
960
|
// Methods.
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
961
|
+
login: "login",
|
|
962
|
+
register: "register",
|
|
963
|
+
requestOTP: "requestOTP",
|
|
964
|
+
loginWithOTP: "loginWithOTP",
|
|
926
965
|
// Session lifecycle.
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
// Password management and account lifecycle
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
966
|
+
refreshToken: "refreshToken",
|
|
967
|
+
logout: "logout",
|
|
968
|
+
joinWaitlist: "joinWaitlist",
|
|
969
|
+
// Password management and account lifecycle.
|
|
970
|
+
requestPasswordReset: "requestPasswordReset",
|
|
971
|
+
resetPassword: "resetPassword",
|
|
972
|
+
updatePassword: "updatePassword",
|
|
973
|
+
verifyEmail: "verifyEmail",
|
|
935
974
|
deleteMyAccount: "deleteMyAccount",
|
|
936
|
-
// The
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
//
|
|
941
|
-
//
|
|
975
|
+
// The user's own profile, and the apps they have agreed may act for them.
|
|
976
|
+
updateMyProfile: "updateMyProfile",
|
|
977
|
+
myAuthorizedApps: "myAuthorizedApps",
|
|
978
|
+
revokeAuthorizedApp: "revokeAuthorizedApp",
|
|
979
|
+
// Where they are signed in, their personal access tokens, and the
|
|
980
|
+
// providers they sign in with.
|
|
981
|
+
mySessions: "mySessions",
|
|
982
|
+
endMySession: "endMySession",
|
|
983
|
+
signOutEverywhere: "signOutEverywhere",
|
|
984
|
+
createPersonalAccessToken: "createPersonalAccessToken",
|
|
985
|
+
mySignInMethods: "mySignInMethods",
|
|
986
|
+
connectSignInMethod: "connectSignInMethod",
|
|
987
|
+
disconnectSignInMethod: "disconnectSignInMethod",
|
|
988
|
+
// Their second factors. A change needs one proven first, by a step-up.
|
|
989
|
+
myFactors: "myFactors",
|
|
990
|
+
beginStepUp: "beginStepUp",
|
|
991
|
+
completeStepUp: "completeStepUp",
|
|
992
|
+
beginFactorEnrollment: "beginFactorEnrollment",
|
|
993
|
+
completeFactorEnrollment: "completeFactorEnrollment",
|
|
994
|
+
removeFactor: "removeFactor",
|
|
995
|
+
makeRecoveryCodes: "makeRecoveryCodes",
|
|
996
|
+
// The organizations the caller may stand in. Nothing selects one: a request
|
|
997
|
+
// names the organization it acts in (`orgId` or `orgUrl` below), and the
|
|
998
|
+
// platform checks the caller stands there every time.
|
|
942
999
|
myOrganizations: "myOrganizations",
|
|
943
|
-
selectMyOrganization: "selectMyOrganization",
|
|
944
1000
|
// Founding one. Present in a manifest only when the project opted in.
|
|
945
|
-
|
|
1001
|
+
registerOrganization: "registerOrganization",
|
|
946
1002
|
// External identity providers.
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
1003
|
+
loginWithProvider: "loginWithProvider",
|
|
1004
|
+
providerCallback: "providerCallback",
|
|
1005
|
+
providerVerifyOTP: "providerVerifyOTP",
|
|
1006
|
+
// A provider sign-in that names an address somebody already holds, waiting
|
|
1007
|
+
// for the code sent to it.
|
|
1008
|
+
confirmProviderLink: "confirmProviderLink",
|
|
1009
|
+
resendProviderLinkCode: "resendProviderLinkCode"
|
|
950
1010
|
};
|
|
951
1011
|
function authClientConfig(env, options) {
|
|
952
1012
|
const apiKey = resolveFoirApiKey(env);
|
|
@@ -1009,28 +1069,28 @@ function stripUndefined(args) {
|
|
|
1009
1069
|
// src/auth/auth.ts
|
|
1010
1070
|
var TOKEN_KEY = "foir:token";
|
|
1011
1071
|
var REFRESH_KEY = "foir:refreshToken";
|
|
1012
|
-
var
|
|
1072
|
+
var USER_ID_KEY = "foir:userId";
|
|
1013
1073
|
var EMAIL_KEY = "foir:email";
|
|
1014
|
-
async function
|
|
1015
|
-
return authOp(env, AUTH_OPS.
|
|
1074
|
+
async function login(env, vars) {
|
|
1075
|
+
return authOp(env, AUTH_OPS.login, vars);
|
|
1016
1076
|
}
|
|
1017
|
-
async function
|
|
1018
|
-
return authOp(env, AUTH_OPS.
|
|
1077
|
+
async function register(env, vars) {
|
|
1078
|
+
return authOp(env, AUTH_OPS.register, vars);
|
|
1019
1079
|
}
|
|
1020
|
-
async function
|
|
1021
|
-
return authOp(env, AUTH_OPS.
|
|
1080
|
+
async function requestOTP(env, email) {
|
|
1081
|
+
return authOp(env, AUTH_OPS.requestOTP, { email });
|
|
1022
1082
|
}
|
|
1023
|
-
async function
|
|
1024
|
-
return authOp(env, AUTH_OPS.
|
|
1083
|
+
async function loginWithOTP(env, vars) {
|
|
1084
|
+
return authOp(env, AUTH_OPS.loginWithOTP, vars);
|
|
1025
1085
|
}
|
|
1026
|
-
async function
|
|
1027
|
-
return authOp(env, AUTH_OPS.
|
|
1086
|
+
async function refreshTokens(env, refreshToken) {
|
|
1087
|
+
return authOp(env, AUTH_OPS.refreshToken, { refreshToken });
|
|
1028
1088
|
}
|
|
1029
|
-
async function
|
|
1030
|
-
return authOp(env, AUTH_OPS.
|
|
1089
|
+
async function loginWithProvider(env, input) {
|
|
1090
|
+
return authOp(env, AUTH_OPS.loginWithProvider, { input });
|
|
1031
1091
|
}
|
|
1032
|
-
async function
|
|
1033
|
-
return authOp(env, AUTH_OPS.
|
|
1092
|
+
async function providerCallback(env, input) {
|
|
1093
|
+
return authOp(env, AUTH_OPS.providerCallback, { input });
|
|
1034
1094
|
}
|
|
1035
1095
|
async function getAuthProviders(env) {
|
|
1036
1096
|
return authOp(env, AUTH_OPS.authProviders);
|
|
@@ -1038,42 +1098,42 @@ async function getAuthProviders(env) {
|
|
|
1038
1098
|
async function saveSession(adapter, result) {
|
|
1039
1099
|
await adapter.set(TOKEN_KEY, result.token);
|
|
1040
1100
|
await adapter.set(REFRESH_KEY, result.refreshToken);
|
|
1041
|
-
if (!result.
|
|
1042
|
-
return await
|
|
1101
|
+
if (!result.userId) {
|
|
1102
|
+
return await getSessionUser(adapter) ?? { id: "", email: "" };
|
|
1043
1103
|
}
|
|
1044
|
-
await adapter.set(
|
|
1104
|
+
await adapter.set(USER_ID_KEY, result.userId);
|
|
1045
1105
|
await adapter.set(EMAIL_KEY, result.email);
|
|
1046
|
-
return { id: result.
|
|
1106
|
+
return { id: result.userId, email: result.email };
|
|
1047
1107
|
}
|
|
1048
1108
|
async function clearSession(adapter) {
|
|
1049
1109
|
await adapter.unset(TOKEN_KEY);
|
|
1050
1110
|
await adapter.unset(REFRESH_KEY);
|
|
1051
|
-
await adapter.unset(
|
|
1111
|
+
await adapter.unset(USER_ID_KEY);
|
|
1052
1112
|
await adapter.unset(EMAIL_KEY);
|
|
1053
1113
|
}
|
|
1054
1114
|
async function getSessionToken(adapter) {
|
|
1055
1115
|
return await adapter.get(TOKEN_KEY) ?? null;
|
|
1056
1116
|
}
|
|
1057
|
-
async function
|
|
1058
|
-
const id = await adapter.get(
|
|
1117
|
+
async function getSessionUser(adapter) {
|
|
1118
|
+
const id = await adapter.get(USER_ID_KEY) ?? null;
|
|
1059
1119
|
const email = await adapter.get(EMAIL_KEY) ?? null;
|
|
1060
1120
|
if (!id) return null;
|
|
1061
1121
|
return { id, email: email ?? "" };
|
|
1062
1122
|
}
|
|
1063
|
-
async function
|
|
1064
|
-
return saveSession(adapter, await
|
|
1123
|
+
async function signInWithPassword(adapter, env, vars) {
|
|
1124
|
+
return saveSession(adapter, await login(env, vars));
|
|
1065
1125
|
}
|
|
1066
|
-
async function
|
|
1067
|
-
return saveSession(adapter, await
|
|
1126
|
+
async function signUpWithPassword(adapter, env, vars) {
|
|
1127
|
+
return saveSession(adapter, await register(env, vars));
|
|
1068
1128
|
}
|
|
1069
|
-
async function
|
|
1070
|
-
return saveSession(adapter, await
|
|
1129
|
+
async function signInWithOTP(adapter, env, vars) {
|
|
1130
|
+
return saveSession(adapter, await loginWithOTP(env, vars));
|
|
1071
1131
|
}
|
|
1072
1132
|
async function refreshSession(adapter, env) {
|
|
1073
1133
|
const refreshToken = await adapter.get(REFRESH_KEY) ?? null;
|
|
1074
1134
|
if (!refreshToken) return null;
|
|
1075
1135
|
try {
|
|
1076
|
-
const result = await
|
|
1136
|
+
const result = await refreshTokens(env, refreshToken);
|
|
1077
1137
|
await saveSession(adapter, result);
|
|
1078
1138
|
return { token: result.token, refreshToken: result.refreshToken };
|
|
1079
1139
|
} catch {
|
|
@@ -1140,10 +1200,10 @@ async function authedOp(adapter, env, op, args) {
|
|
|
1140
1200
|
|
|
1141
1201
|
// src/auth/grant-table.generated.ts
|
|
1142
1202
|
var GRANT_TABLE = [
|
|
1143
|
-
["records:read", true, ["own", "
|
|
1203
|
+
["records:read", true, ["own", "all"], []],
|
|
1144
1204
|
["records:create", true, [], []],
|
|
1145
|
-
["records:write", true, ["own", "
|
|
1146
|
-
["records:delete", true, ["own", "
|
|
1205
|
+
["records:write", true, ["own", "all"], []],
|
|
1206
|
+
["records:delete", true, ["own", "all"], []],
|
|
1147
1207
|
["records:publish", true, ["own", "all"], []],
|
|
1148
1208
|
["records:attribute", true, [], []],
|
|
1149
1209
|
["search:read", true, [], []],
|
|
@@ -1159,8 +1219,8 @@ var GRANT_TABLE = [
|
|
|
1159
1219
|
["notifications:consent", false, [], []],
|
|
1160
1220
|
["pushcredentials:read", false, [], []],
|
|
1161
1221
|
["pushcredentials:write", false, [], []],
|
|
1162
|
-
["notes:read", false, [], []],
|
|
1163
|
-
["notes:write", false, [], []],
|
|
1222
|
+
["notes:read", false, ["own", "all"], []],
|
|
1223
|
+
["notes:write", false, ["own", "all"], []],
|
|
1164
1224
|
["notes:create", false, [], []],
|
|
1165
1225
|
["approvals:read", false, [], []],
|
|
1166
1226
|
["approvals:write", false, [], []],
|
|
@@ -1217,7 +1277,8 @@ var GRANT_TABLE = [
|
|
|
1217
1277
|
["roles:write", false, [], []],
|
|
1218
1278
|
["projects:read", false, [], []],
|
|
1219
1279
|
["projects:write", false, [], []],
|
|
1220
|
-
["project:
|
|
1280
|
+
["project:stand", false, [], []],
|
|
1281
|
+
["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"]],
|
|
1221
1282
|
["projects:create", false, [], []],
|
|
1222
1283
|
["projects:delete", false, [], []],
|
|
1223
1284
|
["tenants:read", false, [], []],
|
|
@@ -1230,12 +1291,14 @@ var GRANT_TABLE = [
|
|
|
1230
1291
|
["usage:read", false, [], []],
|
|
1231
1292
|
["controlkeys:read", false, [], []],
|
|
1232
1293
|
["controlkeys:write", false, [], []],
|
|
1294
|
+
["tenantsigninproviders:read", false, [], []],
|
|
1295
|
+
["tenantsigninproviders:write", false, [], []],
|
|
1233
1296
|
["tenanthooks:read", false, [], []],
|
|
1234
1297
|
["tenanthooks:write", false, [], []],
|
|
1235
1298
|
["apps:install", false, [], []],
|
|
1236
|
-
["tenant:manage", false, [], ["project:manage", "projects:create", "projects:delete", "tenants:read", "tenants:write", "tenants:delete", "
|
|
1299
|
+
["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"]],
|
|
1237
1300
|
["platform:read", false, [], []],
|
|
1238
|
-
["platform:manage", false, [], ["tenant:manage", "platform:read", "platform:create_workspace"
|
|
1301
|
+
["platform:manage", false, [], ["tenant:manage", "platform:read", "platform:create_workspace"]],
|
|
1239
1302
|
["platform:create_workspace", false, [], []],
|
|
1240
1303
|
["users:read", false, ["own", "named", "all"], []],
|
|
1241
1304
|
["users:write", false, ["own", "all"], []],
|
|
@@ -1334,15 +1397,15 @@ function holds(held, required) {
|
|
|
1334
1397
|
|
|
1335
1398
|
// src/auth/access.ts
|
|
1336
1399
|
function accessFrom(who) {
|
|
1337
|
-
const
|
|
1338
|
-
if (!
|
|
1400
|
+
const user = who?.credential?.user;
|
|
1401
|
+
if (!user?.id) return null;
|
|
1339
1402
|
return {
|
|
1340
|
-
|
|
1341
|
-
roles: Array.isArray(
|
|
1403
|
+
userId: user.id,
|
|
1404
|
+
roles: Array.isArray(user.roles) ? [...user.roles] : null,
|
|
1342
1405
|
scopes: [...who?.authority?.effective ?? []]
|
|
1343
1406
|
};
|
|
1344
1407
|
}
|
|
1345
|
-
async function
|
|
1408
|
+
async function userAccess(env, options) {
|
|
1346
1409
|
return accessFrom(await authOp(env, AUTH_OPS.whoami, void 0, options));
|
|
1347
1410
|
}
|
|
1348
1411
|
async function getSessionAccess(adapter, env) {
|
|
@@ -1422,14 +1485,14 @@ function createFoirSessionClient(config) {
|
|
|
1422
1485
|
} catch {
|
|
1423
1486
|
return cached;
|
|
1424
1487
|
}
|
|
1425
|
-
if (!json.access_token || !json.
|
|
1488
|
+
if (!json.access_token || !json.user_id) return cached;
|
|
1426
1489
|
const expiresIn = typeof json.expires_in === "number" ? json.expires_in : 0;
|
|
1427
1490
|
const next = {
|
|
1428
1491
|
token: json.access_token,
|
|
1429
1492
|
expiresAt: Date.now() + expiresIn * 1e3,
|
|
1430
|
-
user: {
|
|
1493
|
+
user: { userId: json.user_id, email: json.email ?? "" }
|
|
1431
1494
|
};
|
|
1432
|
-
const changed = cached?.user.
|
|
1495
|
+
const changed = cached?.user.userId !== next.user.userId;
|
|
1433
1496
|
cached = next;
|
|
1434
1497
|
scheduleRefresh(next.expiresAt);
|
|
1435
1498
|
if (changed) emit(next.user);
|
|
@@ -1530,13 +1593,13 @@ function FoirProvider({
|
|
|
1530
1593
|
const adapterRef = useRef(adapter ?? createLocalStorageAdapter());
|
|
1531
1594
|
if (adapter && adapter !== adapterRef.current) adapterRef.current = adapter;
|
|
1532
1595
|
const [status, setStatus] = useState("loading");
|
|
1533
|
-
const [
|
|
1596
|
+
const [user, setUser] = useState(null);
|
|
1534
1597
|
const [access, setAccess] = useState(null);
|
|
1535
1598
|
const [accessStatus, setAccessStatus] = useState("idle");
|
|
1536
1599
|
const accessRead = useRef(0);
|
|
1537
1600
|
const accessPending = useRef(null);
|
|
1538
1601
|
const apply = useCallback((c) => {
|
|
1539
|
-
|
|
1602
|
+
setUser(c);
|
|
1540
1603
|
setStatus(c ? "authenticated" : "unauthenticated");
|
|
1541
1604
|
accessRead.current += 1;
|
|
1542
1605
|
setAccess(null);
|
|
@@ -1561,10 +1624,10 @@ function FoirProvider({
|
|
|
1561
1624
|
void (async () => {
|
|
1562
1625
|
if (hosted) {
|
|
1563
1626
|
const u = await hosted.getUser();
|
|
1564
|
-
if (!cancelled) apply(u ? { id: u.
|
|
1627
|
+
if (!cancelled) apply(u ? { id: u.userId, email: u.email } : null);
|
|
1565
1628
|
return;
|
|
1566
1629
|
}
|
|
1567
|
-
const c = await
|
|
1630
|
+
const c = await getSessionUser(adapterRef.current);
|
|
1568
1631
|
if (!cancelled) apply(c);
|
|
1569
1632
|
})();
|
|
1570
1633
|
return () => {
|
|
@@ -1579,7 +1642,7 @@ function FoirProvider({
|
|
|
1579
1642
|
(action) => {
|
|
1580
1643
|
if (hosted) {
|
|
1581
1644
|
throw new Error(
|
|
1582
|
-
`[foir] ${action}() is not available in hostedSession mode \u2014 the
|
|
1645
|
+
`[foir] ${action}() is not available in hostedSession mode \u2014 the user signs in at your Foir login domain. Redirect to hostedSignInUrl() instead.`
|
|
1583
1646
|
);
|
|
1584
1647
|
}
|
|
1585
1648
|
},
|
|
@@ -1588,16 +1651,16 @@ function FoirProvider({
|
|
|
1588
1651
|
const signIn = useCallback(
|
|
1589
1652
|
async (vars) => {
|
|
1590
1653
|
assertTokenMode("signIn");
|
|
1591
|
-
const c = await
|
|
1654
|
+
const c = await signInWithPassword(adapterRef.current, envRef.current, vars);
|
|
1592
1655
|
apply(c);
|
|
1593
1656
|
return c;
|
|
1594
1657
|
},
|
|
1595
1658
|
[apply, assertTokenMode]
|
|
1596
1659
|
);
|
|
1597
|
-
const
|
|
1660
|
+
const register2 = useCallback(
|
|
1598
1661
|
async (vars) => {
|
|
1599
1662
|
assertTokenMode("register");
|
|
1600
|
-
const c = await
|
|
1663
|
+
const c = await signUpWithPassword(adapterRef.current, envRef.current, vars);
|
|
1601
1664
|
apply(c);
|
|
1602
1665
|
return c;
|
|
1603
1666
|
},
|
|
@@ -1606,14 +1669,14 @@ function FoirProvider({
|
|
|
1606
1669
|
const requestOtp = useCallback(
|
|
1607
1670
|
(email) => {
|
|
1608
1671
|
assertTokenMode("requestOtp");
|
|
1609
|
-
return
|
|
1672
|
+
return requestOTP(envRef.current, email);
|
|
1610
1673
|
},
|
|
1611
1674
|
[assertTokenMode]
|
|
1612
1675
|
);
|
|
1613
1676
|
const signInWithOtp = useCallback(
|
|
1614
1677
|
async (vars) => {
|
|
1615
1678
|
assertTokenMode("signInWithOtp");
|
|
1616
|
-
const c = await
|
|
1679
|
+
const c = await signInWithOTP(adapterRef.current, envRef.current, vars);
|
|
1617
1680
|
apply(c);
|
|
1618
1681
|
return c;
|
|
1619
1682
|
},
|
|
@@ -1622,7 +1685,7 @@ function FoirProvider({
|
|
|
1622
1685
|
const signInWithProvider = useCallback(
|
|
1623
1686
|
async (providerKey, opts) => {
|
|
1624
1687
|
assertTokenMode("signInWithProvider");
|
|
1625
|
-
const res = await
|
|
1688
|
+
const res = await loginWithProvider(envRef.current, {
|
|
1626
1689
|
providerKey,
|
|
1627
1690
|
...opts
|
|
1628
1691
|
});
|
|
@@ -1635,7 +1698,7 @@ function FoirProvider({
|
|
|
1635
1698
|
const completeProviderLogin = useCallback(
|
|
1636
1699
|
async (input) => {
|
|
1637
1700
|
assertTokenMode("completeProviderLogin");
|
|
1638
|
-
const result = await
|
|
1701
|
+
const result = await providerCallback(envRef.current, input);
|
|
1639
1702
|
const c = await saveSession(adapterRef.current, result);
|
|
1640
1703
|
apply(c);
|
|
1641
1704
|
return c;
|
|
@@ -1671,7 +1734,7 @@ function FoirProvider({
|
|
|
1671
1734
|
const pending = (async () => {
|
|
1672
1735
|
try {
|
|
1673
1736
|
const next = hosted ? await hosted.getToken().then(
|
|
1674
|
-
(bearerToken) => bearerToken ?
|
|
1737
|
+
(bearerToken) => bearerToken ? userAccess(envRef.current, { bearerToken }) : null
|
|
1675
1738
|
) : await getSessionAccess(adapterRef.current, envRef.current);
|
|
1676
1739
|
if (accessRead.current !== read) return;
|
|
1677
1740
|
setAccess(next);
|
|
@@ -1706,7 +1769,7 @@ function FoirProvider({
|
|
|
1706
1769
|
const value = useMemo(
|
|
1707
1770
|
() => ({
|
|
1708
1771
|
status,
|
|
1709
|
-
|
|
1772
|
+
user,
|
|
1710
1773
|
access,
|
|
1711
1774
|
accessStatus,
|
|
1712
1775
|
loadAccess,
|
|
@@ -1714,7 +1777,7 @@ function FoirProvider({
|
|
|
1714
1777
|
env,
|
|
1715
1778
|
adapter: adapterRef.current,
|
|
1716
1779
|
signIn,
|
|
1717
|
-
register,
|
|
1780
|
+
register: register2,
|
|
1718
1781
|
requestOtp,
|
|
1719
1782
|
signInWithOtp,
|
|
1720
1783
|
signInWithProvider,
|
|
@@ -1727,14 +1790,14 @@ function FoirProvider({
|
|
|
1727
1790
|
}),
|
|
1728
1791
|
[
|
|
1729
1792
|
status,
|
|
1730
|
-
|
|
1793
|
+
user,
|
|
1731
1794
|
access,
|
|
1732
1795
|
accessStatus,
|
|
1733
1796
|
loadAccess,
|
|
1734
1797
|
ensureAccess,
|
|
1735
1798
|
env,
|
|
1736
1799
|
signIn,
|
|
1737
|
-
|
|
1800
|
+
register2,
|
|
1738
1801
|
requestOtp,
|
|
1739
1802
|
signInWithOtp,
|
|
1740
1803
|
signInWithProvider,
|
|
@@ -1756,16 +1819,16 @@ function useFoirContext() {
|
|
|
1756
1819
|
return ctx;
|
|
1757
1820
|
}
|
|
1758
1821
|
function useFoirSession() {
|
|
1759
|
-
const { status,
|
|
1822
|
+
const { status, user } = useFoirContext();
|
|
1760
1823
|
return {
|
|
1761
1824
|
status,
|
|
1762
|
-
|
|
1825
|
+
user,
|
|
1763
1826
|
isLoading: status === "loading",
|
|
1764
1827
|
isAuthenticated: status === "authenticated"
|
|
1765
1828
|
};
|
|
1766
1829
|
}
|
|
1767
1830
|
function useFoirUser() {
|
|
1768
|
-
return useFoirContext().
|
|
1831
|
+
return useFoirContext().user;
|
|
1769
1832
|
}
|
|
1770
1833
|
function useFoirAccess() {
|
|
1771
1834
|
const { status, access, accessStatus, loadAccess, ensureAccess } = useFoirContext();
|
|
@@ -1788,7 +1851,7 @@ function useFoirAccess() {
|
|
|
1788
1851
|
function useFoirAuth() {
|
|
1789
1852
|
const {
|
|
1790
1853
|
signIn,
|
|
1791
|
-
register,
|
|
1854
|
+
register: register2,
|
|
1792
1855
|
requestOtp,
|
|
1793
1856
|
signInWithOtp,
|
|
1794
1857
|
signInWithProvider,
|
|
@@ -1801,7 +1864,7 @@ function useFoirAuth() {
|
|
|
1801
1864
|
} = useFoirContext();
|
|
1802
1865
|
return {
|
|
1803
1866
|
signIn,
|
|
1804
|
-
register,
|
|
1867
|
+
register: register2,
|
|
1805
1868
|
requestOtp,
|
|
1806
1869
|
signInWithOtp,
|
|
1807
1870
|
signInWithProvider,
|
|
@@ -1909,8 +1972,8 @@ function SignIn({
|
|
|
1909
1972
|
if (hostedUrl) {
|
|
1910
1973
|
return /* @__PURE__ */ jsx3("div", { className, "data-foir-signin": true, children: /* @__PURE__ */ jsx3("a", { href: hostedUrl, "data-foir-hosted-login": true, style: styled ? submitStyle : void 0, children: "Sign in" }) });
|
|
1911
1974
|
}
|
|
1912
|
-
const finish = (
|
|
1913
|
-
onSignIn?.(
|
|
1975
|
+
const finish = (user) => {
|
|
1976
|
+
onSignIn?.(user);
|
|
1914
1977
|
if (afterSignInUrl && typeof window !== "undefined") {
|
|
1915
1978
|
window.location.assign(afterSignInUrl);
|
|
1916
1979
|
}
|
|
@@ -2186,7 +2249,7 @@ async function uploadFeedbackAttachment(client, screenshot, fetchImpl = globalTh
|
|
|
2186
2249
|
const files = client.ns.files;
|
|
2187
2250
|
if (!files) {
|
|
2188
2251
|
throw new Error(
|
|
2189
|
-
"[foir] this client cannot reach the files namespace, so a screenshot cannot be uploaded. Attaching one needs `files:write` on the key and on the signed-in
|
|
2252
|
+
"[foir] this client cannot reach the files namespace, so a screenshot cannot be uploaded. Attaching one needs `files:write` on the key and on the signed-in user's role."
|
|
2190
2253
|
);
|
|
2191
2254
|
}
|
|
2192
2255
|
const ticket = readUploadTicket(
|
|
@@ -2578,7 +2641,7 @@ function failureCopy(fault) {
|
|
|
2578
2641
|
function reportFault(fault, err, action) {
|
|
2579
2642
|
if (fault.kind === "scope") {
|
|
2580
2643
|
console.error(
|
|
2581
|
-
`[foir] <FoirFeedback>: the API refused with "missing required scope: ${fault.scope}". This is not a bad API key: the key reached the surface. The signed-in
|
|
2644
|
+
`[foir] <FoirFeedback>: the API refused with "missing required scope: ${fault.scope}". This is not a bad API key: the key reached the surface. The signed-in USER's role does not grant ${fault.scope}, and a new project's default user role does not carry the notes scopes. Add it to the role that user holds, or to the project's default user role, and this works with no code change.`,
|
|
2582
2645
|
err
|
|
2583
2646
|
);
|
|
2584
2647
|
return;
|
package/dist/realtime/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* provider's url params current for whenever it next reconnects.
|
|
12
12
|
*
|
|
13
13
|
* Agnostic by design: token-agnostic (caller supplies `getToken` — an internal
|
|
14
|
-
* mint for
|
|
14
|
+
* mint for the console, a user access token for an app), persistence-agnostic
|
|
15
15
|
* (never persists), doc-schema-agnostic (caller owns the `Y.Doc` + shared types).
|
|
16
16
|
*
|
|
17
17
|
* `yjs` + `y-websocket` are OPTIONAL peer deps gated to this subpath, so a
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* pushes a DLQ entry.
|
|
13
13
|
* - `progress({pct, message, metadata})` — non-terminal update; the
|
|
14
14
|
* monotonic guard at the platform DB layer drops out-of-order packets.
|
|
15
|
-
* - `cancel()
|
|
16
|
-
*
|
|
15
|
+
* - `cancel()`: the extension cancels itself (rare; a run is normally
|
|
16
|
+
* cancelled from the console).
|
|
17
17
|
*
|
|
18
18
|
* Each call carries:
|
|
19
19
|
* - `Authorization: Bearer <callback.token>` — Ed25519 scoped token.
|
|
@@ -42,7 +42,7 @@ export interface CallbackResult {
|
|
|
42
42
|
*/
|
|
43
43
|
status: string;
|
|
44
44
|
/**
|
|
45
|
-
* True when
|
|
45
|
+
* True when someone cancelled the execution while the extension was
|
|
46
46
|
* processing — the platform refused to overwrite the cancelled state.
|
|
47
47
|
* Extensions should stop work and (optionally) call `fail('CANCELLED')`.
|
|
48
48
|
*/
|
|
@@ -66,7 +66,7 @@ export interface CallbackClient<TResult = unknown> {
|
|
|
66
66
|
details?: Record<string, unknown>;
|
|
67
67
|
}): Promise<CallbackResult>;
|
|
68
68
|
/**
|
|
69
|
-
* Returns `{cancelled: true}` when
|
|
69
|
+
* Returns `{cancelled: true}` when someone has cancelled it: the extension
|
|
70
70
|
* must stop work. `applied: false` means the monotonic guard dropped
|
|
71
71
|
* the update because pct was lower than the persisted value.
|
|
72
72
|
*/
|