@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/next.js
CHANGED
|
@@ -20,6 +20,26 @@ function createFoirCookieAdapter(store, opts = {}) {
|
|
|
20
20
|
// src/graphql/foir-request.ts
|
|
21
21
|
import { print as print2 } from "graphql";
|
|
22
22
|
|
|
23
|
+
// src/transport/key-kind.ts
|
|
24
|
+
var KEY_PREFIXES = [
|
|
25
|
+
["foir_pub_", "publishable"],
|
|
26
|
+
["foir_srv_", "server"],
|
|
27
|
+
["foir_adm_", "admin"],
|
|
28
|
+
["pk_", "publishable"],
|
|
29
|
+
["sk_", "older"],
|
|
30
|
+
["tc_", "older"]
|
|
31
|
+
];
|
|
32
|
+
function foirKeyKind(apiKey) {
|
|
33
|
+
if (typeof apiKey !== "string") return void 0;
|
|
34
|
+
for (const [prefix, kind] of KEY_PREFIXES) {
|
|
35
|
+
if (apiKey.startsWith(prefix)) return kind;
|
|
36
|
+
}
|
|
37
|
+
return void 0;
|
|
38
|
+
}
|
|
39
|
+
function isPublishableFoirKey(apiKey) {
|
|
40
|
+
return foirKeyKind(apiKey) === "publishable";
|
|
41
|
+
}
|
|
42
|
+
|
|
23
43
|
// src/graphql/cdn-route.ts
|
|
24
44
|
import { print } from "graphql";
|
|
25
45
|
|
|
@@ -123,6 +143,28 @@ function refuseCredentialHeaders(extra) {
|
|
|
123
143
|
}
|
|
124
144
|
}
|
|
125
145
|
var FOIR_VISITOR_IP_HEADER = "X-Foir-Visitor-IP";
|
|
146
|
+
function sentCoordinate(value) {
|
|
147
|
+
return Math.round(value * 100) / 100;
|
|
148
|
+
}
|
|
149
|
+
async function resolveFoirVisitor(source) {
|
|
150
|
+
const value = typeof source === "function" ? await source() : source;
|
|
151
|
+
if (value === null || value === void 0) return { place: {} };
|
|
152
|
+
if (typeof value === "string") {
|
|
153
|
+
const address2 = value.trim();
|
|
154
|
+
return { address: address2 || void 0, place: {} };
|
|
155
|
+
}
|
|
156
|
+
const address = value.address?.trim();
|
|
157
|
+
const country = value.country?.trim();
|
|
158
|
+
const place = {};
|
|
159
|
+
if (country) place.country = country;
|
|
160
|
+
if (Number.isFinite(value.latitude) && Number.isFinite(value.longitude)) {
|
|
161
|
+
place.location = {
|
|
162
|
+
latitude: sentCoordinate(value.latitude),
|
|
163
|
+
longitude: sentCoordinate(value.longitude)
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
return { address: address || void 0, place };
|
|
167
|
+
}
|
|
126
168
|
function buildFoirHeaders(input) {
|
|
127
169
|
const headers = {
|
|
128
170
|
"Content-Type": "application/json"
|
|
@@ -169,8 +211,8 @@ var FoirAuthDowngradedError = class extends FoirRequestError {
|
|
|
169
211
|
downgrade;
|
|
170
212
|
constructor(downgrade) {
|
|
171
213
|
super(
|
|
172
|
-
`[foir] your
|
|
173
|
-
{ graphQLErrors: [{ message: "
|
|
214
|
+
`[foir] your user token was ${downgrade.userToken} (${downgrade.reason}), so the request was served as ${downgrade.servedAs}`,
|
|
215
|
+
{ graphQLErrors: [{ message: "user token rejected", extensions: { code: "AUTH_DOWNGRADED" } }] }
|
|
174
216
|
);
|
|
175
217
|
this.name = "FoirAuthDowngradedError";
|
|
176
218
|
this.downgrade = downgrade;
|
|
@@ -178,7 +220,7 @@ var FoirAuthDowngradedError = class extends FoirRequestError {
|
|
|
178
220
|
};
|
|
179
221
|
var FoirSessionExpiredError = class extends FoirRequestError {
|
|
180
222
|
constructor(options) {
|
|
181
|
-
super("[foir] the
|
|
223
|
+
super("[foir] the user session has expired and could not be refreshed: sign in again", {
|
|
182
224
|
cause: options?.cause,
|
|
183
225
|
graphQLErrors: [{ message: "session expired", extensions: { code: "SESSION_EXPIRED" } }]
|
|
184
226
|
});
|
|
@@ -195,7 +237,7 @@ function reportAuthDowngrade(envelope) {
|
|
|
195
237
|
if (warnedDowngrade === downgrade.reason) return;
|
|
196
238
|
warnedDowngrade = downgrade.reason;
|
|
197
239
|
console.warn(
|
|
198
|
-
`[foir] your
|
|
240
|
+
`[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.")
|
|
199
241
|
);
|
|
200
242
|
}
|
|
201
243
|
function refusePrunedDocument(json, options) {
|
|
@@ -739,10 +781,7 @@ function foirDataEndpoint(apiUrl, path) {
|
|
|
739
781
|
return foirApiEndpoint(apiUrl, path);
|
|
740
782
|
}
|
|
741
783
|
async function resolveVisitorAddress(config) {
|
|
742
|
-
|
|
743
|
-
const value = typeof source === "function" ? await source() : source;
|
|
744
|
-
const address = value?.trim();
|
|
745
|
-
return address ? address : void 0;
|
|
784
|
+
return (await resolveFoirVisitor(config.visitor)).address;
|
|
746
785
|
}
|
|
747
786
|
async function resolveBearerToken(config) {
|
|
748
787
|
const source = config.bearerToken;
|
|
@@ -783,7 +822,7 @@ async function resolveFoirAuth(config) {
|
|
|
783
822
|
};
|
|
784
823
|
}
|
|
785
824
|
function foirRoute(input) {
|
|
786
|
-
if (!input.apiKey
|
|
825
|
+
if (!isPublishableFoirKey(input.apiKey)) return { lane: "origin", reason: "key" };
|
|
787
826
|
if (input.bearerToken) return { lane: "origin", reason: "bearer" };
|
|
788
827
|
if (input.orgId || input.orgUrl) return { lane: "origin", reason: "org" };
|
|
789
828
|
if (input.mutation) return { lane: "origin", reason: "operation" };
|
|
@@ -917,42 +956,63 @@ var AUTH_OPS = {
|
|
|
917
956
|
authConfig: "authConfig",
|
|
918
957
|
authProviders: "authProviders",
|
|
919
958
|
defaultAuthProvider: "defaultAuthProvider",
|
|
920
|
-
|
|
959
|
+
signupContext: "signupContext",
|
|
921
960
|
// Post-credential self reads.
|
|
922
961
|
currentUser: "currentUser",
|
|
923
962
|
myProviderToken: "myProviderToken",
|
|
924
|
-
// What the calling credential may do, and for a signed-in
|
|
963
|
+
// What the calling credential may do, and for a signed-in user the roles
|
|
925
964
|
// they hold. `./access` reads it.
|
|
926
965
|
whoami: "whoami",
|
|
927
966
|
// Methods.
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
967
|
+
login: "login",
|
|
968
|
+
register: "register",
|
|
969
|
+
requestOTP: "requestOTP",
|
|
970
|
+
loginWithOTP: "loginWithOTP",
|
|
932
971
|
// Session lifecycle.
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
// Password management and account lifecycle
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
972
|
+
refreshToken: "refreshToken",
|
|
973
|
+
logout: "logout",
|
|
974
|
+
joinWaitlist: "joinWaitlist",
|
|
975
|
+
// Password management and account lifecycle.
|
|
976
|
+
requestPasswordReset: "requestPasswordReset",
|
|
977
|
+
resetPassword: "resetPassword",
|
|
978
|
+
updatePassword: "updatePassword",
|
|
979
|
+
verifyEmail: "verifyEmail",
|
|
941
980
|
deleteMyAccount: "deleteMyAccount",
|
|
942
|
-
// The
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
//
|
|
947
|
-
//
|
|
981
|
+
// The user's own profile, and the apps they have agreed may act for them.
|
|
982
|
+
updateMyProfile: "updateMyProfile",
|
|
983
|
+
myAuthorizedApps: "myAuthorizedApps",
|
|
984
|
+
revokeAuthorizedApp: "revokeAuthorizedApp",
|
|
985
|
+
// Where they are signed in, their personal access tokens, and the
|
|
986
|
+
// providers they sign in with.
|
|
987
|
+
mySessions: "mySessions",
|
|
988
|
+
endMySession: "endMySession",
|
|
989
|
+
signOutEverywhere: "signOutEverywhere",
|
|
990
|
+
createPersonalAccessToken: "createPersonalAccessToken",
|
|
991
|
+
mySignInMethods: "mySignInMethods",
|
|
992
|
+
connectSignInMethod: "connectSignInMethod",
|
|
993
|
+
disconnectSignInMethod: "disconnectSignInMethod",
|
|
994
|
+
// Their second factors. A change needs one proven first, by a step-up.
|
|
995
|
+
myFactors: "myFactors",
|
|
996
|
+
beginStepUp: "beginStepUp",
|
|
997
|
+
completeStepUp: "completeStepUp",
|
|
998
|
+
beginFactorEnrollment: "beginFactorEnrollment",
|
|
999
|
+
completeFactorEnrollment: "completeFactorEnrollment",
|
|
1000
|
+
removeFactor: "removeFactor",
|
|
1001
|
+
makeRecoveryCodes: "makeRecoveryCodes",
|
|
1002
|
+
// The organizations the caller may stand in. Nothing selects one: a request
|
|
1003
|
+
// names the organization it acts in (`orgId` or `orgUrl` below), and the
|
|
1004
|
+
// platform checks the caller stands there every time.
|
|
948
1005
|
myOrganizations: "myOrganizations",
|
|
949
|
-
selectMyOrganization: "selectMyOrganization",
|
|
950
1006
|
// Founding one. Present in a manifest only when the project opted in.
|
|
951
|
-
|
|
1007
|
+
registerOrganization: "registerOrganization",
|
|
952
1008
|
// External identity providers.
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
1009
|
+
loginWithProvider: "loginWithProvider",
|
|
1010
|
+
providerCallback: "providerCallback",
|
|
1011
|
+
providerVerifyOTP: "providerVerifyOTP",
|
|
1012
|
+
// A provider sign-in that names an address somebody already holds, waiting
|
|
1013
|
+
// for the code sent to it.
|
|
1014
|
+
confirmProviderLink: "confirmProviderLink",
|
|
1015
|
+
resendProviderLinkCode: "resendProviderLinkCode"
|
|
956
1016
|
};
|
|
957
1017
|
function authClientConfig(env, options) {
|
|
958
1018
|
const apiKey = resolveFoirApiKey(env);
|
|
@@ -1015,58 +1075,58 @@ function stripUndefined(args) {
|
|
|
1015
1075
|
// src/auth/auth.ts
|
|
1016
1076
|
var TOKEN_KEY = "foir:token";
|
|
1017
1077
|
var REFRESH_KEY = "foir:refreshToken";
|
|
1018
|
-
var
|
|
1078
|
+
var USER_ID_KEY = "foir:userId";
|
|
1019
1079
|
var EMAIL_KEY = "foir:email";
|
|
1020
|
-
async function
|
|
1021
|
-
return authOp(env, AUTH_OPS.
|
|
1080
|
+
async function login(env, vars) {
|
|
1081
|
+
return authOp(env, AUTH_OPS.login, vars);
|
|
1022
1082
|
}
|
|
1023
|
-
async function
|
|
1024
|
-
return authOp(env, AUTH_OPS.
|
|
1083
|
+
async function register(env, vars) {
|
|
1084
|
+
return authOp(env, AUTH_OPS.register, vars);
|
|
1025
1085
|
}
|
|
1026
|
-
async function
|
|
1027
|
-
return authOp(env, AUTH_OPS.
|
|
1086
|
+
async function loginWithOTP(env, vars) {
|
|
1087
|
+
return authOp(env, AUTH_OPS.loginWithOTP, vars);
|
|
1028
1088
|
}
|
|
1029
|
-
async function
|
|
1030
|
-
return authOp(env, AUTH_OPS.
|
|
1089
|
+
async function providerCallback(env, input) {
|
|
1090
|
+
return authOp(env, AUTH_OPS.providerCallback, { input });
|
|
1031
1091
|
}
|
|
1032
1092
|
async function saveSession(adapter, result) {
|
|
1033
1093
|
await adapter.set(TOKEN_KEY, result.token);
|
|
1034
1094
|
await adapter.set(REFRESH_KEY, result.refreshToken);
|
|
1035
|
-
if (!result.
|
|
1036
|
-
return await
|
|
1095
|
+
if (!result.userId) {
|
|
1096
|
+
return await getSessionUser(adapter) ?? { id: "", email: "" };
|
|
1037
1097
|
}
|
|
1038
|
-
await adapter.set(
|
|
1098
|
+
await adapter.set(USER_ID_KEY, result.userId);
|
|
1039
1099
|
await adapter.set(EMAIL_KEY, result.email);
|
|
1040
|
-
return { id: result.
|
|
1100
|
+
return { id: result.userId, email: result.email };
|
|
1041
1101
|
}
|
|
1042
1102
|
async function clearSession(adapter) {
|
|
1043
1103
|
await adapter.unset(TOKEN_KEY);
|
|
1044
1104
|
await adapter.unset(REFRESH_KEY);
|
|
1045
|
-
await adapter.unset(
|
|
1105
|
+
await adapter.unset(USER_ID_KEY);
|
|
1046
1106
|
await adapter.unset(EMAIL_KEY);
|
|
1047
1107
|
}
|
|
1048
1108
|
async function getSessionToken(adapter) {
|
|
1049
1109
|
return await adapter.get(TOKEN_KEY) ?? null;
|
|
1050
1110
|
}
|
|
1051
|
-
async function
|
|
1052
|
-
const id = await adapter.get(
|
|
1111
|
+
async function getSessionUser(adapter) {
|
|
1112
|
+
const id = await adapter.get(USER_ID_KEY) ?? null;
|
|
1053
1113
|
const email = await adapter.get(EMAIL_KEY) ?? null;
|
|
1054
1114
|
if (!id) return null;
|
|
1055
1115
|
return { id, email: email ?? "" };
|
|
1056
1116
|
}
|
|
1057
|
-
async function
|
|
1058
|
-
return saveSession(adapter, await
|
|
1117
|
+
async function signInWithPassword(adapter, env, vars) {
|
|
1118
|
+
return saveSession(adapter, await login(env, vars));
|
|
1059
1119
|
}
|
|
1060
|
-
async function
|
|
1061
|
-
return saveSession(adapter, await
|
|
1120
|
+
async function signUpWithPassword(adapter, env, vars) {
|
|
1121
|
+
return saveSession(adapter, await register(env, vars));
|
|
1062
1122
|
}
|
|
1063
|
-
async function
|
|
1064
|
-
return saveSession(adapter, await
|
|
1123
|
+
async function signInWithOTP(adapter, env, vars) {
|
|
1124
|
+
return saveSession(adapter, await loginWithOTP(env, vars));
|
|
1065
1125
|
}
|
|
1066
1126
|
|
|
1067
1127
|
// src/next/session.ts
|
|
1068
1128
|
async function getFoirSession(store) {
|
|
1069
|
-
return
|
|
1129
|
+
return getSessionUser(createFoirCookieAdapter(store));
|
|
1070
1130
|
}
|
|
1071
1131
|
async function getFoirToken(store) {
|
|
1072
1132
|
return getSessionToken(createFoirCookieAdapter(store));
|
|
@@ -1081,7 +1141,7 @@ function foirSignInWithPassword({
|
|
|
1081
1141
|
password,
|
|
1082
1142
|
cookieOptions
|
|
1083
1143
|
}) {
|
|
1084
|
-
return
|
|
1144
|
+
return signInWithPassword(createFoirCookieAdapter(store, { cookieOptions }), env, {
|
|
1085
1145
|
email,
|
|
1086
1146
|
password
|
|
1087
1147
|
});
|
|
@@ -1093,7 +1153,7 @@ function foirRegisterWithPassword({
|
|
|
1093
1153
|
password,
|
|
1094
1154
|
cookieOptions
|
|
1095
1155
|
}) {
|
|
1096
|
-
return
|
|
1156
|
+
return signUpWithPassword(createFoirCookieAdapter(store, { cookieOptions }), env, {
|
|
1097
1157
|
email,
|
|
1098
1158
|
password
|
|
1099
1159
|
});
|
|
@@ -1105,13 +1165,13 @@ function foirSignInWithOtp({
|
|
|
1105
1165
|
otp,
|
|
1106
1166
|
cookieOptions
|
|
1107
1167
|
}) {
|
|
1108
|
-
return
|
|
1168
|
+
return signInWithOTP(createFoirCookieAdapter(store, { cookieOptions }), env, {
|
|
1109
1169
|
email,
|
|
1110
1170
|
otp
|
|
1111
1171
|
});
|
|
1112
1172
|
}
|
|
1113
1173
|
async function completeFoirProviderLogin(opts) {
|
|
1114
|
-
const result = await
|
|
1174
|
+
const result = await providerCallback(opts.env, {
|
|
1115
1175
|
providerKey: opts.providerKey,
|
|
1116
1176
|
code: opts.code,
|
|
1117
1177
|
state: opts.state
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `@foir/sdk/placement`: your app's own
|
|
2
|
+
* `@foir/sdk/placement`: your app's own console, shown inside Foir's.
|
|
3
3
|
*
|
|
4
4
|
* A full-page placement is loaded in a frame on the Foir console. No cookie
|
|
5
5
|
* reaches a cross-site frame, so the console hands the page a short-lived token
|
|
6
6
|
* instead, by message, to your placement's origin only. The token is the
|
|
7
|
-
*
|
|
8
|
-
* is installed at, carrying what your placement declares and this
|
|
7
|
+
* person looking at the page, acting through your app at the level your app
|
|
8
|
+
* is installed at, carrying what your placement declares and this person
|
|
9
9
|
* holds, as grants in the permission catalogue's names. Your page runs no
|
|
10
10
|
* login of its own.
|
|
11
11
|
*
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* ```
|
|
22
22
|
*
|
|
23
23
|
* The console also tells the page how it looks, so the page can match it: pass
|
|
24
|
-
* `onAppearance` and apply the theme it names.
|
|
24
|
+
* `onAppearance` and apply the theme it names. Their console theme is
|
|
25
25
|
* their own setting and need not be their system's.
|
|
26
26
|
*
|
|
27
27
|
* The browser shows the page blank, however well it is authenticated, unless
|
|
@@ -40,7 +40,7 @@ export declare const PLACEMENT_MESSAGE: {
|
|
|
40
40
|
readonly refresh: "foir:placement:refresh";
|
|
41
41
|
/** Console to page: no token, and why. */
|
|
42
42
|
readonly refused: "foir:placement:refused";
|
|
43
|
-
/** Console to page: the
|
|
43
|
+
/** Console to page: the person signed out. */
|
|
44
44
|
readonly signedOut: "foir:placement:signed-out";
|
|
45
45
|
/** Console to page: how the console looks, now and whenever it changes. */
|
|
46
46
|
readonly appearance: "foir:placement:appearance";
|
|
@@ -55,7 +55,7 @@ export interface PlacementGrant {
|
|
|
55
55
|
orgId: string;
|
|
56
56
|
/**
|
|
57
57
|
* What the token carries: the grants your placement declares that this
|
|
58
|
-
*
|
|
58
|
+
* person holds, in the permission catalogue's names. A grant the person
|
|
59
59
|
* holds part of is carried as that part, so ask with `can` and not by
|
|
60
60
|
* looking for a spelling here.
|
|
61
61
|
*/
|
|
@@ -65,30 +65,13 @@ export interface PlacementGrant {
|
|
|
65
65
|
grant: string;
|
|
66
66
|
reason: string;
|
|
67
67
|
}[];
|
|
68
|
-
/**
|
|
69
|
-
* @deprecated Read `grants`. The same list under the name it had before a
|
|
70
|
-
* placement declared grants.
|
|
71
|
-
*/
|
|
72
|
-
grantedScopes: string[];
|
|
73
|
-
/**
|
|
74
|
-
* @deprecated Read `withheldGrants`. The same list under the name it had
|
|
75
|
-
* before a placement declared grants.
|
|
76
|
-
*/
|
|
77
|
-
withheld: {
|
|
78
|
-
scope: string;
|
|
79
|
-
reason: string;
|
|
80
|
-
}[];
|
|
81
68
|
}
|
|
82
|
-
/**
|
|
83
|
-
|
|
84
|
-
* only `grantedScopes` and `withheld`, and the session reads `grants` from
|
|
85
|
-
* those.
|
|
86
|
-
*/
|
|
87
|
-
type PlacementGrantMessage = Pick<PlacementGrant, 'accessToken' | 'expiresAt' | 'orgId'> & Partial<Pick<PlacementGrant, 'grants' | 'withheldGrants' | 'grantedScopes' | 'withheld'>>;
|
|
69
|
+
/** What a console sends with a token. A list it does not send is an empty one. */
|
|
70
|
+
type PlacementGrantMessage = Pick<PlacementGrant, 'accessToken' | 'expiresAt' | 'orgId'> & Partial<Pick<PlacementGrant, 'grants' | 'withheldGrants'>>;
|
|
88
71
|
/** How the console looks, for an embedded page to match. */
|
|
89
72
|
export interface PlacementAppearance {
|
|
90
73
|
/**
|
|
91
|
-
* The console's theme as the
|
|
74
|
+
* The console's theme as the person sees it: their own choice, or their
|
|
92
75
|
* system's when they left it to the system.
|
|
93
76
|
*/
|
|
94
77
|
theme: 'light' | 'dark';
|
|
@@ -114,7 +97,7 @@ export type PlacementPageMessage = {
|
|
|
114
97
|
};
|
|
115
98
|
/**
|
|
116
99
|
* The Foir consoles, per environment. A project that serves the console on its
|
|
117
|
-
* own verified
|
|
100
|
+
* own verified console domain adds that origin too: the install preview lists it.
|
|
118
101
|
*/
|
|
119
102
|
export declare const FOIR_CONSOLE_ORIGINS: {
|
|
120
103
|
readonly production: readonly ["https://app.foir.io"];
|
|
@@ -144,7 +127,7 @@ export interface ConnectPlacementOptions {
|
|
|
144
127
|
refreshBeforeMs?: number;
|
|
145
128
|
/**
|
|
146
129
|
* Called with how the console looks: once as soon as the console says, which
|
|
147
|
-
* is before the token arrives, and again whenever the
|
|
130
|
+
* is before the token arrives, and again whenever the person changes it.
|
|
148
131
|
* Apply it instead of the system's preference, so the page matches the
|
|
149
132
|
* console it is shown in.
|
|
150
133
|
*/
|
|
@@ -160,22 +143,22 @@ export interface PlacementSession {
|
|
|
160
143
|
readonly grant: PlacementGrant;
|
|
161
144
|
/**
|
|
162
145
|
* How the console looks now, or null when the console has not said (a console
|
|
163
|
-
* older than this). Kept current as the
|
|
146
|
+
* older than this). Kept current as the person changes it.
|
|
164
147
|
*/
|
|
165
148
|
readonly appearance: PlacementAppearance | null;
|
|
166
149
|
/**
|
|
167
|
-
* Whether the token covers this grant, to hide or grey out what the
|
|
150
|
+
* Whether the token covers this grant, to hide or grey out what the person
|
|
168
151
|
* cannot do. It answers as Foir's API would: a grant for every model covers
|
|
169
152
|
* the same grant for one, and one for every owner's rows covers a person's
|
|
170
153
|
* own. `can('records:read.all:order')` is true for a token that carries
|
|
171
154
|
* `records:read.all`.
|
|
172
155
|
*/
|
|
173
156
|
can(grant: string): boolean;
|
|
174
|
-
/** A GraphQL request to Foir's API as the
|
|
157
|
+
/** A GraphQL request to Foir's API as the person, through your app. */
|
|
175
158
|
request<TResult = unknown>(document: string | {
|
|
176
159
|
toString(): string;
|
|
177
160
|
}, variables?: Record<string, unknown>): Promise<TResult>;
|
|
178
|
-
/** Called when the
|
|
161
|
+
/** Called when the person signs out; the session holds no token from then on. */
|
|
179
162
|
onSignedOut(listener: () => void): () => void;
|
|
180
163
|
/** Stop listening and refreshing. */
|
|
181
164
|
close(): void;
|
package/dist/placement.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/auth/grant-table.generated.ts
|
|
2
2
|
var GRANT_TABLE = [
|
|
3
|
-
["records:read", true, ["own", "
|
|
3
|
+
["records:read", true, ["own", "all"], []],
|
|
4
4
|
["records:create", true, [], []],
|
|
5
|
-
["records:write", true, ["own", "
|
|
6
|
-
["records:delete", true, ["own", "
|
|
5
|
+
["records:write", true, ["own", "all"], []],
|
|
6
|
+
["records:delete", true, ["own", "all"], []],
|
|
7
7
|
["records:publish", true, ["own", "all"], []],
|
|
8
8
|
["records:attribute", true, [], []],
|
|
9
9
|
["search:read", true, [], []],
|
|
@@ -19,8 +19,8 @@ var GRANT_TABLE = [
|
|
|
19
19
|
["notifications:consent", false, [], []],
|
|
20
20
|
["pushcredentials:read", false, [], []],
|
|
21
21
|
["pushcredentials:write", false, [], []],
|
|
22
|
-
["notes:read", false, [], []],
|
|
23
|
-
["notes:write", false, [], []],
|
|
22
|
+
["notes:read", false, ["own", "all"], []],
|
|
23
|
+
["notes:write", false, ["own", "all"], []],
|
|
24
24
|
["notes:create", false, [], []],
|
|
25
25
|
["approvals:read", false, [], []],
|
|
26
26
|
["approvals:write", false, [], []],
|
|
@@ -77,7 +77,8 @@ var GRANT_TABLE = [
|
|
|
77
77
|
["roles:write", false, [], []],
|
|
78
78
|
["projects:read", false, [], []],
|
|
79
79
|
["projects:write", false, [], []],
|
|
80
|
-
["project:
|
|
80
|
+
["project:stand", false, [], []],
|
|
81
|
+
["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"]],
|
|
81
82
|
["projects:create", false, [], []],
|
|
82
83
|
["projects:delete", false, [], []],
|
|
83
84
|
["tenants:read", false, [], []],
|
|
@@ -90,12 +91,14 @@ var GRANT_TABLE = [
|
|
|
90
91
|
["usage:read", false, [], []],
|
|
91
92
|
["controlkeys:read", false, [], []],
|
|
92
93
|
["controlkeys:write", false, [], []],
|
|
94
|
+
["tenantsigninproviders:read", false, [], []],
|
|
95
|
+
["tenantsigninproviders:write", false, [], []],
|
|
93
96
|
["tenanthooks:read", false, [], []],
|
|
94
97
|
["tenanthooks:write", false, [], []],
|
|
95
98
|
["apps:install", false, [], []],
|
|
96
|
-
["tenant:manage", false, [], ["project:manage", "projects:create", "projects:delete", "tenants:read", "tenants:write", "tenants:delete", "
|
|
99
|
+
["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"]],
|
|
97
100
|
["platform:read", false, [], []],
|
|
98
|
-
["platform:manage", false, [], ["tenant:manage", "platform:read", "platform:create_workspace"
|
|
101
|
+
["platform:manage", false, [], ["tenant:manage", "platform:read", "platform:create_workspace"]],
|
|
99
102
|
["platform:create_workspace", false, [], []],
|
|
100
103
|
["users:read", false, ["own", "named", "all"], []],
|
|
101
104
|
["users:write", false, ["own", "all"], []],
|
|
@@ -202,7 +205,7 @@ var PLACEMENT_MESSAGE = {
|
|
|
202
205
|
refresh: "foir:placement:refresh",
|
|
203
206
|
/** Console to page: no token, and why. */
|
|
204
207
|
refused: "foir:placement:refused",
|
|
205
|
-
/** Console to page: the
|
|
208
|
+
/** Console to page: the person signed out. */
|
|
206
209
|
signedOut: "foir:placement:signed-out",
|
|
207
210
|
/** Console to page: how the console looks, now and whenever it changes. */
|
|
208
211
|
appearance: "foir:placement:appearance"
|
|
@@ -210,27 +213,22 @@ var PLACEMENT_MESSAGE = {
|
|
|
210
213
|
function strings(value) {
|
|
211
214
|
return Array.isArray(value) ? value.filter((v) => typeof v === "string") : null;
|
|
212
215
|
}
|
|
213
|
-
function reasons(value
|
|
216
|
+
function reasons(value) {
|
|
214
217
|
if (!Array.isArray(value)) return null;
|
|
215
218
|
const out = [];
|
|
216
219
|
for (const entry of value) {
|
|
217
220
|
const e = entry;
|
|
218
|
-
if (typeof e?.
|
|
221
|
+
if (typeof e?.grant === "string") out.push({ grant: e.grant, reason: typeof e.reason === "string" ? e.reason : "" });
|
|
219
222
|
}
|
|
220
223
|
return out;
|
|
221
224
|
}
|
|
222
225
|
function grantFrom(sent) {
|
|
223
|
-
const grants = strings(sent.grants) ?? strings(sent.grantedScopes) ?? [];
|
|
224
|
-
const withheld = reasons(sent.withheldGrants, "grant") ?? reasons(sent.withheld, "scope") ?? [];
|
|
225
|
-
const before = reasons(sent.withheld, "scope") ?? withheld;
|
|
226
226
|
return {
|
|
227
227
|
accessToken: sent.accessToken,
|
|
228
228
|
expiresAt: sent.expiresAt,
|
|
229
229
|
orgId: sent.orgId,
|
|
230
|
-
grants,
|
|
231
|
-
withheldGrants:
|
|
232
|
-
grantedScopes: strings(sent.grantedScopes) ?? grants,
|
|
233
|
-
withheld: before.map((w) => ({ scope: w.name, reason: w.reason }))
|
|
230
|
+
grants: strings(sent.grants) ?? [],
|
|
231
|
+
withheldGrants: reasons(sent.withheldGrants) ?? []
|
|
234
232
|
};
|
|
235
233
|
}
|
|
236
234
|
var FOIR_CONSOLE_ORIGINS = {
|
|
@@ -301,7 +299,7 @@ function connectPlacement(options) {
|
|
|
301
299
|
};
|
|
302
300
|
const session = {
|
|
303
301
|
get grant() {
|
|
304
|
-
if (!grant) throw new PlacementRefused("the
|
|
302
|
+
if (!grant) throw new PlacementRefused("the person using the console has signed out");
|
|
305
303
|
return grant;
|
|
306
304
|
},
|
|
307
305
|
get appearance() {
|
|
@@ -311,7 +309,7 @@ function connectPlacement(options) {
|
|
|
311
309
|
// names a placement declared then; and by what the list covers.
|
|
312
310
|
can: (asked) => grant ? grant.grants.includes(asked) || holds(grant.grants, asked) : false,
|
|
313
311
|
async request(document, variables) {
|
|
314
|
-
if (!grant) throw new PlacementRefused("the
|
|
312
|
+
if (!grant) throw new PlacementRefused("the person using the console has signed out");
|
|
315
313
|
const res = await doFetch(`${apiUrl}/graphql`, {
|
|
316
314
|
method: "POST",
|
|
317
315
|
headers: { "content-type": "application/json", authorization: `Bearer ${grant.accessToken}` },
|