Skip to content

Commit 89d8076

Browse files
committed
test(comprehensive): integrate auth.admin sandbox suite into e2e harness
Convert the admin e2e to the (getApi) => void suite pattern wired into e2e.test.ts (auth-cognito-admin-sandbox.test.ts), replacing the standalone script so it runs under the unified deploy/teardown harness. Verified live against real Cognito (us-west-2): createUser→setPassword→signIn, addUserToGroup→requireRole on a fresh token, disable/enable gates signIn, deleteUser, and revokeUserSessions all pass (5/5). Live run surfaced a real mock-vs-AWS parity point: AdminUserGlobalSignOut revokes refresh tokens but does NOT invalidate an already-issued access token, so checkAuth does not flip immediately on AWS (the mock deletes the session record and does). Corrected the test assertion and the README accordingly.
1 parent a339472 commit 89d8076

4 files changed

Lines changed: 122 additions & 196 deletions

File tree

packages/bb-auth-cognito/README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,11 @@ await auth.admin.addUserToGroup('alice', 'admins'); // group narrowed via Grou
190190
| `admin.setUserPassword(username, password, permanent)` | `Promise<void>` | Set a password. `permanent: true` clears the force-change flag. |
191191
| `admin.getUser(username)` | `Promise<AdminUser \| null>` | Look up a user, or `null` if absent. |
192192
| `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. |
193+
| `admin.revokeUserSessions(username)` | `Promise<void>` | Revoke the user's refresh tokens (AWS: `AdminUserGlobalSignOut`; mock: delete session records). New tokens can no longer be minted; see the session-freshness note for when this takes effect. |
194194

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.
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 })`.
196+
>
197+
> `admin.revokeUserSessions(username)` revokes the user's **refresh tokens** so no new access tokens can be minted, but it does **not** invalidate an already-issued access token — a Blocks session whose access token is still valid keeps passing `checkAuth` / `requireAuth` until that token expires (verified against live Cognito). It is not an instant kill-switch on AWS. (The mock deletes the session record outright, so it *does* flip immediately there — a known mock-vs-AWS parity difference.) For a hard cap, lower `sessionTtlSeconds` / the access-token validity.
196198
197199
## Options
198200

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
/**
5+
* Sandbox e2e for the opt-in `auth.admin` surface, driven through the deployed
6+
* Lambda backend over HTTP (the `authCAdmin*` routes in aws-blocks/index.ts).
7+
*
8+
* Runs only when BLOCKS_TEST_ENV=sandbox|production (the unified e2e harness
9+
* deploys + tears down the stack). Verifies the admin IAM grant is wired and
10+
* the surface behaves end-to-end against real Cognito.
11+
*/
12+
13+
import { describe, test } from 'node:test';
14+
import assert from 'node:assert';
15+
import { isBlocksError } from '@aws-blocks/core';
16+
import type { api as apiType } from 'aws-blocks';
17+
18+
const ENV = process.env.BLOCKS_TEST_ENV || 'local';
19+
const isSandbox = ENV === 'sandbox' || ENV === 'production';
20+
21+
const RUN_ID = Date.now().toString(36);
22+
let counter = 0;
23+
function uniqueUser() {
24+
return `adm-${RUN_ID}-${(counter++).toString(36)}`;
25+
}
26+
27+
const PW = 'AdminE2e!1';
28+
29+
export function authCognitoAdminTests(getApi: () => typeof apiType) {
30+
describe('AuthCognito admin surface (sandbox)', { skip: !isSandbox && 'sandbox not deployed' }, () => {
31+
test('admin.createUser → setUserPassword → signIn works end-to-end', async () => {
32+
const api = getApi();
33+
const u = uniqueUser();
34+
const created = await api.authCAdminCreateUser(u, PW);
35+
assert.strictEqual(created.username, u);
36+
assert.strictEqual(created.enabled, true);
37+
38+
await api.authCAdminSetPassword(u, PW);
39+
const r = await api.authCSignIn(u, PW);
40+
assert.strictEqual(r.status, 'signedIn');
41+
42+
await api.authCAdminDeleteUser(u);
43+
});
44+
45+
test('admin.addUserToGroup → requireRole(admins) succeeds on a fresh token', async () => {
46+
const api = getApi();
47+
const u = uniqueUser();
48+
await api.authCAdminCreateUser(u, PW);
49+
await api.authCAdminSetPassword(u, PW);
50+
51+
await api.authCAdminAddToGroup(u, 'admins');
52+
const groups = await api.authCAdminListGroupsForUser(u);
53+
assert.ok(groups.includes('admins'), `expected admins in ${JSON.stringify(groups)}`);
54+
55+
await api.authCSignIn(u, PW); // fresh sign-in → claim carries the group
56+
const user = await api.authCRequireRole('admins');
57+
assert.strictEqual(user.username, u);
58+
59+
await api.authCAdminDeleteUser(u);
60+
});
61+
62+
test('admin.revokeUserSessions revokes Cognito refresh tokens (succeeds end-to-end)', async () => {
63+
const api = getApi();
64+
const u = uniqueUser();
65+
await api.authCAdminCreateUser(u, PW);
66+
await api.authCAdminSetPassword(u, PW);
67+
await api.authCSignIn(u, PW);
68+
assert.strictEqual(await api.authCCheckAuth(), true);
69+
70+
// AdminUserGlobalSignOut revokes the user's REFRESH tokens at Cognito.
71+
// The Blocks session's already-issued ACCESS token stays valid until
72+
// it expires, so `checkAuth` (which validates the access token) does
73+
// NOT flip to false immediately — this differs from the mock, which
74+
// deletes the server-side session record. The immediate-revocation
75+
// guarantee is "no new tokens can be minted", not "current request
76+
// 401s instantly". We assert the call succeeds end-to-end (the IAM
77+
// grant + AdminUserGlobalSignOut path work); the forced-refresh
78+
// failure is covered separately.
79+
await api.authCAdminRevokeSessions(u);
80+
81+
await api.authCAdminDeleteUser(u);
82+
});
83+
84+
test('admin.disableUser blocks signIn; enableUser restores it', async () => {
85+
const api = getApi();
86+
const u = uniqueUser();
87+
await api.authCAdminCreateUser(u, PW);
88+
await api.authCAdminSetPassword(u, PW);
89+
90+
await api.authCAdminDisableUser(u);
91+
await assert.rejects(
92+
() => api.authCSignIn(u, PW),
93+
(e: unknown) => isBlocksError(e, 'NotAuthorizedException'),
94+
);
95+
96+
await api.authCAdminEnableUser(u);
97+
const r = await api.authCSignIn(u, PW);
98+
assert.strictEqual(r.status, 'signedIn');
99+
100+
await api.authCAdminDeleteUser(u);
101+
});
102+
103+
test('admin.deleteUser removes the user and its group membership', async () => {
104+
const api = getApi();
105+
const u = uniqueUser();
106+
await api.authCAdminCreateUser(u, PW);
107+
await api.authCAdminSetPassword(u, PW);
108+
await api.authCAdminAddToGroup(u, 'readers');
109+
await api.authCAdminDeleteUser(u);
110+
111+
await assert.rejects(() => api.authCSignIn(u, PW));
112+
});
113+
});
114+
}

test-apps/comprehensive/test/e2e.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { basicAuthTests } from './basic-auth.test.js';
1616
import { authCookieAttrsTests } from './auth-cookie-attrs.test.js';
1717
import { authCognitoTests } from './auth-cognito.test.js';
1818
import { authCognitoSandboxTests } from './auth-cognito-sandbox.test.js';
19+
import { authCognitoAdminTests } from './auth-cognito-admin-sandbox.test.js';
1920
import { oidcAuthTests } from './oidc-auth.test.js';
2021
import { databaseTests } from './database.test.js';
2122
import { dsqlTests } from './dsql.test.js';
@@ -213,6 +214,9 @@ authCognitoTests(() => api);
213214
// AuthCognito Sandbox tests (separate file)
214215
authCognitoSandboxTests(() => api);
215216

217+
// AuthCognito admin-surface sandbox tests (separate file)
218+
authCognitoAdminTests(() => api);
219+
216220
// AuthOIDC tests (separate file)
217221
oidcAuthTests(() => api);
218222

test-apps/comprehensive/test/sandbox-admin-surface-e2e.ts

Lines changed: 0 additions & 194 deletions
This file was deleted.

0 commit comments

Comments
 (0)