Skip to content

Add external OAuth sign-in to the passkeys sample (stacked on #36617) - #36823

Closed
mattleibow wants to merge 1 commit into
mattleibow-spec-passkeys-essentialsfrom
mattleibow-spec-passkeys-oauth
Closed

Add external OAuth sign-in to the passkeys sample (stacked on #36617)#36823
mattleibow wants to merge 1 commit into
mattleibow-spec-passkeys-essentialsfrom
mattleibow-spec-passkeys-oauth

Conversation

@mattleibow

@mattleibow mattleibow commented Jul 27, 2026

Copy link
Copy Markdown
Member

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Stacked on #36617

This PR is stacked on top of the Passkeys Essentials API PR (#36617). Its base branch is mattleibow-spec-passkeys-essentials, not net11.0, so review #36617 first. Because the base is a feature branch (not main/net*.0/release/*/inflight/*), the maui-pr pipeline will not auto-run here — that is expected. When #36617 merges, this PR's base should be retargeted so CI runs.

Because the base is the passkeys branch, the GitHub "Files changed" tab already shows only the OAuth additions. Direct compare link: mattleibow-spec-passkeys-essentials...mattleibow-spec-passkeys-oauth

What this adds

Adds server-brokered external OAuth sign-in (Google / Microsoft / Facebook / Apple) to the Essentials passkeys sample, alongside the existing password and passkey flows. It follows the ASP.NET Core Backend-for-Frontend (BFF) pattern, which is the current official recommendation for native clients:

  • The server runs the OAuth exchange, creates or links a local Identity account, and hands the app only a session cookie.
  • The app never sees or stores the provider's access token. A /me/external endpoint relays profile data that the server fetches on the app's behalf.

This is purely additive to the sample and reference server — it touches no Microsoft.Maui.* product code and adds no public API.

Server (Samples.WebServer)

  • ExternalProviders.csAddConfiguredExternalProviders registers Google/Microsoft/Facebook/Apple only when their credentials are present in configuration, so the sample runs with none, some, or all of them. No provider is hard-coded on.
  • ExternalAuthEndpoints.cs — the native BFF handshake:
    • GET /native-auth/external/providers — discovery (what's configured).
    • GET /native-auth/external/start — challenges the provider via ConfigureExternalAuthenticationProperties.
    • /complete — creates/links the local account and issues a one-time code (an ITimeLimitedDataProtector token — framework crypto, no custom store).
    • POST /native-auth/external/exchange — swaps the one-time code for an Identity cookie session in the app's CookieContainer.
    • GET /me/external — server-relayed provider profile.
  • Provider packages added to the server csproj (Microsoft.AspNetCore.Authentication.{Google,MicrosoftAccount,Facebook} + AspNet.Security.OAuth.Apple), all restored from repo feeds.
  • appsettings.json gains a documented (empty) Authentication section; real credentials go in the server's user-secrets, never in source.

Client (Essentials sample)

  • Dynamic "Sign in with " buttons rendered from the discovery endpoint (nothing hard-coded).
  • A "Show my external profile (server relay)" demo that proves the app can read provider data it never holds a token for.
  • Same local account as the password and passkey flows, so after an OAuth sign-in the app still offers to enrol a passkey.

Security posture

  • No rolled-own security anywhere: framework OAuth handlers, framework data protection, framework Identity accounts.
  • The provider token stays server-side; the client is a public app that only ever holds a cookie session.

To enable a real provider

Add credentials to the server's user-secrets under Authentication:{Google|Microsoft|Facebook|Apple} and register the matching redirect URIs (https://<domain>/signin-google, etc.). No code changes needed.

Adds server-brokered external OAuth sign-in (Google/Microsoft/Facebook/Apple)
next to the password and passkey flows, following the ASP.NET Core
Backend-for-Frontend (BFF) pattern: the server runs the OAuth exchange, creates
or links a local account, and hands the app only a session cookie — the app
never sees the provider token.

Server: ExternalProviders.cs config-gates each provider (only registered when
its credentials are present); ExternalAuthEndpoints.cs exposes the native
handshake (/native-auth/external/providers|start|complete|exchange) plus a
/me/external server relay. One-time codes use ITimeLimitedDataProtector.

Client: dynamic 'Sign in with <provider>' buttons driven by the discovery
endpoint, and a 'Show my external profile' relay demo.

Stacked on the Passkeys Essentials API PR (#36617); CI won't auto-run because
the base branch is a feature branch, not a trigger branch.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8fa25a04-ac6e-44ad-ab51-96d594e4107d
@mattleibow
mattleibow temporarily deployed to copilot-pat-pool July 27, 2026 12:58 — with GitHub Actions Inactive
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
1 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@mattleibow
mattleibow temporarily deployed to copilot-pat-pool July 27, 2026 12:58 — with GitHub Actions Inactive
@mattleibow
mattleibow temporarily deployed to copilot-pat-pool July 27, 2026 12:58 — with GitHub Actions Inactive
@mattleibow
mattleibow temporarily deployed to copilot-pat-pool July 27, 2026 13:00 — with GitHub Actions Inactive
@mattleibow
mattleibow temporarily deployed to copilot-pat-pool July 27, 2026 13:01 — with GitHub Actions Inactive
@github-actions github-actions Bot added the area-essentials Essentials: Device, Display, Connectivity, Secure Storage, Sensors, App Info label Jul 27, 2026
@mattleibow
mattleibow temporarily deployed to copilot-pat-pool July 27, 2026 13:01 — with GitHub Actions Inactive
@mattleibow mattleibow closed this Jul 27, 2026
@mattleibow
mattleibow deleted the mattleibow-spec-passkeys-oauth branch July 27, 2026 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-essentials Essentials: Device, Display, Connectivity, Secure Storage, Sensors, App Info

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant