Aller au contenu

Microsoft Entra ID OAuth

Ce contenu n’est pas encore disponible dans votre langue.

Pia signs users in via OAuth — Google and Microsoft Entra ID are supported out of the box. This page walks through registering an Entra application and configuring the server.

In the Microsoft Entra admin centre, create a new app registration:

Setting Value
Name Pia Server dev (or your production name)
Supported account types Any Entra ID Tenant + Personal Microsoft accounts
Redirect URI platform Web
Redirect URI https://<your-server-url>/signin-microsoft

Add the Client ID and Client Secret to appsettings.json (or as environment variables in production):

{
"OAuth": {
"Microsoft": {
"ClientId": "<from Entra app registration>",
"ClientSecret": "<from Entra → Certificates & secrets>"
}
}
}

In production, prefer environment variables: OAUTH_MICROSOFT_CLIENT_ID and OAUTH_MICROSOFT_CLIENT_SECRET.

The desktop app uses PKCE (S256). It starts a one-shot loopback listener, opens the browser to /auth/login?provider=microsoft&redirect_uri=http://localhost:{port}/&code_challenge=<S256>&code_challenge_method=S256&state=<nonce>, and waits for the server to redirect back with a one-time login code, never tokens. It then trades that code, together with its PKCE verifier, for tokens at POST /auth/token.

┌──────────────┐ ┌─────────────┐ ┌──────────────┐ ┌───────────┐
│ WPF Client │ │ Browser │ │ Pia Server │ │ Microsoft │
└──────┬───────┘ └──────┬──────┘ └──────┬───────┘ └─────┬─────┘
│ 1. Pick port, │ │ │
│ start listener, │ │ │
│ make PKCE pair │ │ │
│ │ │ │
│ 2. Open browser ──►│ │ │
│ to /auth/login │ │ │
│ + code_challenge│ │ │
│ │ 3. GET /auth/login │ │
│ │───────────────────►│ │
│ │ 4. 302 to MS │ │
│ │◄───────────────────│ │
│ │ 5. Sign in ──────────────────────────►│
│ │ │ 6. /signin-microsoft
│ │ │◄──────────────────│
│ │ │ + auth code │
│ │ │ 7. Exchange code │
│ │ │ using secret │
│ │ │ 8. Find/create │
│ │ │ user, mint a │
│ │ │ one-time code │
│ │ 9. 302 to loopback │ │
│ │ ?code=…&state=… │ │
│ 10. Capture code │◄───────────────────│ │
│◄───────────────────│ │ │
│ POST /auth/token ──────────────────────►│ │
│ code + verifier │ │ │
│◄──── JWT + refresh token ───────────────│ │
│ 11. DPAPI encrypt + │ │ │
│ persist │ │ │
│ 12. Start sync │ │ │
└────────────────────┴────────────────────┴───────────────────┘
  1. The desktop app finds a free TCP port, starts an HttpListener on http://localhost:{port}/, and generates a PKCE verifier plus its S256 challenge and a state nonce.
  2. It opens the system browser to /auth/login?provider=microsoft&redirect_uri=http://localhost:{port}/&code_challenge=<S256>&code_challenge_method=S256&state=<nonce>.
  3. The server’s /auth/login endpoint requires the S256 challenge from a loopback caller — see the “Older desktop clients” note below. It also rejects a provider this deployment never configured.
  4. The server returns Results.Challenge(), which 302-redirects to Microsoft’s authorization endpoint.
  5. The user signs in at Microsoft.
  6. Microsoft redirects back to the server’s /signin-microsoft with an authorization code.
  7. ASP.NET Core middleware exchanges the code for tokens using the ClientSecret.
  8. The server extracts user claims and finds or creates the PiaUser. An email already registered under a different sign-in method is refused here with email_exists.
  9. The server redirects the browser to the desktop’s loopback URI with a one-time login code and the echoed state as query parameters — not tokens.
  10. The desktop’s loopback listener captures the code and POSTs it with the PKCE verifier to /auth/token. The server returns the JWT access token and refresh token in the response body, only if the verifier matches. The code is single-use and short-lived.
  11. The desktop encrypts the tokens with Windows DPAPI and persists them to settings.json.
  12. First-sync migration runs and background sync starts.
File Role
src/Pia.Server/Auth/OAuthConfiguration.cs Registers Microsoft OAuth middleware
src/Pia.Server/Controllers/Auth/AuthController.cs /auth/login, /auth/callback, /auth/token, /auth/refresh
src/Pia.Server/Auth/OAuthCallbackService.cs Callback logic: claims, user lookup, login code vs. admin cookie
src/Pia.Server/Auth/LoginCodeExchangeService.cs One-time login code redemption against the PKCE verifier
src/Pia.Server/Auth/JwtService.cs JWT generation, refresh token hashing
src/Pia.Server/appsettings.json OAuth:Microsoft configuration

The client side — loopback listener, PKCE, token storage and the login buttons — lives in src/Pia.Wpf/Services/AuthService.cs in the Pia.Wpf repo.