You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(bb-auth-cognito): document the auth.admin opt-in surface in README
Add an 'Admin surface' section: opt-in via the admin options object, the
compile-time gate, actions scoping the IAM grant (not the typed method set),
the not-an-access-boundary caveat, group + lifecycle method tables, and the
session-freshness note (revokeUserSessions for immediate effect). Update the
generic-narrowing section for the const O type parameter (inline literals
narrow without as const).
Copy file name to clipboardExpand all lines: packages/bb-auth-cognito/README.md
+52-1Lines changed: 52 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -151,6 +151,49 @@ Requires `enablePasskeys: true` and a WebAuthn-configured pool (`webAuthnRelying
151
151
|`listPasskeys(context)`|`Promise<PasskeyDescription[]>`| List the signed-in user's registered passkeys (paginates internally). |
152
152
|`deletePasskey(context, credentialId)`|`Promise<void>`| Remove a registered passkey by `credentialId`. |
153
153
154
+
## Admin surface (`auth.admin`)
155
+
156
+
Server-side admin operations — group membership and user lifecycle — that act on **any** user by `username` (unlike the client methods above, which act on the signed-in user via `context`). These are **opt-in**: pass an `admin` options object to enable the `auth.admin` handle and grant the matching `Admin*` / `List*` IAM. A pool that never opts in has no admin grant — its synthesized role is identical to today.
157
+
158
+
```typescript
159
+
const auth =newAuthCognito(scope, 'auth', {
160
+
groups: ['admins'],
161
+
admin: { actions: ['groups'] }, // enable auth.admin; grant only group Admin* actions
162
+
});
163
+
164
+
// Always gate admin routes behind requireRole — these methods do NOT self-gate.
165
+
awaitauth.requireRole(ctx, 'admins');
166
+
awaitauth.admin.addUserToGroup('alice', 'admins'); // group narrowed via GroupOf<O>
167
+
```
168
+
169
+
-**Compile-time gate.** Without an `admin` options object, `auth.admin` is typed `AdminDisabled` and any access is a compile error whose message names the fix (`construct AuthCognito with { admin: {} }`). The getter also throws at runtime for untyped JS callers.
170
+
-**`actions` scopes the IAM grant, not the method set.**`actions: ['groups']` grants only the group `Admin*` actions; `['lifecycle']` only the user-lifecycle actions; omitted grants both. The **typed surface is always the full set** — narrowing the method types by `actions` is not possible without breaking `AuthCognito<O>` variance, so calling a method whose action wasn't granted fails at runtime with an IAM `AccessDenied`.
171
+
-**Not an access boundary.** Like every block on the shared backend Lambda, client and admin run under one role. Separation is by API surface + lint, not IAM — gate every admin route with `requireRole`.
172
+
173
+
**Group membership** (`actions: 'groups'`):
174
+
175
+
| Method | Returns | Description |
176
+
|---|---|---|
177
+
|`admin.addUserToGroup(username, group)`|`Promise<void>`| Add a user to a seeded group. `group` narrows to `GroupOf<O>`. Throws `GroupNotFound` for an unseeded group, `UserNotFound` for a missing user. |
178
+
|`admin.removeUserFromGroup(username, group)`|`Promise<void>`| Remove a user from a group. |
179
+
|`admin.listGroupsForUser(username)`|`Promise<GroupOf<O>[]>`| Groups the user belongs to. |
180
+
|`admin.listUsersInGroup(group)`|`Promise<AdminUser[]>`| Members of a group (paginates internally). |
181
+
182
+
**User lifecycle** (`actions: 'lifecycle'`):
183
+
184
+
| Method | Returns | Description |
185
+
|---|---|---|
186
+
|`admin.createUser(username, init?)`|`Promise<AdminUser>`| Create a user. `init`: `{ temporaryPassword?, attributes?, suppressInvite? }`. Conflicts with `UserAlreadyExists`. |
187
+
|`admin.deleteUser(username)`|`Promise<void>`| Delete a user (also strips them from every group). |
188
+
|`admin.disableUser(username)` / `admin.enableUser(username)`|`Promise<void>`| Toggle sign-in. A disabled user's `signIn` is rejected with `NotAuthorizedException`. |
189
+
|`admin.resetUserPassword(username)`|`Promise<void>`| Force the user to set a new password on next sign-in. |
190
+
|`admin.setUserPassword(username, password, permanent)`|`Promise<void>`| Set a password. `permanent: true` clears the force-change flag. |
191
+
|`admin.getUser(username)`|`Promise<AdminUser \| null>`| Look up a user, or `null` if absent. |
192
+
|`admin.scan()`|`AsyncIterable<AdminUser>`| Enumerate all users (paginates internally). |
193
+
|`admin.revokeUserSessions(username)`|`Promise<void>`| Revoke all the user's sessions (AWS: `AdminUserGlobalSignOut`) — forces immediate re-authentication. |
194
+
195
+
> **Session freshness.** A group change does not affect a user's **existing** session until their token refreshes — `requireRole` reads the `cognito:groups` claim, not live state. This is inherent Cognito behavior. The change applies on the next sign-in or `fetchAuthSession({ forceRefresh: true })`; for immediate effect, call `admin.revokeUserSessions(username)` to force re-auth.
196
+
154
197
## Options
155
198
156
199
```typescript
@@ -206,7 +249,15 @@ The `signInWith` option controls what end users sign in with. It maps to Cognito
206
249
207
250
## Using AuthCognito generically (literal-narrowing with `as const`)
208
251
209
-
`AuthCognito<O extends AuthCognitoOptions>` is generic on its options literal. When you pass the options object `as const`, TypeScript narrows method signatures to the exact values you configured — typos on group names, custom attributes, and MFA factors become compile errors instead of runtime surprises.
252
+
`AuthCognito<const O extends AuthCognitoOptions>` is generic on its options literal. Because the generic is a **`const` type parameter**, TypeScript narrows method signatures to the exact values you configured for **options passed inline** — typos on group names, custom attributes, and MFA factors become compile errors instead of runtime surprises, with no `as const` needed:
For an options object **declared separately** in a variable, add `as const` so its literals don't widen before reaching the constructor. The example below uses `as const` for that reason; the narrowing it produces is identical.
0 commit comments