@foir/sdk 0.21.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 +84 -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 +234 -135
- package/dist/client.js +4 -4
- package/dist/data/builtins.d.ts +26 -26
- package/dist/data/client.d.ts +2 -2
- package/dist/data/decimal.d.ts +1 -1
- package/dist/data/index.d.ts +1 -1
- package/dist/data/manifest.d.ts +1 -1
- package/dist/data/schema.d.ts +1 -1
- package/dist/data/transport.d.ts +10 -10
- package/dist/data/ws-transport.d.ts +1 -1
- package/dist/data.js +6 -6
- package/dist/graphql/content-client.d.ts +13 -13
- 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 +77 -56
- package/dist/placement/index.d.ts +13 -13
- package/dist/placement.js +9 -6
- package/dist/push.js +61 -40
- 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 +119 -95
- package/dist/realtime/index.d.ts +1 -1
- package/dist/server/callback-client.d.ts +4 -4
- package/dist/server/events-client.d.ts +16 -16
- package/dist/server/operation-types.d.ts +8 -8
- package/dist/server/require-scoped-token.d.ts +3 -3
- package/dist/server/types.d.ts +1 -1
- package/dist/server/verify-scoped-token.d.ts +3 -3
- package/dist/server.js +16 -13
- package/dist/transport/foir-http.d.ts +13 -13
- package/dist/types/index.d.ts +2 -2
- package/package.json +3 -3
package/dist/auth/access.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* What the signed-in
|
|
2
|
+
* What the signed-in user may see: the roles they hold and the scopes those
|
|
3
3
|
* roles resolve to, as the platform resolved them for the request that asked.
|
|
4
4
|
*
|
|
5
5
|
* Use it to decide what to SHOW: a staff menu, an edit button. It guards
|
|
@@ -14,48 +14,48 @@ import { type AuthOpOptions } from './ops';
|
|
|
14
14
|
import { type SessionAdapter } from './auth';
|
|
15
15
|
import type { FoirEnv } from '../graphql/foir-request';
|
|
16
16
|
/**
|
|
17
|
-
* A signed-in
|
|
17
|
+
* A signed-in user's access, as the platform resolved it. Plain data, so it
|
|
18
18
|
* can be returned from a loader or a server component and read on the other
|
|
19
19
|
* side with {@link hasRole} and {@link hasScope}.
|
|
20
20
|
*/
|
|
21
21
|
export interface FoirAccess {
|
|
22
|
-
/** The signed-in
|
|
23
|
-
|
|
22
|
+
/** The signed-in user. */
|
|
23
|
+
userId: string;
|
|
24
24
|
/**
|
|
25
|
-
* The keys of the roles the
|
|
25
|
+
* The keys of the roles the user holds here, now: across the project, and
|
|
26
26
|
* in the organization they stand in. An empty list means they hold none.
|
|
27
27
|
*
|
|
28
28
|
* `null` means the platform did not say, which is not the same thing: treat it
|
|
29
29
|
* as not known, never as "holds no role". {@link hasRole} answers false for
|
|
30
30
|
* it, and {@link requireRole} refuses with `roles_unknown` so an outage is not
|
|
31
|
-
* mistaken for a
|
|
31
|
+
* mistaken for a user who holds no role.
|
|
32
32
|
*/
|
|
33
33
|
roles: string[] | null;
|
|
34
34
|
/**
|
|
35
|
-
* Everything the
|
|
35
|
+
* Everything the user may do through this key: the set the platform's own
|
|
36
36
|
* checks read for the request. Ask it with {@link hasScope} and not by looking
|
|
37
37
|
* for a string, because a wider grant covers a narrower one.
|
|
38
38
|
*/
|
|
39
39
|
scopes: string[];
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
* The access of the
|
|
43
|
-
* carries no
|
|
42
|
+
* The access of the user a token belongs to, or `null` when the request
|
|
43
|
+
* carries no user. Pass the user's access token as
|
|
44
44
|
* `options.bearerToken`.
|
|
45
45
|
*/
|
|
46
|
-
export declare function
|
|
46
|
+
export declare function userAccess(env: FoirEnv, options?: AuthOpOptions): Promise<FoirAccess | null>;
|
|
47
47
|
/**
|
|
48
|
-
* The access of the session's
|
|
48
|
+
* The access of the session's user, or `null` when nobody is signed in.
|
|
49
49
|
* Renews a lapsed session once, as every signed-in read does.
|
|
50
50
|
*/
|
|
51
51
|
export declare function getSessionAccess(adapter: SessionAdapter, env: FoirEnv): Promise<FoirAccess | null>;
|
|
52
52
|
/**
|
|
53
|
-
* Whether the
|
|
53
|
+
* Whether the user holds the role with this key. False when nobody is
|
|
54
54
|
* signed in, and false when their roles are not known.
|
|
55
55
|
*/
|
|
56
56
|
export declare function hasRole(access: FoirAccess | null | undefined, key: string): boolean;
|
|
57
57
|
/**
|
|
58
|
-
* Whether the
|
|
58
|
+
* Whether the user's scopes cover this one, by the platform's rule: a wider
|
|
59
59
|
* reach covers a narrower one (`records:read.all` covers `records:read`), a
|
|
60
60
|
* grant for every model covers one model (`records:write` covers
|
|
61
61
|
* `records:write:post`), and never the other way round. A scope that is not a
|
|
@@ -66,11 +66,11 @@ export declare function hasScope(access: FoirAccess | null | undefined, scope: s
|
|
|
66
66
|
export type FoirAccessDenial =
|
|
67
67
|
/** Nobody is signed in. */
|
|
68
68
|
'signed_out'
|
|
69
|
-
/** The
|
|
69
|
+
/** The user does not hold the role. */
|
|
70
70
|
| 'role_not_held'
|
|
71
|
-
/** The platform did not say which roles the
|
|
71
|
+
/** The platform did not say which roles the user holds. */
|
|
72
72
|
| 'roles_unknown'
|
|
73
|
-
/** The
|
|
73
|
+
/** The user's scopes do not cover the scope. */
|
|
74
74
|
| 'scope_not_held';
|
|
75
75
|
/**
|
|
76
76
|
* What {@link requireRole} and {@link requireScope} throw. Catch it by class or
|
|
@@ -90,16 +90,16 @@ export declare class FoirAccessDeniedError extends Error {
|
|
|
90
90
|
});
|
|
91
91
|
}
|
|
92
92
|
/**
|
|
93
|
-
* Require a signed-in
|
|
93
|
+
* Require a signed-in user who holds the role with this key, in a server
|
|
94
94
|
* route or a loader. Returns their access; throws
|
|
95
95
|
* {@link FoirAccessDeniedError} otherwise.
|
|
96
96
|
*
|
|
97
|
-
* This keeps a page from rendering for the wrong
|
|
97
|
+
* This keeps a page from rendering for the wrong user. It is not what
|
|
98
98
|
* protects the data: the platform checks every request the page then makes.
|
|
99
99
|
*/
|
|
100
100
|
export declare function requireRole(adapter: SessionAdapter, env: FoirEnv, key: string): Promise<FoirAccess>;
|
|
101
101
|
/**
|
|
102
|
-
* Require a signed-in
|
|
102
|
+
* Require a signed-in user whose scopes cover this one, by the rule
|
|
103
103
|
* {@link hasScope} reads. Returns their access; throws
|
|
104
104
|
* {@link FoirAccessDeniedError} otherwise.
|
|
105
105
|
*/
|
package/dist/auth/auth.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Foir
|
|
2
|
+
* Foir sign-in for your app's users: framework-agnostic flows, token
|
|
3
|
+
* lifecycle, and guards.
|
|
3
4
|
*
|
|
4
5
|
* Session I/O is injected via {@link SessionAdapter} (no Hydrogen/React-Router
|
|
5
6
|
* coupling). Pre-login flows authenticate with the project `x-api-key`;
|
|
6
|
-
* post-login reads add the
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* post-login reads add the user's bearer. Anything project-specific about the
|
|
8
|
+
* user (a profile record, say) is resolved by a consumer-supplied
|
|
9
|
+
* `resolveUser`: the SDK never hardcodes a per-project profile shape.
|
|
9
10
|
*
|
|
10
11
|
* TRANSPORT. Every flow below addresses the server's generated `auth` namespace
|
|
11
12
|
* through `./ops`, which is `@foir/sdk/data`'s op transport. This module holds
|
|
@@ -16,23 +17,17 @@
|
|
|
16
17
|
*/
|
|
17
18
|
import { type FoirEnv } from '../graphql/foir-request';
|
|
18
19
|
import { type AuthOpName, type AuthOpOptions } from './ops';
|
|
19
|
-
import { type AuthConfig, type AuthProviderPublic, type CurrentUser, type
|
|
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, type WaitlistJoinResult } from './operations';
|
|
20
21
|
import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
|
|
21
22
|
/** An access token + its rotating refresh token. */
|
|
22
23
|
export interface FoirAuthTokens {
|
|
23
24
|
token: string;
|
|
24
25
|
refreshToken: string;
|
|
25
26
|
}
|
|
26
|
-
/** The
|
|
27
|
-
export interface FoirCustomer {
|
|
28
|
-
id: string;
|
|
29
|
-
email: string;
|
|
30
|
-
}
|
|
31
|
-
/** The resolved customer for a guarded surface. */
|
|
27
|
+
/** The signed-in user an auth result and a session carry. */
|
|
32
28
|
export interface FoirUser {
|
|
33
29
|
id: string;
|
|
34
30
|
email: string;
|
|
35
|
-
isAdmin: boolean;
|
|
36
31
|
}
|
|
37
32
|
/**
|
|
38
33
|
* Injected session store. Methods may be sync or async so consumers can plug
|
|
@@ -44,39 +39,70 @@ export interface SessionAdapter {
|
|
|
44
39
|
unset(key: string): void | Promise<void>;
|
|
45
40
|
}
|
|
46
41
|
/** Login with email + password. Requires the project to have passwords enabled. */
|
|
47
|
-
export declare function
|
|
42
|
+
export declare function login(env: FoirEnv, vars: {
|
|
48
43
|
email: string;
|
|
49
44
|
password: string;
|
|
50
|
-
}): Promise<
|
|
51
|
-
/** Register a new
|
|
52
|
-
export declare function
|
|
45
|
+
}): Promise<AuthResult>;
|
|
46
|
+
/** Register a new user with email + password. */
|
|
47
|
+
export declare function register(env: FoirEnv, vars: {
|
|
53
48
|
email: string;
|
|
54
49
|
password: string;
|
|
55
|
-
}): Promise<
|
|
56
|
-
/** Request a one-time password be emailed to the
|
|
57
|
-
export declare function
|
|
50
|
+
}): Promise<AuthResult>;
|
|
51
|
+
/** Request a one-time password be emailed to the user. */
|
|
52
|
+
export declare function requestOTP(env: FoirEnv, email: string): Promise<boolean>;
|
|
58
53
|
/** Complete an OTP login. */
|
|
59
|
-
export declare function
|
|
54
|
+
export declare function loginWithOTP(env: FoirEnv, vars: {
|
|
60
55
|
email: string;
|
|
61
56
|
otp: string;
|
|
62
|
-
}): Promise<
|
|
57
|
+
}): Promise<AuthResult>;
|
|
63
58
|
/** Rotate an access token using a refresh token. */
|
|
64
|
-
export declare function
|
|
59
|
+
export declare function refreshTokens(env: FoirEnv, refreshToken: string): Promise<AuthResult>;
|
|
65
60
|
/**
|
|
66
61
|
* Initiate provider (OAuth/OIDC) login — returns a `redirectUrl` to send the
|
|
67
62
|
* user to, or an inline `token` for non-redirect providers.
|
|
68
63
|
*/
|
|
69
|
-
export declare function
|
|
64
|
+
export declare function loginWithProvider(env: FoirEnv, input: ProviderLoginInput): Promise<ProviderLoginResult>;
|
|
70
65
|
/** Complete a provider login from the OAuth callback (`code`/`state`). */
|
|
71
|
-
export declare function
|
|
66
|
+
export declare function providerCallback(env: FoirEnv, input: ProviderCallbackInput): Promise<AuthResult>;
|
|
72
67
|
/** Verify an OTP issued by an external provider. */
|
|
73
|
-
export declare function
|
|
68
|
+
export declare function providerVerifyOTP(env: FoirEnv, input: ProviderOTPVerifyInput): Promise<AuthResult>;
|
|
69
|
+
/**
|
|
70
|
+
* Why a provider sign-in did not finish, as the platform said it: a stable code
|
|
71
|
+
* your app shows its own words for (`signup_closed`, `provider_named_no_address`,
|
|
72
|
+
* `account_unavailable`, ...). When the code is `link_code_sent`, the sign-in
|
|
73
|
+
* named an address somebody already holds, through a provider that does not
|
|
74
|
+
* prove the address is theirs, and a code was sent to it: `pending` names the
|
|
75
|
+
* wait, and {@link confirmProviderLink} finishes the sign-in with that code.
|
|
76
|
+
* Null for any other failure.
|
|
77
|
+
*/
|
|
78
|
+
export declare function providerSignInRefusalOf(error: unknown): {
|
|
79
|
+
code: string;
|
|
80
|
+
pending?: string;
|
|
81
|
+
} | null;
|
|
82
|
+
/**
|
|
83
|
+
* Finish a provider sign-in that is waiting for the code sent to its address,
|
|
84
|
+
* connecting the provider to the account that holds the address. See
|
|
85
|
+
* {@link providerSignInRefusalOf}.
|
|
86
|
+
*/
|
|
87
|
+
export declare function confirmProviderLink(env: FoirEnv, input: {
|
|
88
|
+
pending: string;
|
|
89
|
+
code: string;
|
|
90
|
+
}): Promise<AuthResult>;
|
|
91
|
+
/**
|
|
92
|
+
* Send a new code for a provider sign-in that is waiting for one. The new code
|
|
93
|
+
* replaces the one before, and the answer names the new wait.
|
|
94
|
+
*/
|
|
95
|
+
export declare function resendProviderLinkCode(env: FoirEnv, input: {
|
|
96
|
+
pending: string;
|
|
97
|
+
}): Promise<{
|
|
98
|
+
pending: string;
|
|
99
|
+
}>;
|
|
74
100
|
/** List the project's configured auth providers (for a login UI). */
|
|
75
101
|
export declare function getAuthProviders(env: FoirEnv): Promise<AuthProviderPublic[]>;
|
|
76
102
|
/**
|
|
77
103
|
* Read the project's public auth configuration — which built-in methods are
|
|
78
104
|
* enabled and which external providers to offer. This is what a login screen
|
|
79
|
-
* renders itself from, before any
|
|
105
|
+
* renders itself from, before any user token exists.
|
|
80
106
|
*/
|
|
81
107
|
export declare function getAuthConfig(env: FoirEnv): Promise<AuthConfig>;
|
|
82
108
|
/**
|
|
@@ -85,55 +111,55 @@ export declare function getAuthConfig(env: FoirEnv): Promise<AuthConfig>;
|
|
|
85
111
|
*/
|
|
86
112
|
export declare function getDefaultAuthProvider(env: FoirEnv): Promise<DefaultAuthProvider | null>;
|
|
87
113
|
/**
|
|
88
|
-
* Read the project's
|
|
114
|
+
* Read the project's signup mode (public/restricted/waitlist/closed)
|
|
89
115
|
* so a self-hosted frontend can render the right signup surface proactively.
|
|
90
116
|
*/
|
|
91
|
-
export declare function
|
|
117
|
+
export declare function signupContext(env: FoirEnv): Promise<SignupContext>;
|
|
92
118
|
/** Join the project's signup waitlist. The project must be in waitlist mode. */
|
|
93
|
-
export declare function
|
|
119
|
+
export declare function joinWaitlist(env: FoirEnv, email: string): Promise<WaitlistJoinResult>;
|
|
94
120
|
/**
|
|
95
121
|
* Send a password-reset email. Resolves `true` whether or not the address has
|
|
96
122
|
* an account: the server answers identically either way, on purpose, so this
|
|
97
123
|
* endpoint cannot be used to test whether someone has signed up.
|
|
98
124
|
*/
|
|
99
|
-
export declare function
|
|
125
|
+
export declare function requestPasswordReset(env: FoirEnv, email: string): Promise<boolean>;
|
|
100
126
|
/**
|
|
101
127
|
* Redeem an emailed reset token and set a new password. Returns a fresh session
|
|
102
|
-
*
|
|
128
|
+
* (resetting a password signs the user in), so pair it with
|
|
103
129
|
* {@link saveSession}, or use {@link resetPasswordAndSignIn} which does both.
|
|
104
130
|
*/
|
|
105
|
-
export declare function
|
|
131
|
+
export declare function resetPassword(env: FoirEnv, vars: {
|
|
106
132
|
token: string;
|
|
107
133
|
newPassword: string;
|
|
108
|
-
}): Promise<
|
|
134
|
+
}): Promise<AuthResult>;
|
|
109
135
|
/** Redeem an emailed verification token. */
|
|
110
|
-
export declare function
|
|
136
|
+
export declare function verifyEmail(env: FoirEnv, token: string): Promise<boolean>;
|
|
111
137
|
/**
|
|
112
|
-
* Change the calling
|
|
138
|
+
* Change the calling user's password. Requires the current password as well
|
|
113
139
|
* as the bearer, so a stolen token alone cannot take the account over.
|
|
114
140
|
*/
|
|
115
|
-
export declare function
|
|
141
|
+
export declare function updatePassword(env: FoirEnv, vars: {
|
|
116
142
|
currentPassword: string;
|
|
117
143
|
newPassword: string;
|
|
118
144
|
}, options?: AuthOpOptions): Promise<boolean>;
|
|
119
145
|
/**
|
|
120
|
-
* The calling
|
|
146
|
+
* The calling user, or `null` when the request carried no user token.
|
|
121
147
|
* An absent session is an answer rather than a failure, so this does not throw.
|
|
122
148
|
*/
|
|
123
149
|
export declare function currentUser(env: FoirEnv, options?: AuthOpOptions): Promise<CurrentUser | null>;
|
|
124
150
|
/**
|
|
125
|
-
* The stored upstream provider token for the calling
|
|
151
|
+
* The stored upstream provider token for the calling user, refreshed
|
|
126
152
|
* transparently by the server when it has expired. `null` when there is no
|
|
127
|
-
*
|
|
153
|
+
* user token on the request or none stored for that provider.
|
|
128
154
|
*/
|
|
129
155
|
export declare function myProviderToken(env: FoirEnv, providerKey: string, options?: AuthOpOptions): Promise<ProviderToken | null>;
|
|
130
156
|
/**
|
|
131
157
|
* End the session server-side, revoking stored provider tokens. This does NOT
|
|
132
158
|
* clear local session state — {@link signOut} is the pairing that does both.
|
|
133
159
|
*/
|
|
134
|
-
export declare function
|
|
160
|
+
export declare function logout(env: FoirEnv, options?: AuthOpOptions): Promise<boolean>;
|
|
135
161
|
/**
|
|
136
|
-
* Delete the calling
|
|
162
|
+
* Delete the calling user's account and their owned records and files.
|
|
137
163
|
* Irreversible. `password` is required for password accounts and omitted for
|
|
138
164
|
* provider-only ones; the server enforces the actual re-auth gate and fails
|
|
139
165
|
* closed, so omitting it is refused rather than allowed.
|
|
@@ -143,60 +169,140 @@ export declare function deleteMyAccount(env: FoirEnv, vars: {
|
|
|
143
169
|
password?: string;
|
|
144
170
|
}, options?: AuthOpOptions): Promise<DeleteAccountResult>;
|
|
145
171
|
/**
|
|
146
|
-
*
|
|
172
|
+
* Change the calling user's own name, picture or language. It asks no
|
|
173
|
+
* permission: these are theirs.
|
|
174
|
+
*/
|
|
175
|
+
export declare function updateMyProfile(env: FoirEnv, change: MyProfileChange, options?: AuthOpOptions): Promise<MyProfile>;
|
|
176
|
+
/**
|
|
177
|
+
* The apps the calling user has agreed may act for them, and what each may
|
|
178
|
+
* carry: the list an account page shows with a way to take each one back.
|
|
179
|
+
*/
|
|
180
|
+
export declare function myAuthorizedApps(env: FoirEnv, options?: AuthOpOptions): Promise<AuthorizedApp[]>;
|
|
181
|
+
/**
|
|
182
|
+
* Take back an app's authority to act for the calling user. Its tokens for
|
|
183
|
+
* them stop working at once, and it has to ask them again.
|
|
184
|
+
*/
|
|
185
|
+
export declare function revokeAuthorizedApp(env: FoirEnv, id: string, options?: AuthOpOptions): Promise<boolean>;
|
|
186
|
+
/** Where the calling user is signed in, and the personal access tokens they hold. */
|
|
187
|
+
export declare function mySessions(env: FoirEnv, options?: AuthOpOptions): Promise<MySession[]>;
|
|
188
|
+
/** Sign the calling user out of one session, or revoke one of their tokens, by its id. */
|
|
189
|
+
export declare function endMySession(env: FoirEnv, id: string, options?: AuthOpOptions): Promise<boolean>;
|
|
190
|
+
/**
|
|
191
|
+
* Sign the calling user out of every session and every app, and answer how
|
|
192
|
+
* many sessions ended. Their personal access tokens stay. `keepThis` keeps the
|
|
193
|
+
* session making the request.
|
|
194
|
+
*/
|
|
195
|
+
export declare function signOutEverywhere(env: FoirEnv, vars?: {
|
|
196
|
+
keepThis?: boolean;
|
|
197
|
+
}, options?: AuthOpOptions): Promise<number>;
|
|
198
|
+
/**
|
|
199
|
+
* Make a personal access token for the calling user: a standing credential
|
|
200
|
+
* they present beside the app's key, as `Authorization: Bearer`. Its value is
|
|
201
|
+
* answered here once.
|
|
202
|
+
*/
|
|
203
|
+
export declare function createPersonalAccessToken(env: FoirEnv, vars: {
|
|
204
|
+
name: string;
|
|
205
|
+
expiresInDays: number;
|
|
206
|
+
}, options?: AuthOpOptions): Promise<NewPersonalAccessToken>;
|
|
207
|
+
/** The providers the calling user signs in with. */
|
|
208
|
+
export declare function mySignInMethods(env: FoirEnv, options?: AuthOpOptions): Promise<MySignInMethod[]>;
|
|
209
|
+
/**
|
|
210
|
+
* Start connecting one of the project's providers to the calling user's
|
|
211
|
+
* account, and answer the provider's address to send the browser to. It comes
|
|
212
|
+
* back to `returnTo` with `connected` or `connect_error` added.
|
|
213
|
+
*/
|
|
214
|
+
export declare function connectSignInMethod(env: FoirEnv, vars: {
|
|
215
|
+
provider: string;
|
|
216
|
+
returnTo: string;
|
|
217
|
+
}, options?: AuthOpOptions): Promise<string>;
|
|
218
|
+
/** Disconnect a provider from the calling user's account. */
|
|
219
|
+
export declare function disconnectSignInMethod(env: FoirEnv, provider: string, options?: AuthOpOptions): Promise<boolean>;
|
|
220
|
+
/** The second factors, passkeys and recovery codes the calling user holds. */
|
|
221
|
+
export declare function myFactors(env: FoirEnv, options?: AuthOpOptions): Promise<MyFactor[]>;
|
|
222
|
+
/**
|
|
223
|
+
* Begin proving a second factor with the calling user's password.
|
|
224
|
+
* {@link completeStepUp} finishes it; for a user who holds none, the answer's
|
|
225
|
+
* `elevatedHandle` is already the proof.
|
|
226
|
+
*/
|
|
227
|
+
export declare function beginStepUp(env: FoirEnv, password: string, options?: AuthOpOptions): Promise<StepUp>;
|
|
228
|
+
/**
|
|
229
|
+
* Prove a second factor with exactly one of a passkey's or security key's
|
|
230
|
+
* assertion, an authenticator app's code or a recovery code, which is then
|
|
231
|
+
* spent. The session making the request counts as two factors from here, and
|
|
232
|
+
* the answer is an elevated handle for one change to the user's factors in
|
|
233
|
+
* the next ten minutes.
|
|
234
|
+
*/
|
|
235
|
+
export declare function completeStepUp(env: FoirEnv, vars: {
|
|
236
|
+
handle: string;
|
|
237
|
+
webauthnAssertionJson?: string;
|
|
238
|
+
totpCode?: string;
|
|
239
|
+
recoveryCode?: string;
|
|
240
|
+
}, options?: AuthOpOptions): Promise<string>;
|
|
241
|
+
/**
|
|
242
|
+
* Begin adding `totp` (an authenticator app), `webauthn` (a security key held
|
|
243
|
+
* as a second factor) or `passkey` (a passkey that signs the user in on its
|
|
244
|
+
* own), with the `elevatedHandle` {@link beginStepUp} or
|
|
245
|
+
* {@link completeStepUp} answered.
|
|
246
|
+
*/
|
|
247
|
+
export declare function beginFactorEnrollment(env: FoirEnv, vars: {
|
|
248
|
+
method: 'webauthn' | 'totp' | 'passkey';
|
|
249
|
+
elevatedHandle: string;
|
|
250
|
+
}, options?: AuthOpOptions): Promise<FactorEnrollment>;
|
|
251
|
+
/**
|
|
252
|
+
* Finish adding a second factor, with the `navigator.credentials.create`
|
|
253
|
+
* result or the authenticator app's code.
|
|
254
|
+
*/
|
|
255
|
+
export declare function completeFactorEnrollment(env: FoirEnv, vars: {
|
|
256
|
+
handle: string;
|
|
257
|
+
webauthnAttestationJson?: string;
|
|
258
|
+
totpCode?: string;
|
|
259
|
+
name?: string;
|
|
260
|
+
}, options?: AuthOpOptions): Promise<boolean>;
|
|
261
|
+
/** Remove one of the calling user's factors, with an elevated handle from {@link completeStepUp}. */
|
|
262
|
+
export declare function removeFactor(env: FoirEnv, vars: {
|
|
263
|
+
elevatedHandle: string;
|
|
264
|
+
kind: string;
|
|
265
|
+
id: string;
|
|
266
|
+
}, options?: AuthOpOptions): Promise<boolean>;
|
|
267
|
+
/**
|
|
268
|
+
* Make the calling user new recovery codes, with an elevated handle from
|
|
269
|
+
* {@link completeStepUp}. They replace any held and are answered here once.
|
|
270
|
+
*/
|
|
271
|
+
export declare function makeRecoveryCodes(env: FoirEnv, elevatedHandle: string, options?: AuthOpOptions): Promise<string[]>;
|
|
272
|
+
/**
|
|
273
|
+
* The organizations this user may stand in.
|
|
147
274
|
*
|
|
148
|
-
* Answered from the
|
|
149
|
-
* hold one in and every organization beneath it, so
|
|
150
|
-
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
275
|
+
* Answered from the calling user's own memberships: each organization they
|
|
276
|
+
* hold one in and every organization beneath it, so the list is exactly the
|
|
277
|
+
* set a request of theirs may name, and a user with no memberships sees an
|
|
278
|
+
* empty list rather than a refusal. Requires the user's bearer; it is not the
|
|
279
|
+
* project's inventory and cannot be used as one.
|
|
280
|
+
*
|
|
281
|
+
* A MEMBERSHIP REACHES DOWNWARD. Someone who is a member of an organization may
|
|
282
|
+
* stand in any organization beneath it without holding a row of their own
|
|
283
|
+
* there. The reverse never works: a membership at a branch stands in no
|
|
284
|
+
* ancestor and in no sibling branch, and a request naming either is refused as
|
|
285
|
+
* not-found, the same answer an organization that does not exist gets.
|
|
286
|
+
* {@link MyOrganization.role} names ONE of the roles that apply where the
|
|
287
|
+
* person stands (the membership held in the organization itself, when there is
|
|
288
|
+
* one); their authority there is the union of every membership that reaches
|
|
289
|
+
* it, together with their own roles in the project.
|
|
153
290
|
*/
|
|
154
291
|
export declare function myOrganizations(env: FoirEnv, vars?: {
|
|
155
292
|
first?: number;
|
|
156
293
|
after?: string;
|
|
157
294
|
}, options?: AuthOpOptions): Promise<MyOrganization[]>;
|
|
158
|
-
/**
|
|
159
|
-
* Stand in one of them, for this customer, until they choose otherwise.
|
|
160
|
-
*
|
|
161
|
-
* Set once — not per call. The selection is stored against the customer and
|
|
162
|
-
* resolved into the claim on every subsequent request, so nothing downstream
|
|
163
|
-
* takes an org argument. Pass an empty `orgId` to clear it and return to the
|
|
164
|
-
* project-shared view.
|
|
165
|
-
*
|
|
166
|
-
* The server authorises the choice against a live membership and refuses
|
|
167
|
-
* otherwise, so a caller cannot select their way into an organization they do
|
|
168
|
-
* not run.
|
|
169
|
-
*
|
|
170
|
-
* A MEMBERSHIP REACHES DOWNWARD, which is what "a live membership" means now
|
|
171
|
-
* that organizations nest. Someone who is a member of an organization may stand in any
|
|
172
|
-
* organization BENEATH it without holding a row of their own there, and
|
|
173
|
-
* {@link myOrganizations} offers exactly that set — so any id it returned can
|
|
174
|
-
* be passed here. The reverse never works: a membership at a branch stands in
|
|
175
|
-
* no ancestor and in no sibling branch, and both are refused as not-found,
|
|
176
|
-
* which is the same answer an organization that does not exist gets.
|
|
177
|
-
*
|
|
178
|
-
* WHAT STANDING THERE MEANS is every membership that reaches the organization
|
|
179
|
-
* selected. Reach a client through a membership above it and the role shown is
|
|
180
|
-
* that membership's; authority is the union of every membership that reaches
|
|
181
|
-
* it, together with the customer's own roles in the project.
|
|
182
|
-
* {@link MyOrganization.role} names ONE of those roles (the membership held in
|
|
183
|
-
* the organization itself, when there is one), so it is part of what the next
|
|
184
|
-
* request resolves and not the whole of it.
|
|
185
|
-
*/
|
|
186
|
-
export declare function selectOrganization(env: FoirEnv, vars: {
|
|
187
|
-
orgId: string;
|
|
188
|
-
}, options?: AuthOpOptions): Promise<SelectedOrganization>;
|
|
189
295
|
/**
|
|
190
296
|
* Found an organization, as its first member.
|
|
191
297
|
*
|
|
192
298
|
* The caller decides the NAME and, optionally, where it hangs — and nothing
|
|
193
|
-
* else. The founder is the
|
|
299
|
+
* else. The founder is the user behind the bearer, the id is minted
|
|
194
300
|
* server-side the way a project's and a tenant's are, and the role they hold in
|
|
195
301
|
* the new organization is the one the PROJECT named in its settings
|
|
196
|
-
* (`
|
|
302
|
+
* (`organizationSignupRole`). The server creates the organization and that one
|
|
197
303
|
* membership together.
|
|
198
304
|
*
|
|
199
|
-
* `parentId` FOUNDS A CHILD, which is what lets
|
|
305
|
+
* `parentId` FOUNDS A CHILD, which is what lets the people you serve create their own
|
|
200
306
|
* sub-tenants with no administrator in the loop — a reseller adding a client, a
|
|
201
307
|
* franchise adding a branch. It is resolved against the caller's own
|
|
202
308
|
* MEMBERSHIPS, not merely against the project: on this lane "an organization
|
|
@@ -204,59 +310,59 @@ export declare function selectOrganization(env: FoirEnv, vars: {
|
|
|
204
310
|
* not-found. Omit it to found a root, which is what every registration did
|
|
205
311
|
* before organizations nested.
|
|
206
312
|
*
|
|
207
|
-
* Exists on the wire only while the project's `
|
|
313
|
+
* Exists on the wire only while the project's `organizationSignupEnabled` setting
|
|
208
314
|
* is on; a project that never opted in has no such field, and this refuses
|
|
209
315
|
* naming it. Returns the same entry `myOrganizations` would then list, role
|
|
210
|
-
* included, so the application can
|
|
316
|
+
* included, so the application can name it on its next request.
|
|
211
317
|
*/
|
|
212
318
|
export declare function registerOrganization(env: FoirEnv, vars: {
|
|
213
319
|
name: string;
|
|
214
320
|
parentId?: string;
|
|
215
321
|
}, options?: AuthOpOptions): Promise<MyOrganization>;
|
|
216
322
|
/**
|
|
217
|
-
* Persist an auth result to the session; returns the
|
|
323
|
+
* Persist an auth result to the session; returns the signed-in user.
|
|
218
324
|
*
|
|
219
|
-
* A result that names no
|
|
325
|
+
* A result that names no user keeps the identity already stored. A refresh
|
|
220
326
|
* rotates the tokens of a session that already knows who it belongs to, and a
|
|
221
|
-
* platform that answers a refresh without the
|
|
327
|
+
* platform that answers a refresh without the user must not blank that:
|
|
222
328
|
* {@link requireAuth} reads the stored identity, so a valid token beside an
|
|
223
329
|
* empty identity reads as signed out.
|
|
224
330
|
*/
|
|
225
|
-
export declare function saveSession(adapter: SessionAdapter, result:
|
|
331
|
+
export declare function saveSession(adapter: SessionAdapter, result: AuthResult): Promise<FoirUser>;
|
|
226
332
|
/** Clear all Foir auth keys from the session. */
|
|
227
333
|
export declare function clearSession(adapter: SessionAdapter): Promise<void>;
|
|
228
334
|
/** Read the stored access token (null when signed out). */
|
|
229
335
|
export declare function getSessionToken(adapter: SessionAdapter): Promise<string | null>;
|
|
230
|
-
/** Read the stored
|
|
231
|
-
export declare function
|
|
336
|
+
/** Read the stored user (null when signed out). */
|
|
337
|
+
export declare function getSessionUser(adapter: SessionAdapter): Promise<FoirUser | null>;
|
|
232
338
|
/** Login with email + password and persist the session. */
|
|
233
|
-
export declare function
|
|
339
|
+
export declare function signInWithPassword(adapter: SessionAdapter, env: FoirEnv, vars: {
|
|
234
340
|
email: string;
|
|
235
341
|
password: string;
|
|
236
|
-
}): Promise<
|
|
342
|
+
}): Promise<FoirUser>;
|
|
237
343
|
/** Register with email + password and persist the session. */
|
|
238
|
-
export declare function
|
|
344
|
+
export declare function signUpWithPassword(adapter: SessionAdapter, env: FoirEnv, vars: {
|
|
239
345
|
email: string;
|
|
240
346
|
password: string;
|
|
241
|
-
}): Promise<
|
|
347
|
+
}): Promise<FoirUser>;
|
|
242
348
|
/** Complete an OTP login and persist the session. */
|
|
243
|
-
export declare function
|
|
349
|
+
export declare function signInWithOTP(adapter: SessionAdapter, env: FoirEnv, vars: {
|
|
244
350
|
email: string;
|
|
245
351
|
otp: string;
|
|
246
|
-
}): Promise<
|
|
352
|
+
}): Promise<FoirUser>;
|
|
247
353
|
/**
|
|
248
354
|
* Complete a forgot-password flow: redeem the emailed token, set the new
|
|
249
355
|
* password, and persist the session the server mints. The peer of
|
|
250
|
-
* {@link
|
|
356
|
+
* {@link signInWithPassword} for the branch where the user cannot log in.
|
|
251
357
|
*/
|
|
252
358
|
export declare function resetPasswordAndSignIn(adapter: SessionAdapter, env: FoirEnv, vars: {
|
|
253
359
|
token: string;
|
|
254
360
|
newPassword: string;
|
|
255
|
-
}): Promise<
|
|
361
|
+
}): Promise<FoirUser>;
|
|
256
362
|
/**
|
|
257
363
|
* End the session on both sides: revoke it server-side, then clear local state.
|
|
258
364
|
*
|
|
259
|
-
* The local clear happens even when the server call fails.
|
|
365
|
+
* The local clear happens even when the server call fails. Someone who
|
|
260
366
|
* pressed sign-out and got a network error must not be left holding a session
|
|
261
367
|
* on the device in front of them — the token expires on its own, and the
|
|
262
368
|
* failure mode of clearing anyway (one orphaned provider token) is strictly
|
|
@@ -295,16 +401,18 @@ export declare function authedRequest<TData, TVariables>(adapter: SessionAdapter
|
|
|
295
401
|
* management, which is the half a generated namespace does not give you.
|
|
296
402
|
*/
|
|
297
403
|
export declare function authedOp<T>(adapter: SessionAdapter, env: FoirEnv, op: AuthOpName, args?: Record<string, unknown>): Promise<T>;
|
|
298
|
-
export interface RequireAuthOptions {
|
|
404
|
+
export interface RequireAuthOptions<TUser = FoirUser> {
|
|
299
405
|
/**
|
|
300
|
-
* Resolve the
|
|
301
|
-
*
|
|
302
|
-
*
|
|
406
|
+
* Resolve what the guarded surface needs about the user from the session:
|
|
407
|
+
* typically the project's own profile record, read through
|
|
408
|
+
* {@link authedRequest}. Without it the guard answers the {@link FoirUser}
|
|
409
|
+
* the session holds. What the user may do is a role or a scope: guard on
|
|
410
|
+
* those with {@link requireRole} and {@link requireScope}.
|
|
303
411
|
*/
|
|
304
412
|
resolveUser?: (ctx: {
|
|
305
413
|
token: string;
|
|
306
|
-
|
|
307
|
-
}) =>
|
|
414
|
+
user: FoirUser;
|
|
415
|
+
}) => TUser | Promise<TUser>;
|
|
308
416
|
/**
|
|
309
417
|
* Called when there's no valid session — throw your framework's redirect
|
|
310
418
|
* (e.g. `throw redirect(loginUrl)`). Must not return.
|
|
@@ -319,14 +427,8 @@ export interface RequireAuthOptions {
|
|
|
319
427
|
env?: FoirEnv;
|
|
320
428
|
}
|
|
321
429
|
/**
|
|
322
|
-
* Require a signed-in
|
|
323
|
-
*
|
|
324
|
-
*
|
|
430
|
+
* Require a signed-in user. Answers what `resolveUser` resolves (or the
|
|
431
|
+
* session's {@link FoirUser}), or invokes `onUnauthenticated` when there's no
|
|
432
|
+
* session.
|
|
325
433
|
*/
|
|
326
|
-
export declare function requireAuth(adapter: SessionAdapter, options: RequireAuthOptions): Promise<
|
|
327
|
-
export interface RequireAdminOptions extends RequireAuthOptions {
|
|
328
|
-
/** Called when the user is authenticated but not an admin. Must not return. */
|
|
329
|
-
onForbidden: () => never | Promise<never>;
|
|
330
|
-
}
|
|
331
|
-
/** Require a signed-in customer who is also an admin. */
|
|
332
|
-
export declare function requireAdmin(adapter: SessionAdapter, options: RequireAdminOptions): Promise<FoirUser>;
|
|
434
|
+
export declare function requireAuth<TUser = FoirUser>(adapter: SessionAdapter, options: RequireAuthOptions<TUser>): Promise<TUser>;
|