Skip to content

feat(identity): verify-email session + PKCE callback to provisioning#155

Merged
phuongnse merged 4 commits into
mainfrom
cursor/register-org-verify-session-9e73
Jun 1, 2026
Merged

feat(identity): verify-email session + PKCE callback to provisioning#155
phuongnse merged 4 commits into
mainfrom
cursor/register-org-verify-session-9e73

Conversation

@phuongnse

@phuongnse phuongnse commented May 30, 2026

Copy link
Copy Markdown
Owner

Summary

After email verification, the API establishes a short-lived session cookie and returns { sessionEstablished: true }. The SPA callback checks for a stored provisioning token and redirects to /provisioning?token= after PKCE instead of the dashboard.

Linked spec

register-org — verify link activates account and signs user in.

Requirements

  • POST /api/auth/verify-email → 200 + session cookie (was 204).
  • Frontend: completePostVerifyPkceFlow, callback provisioning redirect.
  • Integration/unit tests updated for verify 200 + AuthHelper PKCE path.

Merge independence: Backend verify change is backward-compatible for clients that ignore the body. Callback provisioning redirect is a no-op until slice 1 (journey) stores the token via completePostVerifyPkceFlow. Recommended merge order: slice 1 → slice 3.

Open in Web Open in Cursor 

Summary by CodeRabbit

  • Bug Fixes

    • Post-verify provisioning token is now consumed only after a successful authorization-code exchange, avoiding token loss on failed callbacks.
  • New Features

    • Email verification now establishes a session (200 OK + session flag), enabling seamless PKCE hand-off and automatic workspace provisioning without an extra login.
  • Documentation

    • Updated implementation-status matrix to reflect frontend provisioning flow and session behavior.
  • Tests

    • Tests and test helpers updated to expect 200 OK and to support PKCE flows with an established session.

…provisioning

Co-authored-by: Phuong Nguyen <phuongnse@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented May 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7ba28d2a-d03a-40b4-a20f-114590f8d47f

📥 Commits

Reviewing files that changed from the base of the PR and between 90d53f1 and 055cffc.

📒 Files selected for processing (8)
  • frontend/src/features/auth/hooks/useVerifyEmail.ts
  • frontend/src/features/auth/types.ts
  • frontend/src/lib/api-types.ts
  • openapi.json
  • src/Axis.Api/Endpoints/AuthEndpoints.cs
  • src/Modules/Identity/Axis.Identity.Application/Commands/VerifyEmail/VerifyEmailHandler.cs
  • src/Modules/Identity/Axis.Identity.Application/Commands/VerifyEmail/VerifyEmailSessionEstablishedDto.cs
  • src/Modules/Identity/Axis.Identity.Application/Commands/VerifyEmail/VerifyEmailSuccessDto.cs
✅ Files skipped from review due to trivial changes (1)
  • src/Modules/Identity/Axis.Identity.Application/Commands/VerifyEmail/VerifyEmailSessionEstablishedDto.cs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/Modules/Identity/Axis.Identity.Application/Commands/VerifyEmail/VerifyEmailSuccessDto.cs
  • src/Modules/Identity/Axis.Identity.Application/Commands/VerifyEmail/VerifyEmailHandler.cs

📝 Walkthrough

Walkthrough

The email verification endpoint now establishes a short-lived cookie session and returns typed success data; frontend PKCE callback consumption of the provisioning token is delayed until after successful code exchange; OpenAPI/TS types, test helpers, and tests are updated accordingly.

Changes

Email Verification with Cookie Session and Provisioning Flow

Layer / File(s) Summary
New response DTOs
src/Modules/Identity/Axis.Identity.Application/Commands/VerifyEmail/VerifyEmailSuccessDto.cs, src/Modules/Identity/Axis.Identity.Application/Commands/VerifyEmail/VerifyEmailSessionEstablishedDto.cs
Adds VerifyEmailSuccessDto with user/org/email/fullname/permissions and VerifyEmailSessionEstablishedDto(SessionEstablished: bool) for the verify-email response.
VerifyEmail command and handler typing
src/Modules/Identity/Axis.Identity.Application/Commands/VerifyEmail/VerifyEmailCommand.cs, src/Modules/Identity/Axis.Identity.Application/Commands/VerifyEmail/VerifyEmailHandler.cs
Command implements ICommand<VerifyEmailSuccessDto>; handler implements ICommandHandler<VerifyEmailCommand, VerifyEmailSuccessDto> and returns Result<VerifyEmailSuccessDto> with typed failures and constructed DTO on success.
Verify-email endpoint session establishment
src/Axis.Api/Endpoints/AuthEndpoints.cs
Endpoint accepts HttpContext, signs in a short-lived non-persistent cookie via SignInPkceSessionAsync using VerifyEmailSuccessDto claims, and returns Ok(new VerifyEmailSessionEstablishedDto(true)) (200) instead of 204.
OpenAPI and generated TS types
openapi.json, frontend/src/lib/api-types.ts
OpenAPI changed POST /api/auth/verify-email from 204 to 200 with VerifyEmailSessionEstablishedDto schema (session_established: boolean); generated TS types and operation response updated accordingly.
Frontend callback and PKCE sequencing
frontend/src/routes/callback.lazy.tsx, frontend/src/features/auth/api.ts, frontend/src/features/auth/hooks/useVerifyEmail.ts, frontend/src/features/auth/types.ts
Callback consumption of post-verify provisioning token moved to the successful exchangeAuthorizationCode path; hook checks data.session_established before running PKCE completion; inline docs updated.
Test helper for session-based PKCE flow
tests/Api/Axis.Api.Tests/Helpers/AuthHelper.cs
Adds CompletePkceFlowWithSessionAsync(HttpClient) to exchange authorization code when a session cookie exists; CreateAdminClientAsync updated to expect 200 from verify-email and use the session-based flow.
Test assertions for new endpoint behavior
tests/Api/Axis.Api.Tests/Identity/AuthEndpointTests.cs, tests/Modules/Identity/Axis.Identity.Application.Tests/Commands/VerifyEmailHandlerTests.cs
Integration and unit tests updated to expect 200 OK from verify-email and to capture/assert typed Result<VerifyEmailSuccessDto> payloads where applicable; failure behavior assertions unchanged.
Implementation Status Documentation
docs/use-cases/identity-access/README.md
README expanded to describe the post-verify PKCE callback handling and the provisioning redirect into /provisioning.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • phuongnse/axis#153: Related frontend post-verify PKCE + provisioning redirection behavior and session establishment coordination.
  • phuongnse/axis#125: Modifies verify-email/token handling in the same handler area; closely related to typed success flow changes.
  • phuongnse/axis#93: Also touches the verify-email pipeline and organization verification events; related to server-side verification flow changes.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 21.05% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: email verification now establishes a session and integrates with PKCE callback for provisioning.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/register-org-verify-session-9e73

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…ments

- callback: consume the post-verify provisioning token only after the code
  exchange succeeds, so an invalid callback no longer discards a valid token;
  redirect to /provisioning via location.assign so this slice compiles and
  degrades gracefully without the journey slice's typed route.
- restore the explanatory comments in SignOut (token revoke, JTI blacklist,
  cookie clearing) that were inadvertently dropped.
- fix import ordering flagged by Biome in api.ts and callback.lazy.tsx.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@phuongnse phuongnse marked this pull request as ready for review June 1, 2026 06:51
main gained the OpenAPI-generated FE types (#165), legal/slug fields, and
the typed /provisioning route after this branch forked. Resolution:
- types.ts/api.ts: keep generated types + main's resend/provisioning
  helpers alongside this branch's verify-email session + PKCE helpers.
- callback.lazy.tsx: adopt main's typed navigate to /provisioning, and
  consume the post-verify token only after a successful code exchange
  (drop the early consume so a failed callback keeps a valid token).
- AuthHelper.cs: use main's TestRegistrationPayload (carries required
  legal versions).
- identity-access README: record the verify-email session + post-verify
  PKCE→provisioning behavior in the implementation status.

Verified: dotnet build Axis.sln 0/0, frontend npm run ci + 34 tests,
doc-drift green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Axis.Api/Endpoints/AuthEndpoints.cs`:
- Around line 35-38: The endpoint in AuthEndpoints.cs currently uses
.Produces<object>() and returns an anonymous new { sessionEstablished = true },
which yields an opaque OpenAPI schema; define a concrete response DTO named
VerifyEmailSessionEstablishedDto in the Application layer (per repo guidelines),
change the endpoint signature to return/serialize that DTO and update the
OpenAPI annotation to .Produces<VerifyEmailSessionEstablishedDto>(), and replace
any anonymous response creation with new VerifyEmailSessionEstablishedDto {
SessionEstablished = true } (or equivalent property name) so the contract is
explicit and discoverable.
- Around line 118-129: The endpoint is orchestrating two mediator calls
(VerifyEmailCommand and GetUserTokenClaimsQuery) and calling
SignInPkceSessionAsync itself, which risks partial success if the second call
fails; instead collapse this flow into a single mediator operation that performs
verification, provisioning and returns the token claims (e.g., create a
VerifyEmailAndGetClaimsCommand/Query that returns UserTokenClaimsDto or a Result
wrapper), then replace the two mediator.Send calls and SignInPkceSessionAsync
usage with a single mediator.Send of that new operation and translate its Result
to ProblemDetails; ensure the new handler performs the verification and any DB
commits before fetching claims so the endpoint remains thin (route →
mediator.Send → ToProblemDetails) and use the existing VerifyEmailCommand and
GetUserTokenClaimsQuery logic within that handler or delegate to them
internally.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5a097e64-8b25-4f71-80b1-4aa109b1b8db

📥 Commits

Reviewing files that changed from the base of the PR and between 0294cfa and 90d53f1.

📒 Files selected for processing (10)
  • docs/use-cases/identity-access/README.md
  • frontend/src/features/auth/api.ts
  • frontend/src/routes/callback.lazy.tsx
  • src/Axis.Api/Endpoints/AuthEndpoints.cs
  • src/Modules/Identity/Axis.Identity.Application/Commands/VerifyEmail/VerifyEmailCommand.cs
  • src/Modules/Identity/Axis.Identity.Application/Commands/VerifyEmail/VerifyEmailHandler.cs
  • src/Modules/Identity/Axis.Identity.Application/Commands/VerifyEmail/VerifyEmailSuccessDto.cs
  • tests/Api/Axis.Api.Tests/Helpers/AuthHelper.cs
  • tests/Api/Axis.Api.Tests/Identity/AuthEndpointTests.cs
  • tests/Modules/Identity/Axis.Identity.Application.Tests/Commands/VerifyEmailHandlerTests.cs

Comment thread src/Axis.Api/Endpoints/AuthEndpoints.cs
Comment thread src/Axis.Api/Endpoints/AuthEndpoints.cs Outdated
Comment on lines +118 to +129
Result<VerifyEmailSuccessDto> result =
await mediator.Send(new VerifyEmailCommand(request.Token), ct);
if (result.IsFailure)
return result.ToProblemDetails();

Result<UserTokenClaimsDto> claimsResult = await mediator.Send(
new GetUserTokenClaimsQuery(result.Value.UserId, result.Value.OrganizationId),
ct);
if (claimsResult.IsFailure)
return claimsResult.ToProblemDetails();

await SignInPkceSessionAsync(httpContext, claimsResult.Value);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Collapse the verify-email flow into a single application result.

The endpoint now orchestrates VerifyEmailCommand and GetUserTokenClaimsQuery itself. If the first call commits verification/provisioning and the second call fails, the client gets an error even though the one-time link has already been consumed. This also breaks the repo rule that Axis.Api endpoints stay as route → mediator.SendToProblemDetails.

As per coding guidelines, "Endpoint surface changes: keep Axis.Api endpoints thin (route → mediator.Send), translate failures to ProblemDetails (don’t embed logic in the endpoint)."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Axis.Api/Endpoints/AuthEndpoints.cs` around lines 118 - 129, The endpoint
is orchestrating two mediator calls (VerifyEmailCommand and
GetUserTokenClaimsQuery) and calling SignInPkceSessionAsync itself, which risks
partial success if the second call fails; instead collapse this flow into a
single mediator operation that performs verification, provisioning and returns
the token claims (e.g., create a VerifyEmailAndGetClaimsCommand/Query that
returns UserTokenClaimsDto or a Result wrapper), then replace the two
mediator.Send calls and SignInPkceSessionAsync usage with a single mediator.Send
of that new operation and translate its Result to ProblemDetails; ensure the new
handler performs the verification and any DB commits before fetching claims so
the endpoint remains thin (route → mediator.Send → ToProblemDetails) and use the
existing VerifyEmailCommand and GetUserTokenClaimsQuery logic within that
handler or delegate to them internally.

…x casing

Addresses the CodeRabbit review on the verify-email endpoint and a latent
FE/BE casing bug:

- Response DTO (CodeRabbit #1): replace `.Produces<object>()` +
  `new { sessionEstablished = true }` with VerifyEmailSessionEstablishedDto
  in the Application layer (repo rule: response DTOs live in Application).
- Single application result (CodeRabbit #2): VerifyEmailHandler now gathers
  the sign-in permissions (before commit) and returns them on
  VerifyEmailSuccessDto, so the endpoint is one mediator.Send → sign in →
  return. Removes the second GetUserTokenClaims round-trip that could fail
  after the one-time link was already consumed.
- Casing: regenerate openapi.json + api-types.ts (session_established),
  point VerifyEmailResponse at the generated schema, and read
  data?.session_established in useVerifyEmail — the post-verify PKCE branch
  was dead before (FE read camelCase off a snake_case wire).

Verified: full dotnet test Axis.sln green (0 failed), frontend ci + tests,
OpenApiDocumentTests green/idempotent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
phuongnse added a commit that referenced this pull request Jun 1, 2026
Two judgment-class findings CodeRabbit raised on #155 (response DTO must
be an Application type, not object/anonymous; endpoints must not
orchestrate multiple mediator.Send calls). Both are semi-deterministic and
recurring → logged as candidate architecture fitness tests (not built yet,
per the three-question rule).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@phuongnse phuongnse merged commit 626f25a into main Jun 1, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants