OAuth 2.0 and OpenID Connect in .NET MAUI
π OAuth 2.0 and OpenID Connect in .NET MAUI
A Secure, Native Approach with WebAuthenticator
Modern mobile apps demand secure, seamless authenticationβand rolling your own auth is not an option anymore. Standards like OAuth 2.0 and OpenID Connect (OIDC) provide a robust, interoperable way to authenticate users and access APIs securely. In this guide, weβll break down how to implement OAuth 2.0 + OIDC in .NET MAUI using WebAuthenticator, leveraging native browser flows for maximum security and compliance.
π Why OAuth 2.0 + OpenID Connect?
Before jumping into code, letβs clarify roles:
| Concept | Purpose | Output |
|---|---|---|
| OAuth 2.0 | Authorization (access to APIs) | Access Token |
| OpenID Connect | Authentication (user identity) | ID Token (JWT) |
π In mobile apps, you almost always use both together.
π§ Why NOT Embedded WebViews?
Using WebView for authentication is strongly discouraged: β No shared cookies
β Vulnerable to phishing
β Violates platform security guidelines (Apple/Google) β
Instead, use system browser via WebAuthenticator
βοΈ What is WebAuthenticator in .NET MAUI?
WebAuthenticator is a cross-platform API that:
- Opens the system browser (Safari / Chrome Custom Tabs)
- Handles deep link callbacks
- Returns authentication results securely
π§© Architecture Overview
MAUI App
β
WebAuthenticator
β
System Browser (OAuth Provider)
β
Redirect URI (App)
β
Access Token + ID Token
π Step 1: Register Your App (OAuth Provider)
Example providers:
- Azure AD / Entra ID
- Auth0
- IdentityServer
- Google / Facebook Youβll need:
- Client ID
- Redirect URI (custom scheme) Example:
myapp://callback
π± Step 2: Configure Redirect URI in MAUI
Android (AndroidManifest.xml)
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="myapp" android:host="callback" />
</intent-filter>
iOS (Info.plist)
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
</dict>
</array>
π Step 3: Build the Authorization Request
var authUrl = new Uri(
"https://your-auth-server/authorize" +
"?client_id=YOUR_CLIENT_ID" +
"&response_type=code" +
"&scope=openid profile email" +
"&redirect_uri=myapp://callback" +
"&state=12345" +
"&code_challenge=XYZ" +
"&code_challenge_method=S256"
);
β οΈ Note:
- Use PKCE (Proof Key for Code Exchange) β mandatory for mobile apps.
π Step 4: Authenticate with WebAuthenticator
var result = await WebAuthenticator.AuthenticateAsync(
authUrl,
new Uri("myapp://callback")
);
π₯ Step 5: Handle the Response
var code = result?.Properties["code"];
Youβll receive:
codeβ exchange for tokens- optionally
id_token
π Step 6: Exchange Code for Tokens
var httpClient = new HttpClient();
var response = await httpClient.PostAsync(
"https://your-auth-server/token",
new FormUrlEncodedContent(new Dictionary<string, string>
{
{ "grant_type", "authorization_code" },
{ "client_id", "YOUR_CLIENT_ID" },
{ "code", code },
{ "redirect_uri", "myapp://callback" },
{ "code_verifier", "YOUR_CODE_VERIFIER" }
})
);
var json = await response.Content.ReadAsStringAsync();
π Step 7: Store Tokens Securely
Use:
await SecureStorage.SetAsync("access_token", accessToken);
Never store tokens in: β Preferences
β Plain text files
π§Ύ OAuth Flow Breakdown
| Step | Description |
|---|---|
| 1 | User taps login |
| 2 | Browser opens (WebAuthenticator) |
| 3 | User authenticates |
| 4 | Redirect to app |
| 5 | App receives authorization code |
| 6 | Exchange for tokens |
| 7 | Store securely |
βοΈ Security Best Practices
β Always Use PKCE
Prevents authorization code interception.
β Use HTTPS Only
Never allow non-secure endpoints.
β Validate State Parameter
if (result.Properties["state"] != expectedState)
{
throw new Exception("Invalid state");
}
β Token Expiration Handling
Implement refresh tokens if supported.
β Avoid Hardcoding Secrets
Mobile apps are public clients.
βοΈ WebAuthenticator vs WebView
| Feature | WebAuthenticator | WebView |
|---|---|---|
| Security | β High | β Low |
| SSO Support | β Yes | β No |
| Compliance | β Apple/Google | β Rejected |
| UX | β Native | β οΈ Inconsistent |
π§ͺ Advanced: Using OpenID Connect ID Token
You can decode the ID Token (JWT):
var handler = new JwtSecurityTokenHandler();
var jwt = handler.ReadJwtToken(idToken);
var email = jwt.Claims.First(c => c.Type == "email").Value;
π§ Pro Tip: Use Libraries When Needed
If you donβt want to implement manually:
- IdentityModel.OidcClient
- MSAL (Microsoft)
- Auth0 SDK But WebAuthenticator remains the foundation in MAUI.
π Conclusion
Implementing OAuth 2.0 + OpenID Connect in .NET MAUI using WebAuthenticator gives you: β
Native, secure authentication
β
Standards-compliant architecture
β
Cross-platform consistency
β
Future-proof identity integration If you're building modern mobile apps, this approach is not optionalβitβs baseline security hygiene π
π References
- https://learn.microsoft.com/dotnet/maui/platform-integration/communication/authentication
- https://oauth.net/2/
- https://openid.net/connect/
- https://datatracker.ietf.org/doc/html/rfc7636 (PKCE)
