Loading...
Loading...
Use when adding Auth0 login, logout, or token management to a .NET MAUI cross-platform app (iOS, Android, macOS, or Windows). Integrates Auth0.OidcClient.MAUI — use even if the user says "add login to my MAUI app".
npx skill4agent add auth0/agent-skills auth0-mauiAuth0.OidcClient.MAUIWebAuthenticatorAgent instruction: Before providing SDK setup instructions, fetch the latest MAUI release version by running:bashgh api repos/auth0/auth0-oidc-client-net/releases --jq '[.[] | select(.tag_name | startswith("maui-")) | .tag_name | ltrimstr("maui-")] | .[0]'Use the returned version in all dependency lines instead of any hardcoded version below.
| Use Case | Recommended Skill |
|---|---|
| ASP.NET Core server-side web app | |
| ASP.NET Core Web API (JWT validation) | |
| React Native mobile app | |
| iOS-only Swift app | |
| Android-only Kotlin app | |
| Expo React Native app | |
Agent instruction: Before starting, examine the user's project:
- Identify the .NET version from the
file (.csproj)TargetFrameworks- Check for existing authentication implementations — search for existing login/logout handlers and hook into them if found
- Note the project's namespace and directory conventions
- Look for existing
orAuth0Clientusage to avoid duplicate configurationAuth0ClientOptions
dotnet add package Auth0.OidcClient.MAUIAuth0ClientScope = "openid profile email offline_access"offline_accessawait SecureStorage.Default.SetAsync("refresh_token", loginResult.RefreshToken)RefreshTokenAsyncSecureStorage.Default.Remove("refresh_token")WebAuthenticatorActivitymyapp<uap:Extension Category="windows.protocol"><uap:Protocol Name="myapp"/></uap:Extension>Platforms/Windows/Package.appxmanifestActivator.Default.CheckRedirectionActivation()Platforms/Windows/App.xaml.csdotnet buildAgent instruction: When writing the Auth0Client configuration:
- ALWAYS include
in the Scope string — without it, no refresh token is returned and the user must re-authenticate every time the access token expires.offline_access- ALWAYS implement token persistence using
after login andSecureStorage.Default.SetAsync("refresh_token", ...)on app startup to restore sessions silently.SecureStorage.Default.GetAsync("refresh_token")- Clear stored tokens on logout with
.SecureStorage.Default.Remove("refresh_token")- ALWAYS create/update
to register the custom URL scheme protocol. Without this, Windows will not intercept the callback URL after authentication. Add aPlatforms/Windows/Package.appxmanifestinside the<uap:Extension Category="windows.protocol"><uap:Protocol Name="myapp"/></uap:Extension>element of the<Extensions>node.<Application>- ALWAYS add
inCheckRedirectionActivation()as the first line in the constructor, beforePlatforms/Windows/App.xaml.cs.InitializeComponent()After writing configuration and code, verify the build succeeds:bashdotnet buildIf the build fails, attempt to fix the issue. After 5-6 failed attempts, ask the user for help.
myapp://callbackhttps://{domain}/{platform}/{bundleId}/callback{bundleId}.auth0://{domain}/ios/{bundleId}/callbackcom.mycompany.myapp://callbackIntentFilterPackage.appxmanifestAuth0ClientOptions.RedirectUriAuth0ClientOptions.PostLogoutRedirectUriNote: For production apps, use a reverse-domain scheme (e.g.,) to reduce the risk of URL scheme hijacking.com.yourcompany.yourapp://callback
Auth0.OidcClient.MAUIAuth0ClientScopeoffline_accessmyapp://callbackSecureStorage.Default.SetAsyncSecureStorage.Default.GetAsyncRefreshTokenAsyncAuth0ClientOptions| Mistake | Fix |
|---|---|
| App type not set to Native in Auth0 Dashboard | Change application type to "Native" in Dashboard settings |
| Missing callback URL in Auth0 Dashboard | Add |
Android: Missing | Create activity class with |
Windows: Not calling | Add |
Using | Domain should be hostname only (e.g., |
Not requesting | Add |
| RedirectUri/PostLogoutRedirectUri not matching Dashboard | Ensure the exact URI (scheme + path) matches what's configured in Auth0 Dashboard |
| Not registering URL scheme on Android/Windows | Register the custom URL scheme in platform-specific config (see Platform setup step) |
| Storing tokens in plain text | Use |
Agent instruction: Remind the user to test on a physical device in addition to emulators/simulators. Some WebAuthenticator behaviors (system browser integration, URL scheme interception) may differ on physical devices vs. emulators. Test the full login → callback → token flow on real hardware before shipping.
RefreshTokenAsyncUserCancelusing Auth0.OidcClient;
// Initialize client
var client = new Auth0Client(new Auth0ClientOptions
{
Domain = "YOUR_AUTH0_DOMAIN",
ClientId = "YOUR_AUTH0_CLIENT_ID",
RedirectUri = "myapp://callback",
PostLogoutRedirectUri = "myapp://callback",
Scope = "openid profile email offline_access"
});
// Login — opens system browser
var loginResult = await client.LoginAsync();
if (!loginResult.IsError)
{
var user = loginResult.User;
var accessToken = loginResult.AccessToken;
var idToken = loginResult.IdentityToken;
var refreshToken = loginResult.RefreshToken;
// Access user claims
var name = user.FindFirst("name")?.Value;
var email = user.FindFirst("email")?.Value;
// Persist refresh token securely for session restoration
if (!string.IsNullOrEmpty(refreshToken))
await SecureStorage.Default.SetAsync("refresh_token", refreshToken);
}
// Logout — clears Auth0 session and stored tokens
await client.LogoutAsync();
SecureStorage.Default.Remove("refresh_token");
// Restore session on app startup (no user interaction needed)
var savedToken = await SecureStorage.Default.GetAsync("refresh_token");
if (!string.IsNullOrEmpty(savedToken))
{
var refreshResult = await client.RefreshTokenAsync(savedToken);
if (!refreshResult.IsError)
{
var newAccessToken = refreshResult.AccessToken;
// Update stored token if rotated
if (!string.IsNullOrEmpty(refreshResult.RefreshToken))
await SecureStorage.Default.SetAsync("refresh_token", refreshResult.RefreshToken);
}
else
{
// Refresh failed — clear and require re-login
SecureStorage.Default.Remove("refresh_token");
}
}
// Get user info from /userinfo endpoint
var userInfo = await client.GetUserInfoAsync(accessToken);
// Login with extra parameters (organization, audience, connection)
var orgLogin = await client.LoginAsync(new { organization = "org_abc123" });
var apiLogin = await client.LoginAsync(new { audience = "https://my-api.example.com" });[Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop, Exported = true)]
[IntentFilter(new[] { Intent.ActionView },
Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
DataScheme = CALLBACK_SCHEME)]
public class WebAuthenticatorActivity : Microsoft.Maui.Authentication.WebAuthenticatorCallbackActivity
{
const string CALLBACK_SCHEME = "myapp";
}Platforms/Windows/Package.appxmanifest<Extensions>
<uap:Extension Category="windows.protocol">
<uap:Protocol Name="myapp"/>
</uap:Extension>
</Extensions>Platforms/Windows/App.xaml.cs// In Platforms/Windows/App.xaml.cs
public App()
{
if (Auth0.OidcClient.Platforms.Windows.Activator.Default.CheckRedirectionActivation())
return;
this.InitializeComponent();
}