At a Glance
| Concern | Server Web App | Browser SPA | Native Mobile |
|---|---|---|---|
| PKCE required? | Recommended | Mandatory | Mandatory |
| Client secret? | Yes (server-side only) | No | No |
| Token storage | HttpOnly cookie / server session | sessionStorage or BFF cookie | Keychain (iOS) / Keystore (Android) |
| Redirect URI scheme | https:// | https:// | Custom scheme or HTTPS Universal Link |
| In-app browser | N/A | N/A | ASWebAuthSession / Chrome Custom Tab |
| Silent renewal | Server-side refresh token call | Hidden iframe or silent_redirect_uri | Background refresh token grant |
Server-Rendered Web Apps (.NET, Rails, Laravel, Django)
Server-rendered apps are confidential clients — they run on a server you control and can securely store a client secret. The auth flow runs entirely server-side: the user is redirected to the identity provider, the server exchanges the authorisation code for tokens at the token endpoint, and a session cookie is set in the browser. Tokens never touch the browser DOM.
Key advantages: The browser never sees a raw token. Tokens live in server memory or an encrypted session store. Even a complete XSS compromise of the front-end HTML cannot exfiltrate tokens. This is the most secure architecture for B2C web apps when server-rendering is an option.
Session management: Set a short absolute timeout (e.g. 8 hours) and a shorter idle timeout (e.g. 30 minutes). Renew the access token server-side using the stored refresh token before it expires — the user never experiences a re-login.
// .NET 10 — server-rendered Razor Pages, confidential client
.AddOpenIdConnect(options => {
options.Authority = "https://auth.ailacs.com";
options.ClientId = config["Ailacs:ClientId"];
options.ClientSecret = config["Ailacs:ClientSecret"]; // safe server-side
options.ResponseType = "code";
options.UsePkce = true; // defence-in-depth even for confidential clients
options.SaveTokens = true;
options.UseTokenLifetime = false; // manage session lifetime separately
});
Single-Page Applications (React, Vue, Angular)
SPAs are public clients — all code is delivered to the browser and no secret can be kept confidential. PKCE is therefore mandatory, not optional, for SPAs. There is no client secret; the PKCE code verifier is the only security mechanism preventing code interception attacks.
The BFF pattern is preferred in 2026: Rather than handling tokens in the browser, a Backend-for-Frontend (Next.js API route, Express middleware) performs the OIDC flow server-side and issues the SPA an HttpOnly session cookie. The SPA calls the BFF's API endpoints; it never sees an access or refresh token. This eliminates the XSS token theft attack class entirely.
If you must handle tokens in the browser: Store in sessionStorage (cleared on tab close, not accessible cross-origin), use automaticSilentRenew with a silent_redirect_uri, and implement a strict CSP that prevents unsafe-inline scripts.
Native Mobile Apps (iOS / Android)
Native mobile apps are also public clients — PKCE is mandatory. However, the auth flow has mobile-specific requirements that differ meaningfully from web.
Use the system browser, not a WebView
Apple's ASWebAuthenticationSession (iOS) and Chrome Custom Tabs (Android) are the correct way to show the authorisation endpoint in a mobile app. Never embed an in-app WebView for auth — the app can inspect the WebView's cookies and intercept credentials, which defeats the security model of the authorisation code flow entirely. App Store and Play Store guidelines now prohibit WebView-based OAuth for this reason.
Redirect URIs: custom scheme vs. Universal Links
After authentication the identity provider redirects to your app. Two options: (1) Custom URI scheme (com.yourapp.auth://callback) — simple to configure but can be intercepted by a malicious app that registers the same scheme. (2) HTTPS Universal Links (iOS) / App Links (Android) — associate the redirect URI with your HTTPS domain, cryptographically verified by the OS. HTTPS Universal Links are recommended for high-security B2C apps.
Token storage on mobile
Store tokens in the iOS Keychain or Android Keystore — hardware-backed secure enclaves. Never store tokens in UserDefaults, SharedPreferences, or flat files. Use biometric authentication to gate Keychain/Keystore access for an additional layer of protection on shared or lost devices.
Token Storage Rules — Quick Reference
Redirect URI Best Practices
Register exact redirect URIs in the Ailacs Identity Portal — no wildcards, no trailing slashes unless intentional. The identity server validates the redirect URI strictly against the registered values. A mismatch returns an error; a wildcard allows open redirect attacks.
- Web:
https://yourapp.com/callback - SPA:
https://yourapp.com/callback+https://yourapp.com/silent-renew - iOS Universal Link:
https://yourapp.com/oauth/callback - Android App Link:
https://yourapp.com/oauth/callback - iOS custom scheme (fallback):
com.yourapp.auth://callback
Part of the Ultimate Guide to B2C Authentication in 2026
← Back to Main Guide Next: Passwordless B2C →