Microsoft Entra ID OAuth
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.
1. Register the Entra application
Section titled “1. Register the Entra application”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 |
2. Configure the server
Section titled “2. Configure the server”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.
3. The flow
Section titled “3. The flow”The desktop app starts a one-shot loopback listener, opens the browser to /auth/login?provider=microsoft&redirect_uri=http://localhost:{port}/, and waits for the server to redirect back with tokens in the query string.
┌──────────────┐ ┌─────────────┐ ┌──────────────┐ ┌───────────┐│ WPF Client │ │ Browser │ │ Pia Server │ │ Microsoft │└──────┬───────┘ └──────┬──────┘ └──────┬───────┘ └─────┬─────┘ │ 1. Pick port, │ │ │ │ start listener │ │ │ │ │ │ │ │ 2. Open browser ──►│ │ │ │ to /auth/login │ │ │ │ │ 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, │ │ │ issue JWT + │ │ │ │ refresh token │ │ │ 9. 302 to loopback │ │ │ 10. Capture tokens │◄───────────────────│ │ │◄───────────────────│ │ │ │ 11. DPAPI encrypt + │ │ │ │ persist │ │ │ │ 12. Start sync │ │ │ └────────────────────┴────────────────────┴───────────────────┘Step-by-step
Section titled “Step-by-step”- The desktop app finds a free TCP port and starts an
HttpListeneronhttp://localhost:{port}/. - It opens the system browser to
/auth/login?provider=microsoft&redirect_uri=http://localhost:{port}/. - The browser hits the server’s
/auth/loginendpoint. - The server returns
Results.Challenge(), which 302-redirects to Microsoft’s authorization endpoint. - The user signs in at Microsoft.
- Microsoft redirects back to the server’s
/signin-microsoftwith an authorization code. - ASP.NET Core middleware exchanges the code for tokens using the
ClientSecret. - The server extracts user claims, finds-or-creates the
PiaUser, and issues a JWT access token + refresh token. - The server redirects the browser to the desktop’s loopback URI with the tokens as query parameters.
- The desktop’s loopback listener captures the request and reads the tokens.
- The desktop encrypts the tokens with Windows DPAPI and persists them to
settings.json. - First-sync migration runs and background sync starts.
Key files
Section titled “Key files”| File | Role |
|---|---|
src/Pia/Services/AuthService.cs | Client-side: loopback listener, token storage |
src/Pia/ViewModels/SettingsViewModel.cs | Login command handler in the desktop UI |
src/Pia.Server/Auth/OAuthConfiguration.cs | Registers Microsoft OAuth middleware |
src/Pia.Server/Auth/AuthEndpoints.cs | /auth/login, /auth/callback, /auth/refresh |
src/Pia.Server/Auth/JwtService.cs | JWT generation, refresh token hashing |
src/Pia.Server/appsettings.json | OAuth:Microsoft configuration |