Web authenticator
This article describes how you can use the .NET Multi-platform App UI (.NET MAUI) the IWebAuthenticator interface. This interface lets you start browser-based authentication flows, which listen for a callback to a specific URL registered to the app.
The default implementation of the IWebAuthenticator
interface is available through the WebAuthenticator.Default property. Both the IWebAuthenticator
interface and WebAuthenticator
class are contained in the Microsoft.Maui.Authentication
namespace.
Overview
Many apps require adding user authentication, and this often means enabling your users to sign in to their existing Microsoft, Facebook, Google, or Apple Sign In account.
Tip
Microsoft Authentication Library (MSAL) provides an excellent turn-key solution to adding authentication to your app.
If you're interested in using your own web service for authentication, it's possible to use WebAuthenticator to implement the client-side functionality.
Why use a server back end
Many authentication providers have moved to only offering explicit or two-legged authentication flows to ensure better security. This means you'll need a client secret from the provider to complete the authentication flow. Unfortunately, mobile apps aren't a great place to store secrets and anything stored in a mobile app's code, binaries, or otherwise, is considered to be insecure.
The best practice here's to use a web backend as a middle layer between your mobile app and the authentication provider.
Important
We strongly recommend against using older mobile-only authentication libraries and patterns which do not leverage a web backend in the authentication flow, due to their inherent lack of security for storing client secrets.
Get started
To access the WebAuthenticator functionality the following platform-specific setup is required.
Android requires an Intent Filter setup to handle your callback URI. This is accomplished by inheriting from the WebAuthenticatorCallbackActivity
class:
using Android.App;
using Android.Content.PM;
namespace YourNameSpace;
[Activity(NoHistory = true, LaunchMode = LaunchMode.SingleTop, Exported = true)]
[IntentFilter(new[] { Android.Content.Intent.ActionView },
Categories = new[] { Android.Content.Intent.CategoryDefault, Android.Content.Intent.CategoryBrowsable },
DataScheme = CALLBACK_SCHEME)]
public class WebAuthenticationCallbackActivity : Microsoft.Maui.Authentication.WebAuthenticatorCallbackActivity
{
const string CALLBACK_SCHEME = "myapp";
}
If your project's Target Android version is set to Android 11 (R API 30) or higher, you must update your Android Manifest with queries that use Android's package visibility requirements.
In the Platforms/Android/AndroidManifest.xml file, add the following queries/intent
nodes in the manifest
node:
<queries>
<intent>
<action android:name="android.support.customtabs.action.CustomTabsService" />
</intent>
</queries>
Using WebAuthenticator
The API consists mainly of a single method, AuthenticateAsync, which takes two parameters:
- The URL used to start the web browser flow.
- The URI the flow is expected to ultimately call back to, that is registered to your app.
The result is a `WebAuthenticatorResult, which includes any query parameters parsed from the callback URI:
try
{
WebAuthenticatorResult authResult = await WebAuthenticator.Default.AuthenticateAsync(
new Uri("https://mysite.com/mobileauth/Microsoft"),
new Uri("myapp://"));
string accessToken = authResult?.AccessToken;
// Do something with the token
}
catch (TaskCanceledException e)
{
// Use stopped auth
}
The WebAuthenticator API takes care of launching the url in the browser and waiting until the callback is received:
If the user cancels the flow at any point, a TaskCanceledException is thrown.
Private authentication session
iOS 13 introduced an ephemeral web browser API for developers to launch the authentication session as private. This enables developers to request that no shared cookies or browsing data is available between authentication sessions and will be a fresh login session each time. This is available through the WebAuthenticatorOptions parameter passed to the AuthenticateAsync method:
try
{
WebAuthenticatorResult authResult = await WebAuthenticator.Default.AuthenticateAsync(
new WebAuthenticatorOptions()
{
Url = new Uri("https://mysite.com/mobileauth/Microsoft"),
CallbackUrl = new Uri("myapp://"),
PrefersEphemeralWebBrowserSession = true
});
string accessToken = authResult?.AccessToken;
// Do something with the token
}
catch (TaskCanceledException e)
{
// Use stopped auth
}
Platform differences
This section describes the platform-specific differences with the web authentication API.
Custom Tabs are used whenever available, otherwise the system browser is used as a fallback.
Apple Sign In
According to Apple's review guidelines, if your Apple app uses any social login service to authenticate, it must also offer Apple Sign In as an option. To add Apple Sign In to your apps, you'll need to add the sign in with Apple entitlement to your app. This entitlement is defined using the com.apple.developer.applesignin
key, of type Array
of String
:
<key>com.apple.developer.applesignin</key>
<array>
<string>Default</string>
</array>
For more information, see Sign in with Apple Entitlement on developer.apple.com.
For iOS 13 and higher, call the AppleSignInAuthenticator.AuthenticateAsync method. This uses the native Apple Sign in APIs so your users get the best experience possible on these devices. For example, you can write your shared code to use the correct API at runtime:
var scheme = "..."; // Apple, Microsoft, Google, Facebook, etc.
var authUrlRoot = "https://mysite.com/mobileauth/";
WebAuthenticatorResult result = null;
if (scheme.Equals("Apple")
&& DeviceInfo.Platform == DevicePlatform.iOS
&& DeviceInfo.Version.Major >= 13)
{
// Use Native Apple Sign In API's
result = await AppleSignInAuthenticator.AuthenticateAsync();
}
else
{
// Web Authentication flow
var authUrl = new Uri($"{authUrlRoot}{scheme}");
var callbackUrl = new Uri("myapp://");
result = await WebAuthenticator.Default.AuthenticateAsync(authUrl, callbackUrl);
}
var authToken = string.Empty;
if (result.Properties.TryGetValue("name", out string name) && !string.IsNullOrEmpty(name))
authToken += $"Name: {name}{Environment.NewLine}";
if (result.Properties.TryGetValue("email", out string email) && !string.IsNullOrEmpty(email))
authToken += $"Email: {email}{Environment.NewLine}";
// Note that Apple Sign In has an IdToken and not an AccessToken
authToken += result?.AccessToken ?? result?.IdToken;
Tip
For non-iOS 13 devices, this will start the web authentication flow, which can also be used to enable Apple Sign In on your Android and Windows devices. You can sign into your iCloud account on your iOS simulator to test Apple Sign In.
ASP.NET core server back end
It's possible to use the WebAuthenticator API with any web back-end service. To use it with an ASP.NET core app, configure the web app with the following steps:
- Set up your external social authentication providers in an ASP.NET Core web app.
- Set the Default Authentication Scheme to CookieAuthenticationDefaults.AuthenticationScheme in your
.AddAuthentication()
call. - Use
.AddCookie()
in your Startup.cs.AddAuthentication()
call. - All providers must be configured with
.SaveTokens = true;
.
services.AddAuthentication(o =>
{
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie()
.AddFacebook(fb =>
{
fb.AppId = Configuration["FacebookAppId"];
fb.AppSecret = Configuration["FacebookAppSecret"];
fb.SaveTokens = true;
});
Tip
If you'd like to include Apple Sign In, you can use the AspNet.Security.OAuth.Apple
NuGet package. You can view the full Startup.cs sample.
Add a custom mobile auth controller
With a mobile authentication flow, you usually start the flow directly to a provider the user has chosen. For example, clicking a "Microsoft" button on the sign-in screen of the app. It's also important to return relevant information to your app at a specific callback URI to end the authentication flow.
To achieve this, use a custom API Controller:
[Route("mobileauth")]
[ApiController]
public class AuthController : ControllerBase
{
const string callbackScheme = "myapp";
[HttpGet("{scheme}")] // eg: Microsoft, Facebook, Apple, etc
public async Task Get([FromRoute]string scheme)
{
// 1. Initiate authentication flow with the scheme (provider)
// 2. When the provider calls back to this URL
// a. Parse out the result
// b. Build the app callback URL
// c. Redirect back to the app
}
}
The purpose of this controller is to infer the scheme (provider) the app is requesting, and start the authentication flow with the social provider. When the provider calls back to the web backend, the controller parses out the result and redirects to the app's callback URI with parameters.
Sometimes you may want to return data such as the provider's access_token
back to the app, which you can do via the callback URI's query parameters. Or, you may want to instead create your own identity on your server and pass back your own token to the app. What and how you do this part is up to you!
Check out the full controller sample.
Note
The above sample demonstrates how to return the access token from the 3rd party authentication (ie: OAuth) provider. To obtain a token you can use to authorize web requests to the web backend itself, you should create your own token in your web app, and return that instead. The Overview of ASP.NET Core authentication has more information about advanced authentication scenarios in ASP.NET Core.