@foir/sdk 0.24.0 → 0.26.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 +67 -0
- package/dist/app/client.d.ts +52 -4
- package/dist/app/index.d.ts +1 -1
- package/dist/app.js +37 -3
- package/dist/auth/auth.d.ts +60 -4
- package/dist/auth/hosted.d.ts +14 -3
- package/dist/auth/index.d.ts +3 -3
- package/dist/auth/operations.d.ts +55 -0
- package/dist/auth/ops.d.ts +8 -0
- package/dist/auth/session-client.d.ts +7 -2
- package/dist/auth-session.js +2 -1
- package/dist/auth.js +52 -3
- package/dist/next.js +11 -0
- package/dist/placement/index.d.ts +17 -0
- package/dist/placement.js +16 -3
- package/dist/push.js +11 -0
- package/dist/react/FoirProvider.d.ts +5 -4
- package/dist/react.js +17 -3
- package/dist/server.js +3 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -108,6 +108,73 @@ nobody asking for `@foir/sdk` gets it early. The push that carries the commit to
|
|
|
108
108
|
|
|
109
109
|
Changes on `dev` that have not been published to npm.
|
|
110
110
|
|
|
111
|
+
## [0.26.0] — 2026-10-19
|
|
112
|
+
|
|
113
|
+
### Changed
|
|
114
|
+
|
|
115
|
+
- **`session.request` in `@foir/sdk/placement` throws `PlacementRequestFailed`.**
|
|
116
|
+
Its message says each distinct refusal once, where it repeated a read
|
|
117
|
+
refused on a field once per row, and its `errors` keeps every error with its
|
|
118
|
+
`path`. It is still an `Error`, so a caller that reads `message` is
|
|
119
|
+
unaffected.
|
|
120
|
+
|
|
121
|
+
### Added
|
|
122
|
+
|
|
123
|
+
- **A hosted sign-in can name the organization it is for** (`@foir/sdk/auth`,
|
|
124
|
+
`@foir/sdk/react`): `hostedLoginUrl(config, returnTo, { org })`, a session
|
|
125
|
+
client's `signInUrl(returnTo, { org })` and the provider's
|
|
126
|
+
`hostedSignInUrl(returnTo, { org })`. Use it when one auth client serves an
|
|
127
|
+
address per organization. The login domain's front door passes it on, and
|
|
128
|
+
the sign-in takes that organization's branding, providers and return
|
|
129
|
+
addresses. The platform refuses an organization other than the one the
|
|
130
|
+
domain or app is bound to, one of another project, and one that is not
|
|
131
|
+
active.
|
|
132
|
+
- **`HostedLoginOptions`** (`@foir/sdk/auth`, type).
|
|
133
|
+
|
|
134
|
+
## [0.25.0] — 2026-10-18
|
|
135
|
+
|
|
136
|
+
### Changed
|
|
137
|
+
|
|
138
|
+
- **`deleteMyAccount` for an account with no password needs a recent sign-in**
|
|
139
|
+
(`@foir/sdk/auth`). `confirm` alone used to be enough. Now it's refused
|
|
140
|
+
unless the user signed in within the last ten minutes, so a stolen session
|
|
141
|
+
can't delete an account. Migration: when it's refused, send the user through
|
|
142
|
+
sign-in again, then call it once more.
|
|
143
|
+
|
|
144
|
+
### Added
|
|
145
|
+
|
|
146
|
+
- **`organizations.pool`, `organizations.startPool` and `organizations.dropPool`**
|
|
147
|
+
(`@foir/sdk/app`). In an operation, read which pool the people of the
|
|
148
|
+
organisation the run acts in sign in to, start its own pool, or drop it.
|
|
149
|
+
Reading needs `organizations:read`, and the other two `organizations:write`.
|
|
150
|
+
Starting one is refused while anybody of the pool it signs in to now stands
|
|
151
|
+
in it or beneath it, and dropping one while it holds anybody; each refusal
|
|
152
|
+
says how many. Each answers an `AppOrganizationPool`.
|
|
153
|
+
- **`AppOrganizationPool`** (`@foir/sdk/app`, type).
|
|
154
|
+
- **`organizations.provision` makes its first member by address**
|
|
155
|
+
(`@foir/sdk/app`): `firstMember: { email, firstName?, lastName? }`, made in
|
|
156
|
+
the pool that serves the new organisation (its own with `ownPool`). It also
|
|
157
|
+
needs `users:create`.
|
|
158
|
+
- **`leaveMembership`** (`@foir/sdk/auth`). The signed-in person ends their own
|
|
159
|
+
membership where the request stands: the organisation `orgId` names, or the
|
|
160
|
+
project's own. It asks no permission. It's refused where it would leave an
|
|
161
|
+
organisation with nobody who can staff it, or leave them nowhere to sign in,
|
|
162
|
+
since a project's people sign in only where they stand.
|
|
163
|
+
- **`myAddresses`, `addMyAddress`, `proveMyAddress`, `makeMyAddressPrimary`
|
|
164
|
+
and `removeMyAddress`** (`@foir/sdk/auth`), and the **`MyAddress`** type. A
|
|
165
|
+
signed-in person adds an email address to their own account. It's kept only
|
|
166
|
+
once the code it was sent proves it. Adding one asks for their current
|
|
167
|
+
password, or, for an account with none, a sign-in in the last ten minutes.
|
|
168
|
+
An address another account in the project holds isn't added. The address
|
|
169
|
+
the account uses moves only to a proven one, and it can't be removed.
|
|
170
|
+
- **`exportMyData`** (`@foir/sdk/auth`), and the **`MyDataExport`** type. A
|
|
171
|
+
signed-in person takes a copy of their own data: their account, and
|
|
172
|
+
everything deleting it would erase (the records and files they own). It
|
|
173
|
+
holds no password, code or token value.
|
|
174
|
+
- **`renameFactor`** (`@foir/sdk/auth`). A signed-in person renames one of
|
|
175
|
+
their own security keys or passkeys. A name proves nothing, so it asks for
|
|
176
|
+
no step-up.
|
|
177
|
+
|
|
111
178
|
## [0.24.0] — 2026-10-18
|
|
112
179
|
|
|
113
180
|
### Changed
|
package/dist/app/client.d.ts
CHANGED
|
@@ -222,20 +222,42 @@ export interface AppOrganizationProvision {
|
|
|
222
222
|
data: Record<string, unknown>;
|
|
223
223
|
};
|
|
224
224
|
/**
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
225
|
+
* Who becomes its first member: a user the project has, by `userId`, or
|
|
226
|
+
* somebody the provision makes by `email` in the pool that serves the new
|
|
227
|
+
* organisation, which is its own with `ownPool`. That is how an organisation
|
|
228
|
+
* that signs its own people in gets its first person; making one also needs
|
|
229
|
+
* `users:create`. They are given the role the project names for an
|
|
230
|
+
* organisation's founder, in its settings: the operation chooses who, and
|
|
231
|
+
* the project decides what they hold.
|
|
228
232
|
*/
|
|
229
233
|
firstMember?: {
|
|
230
234
|
userId: string;
|
|
235
|
+
} | {
|
|
236
|
+
email: string;
|
|
237
|
+
firstName?: string;
|
|
238
|
+
lastName?: string;
|
|
231
239
|
};
|
|
232
240
|
/**
|
|
233
241
|
* The organisation signs its own people in, to a pool of its own, apart
|
|
234
242
|
* from its parent's and the project's. Its descendants share that pool
|
|
235
|
-
* unless one of them starts another.
|
|
243
|
+
* unless one of them starts another. One that exists already starts one
|
|
244
|
+
* with `organizations.startPool`.
|
|
236
245
|
*/
|
|
237
246
|
ownPool?: boolean;
|
|
238
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* The pool an organisation's people sign in to, and where it comes from: its
|
|
250
|
+
* own, the nearest organisation above it that started one, or the project's.
|
|
251
|
+
*/
|
|
252
|
+
export interface AppOrganizationPool {
|
|
253
|
+
organizationId: string;
|
|
254
|
+
poolId: string;
|
|
255
|
+
source: 'OWN' | 'ANCESTOR' | 'PROJECT';
|
|
256
|
+
/** The organisation whose own pool it is. Null for the project's. */
|
|
257
|
+
sourceOrganizationId: string | null;
|
|
258
|
+
/** How many people the pool holds, when it is the organisation's own. Null for any other. */
|
|
259
|
+
people: number | null;
|
|
260
|
+
}
|
|
239
261
|
/**
|
|
240
262
|
* Who a consent change is for: a user whose id the app holds, or an
|
|
241
263
|
* address.
|
|
@@ -556,6 +578,32 @@ export interface AppClient {
|
|
|
556
578
|
name: string;
|
|
557
579
|
}[];
|
|
558
580
|
}>;
|
|
581
|
+
/**
|
|
582
|
+
* Which pool the people of the organisation this run acts in sign in to.
|
|
583
|
+
* Needs `organizations:read`. Like every call here it takes no
|
|
584
|
+
* organisation: the run acts in the one whoever triggered it stands in.
|
|
585
|
+
*/
|
|
586
|
+
pool(): Promise<AppOrganizationPool>;
|
|
587
|
+
/**
|
|
588
|
+
* Start the own pool of the organisation this run acts in, which it and
|
|
589
|
+
* everything beneath it sign their people in to. Needs
|
|
590
|
+
* `organizations:write`. Refused while
|
|
591
|
+
* anybody of the pool it signs in to now holds a membership in it or
|
|
592
|
+
* beneath it, or an invitation there is open to that pool, and the
|
|
593
|
+
* refusal says how many: nobody is moved between pools, so those
|
|
594
|
+
* memberships are removed first and those people sign up again in the
|
|
595
|
+
* new pool. Its apps, and those beneath it, then sign their people in
|
|
596
|
+
* there, and anybody signed in through them is signed out. Safe to run
|
|
597
|
+
* again: it answers the pool it has.
|
|
598
|
+
*/
|
|
599
|
+
startPool(): Promise<AppOrganizationPool>;
|
|
600
|
+
/**
|
|
601
|
+
* Drop the own pool of the organisation this run acts in, so it signs its
|
|
602
|
+
* people in to the pool above it. Needs `organizations:write`. Refused
|
|
603
|
+
* while the pool holds anybody or an invitation is open to it: dropping a
|
|
604
|
+
* pool deletes nobody.
|
|
605
|
+
*/
|
|
606
|
+
dropPool(): Promise<AppOrganizationPool>;
|
|
559
607
|
};
|
|
560
608
|
notifications: {
|
|
561
609
|
/** Tell a user something. Needs `notifications:write.named`. */
|
package/dist/app/index.d.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* The manifest's own messages are on `@foir/sdk/app/contract`.
|
|
22
22
|
*/
|
|
23
23
|
export { createAppClient } from './client';
|
|
24
|
-
export type { AppBinding, AppClient, AppClientConfig, AppConsentChange, AppConsentSubject, AppUser, AppUserEraseResult, AppUserList, AppUserSaveResult, AppFieldMapping, AppInstallConfig, AppNotification, AppOperationStart, AppOrganizationProvision, AppRecord, AppRecordList, AppStartedOperation, AppStatusReport, AppSyncDirection, AppUpsertResult, AppWhoami, } from './client';
|
|
24
|
+
export type { AppBinding, AppClient, AppClientConfig, AppConsentChange, AppConsentSubject, AppUser, AppUserEraseResult, AppUserList, AppUserSaveResult, AppFieldMapping, AppInstallConfig, AppNotification, AppOperationStart, AppOrganizationPool, AppOrganizationProvision, AppRecord, AppRecordList, AppStartedOperation, AppStatusReport, AppSyncDirection, AppUpsertResult, AppWhoami, } from './client';
|
|
25
25
|
export { appUserEmailUnavailableOf, appUserUnlinkedOf, appLookupConflictOf, isAppFieldWithheld } from './errors';
|
|
26
26
|
export type { AppUserEmailUnavailableReason, AppLookupConflict } from './errors';
|
|
27
27
|
export { appModelFieldName, appModelTypeName, appSelectorEnumValue } from './naming';
|
package/dist/app.js
CHANGED
|
@@ -352,6 +352,20 @@ function createAppClient(config) {
|
|
|
352
352
|
const refuse = (message) => {
|
|
353
353
|
throw new FoirRequestError(`[foir] ${message}`);
|
|
354
354
|
};
|
|
355
|
+
const POOL_FIELDS = "organizationId poolId source sourceOrganizationId people";
|
|
356
|
+
async function poolOf(name, document) {
|
|
357
|
+
const answer = await field(name, document, {});
|
|
358
|
+
if (!answer?.poolId || !answer.organizationId || answer.source !== "OWN" && answer.source !== "ANCESTOR" && answer.source !== "PROJECT") {
|
|
359
|
+
return refuse(`${name} reported no error and answered no pool`);
|
|
360
|
+
}
|
|
361
|
+
return {
|
|
362
|
+
organizationId: answer.organizationId,
|
|
363
|
+
poolId: answer.poolId,
|
|
364
|
+
source: answer.source,
|
|
365
|
+
sourceOrganizationId: answer.sourceOrganizationId ?? null,
|
|
366
|
+
people: answer.people ?? null
|
|
367
|
+
};
|
|
368
|
+
}
|
|
355
369
|
let surface;
|
|
356
370
|
function served() {
|
|
357
371
|
surface ??= withFoirRetry(async () => {
|
|
@@ -825,9 +839,20 @@ function createAppClient(config) {
|
|
|
825
839
|
if (settings.modelKey.trim() === "") return refuse("an organisation's settings name the model they are a record of");
|
|
826
840
|
input.settings = { modelKey: settings.modelKey.trim(), data: settings.data };
|
|
827
841
|
}
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
842
|
+
const first = organization.firstMember;
|
|
843
|
+
if (first !== void 0) {
|
|
844
|
+
if ("email" in first) {
|
|
845
|
+
const email = first.email.trim();
|
|
846
|
+
if (email === "") return refuse("a first member made by address names the address");
|
|
847
|
+
input.firstMember = {
|
|
848
|
+
email,
|
|
849
|
+
...first.firstName !== void 0 ? { firstName: first.firstName } : {},
|
|
850
|
+
...first.lastName !== void 0 ? { lastName: first.lastName } : {}
|
|
851
|
+
};
|
|
852
|
+
} else {
|
|
853
|
+
if (first.userId === "") return refuse("a first member names a user");
|
|
854
|
+
input.firstMember = { userId: first.userId };
|
|
855
|
+
}
|
|
831
856
|
}
|
|
832
857
|
if (organization.ownPool === true) input.ownPool = true;
|
|
833
858
|
const answered = settings === void 0 ? "organizationId status" : "organizationId settingsRecordId status";
|
|
@@ -853,6 +878,15 @@ function createAppClient(config) {
|
|
|
853
878
|
deletionScheduledAt: answer.deletionScheduledAt ?? null,
|
|
854
879
|
organizations: (answer.organizations ?? []).map((o) => ({ id: o.id, name: o.name }))
|
|
855
880
|
};
|
|
881
|
+
},
|
|
882
|
+
async pool() {
|
|
883
|
+
return poolOf("organizationPool", `query AppOrganizationPool { organizationPool { ${POOL_FIELDS} } }`);
|
|
884
|
+
},
|
|
885
|
+
async startPool() {
|
|
886
|
+
return poolOf("startOrganizationPool", `mutation AppStartOrganizationPool { startOrganizationPool { ${POOL_FIELDS} } }`);
|
|
887
|
+
},
|
|
888
|
+
async dropPool() {
|
|
889
|
+
return poolOf("dropOrganizationPool", `mutation AppDropOrganizationPool { dropOrganizationPool { ${POOL_FIELDS} } }`);
|
|
856
890
|
}
|
|
857
891
|
},
|
|
858
892
|
notifications: {
|
package/dist/auth/auth.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { type FoirEnv } from '../graphql/foir-request';
|
|
19
19
|
import { type AuthOpName, type AuthOpOptions } from './ops';
|
|
20
|
-
import { type AuthConfig, type AuthProviderPublic, type CurrentUser, type AuthResult, type DefaultAuthProvider, type AuthorizedApp, type DeleteAccountResult, type MyOrganization, type MyProfile, type MyProfileChange, type MySession, type MyFactor, type StepUp, type FactorEnrollment, type MySignInMethod, type NewPersonalAccessToken, type ProviderCallbackInput, type ProviderLoginInput, type ProviderLoginResult, type ProviderOTPVerifyInput, type ProviderToken, type SignupContext } from './operations';
|
|
20
|
+
import { type AuthConfig, type AuthProviderPublic, type CurrentUser, type AuthResult, type DefaultAuthProvider, type AuthorizedApp, type DeleteAccountResult, type MyOrganization, type MyProfile, type MyProfileChange, type MySession, type MyAddress, type MyDataExport, type MyFactor, type StepUp, type FactorEnrollment, type MySignInMethod, type NewPersonalAccessToken, type ProviderCallbackInput, type ProviderLoginInput, type ProviderLoginResult, type ProviderOTPVerifyInput, type ProviderToken, type SignupContext } from './operations';
|
|
21
21
|
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
22
22
|
/** An access token + its rotating refresh token. */
|
|
23
23
|
export interface FoirAuthTokens {
|
|
@@ -191,14 +191,26 @@ export declare function myProviderToken(env: FoirEnv, providerKey: string, optio
|
|
|
191
191
|
export declare function logout(env: FoirEnv, options?: AuthOpOptions): Promise<boolean>;
|
|
192
192
|
/**
|
|
193
193
|
* Delete the calling user's account and their owned records and files.
|
|
194
|
-
* Irreversible. `password` is required for
|
|
195
|
-
*
|
|
196
|
-
*
|
|
194
|
+
* Irreversible. `password` is required for an account that holds one. An
|
|
195
|
+
* account with none sets `confirm`, on a sign-in made in the last ten minutes:
|
|
196
|
+
* an older one is refused, and the user signs in again. The server enforces
|
|
197
|
+
* the re-auth gate and fails closed.
|
|
197
198
|
*/
|
|
198
199
|
export declare function deleteMyAccount(env: FoirEnv, vars: {
|
|
199
200
|
confirm: boolean;
|
|
200
201
|
password?: string;
|
|
201
202
|
}, options?: AuthOpOptions): Promise<DeleteAccountResult>;
|
|
203
|
+
/**
|
|
204
|
+
* End the calling user's own membership where the request stands: the
|
|
205
|
+
* organization `orgId` names, or, with none, the project's own. Every role
|
|
206
|
+
* they hold there ends with it; their account and what they made stay.
|
|
207
|
+
* `false` when they held no membership there.
|
|
208
|
+
*
|
|
209
|
+
* Refused where it would leave an organization with nobody who can staff it,
|
|
210
|
+
* or leave them nowhere to sign in: a person keeps one membership in the
|
|
211
|
+
* project, and {@link deleteMyAccount} is how they leave altogether.
|
|
212
|
+
*/
|
|
213
|
+
export declare function leaveMembership(env: FoirEnv, options?: AuthOpOptions): Promise<boolean>;
|
|
202
214
|
/**
|
|
203
215
|
* Change the calling user's own name, picture or language. It asks no
|
|
204
216
|
* permission: these are theirs.
|
|
@@ -235,6 +247,41 @@ export declare function createPersonalAccessToken(env: FoirEnv, vars: {
|
|
|
235
247
|
name: string;
|
|
236
248
|
expiresInDays: number;
|
|
237
249
|
}, options?: AuthOpOptions): Promise<NewPersonalAccessToken>;
|
|
250
|
+
/** The email addresses the calling user is known by, the one their account uses first. */
|
|
251
|
+
export declare function myAddresses(env: FoirEnv, options?: AuthOpOptions): Promise<MyAddress[]>;
|
|
252
|
+
/**
|
|
253
|
+
* Send a code to an address the calling user is adding to their account, and
|
|
254
|
+
* answer when it stops working (ISO-8601, ten minutes on). The address is kept
|
|
255
|
+
* by {@link proveMyAddress}. `password` is their current one; an account with
|
|
256
|
+
* none needs a sign-in in the last ten minutes instead. At most five codes an
|
|
257
|
+
* hour.
|
|
258
|
+
*/
|
|
259
|
+
export declare function addMyAddress(env: FoirEnv, vars: {
|
|
260
|
+
email: string;
|
|
261
|
+
password?: string;
|
|
262
|
+
}, options?: AuthOpOptions): Promise<string>;
|
|
263
|
+
/**
|
|
264
|
+
* Keep the address a code was sent to, as proven. Refused where another
|
|
265
|
+
* account in the project holds the address: nobody's address is taken.
|
|
266
|
+
*/
|
|
267
|
+
export declare function proveMyAddress(env: FoirEnv, vars: {
|
|
268
|
+
email: string;
|
|
269
|
+
code: string;
|
|
270
|
+
}, options?: AuthOpOptions): Promise<MyAddress>;
|
|
271
|
+
/** Make one of the calling user's proven addresses the one their account uses. */
|
|
272
|
+
export declare function makeMyAddressPrimary(env: FoirEnv, email: string, options?: AuthOpOptions): Promise<MyAddress>;
|
|
273
|
+
/**
|
|
274
|
+
* Remove one of the calling user's addresses. The one their account uses
|
|
275
|
+
* stays: make another primary first.
|
|
276
|
+
*/
|
|
277
|
+
export declare function removeMyAddress(env: FoirEnv, email: string, options?: AuthOpOptions): Promise<boolean>;
|
|
278
|
+
/**
|
|
279
|
+
* A copy of the calling user's own data: their account (profile, addresses,
|
|
280
|
+
* memberships and roles, sign-in providers, authorised apps, sessions and
|
|
281
|
+
* notification preferences) and everything deleting it would erase, the
|
|
282
|
+
* records and files they own.
|
|
283
|
+
*/
|
|
284
|
+
export declare function exportMyData(env: FoirEnv, options?: AuthOpOptions): Promise<MyDataExport>;
|
|
238
285
|
/** The providers the calling user signs in with. */
|
|
239
286
|
export declare function mySignInMethods(env: FoirEnv, options?: AuthOpOptions): Promise<MySignInMethod[]>;
|
|
240
287
|
/**
|
|
@@ -295,6 +342,15 @@ export declare function removeFactor(env: FoirEnv, vars: {
|
|
|
295
342
|
kind: string;
|
|
296
343
|
id: string;
|
|
297
344
|
}, options?: AuthOpOptions): Promise<boolean>;
|
|
345
|
+
/**
|
|
346
|
+
* Rename one of the calling user's security keys or passkeys. A name proves
|
|
347
|
+
* nothing and admits nobody, so no step-up is asked.
|
|
348
|
+
*/
|
|
349
|
+
export declare function renameFactor(env: FoirEnv, vars: {
|
|
350
|
+
kind: 'security_key' | 'passkey';
|
|
351
|
+
id: string;
|
|
352
|
+
name: string;
|
|
353
|
+
}, options?: AuthOpOptions): Promise<MyFactor>;
|
|
298
354
|
/**
|
|
299
355
|
* Make the calling user new recovery codes, with an elevated handle from
|
|
300
356
|
* {@link completeStepUp}. They replace any held and are answered here once.
|
package/dist/auth/hosted.d.ts
CHANGED
|
@@ -27,11 +27,22 @@ export interface HostedLoginConfig {
|
|
|
27
27
|
*/
|
|
28
28
|
clientId?: string;
|
|
29
29
|
}
|
|
30
|
+
/** What a sign-in started at the hosted front door is for. */
|
|
31
|
+
export interface HostedLoginOptions {
|
|
32
|
+
/**
|
|
33
|
+
* The organization the person signs in to, when one auth client serves an
|
|
34
|
+
* address per organization. It decides the branding, the pool, and which
|
|
35
|
+
* addresses the person may come back to. The platform refuses an
|
|
36
|
+
* organization other than the one the login domain or app is bound to, one
|
|
37
|
+
* of another project, and one that is not active.
|
|
38
|
+
*/
|
|
39
|
+
org?: string;
|
|
40
|
+
}
|
|
30
41
|
/**
|
|
31
|
-
* Build the redirect URL to the hosted front door, forwarding
|
|
32
|
-
*
|
|
42
|
+
* Build the redirect URL to the hosted front door, forwarding an absolute
|
|
43
|
+
* `returnTo` and, where the app names one, the organization the sign-in is for.
|
|
33
44
|
*/
|
|
34
|
-
export declare function hostedLoginUrl(config: HostedLoginConfig, returnTo?: string): string;
|
|
45
|
+
export declare function hostedLoginUrl(config: HostedLoginConfig, returnTo?: string, options?: HostedLoginOptions): string;
|
|
35
46
|
/**
|
|
36
47
|
* Build the redirect URL that ENDS the hosted session — the mirror of
|
|
37
48
|
* `hostedLoginUrl`, and the only thing that actually signs a user out on
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* the caller's own manifest advertises, through the same op transport
|
|
11
11
|
* `@foir/sdk/data` uses, so `graphql` is not on this module's path at all.
|
|
12
12
|
*/
|
|
13
|
-
export { login, register, requestOTP, loginWithOTP, refreshTokens, loginWithProvider, providerCallback, providerVerifyOTP, providerSignInRefusalOf, signInRefusalOf, confirmProviderLink, resendProviderLinkCode, getAuthProviders, getAuthConfig, getDefaultAuthProvider, signupContext, joinWaitlist, requestPasswordReset, resetPassword, updatePassword, currentUser, myProviderToken, logout, deleteMyAccount, updateMyProfile, myAuthorizedApps, revokeAuthorizedApp, mySessions, endMySession, signOutEverywhere, createPersonalAccessToken, mySignInMethods, connectSignInMethod, disconnectSignInMethod, myFactors, beginStepUp, completeStepUp, beginFactorEnrollment, completeFactorEnrollment, removeFactor, makeRecoveryCodes, myOrganizations, registerOrganization, saveSession, clearSession, getSessionToken, getSessionUser, signInWithPassword, signUpWithPassword, signInWithOTP, resetPasswordAndSignIn, refreshSession, refreshSessionOnce, signOut, authedRequest, authedOp, requireAuth, } from './auth';
|
|
13
|
+
export { login, register, requestOTP, loginWithOTP, refreshTokens, loginWithProvider, providerCallback, providerVerifyOTP, providerSignInRefusalOf, signInRefusalOf, confirmProviderLink, resendProviderLinkCode, getAuthProviders, getAuthConfig, getDefaultAuthProvider, signupContext, joinWaitlist, requestPasswordReset, resetPassword, updatePassword, currentUser, myProviderToken, logout, deleteMyAccount, leaveMembership, updateMyProfile, myAuthorizedApps, revokeAuthorizedApp, mySessions, endMySession, myAddresses, addMyAddress, proveMyAddress, makeMyAddressPrimary, removeMyAddress, exportMyData, signOutEverywhere, createPersonalAccessToken, mySignInMethods, connectSignInMethod, disconnectSignInMethod, myFactors, beginStepUp, completeStepUp, beginFactorEnrollment, completeFactorEnrollment, removeFactor, renameFactor, makeRecoveryCodes, myOrganizations, registerOrganization, saveSession, clearSession, getSessionToken, getSessionUser, signInWithPassword, signUpWithPassword, signInWithOTP, resetPasswordAndSignIn, refreshSession, refreshSessionOnce, signOut, authedRequest, authedOp, requireAuth, } from './auth';
|
|
14
14
|
export type { FoirAuthTokens, FoirUser, SessionAdapter, RequireAuthOptions, } from './auth';
|
|
15
15
|
export { userAccess, getSessionAccess, hasRole, hasScope, requireRole, requireScope, FoirAccessDeniedError, } from './access';
|
|
16
16
|
export type { FoirAccess, FoirAccessDenial } from './access';
|
|
@@ -18,7 +18,7 @@ export { FoirAuthDowngradedError, FoirSessionExpiredError } from '../graphql/foi
|
|
|
18
18
|
export { AUTH_OPS } from './ops';
|
|
19
19
|
export type { AuthOpName, AuthOpOptions } from './ops';
|
|
20
20
|
export { hostedLoginUrl, hostedLogoutUrl, fetchHostedSession, hostedSignInNoticeOf } from './hosted';
|
|
21
|
-
export type { HostedLoginConfig, HostedSession, HostedSessionResult, HostedSignInNotice } from './hosted';
|
|
21
|
+
export type { HostedLoginConfig, HostedLoginOptions, HostedSession, HostedSessionResult, HostedSignInNotice } from './hosted';
|
|
22
22
|
export { createFoirSessionClient } from './session-client';
|
|
23
23
|
export type { FoirSessionClient, FoirSessionConfig, FoirSessionUser, } from './session-client';
|
|
24
|
-
export type { AuthResult, ProviderLoginResult, AuthProviderPublic, ProviderLoginInput, ProviderCallbackInput, ProviderOTPVerifyInput, SignupContext, SignupMode, AuthConfig, AuthConfigProvider, DefaultAuthProvider, CurrentUser, ProviderToken, ProviderTokenRefresh, DeleteAccountResult, MyOrganization, MyProfile, MyProfileChange, AuthorizedApp, MySession, NewPersonalAccessToken, MySignInMethod, MyFactor, StepUp, FactorEnrollment, } from './operations';
|
|
24
|
+
export type { AuthResult, ProviderLoginResult, AuthProviderPublic, ProviderLoginInput, ProviderCallbackInput, ProviderOTPVerifyInput, SignupContext, SignupMode, AuthConfig, AuthConfigProvider, DefaultAuthProvider, CurrentUser, ProviderToken, ProviderTokenRefresh, DeleteAccountResult, MyOrganization, MyProfile, MyProfileChange, AuthorizedApp, MySession, MyAddress, MyDataExport, NewPersonalAccessToken, MySignInMethod, MyFactor, StepUp, FactorEnrollment, } from './operations';
|
|
@@ -200,6 +200,61 @@ export interface MySession {
|
|
|
200
200
|
/** Whether it is the session making the request. */
|
|
201
201
|
current: boolean;
|
|
202
202
|
}
|
|
203
|
+
/** An email address the calling user is known by. */
|
|
204
|
+
export interface MyAddress {
|
|
205
|
+
email: string;
|
|
206
|
+
/** Whether it is the address their account uses: shown for them, and where mail to them goes. */
|
|
207
|
+
primary: boolean;
|
|
208
|
+
/** Whether they proved they hold it. Every address they added is proven. */
|
|
209
|
+
proven: boolean;
|
|
210
|
+
/** ISO-8601. */
|
|
211
|
+
addedAt: string | null;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* The calling user's copy of their own data, the `foir.my-data.v1` document:
|
|
215
|
+
* their account and everything deleting it would erase. No password, code or
|
|
216
|
+
* token value is in it.
|
|
217
|
+
*/
|
|
218
|
+
export interface MyDataExport {
|
|
219
|
+
format: 'foir.my-data.v1';
|
|
220
|
+
/** ISO-8601. */
|
|
221
|
+
exportedAt: string;
|
|
222
|
+
account: Record<string, unknown> & {
|
|
223
|
+
id: string;
|
|
224
|
+
pool: string;
|
|
225
|
+
poolKind: string;
|
|
226
|
+
status: string;
|
|
227
|
+
};
|
|
228
|
+
addresses: Array<{
|
|
229
|
+
email: string;
|
|
230
|
+
primary: boolean;
|
|
231
|
+
proven: boolean;
|
|
232
|
+
addedAt?: string;
|
|
233
|
+
}>;
|
|
234
|
+
memberships: Array<Record<string, unknown> & {
|
|
235
|
+
roles: string[];
|
|
236
|
+
}>;
|
|
237
|
+
signInMethods: Array<Record<string, unknown>>;
|
|
238
|
+
authorizedApps: Array<Record<string, unknown>>;
|
|
239
|
+
sessions: Array<Record<string, unknown>>;
|
|
240
|
+
notificationPreferences: Array<Record<string, unknown>>;
|
|
241
|
+
/** The records the user owns, each with the content it holds now. */
|
|
242
|
+
records: Array<{
|
|
243
|
+
id: string;
|
|
244
|
+
model: string;
|
|
245
|
+
key?: string;
|
|
246
|
+
project: string;
|
|
247
|
+
data: Record<string, unknown>;
|
|
248
|
+
}>;
|
|
249
|
+
/** The files the user owns; their bytes are fetched from `url`. */
|
|
250
|
+
files: Array<{
|
|
251
|
+
id: string;
|
|
252
|
+
name: string;
|
|
253
|
+
mimeType: string;
|
|
254
|
+
size: number;
|
|
255
|
+
url: string;
|
|
256
|
+
}>;
|
|
257
|
+
}
|
|
203
258
|
/** A personal access token just made: its value is shown here once. */
|
|
204
259
|
export interface NewPersonalAccessToken {
|
|
205
260
|
token: MySession;
|
package/dist/auth/ops.d.ts
CHANGED
|
@@ -56,12 +56,19 @@ export declare const AUTH_OPS: {
|
|
|
56
56
|
readonly resetPassword: "resetPassword";
|
|
57
57
|
readonly updatePassword: "updatePassword";
|
|
58
58
|
readonly deleteMyAccount: "deleteMyAccount";
|
|
59
|
+
readonly leaveMembership: "leaveMembership";
|
|
59
60
|
readonly updateMyProfile: "updateMyProfile";
|
|
60
61
|
readonly myAuthorizedApps: "myAuthorizedApps";
|
|
61
62
|
readonly revokeAuthorizedApp: "revokeAuthorizedApp";
|
|
62
63
|
readonly mySessions: "mySessions";
|
|
63
64
|
readonly endMySession: "endMySession";
|
|
64
65
|
readonly signOutEverywhere: "signOutEverywhere";
|
|
66
|
+
readonly myAddresses: "myAddresses";
|
|
67
|
+
readonly addMyAddress: "addMyAddress";
|
|
68
|
+
readonly proveMyAddress: "proveMyAddress";
|
|
69
|
+
readonly makeMyAddressPrimary: "makeMyAddressPrimary";
|
|
70
|
+
readonly removeMyAddress: "removeMyAddress";
|
|
71
|
+
readonly exportMyData: "exportMyData";
|
|
65
72
|
readonly createPersonalAccessToken: "createPersonalAccessToken";
|
|
66
73
|
readonly mySignInMethods: "mySignInMethods";
|
|
67
74
|
readonly connectSignInMethod: "connectSignInMethod";
|
|
@@ -72,6 +79,7 @@ export declare const AUTH_OPS: {
|
|
|
72
79
|
readonly beginFactorEnrollment: "beginFactorEnrollment";
|
|
73
80
|
readonly completeFactorEnrollment: "completeFactorEnrollment";
|
|
74
81
|
readonly removeFactor: "removeFactor";
|
|
82
|
+
readonly renameFactor: "renameFactor";
|
|
75
83
|
readonly makeRecoveryCodes: "makeRecoveryCodes";
|
|
76
84
|
readonly myOrganizations: "myOrganizations";
|
|
77
85
|
readonly registerOrganization: "registerOrganization";
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
* api.foir.dev but a browser cannot reach them there — the cookie would be
|
|
28
28
|
* cross-site, and that CORS surface deliberately refuses credentials.
|
|
29
29
|
*/
|
|
30
|
+
import type { HostedLoginOptions } from './hosted';
|
|
30
31
|
export interface FoirSessionConfig {
|
|
31
32
|
/**
|
|
32
33
|
* Your verified custom login domain, e.g. `https://login.acme.com`. Must share
|
|
@@ -66,8 +67,12 @@ export interface FoirSessionClient {
|
|
|
66
67
|
getUser(): Promise<FoirSessionUser | null>;
|
|
67
68
|
/** Revoke the session server-side and clear the cookie. Idempotent. */
|
|
68
69
|
signOut(): Promise<void>;
|
|
69
|
-
/**
|
|
70
|
-
|
|
70
|
+
/**
|
|
71
|
+
* Where to send the browser to sign in. Name the organization the sign-in is
|
|
72
|
+
* for with `options.org` when one auth client serves an address per
|
|
73
|
+
* organization.
|
|
74
|
+
*/
|
|
75
|
+
signInUrl(returnTo?: string, options?: HostedLoginOptions): string;
|
|
71
76
|
/** Observe session changes. Returns an unsubscribe function. */
|
|
72
77
|
subscribe(listener: (user: FoirSessionUser | null) => void): () => void;
|
|
73
78
|
/** Cancel the refresh timer. Call on unmount. */
|
package/dist/auth-session.js
CHANGED
|
@@ -113,10 +113,11 @@ function createFoirSessionClient(config) {
|
|
|
113
113
|
forget();
|
|
114
114
|
config.onSignOut?.();
|
|
115
115
|
},
|
|
116
|
-
signInUrl(returnTo) {
|
|
116
|
+
signInUrl(returnTo, options = {}) {
|
|
117
117
|
const dest = new URL(trimSlash(config.loginOrigin));
|
|
118
118
|
const target = returnTo ?? (typeof window !== "undefined" ? window.location.href : void 0);
|
|
119
119
|
if (target && target !== "/") dest.searchParams.set("returnTo", target);
|
|
120
|
+
if (options.org) dest.searchParams.set("org", options.org);
|
|
120
121
|
return dest.toString();
|
|
121
122
|
},
|
|
122
123
|
subscribe(listener) {
|
package/dist/auth.js
CHANGED
|
@@ -1094,6 +1094,8 @@ var AUTH_OPS = {
|
|
|
1094
1094
|
resetPassword: "resetPassword",
|
|
1095
1095
|
updatePassword: "updatePassword",
|
|
1096
1096
|
deleteMyAccount: "deleteMyAccount",
|
|
1097
|
+
// Ending their own membership where the request stands.
|
|
1098
|
+
leaveMembership: "leaveMembership",
|
|
1097
1099
|
// The user's own profile, and the apps they have agreed may act for them.
|
|
1098
1100
|
updateMyProfile: "updateMyProfile",
|
|
1099
1101
|
myAuthorizedApps: "myAuthorizedApps",
|
|
@@ -1103,6 +1105,14 @@ var AUTH_OPS = {
|
|
|
1103
1105
|
mySessions: "mySessions",
|
|
1104
1106
|
endMySession: "endMySession",
|
|
1105
1107
|
signOutEverywhere: "signOutEverywhere",
|
|
1108
|
+
// Their email addresses: one added is kept once the code it was sent proves it.
|
|
1109
|
+
myAddresses: "myAddresses",
|
|
1110
|
+
addMyAddress: "addMyAddress",
|
|
1111
|
+
proveMyAddress: "proveMyAddress",
|
|
1112
|
+
makeMyAddressPrimary: "makeMyAddressPrimary",
|
|
1113
|
+
removeMyAddress: "removeMyAddress",
|
|
1114
|
+
// A copy of their own data.
|
|
1115
|
+
exportMyData: "exportMyData",
|
|
1106
1116
|
createPersonalAccessToken: "createPersonalAccessToken",
|
|
1107
1117
|
mySignInMethods: "mySignInMethods",
|
|
1108
1118
|
connectSignInMethod: "connectSignInMethod",
|
|
@@ -1114,6 +1124,7 @@ var AUTH_OPS = {
|
|
|
1114
1124
|
beginFactorEnrollment: "beginFactorEnrollment",
|
|
1115
1125
|
completeFactorEnrollment: "completeFactorEnrollment",
|
|
1116
1126
|
removeFactor: "removeFactor",
|
|
1127
|
+
renameFactor: "renameFactor",
|
|
1117
1128
|
makeRecoveryCodes: "makeRecoveryCodes",
|
|
1118
1129
|
// The organizations the caller may stand in. Nothing selects one: a request
|
|
1119
1130
|
// names the organization it acts in (`orgId` or `orgUrl` below), and the
|
|
@@ -1282,6 +1293,9 @@ async function logout(env, options) {
|
|
|
1282
1293
|
async function deleteMyAccount(env, vars, options) {
|
|
1283
1294
|
return authOp(env, AUTH_OPS.deleteMyAccount, vars, options);
|
|
1284
1295
|
}
|
|
1296
|
+
async function leaveMembership(env, options) {
|
|
1297
|
+
return authOp(env, AUTH_OPS.leaveMembership, void 0, options);
|
|
1298
|
+
}
|
|
1285
1299
|
async function updateMyProfile(env, change, options) {
|
|
1286
1300
|
return authOp(env, AUTH_OPS.updateMyProfile, change, options);
|
|
1287
1301
|
}
|
|
@@ -1303,6 +1317,24 @@ async function signOutEverywhere(env, vars = {}, options) {
|
|
|
1303
1317
|
async function createPersonalAccessToken(env, vars, options) {
|
|
1304
1318
|
return authOp(env, AUTH_OPS.createPersonalAccessToken, vars, options);
|
|
1305
1319
|
}
|
|
1320
|
+
async function myAddresses(env, options) {
|
|
1321
|
+
return authOp(env, AUTH_OPS.myAddresses, void 0, options);
|
|
1322
|
+
}
|
|
1323
|
+
async function addMyAddress(env, vars, options) {
|
|
1324
|
+
return authOp(env, AUTH_OPS.addMyAddress, vars, options);
|
|
1325
|
+
}
|
|
1326
|
+
async function proveMyAddress(env, vars, options) {
|
|
1327
|
+
return authOp(env, AUTH_OPS.proveMyAddress, vars, options);
|
|
1328
|
+
}
|
|
1329
|
+
async function makeMyAddressPrimary(env, email, options) {
|
|
1330
|
+
return authOp(env, AUTH_OPS.makeMyAddressPrimary, { email }, options);
|
|
1331
|
+
}
|
|
1332
|
+
async function removeMyAddress(env, email, options) {
|
|
1333
|
+
return authOp(env, AUTH_OPS.removeMyAddress, { email }, options);
|
|
1334
|
+
}
|
|
1335
|
+
async function exportMyData(env, options) {
|
|
1336
|
+
return authOp(env, AUTH_OPS.exportMyData, void 0, options);
|
|
1337
|
+
}
|
|
1306
1338
|
async function mySignInMethods(env, options) {
|
|
1307
1339
|
return authOp(env, AUTH_OPS.mySignInMethods, void 0, options);
|
|
1308
1340
|
}
|
|
@@ -1330,6 +1362,9 @@ async function completeFactorEnrollment(env, vars, options) {
|
|
|
1330
1362
|
async function removeFactor(env, vars, options) {
|
|
1331
1363
|
return authOp(env, AUTH_OPS.removeFactor, vars, options);
|
|
1332
1364
|
}
|
|
1365
|
+
async function renameFactor(env, vars, options) {
|
|
1366
|
+
return authOp(env, AUTH_OPS.renameFactor, vars, options);
|
|
1367
|
+
}
|
|
1333
1368
|
async function makeRecoveryCodes(env, elevatedHandle, options) {
|
|
1334
1369
|
return authOp(env, AUTH_OPS.makeRecoveryCodes, { elevatedHandle }, options);
|
|
1335
1370
|
}
|
|
@@ -1532,6 +1567,8 @@ var GRANT_TABLE = [
|
|
|
1532
1567
|
["authclients:write", "none", [], []],
|
|
1533
1568
|
["domains:read", "none", [], []],
|
|
1534
1569
|
["domains:write", "none", [], []],
|
|
1570
|
+
["loginpages:read", "none", [], []],
|
|
1571
|
+
["loginpages:write", "none", [], []],
|
|
1535
1572
|
["secrets:read", "none", [], []],
|
|
1536
1573
|
["secrets:list", "none", [], []],
|
|
1537
1574
|
["secrets:put", "none", [], []],
|
|
@@ -1569,7 +1606,7 @@ var GRANT_TABLE = [
|
|
|
1569
1606
|
["projects:read", "none", [], []],
|
|
1570
1607
|
["projects:write", "none", [], []],
|
|
1571
1608
|
["project:stand", "none", [], []],
|
|
1572
|
-
["project:manage", "none", [], ["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", "authclients:read", "authclients: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"]],
|
|
1609
|
+
["project:manage", "none", [], ["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", "authclients:read", "authclients:write", "domains:read", "domains:write", "loginpages:read", "loginpages: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"]],
|
|
1573
1610
|
["projects:create", "none", [], []],
|
|
1574
1611
|
["projects:delete", "none", [], []],
|
|
1575
1612
|
["tenants:read", "none", [], []],
|
|
@@ -1762,11 +1799,14 @@ async function requireScope(adapter, env, scope) {
|
|
|
1762
1799
|
var DEFAULT_COOKIE_NAME = "foir_session";
|
|
1763
1800
|
var DEFAULT_SESSION_PATH = "/session/token";
|
|
1764
1801
|
var DEFAULT_END_SESSION_PATH = "/session/end-session";
|
|
1765
|
-
function hostedLoginUrl(config, returnTo) {
|
|
1802
|
+
function hostedLoginUrl(config, returnTo, options = {}) {
|
|
1766
1803
|
const dest = new URL(config.loginOrigin);
|
|
1767
1804
|
if (returnTo && returnTo !== "/") {
|
|
1768
1805
|
dest.searchParams.set("returnTo", returnTo);
|
|
1769
1806
|
}
|
|
1807
|
+
if (options.org) {
|
|
1808
|
+
dest.searchParams.set("org", options.org);
|
|
1809
|
+
}
|
|
1770
1810
|
return dest.toString();
|
|
1771
1811
|
}
|
|
1772
1812
|
function hostedLogoutUrl(config, clientId, returnTo) {
|
|
@@ -1982,10 +2022,11 @@ function createFoirSessionClient(config) {
|
|
|
1982
2022
|
forget();
|
|
1983
2023
|
config.onSignOut?.();
|
|
1984
2024
|
},
|
|
1985
|
-
signInUrl(returnTo) {
|
|
2025
|
+
signInUrl(returnTo, options = {}) {
|
|
1986
2026
|
const dest = new URL(trimSlash(config.loginOrigin));
|
|
1987
2027
|
const target = returnTo ?? (typeof window !== "undefined" ? window.location.href : void 0);
|
|
1988
2028
|
if (target && target !== "/") dest.searchParams.set("returnTo", target);
|
|
2029
|
+
if (options.org) dest.searchParams.set("org", options.org);
|
|
1989
2030
|
return dest.toString();
|
|
1990
2031
|
},
|
|
1991
2032
|
subscribe(listener) {
|
|
@@ -2007,6 +2048,7 @@ export {
|
|
|
2007
2048
|
FoirAccessDeniedError,
|
|
2008
2049
|
FoirAuthDowngradedError,
|
|
2009
2050
|
FoirSessionExpiredError,
|
|
2051
|
+
addMyAddress,
|
|
2010
2052
|
authedOp,
|
|
2011
2053
|
authedRequest,
|
|
2012
2054
|
beginFactorEnrollment,
|
|
@@ -2022,6 +2064,7 @@ export {
|
|
|
2022
2064
|
deleteMyAccount,
|
|
2023
2065
|
disconnectSignInMethod,
|
|
2024
2066
|
endMySession,
|
|
2067
|
+
exportMyData,
|
|
2025
2068
|
fetchHostedSession,
|
|
2026
2069
|
getAuthConfig,
|
|
2027
2070
|
getAuthProviders,
|
|
@@ -2035,17 +2078,21 @@ export {
|
|
|
2035
2078
|
hostedLogoutUrl,
|
|
2036
2079
|
hostedSignInNoticeOf,
|
|
2037
2080
|
joinWaitlist,
|
|
2081
|
+
leaveMembership,
|
|
2038
2082
|
login,
|
|
2039
2083
|
loginWithOTP,
|
|
2040
2084
|
loginWithProvider,
|
|
2041
2085
|
logout,
|
|
2086
|
+
makeMyAddressPrimary,
|
|
2042
2087
|
makeRecoveryCodes,
|
|
2088
|
+
myAddresses,
|
|
2043
2089
|
myAuthorizedApps,
|
|
2044
2090
|
myFactors,
|
|
2045
2091
|
myOrganizations,
|
|
2046
2092
|
myProviderToken,
|
|
2047
2093
|
mySessions,
|
|
2048
2094
|
mySignInMethods,
|
|
2095
|
+
proveMyAddress,
|
|
2049
2096
|
providerCallback,
|
|
2050
2097
|
providerSignInRefusalOf,
|
|
2051
2098
|
providerVerifyOTP,
|
|
@@ -2055,6 +2102,8 @@ export {
|
|
|
2055
2102
|
register,
|
|
2056
2103
|
registerOrganization,
|
|
2057
2104
|
removeFactor,
|
|
2105
|
+
removeMyAddress,
|
|
2106
|
+
renameFactor,
|
|
2058
2107
|
requestOTP,
|
|
2059
2108
|
requestPasswordReset,
|
|
2060
2109
|
requireAuth,
|
package/dist/next.js
CHANGED
|
@@ -974,6 +974,8 @@ var AUTH_OPS = {
|
|
|
974
974
|
resetPassword: "resetPassword",
|
|
975
975
|
updatePassword: "updatePassword",
|
|
976
976
|
deleteMyAccount: "deleteMyAccount",
|
|
977
|
+
// Ending their own membership where the request stands.
|
|
978
|
+
leaveMembership: "leaveMembership",
|
|
977
979
|
// The user's own profile, and the apps they have agreed may act for them.
|
|
978
980
|
updateMyProfile: "updateMyProfile",
|
|
979
981
|
myAuthorizedApps: "myAuthorizedApps",
|
|
@@ -983,6 +985,14 @@ var AUTH_OPS = {
|
|
|
983
985
|
mySessions: "mySessions",
|
|
984
986
|
endMySession: "endMySession",
|
|
985
987
|
signOutEverywhere: "signOutEverywhere",
|
|
988
|
+
// Their email addresses: one added is kept once the code it was sent proves it.
|
|
989
|
+
myAddresses: "myAddresses",
|
|
990
|
+
addMyAddress: "addMyAddress",
|
|
991
|
+
proveMyAddress: "proveMyAddress",
|
|
992
|
+
makeMyAddressPrimary: "makeMyAddressPrimary",
|
|
993
|
+
removeMyAddress: "removeMyAddress",
|
|
994
|
+
// A copy of their own data.
|
|
995
|
+
exportMyData: "exportMyData",
|
|
986
996
|
createPersonalAccessToken: "createPersonalAccessToken",
|
|
987
997
|
mySignInMethods: "mySignInMethods",
|
|
988
998
|
connectSignInMethod: "connectSignInMethod",
|
|
@@ -994,6 +1004,7 @@ var AUTH_OPS = {
|
|
|
994
1004
|
beginFactorEnrollment: "beginFactorEnrollment",
|
|
995
1005
|
completeFactorEnrollment: "completeFactorEnrollment",
|
|
996
1006
|
removeFactor: "removeFactor",
|
|
1007
|
+
renameFactor: "renameFactor",
|
|
997
1008
|
makeRecoveryCodes: "makeRecoveryCodes",
|
|
998
1009
|
// The organizations the caller may stand in. Nothing selects one: a request
|
|
999
1010
|
// names the organization it acts in (`orgId` or `orgUrl` below), and the
|
|
@@ -113,6 +113,23 @@ export declare function placementFrameAncestors(origins: readonly string[]): str
|
|
|
113
113
|
export declare class PlacementRefused extends Error {
|
|
114
114
|
constructor(reason: string);
|
|
115
115
|
}
|
|
116
|
+
/** One error of a GraphQL answer, as Foir's API sends it. */
|
|
117
|
+
export interface PlacementRequestError {
|
|
118
|
+
message: string;
|
|
119
|
+
path?: (string | number)[];
|
|
120
|
+
extensions?: Record<string, unknown>;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* A request the page made on its token that Foir's API refused or failed.
|
|
124
|
+
* The message says each distinct refusal once: a read refused on a field is
|
|
125
|
+
* answered once per row, and one missing grant over five hundred rows would
|
|
126
|
+
* otherwise read five hundred times. `errors` keeps every one, with its path.
|
|
127
|
+
*/
|
|
128
|
+
export declare class PlacementRequestFailed extends Error {
|
|
129
|
+
readonly errors: PlacementRequestError[];
|
|
130
|
+
readonly status: number;
|
|
131
|
+
constructor(errors: PlacementRequestError[], status: number);
|
|
132
|
+
}
|
|
116
133
|
export interface ConnectPlacementOptions {
|
|
117
134
|
/** Foir's API, e.g. `https://api.foir.dev`. */
|
|
118
135
|
apiUrl: string;
|
package/dist/placement.js
CHANGED
|
@@ -41,6 +41,8 @@ var GRANT_TABLE = [
|
|
|
41
41
|
["authclients:write", "none", [], []],
|
|
42
42
|
["domains:read", "none", [], []],
|
|
43
43
|
["domains:write", "none", [], []],
|
|
44
|
+
["loginpages:read", "none", [], []],
|
|
45
|
+
["loginpages:write", "none", [], []],
|
|
44
46
|
["secrets:read", "none", [], []],
|
|
45
47
|
["secrets:list", "none", [], []],
|
|
46
48
|
["secrets:put", "none", [], []],
|
|
@@ -78,7 +80,7 @@ var GRANT_TABLE = [
|
|
|
78
80
|
["projects:read", "none", [], []],
|
|
79
81
|
["projects:write", "none", [], []],
|
|
80
82
|
["project:stand", "none", [], []],
|
|
81
|
-
["project:manage", "none", [], ["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", "authclients:read", "authclients: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"]],
|
|
83
|
+
["project:manage", "none", [], ["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", "authclients:read", "authclients:write", "domains:read", "domains:write", "loginpages:read", "loginpages: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"]],
|
|
82
84
|
["projects:create", "none", [], []],
|
|
83
85
|
["projects:delete", "none", [], []],
|
|
84
86
|
["tenants:read", "none", [], []],
|
|
@@ -266,6 +268,17 @@ var PlacementRefused = class extends Error {
|
|
|
266
268
|
this.name = "PlacementRefused";
|
|
267
269
|
}
|
|
268
270
|
};
|
|
271
|
+
var PlacementRequestFailed = class extends Error {
|
|
272
|
+
errors;
|
|
273
|
+
status;
|
|
274
|
+
constructor(errors, status) {
|
|
275
|
+
const said = [...new Set(errors.map((e) => e.message))];
|
|
276
|
+
super(`[foir] placement request failed: ${said.length > 0 ? said.join("; ") : `HTTP ${status}`}`);
|
|
277
|
+
this.name = "PlacementRequestFailed";
|
|
278
|
+
this.errors = errors;
|
|
279
|
+
this.status = status;
|
|
280
|
+
}
|
|
281
|
+
};
|
|
269
282
|
function isHostMessage(data) {
|
|
270
283
|
if (typeof data !== "object" || data === null) return false;
|
|
271
284
|
const t = data.type;
|
|
@@ -325,8 +338,7 @@ function connectPlacement(options) {
|
|
|
325
338
|
});
|
|
326
339
|
const body = await res.json().catch(() => ({}));
|
|
327
340
|
if (!res.ok || body.errors && body.errors.length > 0) {
|
|
328
|
-
|
|
329
|
-
throw new Error(`[foir] placement request failed: ${messages}`);
|
|
341
|
+
throw new PlacementRequestFailed(body.errors ?? [], res.status);
|
|
330
342
|
}
|
|
331
343
|
return body.data;
|
|
332
344
|
},
|
|
@@ -381,6 +393,7 @@ export {
|
|
|
381
393
|
FOIR_CONSOLE_ORIGINS,
|
|
382
394
|
PLACEMENT_MESSAGE,
|
|
383
395
|
PlacementRefused,
|
|
396
|
+
PlacementRequestFailed,
|
|
384
397
|
connectPlacement,
|
|
385
398
|
placementFrameAncestors
|
|
386
399
|
};
|
package/dist/push.js
CHANGED
|
@@ -1094,6 +1094,8 @@ var AUTH_OPS = {
|
|
|
1094
1094
|
resetPassword: "resetPassword",
|
|
1095
1095
|
updatePassword: "updatePassword",
|
|
1096
1096
|
deleteMyAccount: "deleteMyAccount",
|
|
1097
|
+
// Ending their own membership where the request stands.
|
|
1098
|
+
leaveMembership: "leaveMembership",
|
|
1097
1099
|
// The user's own profile, and the apps they have agreed may act for them.
|
|
1098
1100
|
updateMyProfile: "updateMyProfile",
|
|
1099
1101
|
myAuthorizedApps: "myAuthorizedApps",
|
|
@@ -1103,6 +1105,14 @@ var AUTH_OPS = {
|
|
|
1103
1105
|
mySessions: "mySessions",
|
|
1104
1106
|
endMySession: "endMySession",
|
|
1105
1107
|
signOutEverywhere: "signOutEverywhere",
|
|
1108
|
+
// Their email addresses: one added is kept once the code it was sent proves it.
|
|
1109
|
+
myAddresses: "myAddresses",
|
|
1110
|
+
addMyAddress: "addMyAddress",
|
|
1111
|
+
proveMyAddress: "proveMyAddress",
|
|
1112
|
+
makeMyAddressPrimary: "makeMyAddressPrimary",
|
|
1113
|
+
removeMyAddress: "removeMyAddress",
|
|
1114
|
+
// A copy of their own data.
|
|
1115
|
+
exportMyData: "exportMyData",
|
|
1106
1116
|
createPersonalAccessToken: "createPersonalAccessToken",
|
|
1107
1117
|
mySignInMethods: "mySignInMethods",
|
|
1108
1118
|
connectSignInMethod: "connectSignInMethod",
|
|
@@ -1114,6 +1124,7 @@ var AUTH_OPS = {
|
|
|
1114
1124
|
beginFactorEnrollment: "beginFactorEnrollment",
|
|
1115
1125
|
completeFactorEnrollment: "completeFactorEnrollment",
|
|
1116
1126
|
removeFactor: "removeFactor",
|
|
1127
|
+
renameFactor: "renameFactor",
|
|
1117
1128
|
makeRecoveryCodes: "makeRecoveryCodes",
|
|
1118
1129
|
// The organizations the caller may stand in. Nothing selects one: a request
|
|
1119
1130
|
// names the organization it acts in (`orgId` or `orgUrl` below), and the
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import type { AuthProviderPublic, FoirAccess, FoirUser, FoirSessionConfig, SessionAdapter } from '../auth';
|
|
2
|
+
import type { AuthProviderPublic, FoirAccess, FoirUser, FoirSessionConfig, HostedLoginOptions, SessionAdapter } from '../auth';
|
|
3
3
|
import type { ProviderCallbackInput, ProviderLoginInput } from '../auth';
|
|
4
4
|
import type { FoirEnv } from '../graphql/foir-request';
|
|
5
5
|
/** Where the session hydration stands. */
|
|
@@ -53,9 +53,10 @@ export interface FoirContextValue {
|
|
|
53
53
|
/**
|
|
54
54
|
* In `hostedSession` mode, where to send the browser to sign in — sign-in is a
|
|
55
55
|
* redirect to your Foir login domain, not an in-page form. Null in token mode,
|
|
56
|
-
* where you call {@link FoirContextValue.signIn} instead.
|
|
56
|
+
* where you call {@link FoirContextValue.signIn} instead. `options.org` names
|
|
57
|
+
* the organization the sign-in is for.
|
|
57
58
|
*/
|
|
58
|
-
hostedSignInUrl: ((returnTo?: string) => string) | null;
|
|
59
|
+
hostedSignInUrl: ((returnTo?: string, options?: HostedLoginOptions) => string) | null;
|
|
59
60
|
}
|
|
60
61
|
export interface FoirProviderProps {
|
|
61
62
|
/** API config: `{ PLATFORM_API_URL, PLATFORM_API_KEY }` (a publishable key). */
|
|
@@ -151,5 +152,5 @@ export declare function useFoirAuth(): {
|
|
|
151
152
|
refresh: () => Promise<boolean>;
|
|
152
153
|
getToken: () => Promise<string | null>;
|
|
153
154
|
getProviders: () => Promise<AuthProviderPublic[]>;
|
|
154
|
-
hostedSignInUrl: ((returnTo?: string) => string) | null;
|
|
155
|
+
hostedSignInUrl: ((returnTo?: string, options?: HostedLoginOptions) => string) | null;
|
|
155
156
|
};
|
package/dist/react.js
CHANGED
|
@@ -968,6 +968,8 @@ var AUTH_OPS = {
|
|
|
968
968
|
resetPassword: "resetPassword",
|
|
969
969
|
updatePassword: "updatePassword",
|
|
970
970
|
deleteMyAccount: "deleteMyAccount",
|
|
971
|
+
// Ending their own membership where the request stands.
|
|
972
|
+
leaveMembership: "leaveMembership",
|
|
971
973
|
// The user's own profile, and the apps they have agreed may act for them.
|
|
972
974
|
updateMyProfile: "updateMyProfile",
|
|
973
975
|
myAuthorizedApps: "myAuthorizedApps",
|
|
@@ -977,6 +979,14 @@ var AUTH_OPS = {
|
|
|
977
979
|
mySessions: "mySessions",
|
|
978
980
|
endMySession: "endMySession",
|
|
979
981
|
signOutEverywhere: "signOutEverywhere",
|
|
982
|
+
// Their email addresses: one added is kept once the code it was sent proves it.
|
|
983
|
+
myAddresses: "myAddresses",
|
|
984
|
+
addMyAddress: "addMyAddress",
|
|
985
|
+
proveMyAddress: "proveMyAddress",
|
|
986
|
+
makeMyAddressPrimary: "makeMyAddressPrimary",
|
|
987
|
+
removeMyAddress: "removeMyAddress",
|
|
988
|
+
// A copy of their own data.
|
|
989
|
+
exportMyData: "exportMyData",
|
|
980
990
|
createPersonalAccessToken: "createPersonalAccessToken",
|
|
981
991
|
mySignInMethods: "mySignInMethods",
|
|
982
992
|
connectSignInMethod: "connectSignInMethod",
|
|
@@ -988,6 +998,7 @@ var AUTH_OPS = {
|
|
|
988
998
|
beginFactorEnrollment: "beginFactorEnrollment",
|
|
989
999
|
completeFactorEnrollment: "completeFactorEnrollment",
|
|
990
1000
|
removeFactor: "removeFactor",
|
|
1001
|
+
renameFactor: "renameFactor",
|
|
991
1002
|
makeRecoveryCodes: "makeRecoveryCodes",
|
|
992
1003
|
// The organizations the caller may stand in. Nothing selects one: a request
|
|
993
1004
|
// names the organization it acts in (`orgId` or `orgUrl` below), and the
|
|
@@ -1237,6 +1248,8 @@ var GRANT_TABLE = [
|
|
|
1237
1248
|
["authclients:write", "none", [], []],
|
|
1238
1249
|
["domains:read", "none", [], []],
|
|
1239
1250
|
["domains:write", "none", [], []],
|
|
1251
|
+
["loginpages:read", "none", [], []],
|
|
1252
|
+
["loginpages:write", "none", [], []],
|
|
1240
1253
|
["secrets:read", "none", [], []],
|
|
1241
1254
|
["secrets:list", "none", [], []],
|
|
1242
1255
|
["secrets:put", "none", [], []],
|
|
@@ -1274,7 +1287,7 @@ var GRANT_TABLE = [
|
|
|
1274
1287
|
["projects:read", "none", [], []],
|
|
1275
1288
|
["projects:write", "none", [], []],
|
|
1276
1289
|
["project:stand", "none", [], []],
|
|
1277
|
-
["project:manage", "none", [], ["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", "authclients:read", "authclients: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"]],
|
|
1290
|
+
["project:manage", "none", [], ["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", "authclients:read", "authclients:write", "domains:read", "domains:write", "loginpages:read", "loginpages: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"]],
|
|
1278
1291
|
["projects:create", "none", [], []],
|
|
1279
1292
|
["projects:delete", "none", [], []],
|
|
1280
1293
|
["tenants:read", "none", [], []],
|
|
@@ -1538,10 +1551,11 @@ function createFoirSessionClient(config) {
|
|
|
1538
1551
|
forget();
|
|
1539
1552
|
config.onSignOut?.();
|
|
1540
1553
|
},
|
|
1541
|
-
signInUrl(returnTo) {
|
|
1554
|
+
signInUrl(returnTo, options = {}) {
|
|
1542
1555
|
const dest = new URL(trimSlash(config.loginOrigin));
|
|
1543
1556
|
const target = returnTo ?? (typeof window !== "undefined" ? window.location.href : void 0);
|
|
1544
1557
|
if (target && target !== "/") dest.searchParams.set("returnTo", target);
|
|
1558
|
+
if (options.org) dest.searchParams.set("org", options.org);
|
|
1545
1559
|
return dest.toString();
|
|
1546
1560
|
},
|
|
1547
1561
|
subscribe(listener) {
|
|
@@ -1768,7 +1782,7 @@ function FoirProvider({
|
|
|
1768
1782
|
}, [refresh, loadAccess, accessWasRead]);
|
|
1769
1783
|
const getProviders = useCallback(() => getAuthProviders(envRef.current), []);
|
|
1770
1784
|
const hostedSignInUrl = useMemo(
|
|
1771
|
-
() => hosted ? (returnTo) => hosted.signInUrl(returnTo) : null,
|
|
1785
|
+
() => hosted ? (returnTo, options) => hosted.signInUrl(returnTo, options) : null,
|
|
1772
1786
|
[hosted]
|
|
1773
1787
|
);
|
|
1774
1788
|
const value = useMemo(
|
package/dist/server.js
CHANGED
|
@@ -115,6 +115,8 @@ var GRANT_TABLE = [
|
|
|
115
115
|
["authclients:write", "none", [], []],
|
|
116
116
|
["domains:read", "none", [], []],
|
|
117
117
|
["domains:write", "none", [], []],
|
|
118
|
+
["loginpages:read", "none", [], []],
|
|
119
|
+
["loginpages:write", "none", [], []],
|
|
118
120
|
["secrets:read", "none", [], []],
|
|
119
121
|
["secrets:list", "none", [], []],
|
|
120
122
|
["secrets:put", "none", [], []],
|
|
@@ -152,7 +154,7 @@ var GRANT_TABLE = [
|
|
|
152
154
|
["projects:read", "none", [], []],
|
|
153
155
|
["projects:write", "none", [], []],
|
|
154
156
|
["project:stand", "none", [], []],
|
|
155
|
-
["project:manage", "none", [], ["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", "authclients:read", "authclients: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"]],
|
|
157
|
+
["project:manage", "none", [], ["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", "authclients:read", "authclients:write", "domains:read", "domains:write", "loginpages:read", "loginpages: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"]],
|
|
156
158
|
["projects:create", "none", [], []],
|
|
157
159
|
["projects:delete", "none", [], []],
|
|
158
160
|
["tenants:read", "none", [], []],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foir/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Official Foir SDK — editor iframe client + server-side platform helpers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
"blurhash": "^2.0.5",
|
|
169
169
|
"graphql": "^16.10.0",
|
|
170
170
|
"hls.js": "^1.6.16",
|
|
171
|
-
"jsdom": "^
|
|
171
|
+
"jsdom": "^28.1.0",
|
|
172
172
|
"react": "^19.2.0",
|
|
173
173
|
"react-dom": "^19.2.0",
|
|
174
174
|
"tsup": "^8.5.1",
|
|
@@ -177,8 +177,8 @@
|
|
|
177
177
|
"ws": "^8.18.0",
|
|
178
178
|
"y-websocket": "^3.0.0",
|
|
179
179
|
"yjs": "^13.6.30",
|
|
180
|
-
"@foir/
|
|
181
|
-
"@foir/
|
|
180
|
+
"@foir/access-vocab": "0.0.0",
|
|
181
|
+
"@foir/proto-ts": "0.109.0"
|
|
182
182
|
},
|
|
183
183
|
"engines": {
|
|
184
184
|
"node": ">=18.0.0"
|