Xamarin app ↔ OAuth backend: Secure Storage credential validity and refresh decision
0 reputation · 07 Feb 2023, 06:26 UTC
0 reputation · 07 Feb 2023, 06:26 UTC
Goal: Ensure a Xamarin mobile app stores only the minimum credentials needed for API access and recovers gracefully when those credentials become invalid at the integration boundary with its OAuth‑protected backend.
Constraints: Xamarin.Essentials Secure Storage is limited to small values and relies on platform key stores (iOS Keychain, Android Keystore) that may become unreadable after a device backup/restore or key invalidation, resulting in a null or exception from GetAsync. The app must also follow least‑privilege by storing only short‑lived access tokens plus a refresh token and requesting only the required scopes, while avoiding repeated refresh‑token exchanges that could cause looping or violate provider policies.
Should the app treat a SecureStorage GetAsync failure as an immediate trigger for re‑authentication, or first attempt a silent refresh‑token exchange? How many refresh attempts are safe before falling back to full re‑authentication without risking token‑looping or exceeding provider limits?
32775 reputation · 07 Feb 2023, 16:19 UTC
Treat a SecureStorage.GetAsync failure (null return or a platform keystore exception) as an immediate trigger for re-authentication — do not attempt a refresh first. A storage failure means the credential is unreadable, so there is no refresh token to exchange. Silent refresh is only appropriate on a different failure mode: you successfully read a refresh token, but the access token is expired or the API returns 401.
For refresh attempts, a safe bound is: one refresh exchange per 401, one retry of the original request, and a second consecutive 401 (or an invalid_grant from the token endpoint) falls back to full re-authentication. That gives you at most two token-endpoint calls per failed request and no loop.
iOS Keychain and Android Keystore entries can become unreadable after a backup/restore to a new device, or after lock-screen/biometric changes invalidate Keystore keys (behavior varies by OS version and key configuration). This is an expected lifecycle event, not a transient error — retrying GetAsync or attempting a refresh with a token you couldn't read accomplishes nothing. Route it straight to the login flow.
By contrast, a 401 with a readable refresh token usually means ordinary access-token expiry, which is exactly what the refresh token exists for.
invalid_grant (HTTP 400 from the token endpoint), re-authenticate immediately — the refresh token is dead.Store only the refresh token (and optionally the short-lived access token) in Secure Storage, request minimum scopes, and keep values small. Nothing else belongs there for API access.
GetAsync returns null/throws and the app goes straight to login without touching the token endpoint.invalid_grant: confirm the app stops refreshing and prompts for credentials.Note: Xamarin.Essentials is in maintenance with .NET MAUI as its successor; if you migrate, re-verify the keystore-invalidation behavior, as details may differ.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.