Show linked sign-in providers on account settings #86

Merged
rob merged 2 commits from feat/show-linked-providers into main 2026-08-06 14:46:32 +00:00
Owner

Closes task 161's core requirement: /account now reads GET /api/users/me/linked-providers and shows which providers are linked, instead of only the transient "just linked" cue.

  • New endpoint, not a UserResponse field — same shape as GET /api/users/me/invitations (ADR-0075).
  • subject is excluded at the SQL projection, never read into the process — only issuer and linked_at reach the wire.
  • Unlinking is deliberately not built here — ADR-0075 explains the call and lists it as a follow-up ticket, including the invariant-5 lockout refusal it will need and the step-up question ADR-0072 leaves open.

ADR-0075 added (0074 is claimed by PR #85).

Closes task 161's core requirement: `/account` now reads `GET /api/users/me/linked-providers` and shows which providers are linked, instead of only the transient "just linked" cue. - New endpoint, not a `UserResponse` field — same shape as `GET /api/users/me/invitations` (ADR-0075). - `subject` is excluded at the SQL projection, never read into the process — only `issuer` and `linked_at` reach the wire. - Unlinking is deliberately not built here — ADR-0075 explains the call and lists it as a follow-up ticket, including the invariant-5 lockout refusal it will need and the step-up question ADR-0072 leaves open. ADR-0075 added (0074 is claimed by PR #85).
Show linked sign-in providers on account settings
All checks were successful
CI / build (pull_request) Successful in 2m37s
ba4427ab59
rob left a comment

Verdict: mergeable

subject is excluded at the SQL projection (SelectLinkedIdentitiesByUserId never selects it), LinkedIdentity/LinkedProviderResponse have no field for it, and mutating the SQL to drop the WHERE user_id scope makes both LinkedProvidersEndpointTests isolation tests fail as expected — the endpoint is genuinely me-scoped. No composite key or id is exposed.

Endpoint-shape reasoning holds: PutMe's single-row RETURNING is a real constraint against folding this into UserResponse, and the round-trip cost is paid once per page load, not per action.

Scope discipline is clean — StepUpPurpose has its one existing member, no unlink endpoint or dead branch. The empty/failure distinction in Account.razor is correct (_linkedProviders is not null gates the empty message, so a failed load only ever shows the error); confirmed by swallowing the load error into _linkedProviders = [], which breaks Account_LinkedProvidersFailToLoad_... as expected.

No schema change, no DbUp script, contract types only on the wire, GroupScopedEndpointAuthorisationTests' route enumeration correctly doesn't need updating (prefix-based, doesn't match /api/users/me/...). ADR-0075 is free on main and PR #85 claims 0074 as stated. Build and full test suite (including Testcontainers-backed integration tests) pass clean on SDK 10.0.100; CI is green on the head commit; merges cleanly against main.

Verdict: mergeable `subject` is excluded at the SQL projection (`SelectLinkedIdentitiesByUserId` never selects it), `LinkedIdentity`/`LinkedProviderResponse` have no field for it, and mutating the SQL to drop the `WHERE user_id` scope makes both `LinkedProvidersEndpointTests` isolation tests fail as expected — the endpoint is genuinely `me`-scoped. No composite key or id is exposed. Endpoint-shape reasoning holds: `PutMe`'s single-row `RETURNING` is a real constraint against folding this into `UserResponse`, and the round-trip cost is paid once per page load, not per action. Scope discipline is clean — `StepUpPurpose` has its one existing member, no unlink endpoint or dead branch. The empty/failure distinction in `Account.razor` is correct (`_linkedProviders is not null` gates the empty message, so a failed load only ever shows the error); confirmed by swallowing the load error into `_linkedProviders = []`, which breaks `Account_LinkedProvidersFailToLoad_...` as expected. No schema change, no DbUp script, contract types only on the wire, `GroupScopedEndpointAuthorisationTests`' route enumeration correctly doesn't need updating (prefix-based, doesn't match `/api/users/me/...`). ADR-0075 is free on main and PR #85 claims 0074 as stated. Build and full test suite (including Testcontainers-backed integration tests) pass clean on SDK 10.0.100; CI is green on the head commit; merges cleanly against main.
Merge remote-tracking branch 'origin/main' into feat/show-linked-providers
All checks were successful
CI / build (pull_request) Successful in 2m41s
18c015131d
# Conflicts:
#	docs/adr/README.md
rob merged commit 116fcb0d9a into main 2026-08-06 14:46:32 +00:00
rob deleted branch feat/show-linked-providers 2026-08-06 14:46:32 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rob/PlaceMark!86
No description provided.