Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,7 +1,102 @@
describe("ExampleResourceServer", () => {
it("can visit the example resource server", async () => {
cy.visit("http://example-nextjs-resource-server:3007");
cy.url().should("include", "example-nextjs-resource-server");
cy.contains("h1", "@schemavaults/example-nextjs-resource-server");
const exampleAppUrl: string =
Cypress.env("EXAMPLE_NEXTJS_RESOURCE_SERVER_URL") ||
"http://example-nextjs-resource-server:3007";
// Normalize origin to strip default port 80 — cy.origin() requires
// the argument to match the browser's normalised origin exactly.
const exampleAppOrigin: string = new URL(exampleAppUrl).origin;

it("can visit the example resource server", () => {
cy.origin(exampleAppOrigin, () => {
cy.visit("/");
cy.url().should("include", "example-nextjs-resource-server");
cy.contains("h1", "@schemavaults/example-nextjs-resource-server");
});
});

it("can register a new user through the full OAuth2 PKCE flow and access the protected /account route", () => {
// Step 1: Login as admin and create an invite code for the new user
cy.create_and_login_as_superuser().then((success: boolean) => {
if (!success) {
throw new Error("Failed to login as superuser");
}

cy.generate_random_code(24).then((inviteCode: string) => {
cy.create_invite_code(inviteCode, 1).then((created: boolean) => {
if (!created) {
throw new Error("Failed to create invite code");
}

// Step 2: Logout from admin session
cy.logout();

// Step 3: Visit example app and click "Register".
// Wrap in cy.origin() since example app is cross-origin
// from the auth server base URL. cy.visit() inside
// cy.origin() is relative to the given origin.
cy.origin(exampleAppOrigin, () => {
cy.visit("/");
cy.contains("h1", "@schemavaults/example-nextjs-resource-server");
cy.contains("button", "Register").click();
});

// Step 4: Example app's /auth/register generates PKCE params and
// redirects to auth server's /auth/register with code_challenge,
// redirect_uri, and app_id query parameters.
// After the redirect we're back on the auth server (base URL) origin.
cy.url({ timeout: 20000 }).should("include", "/auth/register");
cy.url().should("include", "code_challenge");
cy.wait_for_page_hydration();

// Step 5: Fill the registration form on the auth server
cy.generate_random_code(12).then((suffix: string) => {
const email = `pkce-reg-test-${suffix}@example.com`;
const password = "TestPassword123!";

cy.get("input[name='email']")
.should("be.visible")
.type(email, { force: true });
cy.get("input[name='password']")
.should("be.visible")
.type(password, { force: true });
cy.get("input[name='confirm']")
.should("be.visible")
.type(password, { force: true });
cy.get("input[name='invite_code']")
.should("not.be.disabled")
.type(inviteCode, { force: true });

cy.get("button[type='submit']")
.should("not.be.disabled")
.click();

// Step 6: Consent screen appears because the example app is not a
// hardcoded app — AppAuthorizationConsentScreen renders in
// authorize-only mode. Click "Authorize & Continue" to approve.
cy.contains("Authorize & Continue", { timeout: 15000 })
.should("be.visible")
.click();

// Step 7: Auth server redirects back to example app's
// /auth/authorize?authorization_code=...&challenge_time=...
// The example app's useTradeAuthorizationCodeForTokensEffect
// exchanges the auth code + stored code_verifier for tokens,
// then redirects to /account.

// Step 8: Verify the protected /account page renders successfully.
// We're crossing back to the example app origin from the auth server.
cy.origin(exampleAppOrigin, () => {
cy.url({ timeout: 30000 }).should("include", "/account");
cy.contains("Example Account Page", {
timeout: 15000,
}).should("be.visible");
cy.contains(
"If you're seeing this it means that you were not redirected because you are logged in!",
).should("be.visible");
});
});
});
});
});
});
});
6 changes: 3 additions & 3 deletions tests/e2e-auth-tests/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ services:
CYPRESS_BASE_URL: http://schemavaults-auth:80
SCHEMAVAULTS_APP_ENVIRONMENT: test
TEST_SUITE_NAME: ${TEST_SUITE_NAME:?error}
EXAMPLE_NEXTJS_RESOURCE_SERVER_URL: http://example-nextjs-resource-server:3007
EXAMPLE_NEXTJS_RESOURCE_SERVER_URL: http://example-nextjs-resource-server
EXAMPLE_NEXTJS_RESOURCE_SERVER_JWKS_ACCESS_PUBLIC_KEY: ${EXAMPLE_NEXTJS_RESOURCE_SERVER_JWKS_ACCESS_PUBLIC_KEY:-null}
profiles:
- e2e
Expand Down Expand Up @@ -47,9 +47,9 @@ services:
SCHEMAVAULTS_CLIENT_APP_ID: 00000000-0000-0000-0000-000000000000
SCHEMAVAULTS_AUTH_JWKS_ACCESS_PRIVATE_KEY: ${EXAMPLE_NEXTJS_RESOURCE_SERVER_JWKS_ACCESS_PRIVATE_KEY:-null}
expose:
- 3007
- 80
ports:
- 3007:3007
- 3007:80
profiles:
- e2e_with_resource_server

Expand Down
Loading