How-To: Get Started with Web Authentication
UnoFeatures:
Authentication(add to<UnoFeatures>in your.csproj)
WebAuthenticationProvider provides an implementation that displays a web view in order for the user to login. After login, the web view redirects back to the application, along with any tokens. This tutorial will use web authorization to validate user credentials.
Step-by-step
Important
This guide assumes you used the template wizard or dotnet new unoapp to create your solution. If not, it is recommended that you follow the Creating an application with Uno.Extensions documentation to create an application from the template.
1. Prepare for web authentication
For this type of authentication, the application must already be registered with the desired identity provider.
A client id (and client secret) will be provided to you.
Add
Authenticationto the<UnoFeatures>property in the Class Library (.csproj) file.<UnoFeatures> Material; + Authentication; Toolkit; MVUX; </UnoFeatures>
2. Set up web authentication
Use the
UseAuthentication()extension method to configure theIHostBuilderto use an authentication provider. In our case, we will be using theWebAuthenticationProvider.private IHost Host { get; set; } protected override void OnLaunched(LaunchActivatedEventArgs args) { var builder = this.CreateBuilder(args) .Configure(host => { host .UseAuthentication(builder => { // Add the authentication provider here }); }); ... }Add the
WebAuthenticationProviderusing theAddWeb()extension method which configures theIAuthenticationBuilderto use it.private IHost Host { get; set; } protected override void OnLaunched(LaunchActivatedEventArgs args) { var builder = this.CreateBuilder(args) .Configure(host => { host .UseAuthentication(builder => { builder.AddWeb(); }); }); ... }The
IAuthenticationBuilderis responsible for managing the lifecycle of the associated provider that was built.WebAuthenticationProviderwill store the user's access token in credential storage.
3. Configure the provider
While the
WebAuthenticationProvideris added using theAddWeb()extension method, you will need to add a configuration section for basic settings to appsettings.json.We will be using the default name of
Webfor the configuration section.{ "Web": { "LoginStartUri": "URI_TO_LOGIN", "LoginCallbackUri": "URI_TO_RETURN_TO_APP", "LogoutStartUri": "URI_TO_LOGOUT" } }LoginStartUri: The URI that will be used to start the login process. This is the URI that will be opened in the web view.LoginCallbackUri: The URI the identity provider redirects to when the login completes. The web view closes when it navigates to this URI, and the tokens are read from it. It can be omitted only whenLoginStartUriincludes aredirect_uriquery parameter, which is then used instead. Without either, the login fails.LogoutStartUri: The URI that will be used to start the logout process. Without it,LogoutAsyncreturnsfalseand the stored tokens are kept.Calling
LoginAsyncopens theLoginStartUriin the web view. TheLoginStartUriwill then redirect the user to the identity provider's login page. After the user successfully logs in, the identity provider will redirect the user back to the application. TheWebAuthenticationProviderwill then store the user's access token in credential storage.
Skia Desktop (Windows, macOS, Linux)
Uno Platform has no built-in WebAuthenticationBroker on the Skia Desktop target, so AddWeb() registers one: DesktopWebAuthenticationBrokerProvider, which opens LoginStartUri in the system browser and receives the redirect on a loopback HTTP listener. It is registered through Uno's ApiExtensibility registry, where the first registration wins, so an app that supplies its own IWebAuthenticationBrokerProvider must register it before calling AddWeb().
LoginCallbackUri(andLogoutCallbackUri) must be a loopback HTTP address, for examplehttp://localhost:5001/authentication-callback, registered with the identity provider as is. A custom scheme is not routed back to a desktop app.WebAuthenticationBroker.GetCurrentApplicationCallbackUri()returnshttp://localhost:{port}/authentication-callbackon this target, with a port the broker picks once per process; setWinRTFeatureConfiguration.WebAuthenticationBroker.DefaultReturnUrito pin it. Do not call it beforeAddWeb()has run: the broker resolves its provider on first use, and anything that touchesWebAuthenticationBrokerearlier pins Uno's built-in provider, which throws on this target. A sign-in that fails withNotImplementedExceptionis this case: move the earlier call afterUseAuthentication. A provider the app registers afterAddWeb()is ignored for the same reason: the registry keeps the first registration.- Whether the loopback broker took the registration, or an earlier one kept it, is logged through the host's logger once the host is built (
Uno.Extensions.Authenticationcategory, Debug and Information). That line is written by anIServiceInitialize, which only the Uno host (this.CreateBuilder(args)orUnoHost.CreateDefaultBuilder()) runs, so a host built some other way never writes it. Independently of the host, the broker's first sign-in flow logs at Information on Uno's ambient logger that the loopback broker is handling it; if the first sign-in logs no such line, an app-supplied provider handled it. Both lines sit under theUno.category, so they only show onceCoreLogLevelisInformationor lower. The broker's own diagnostics (a port that could not be bound, a timed-out flow, a browser that would not open) go to Uno's ambient logger, whichUseLogging()only connects withenableUnoLogging: true; its per-flow lines are at Debug, below theUnofilter the usual log levels apply. - The listener completes on the first request to the callback path, which any page in the same browser (or any local process) can reach, and neither the broker nor the Web provider binds that request to the sign-in it started. Bind it yourself: send a
statevalue your backend echoes back on the redirect (viaPrepareLoginStartUri) and check it inPostLoginbefore accepting the tokens, rather than trusting whatever arrives. The OIDC provider does this as part of the protocol. - If no redirect arrives within
WinRTFeatureConfiguration.WebAuthenticationBroker.AuthenticationTimeout, the flow is reported as cancelled withResponseErrorDetailset toDesktopWebAuthenticationBrokerProvider.TimeoutErrorDetail(408). - To open the page some other way (a specific browser, a private window), derive from
DesktopWebAuthenticationBrokerProvider, overrideLaunchBrowserAsync, and register your type withApiExtensibility.RegisterbeforeAddWeb(). Apps that callWebAuthenticationBrokerdirectly can register the broker themselves withDesktopWebAuthenticationBrokerProvider.TryRegister()during startup. The type only exists in the non-WinAppSDK builds of the package, so guard the reference with#if !WINDOWSin code shared with a WinAppSDK head.
4. Process post-login tokens
You can process the user's returned response for tokens by registering a delegate with the
WebAuthenticationProvider.private IHost Host { get; set; } protected override void OnLaunched(LaunchActivatedEventArgs args) { var builder = this.CreateBuilder(args) .Configure(host => { host .UseAuthentication(builder => { builder.AddWeb(options => { options.PostLogin(async (tokens, ct) => { // Process the response here return tokens; }); }); }); }); ... }The
PostLogindelegate will be invoked after the user has successfully logged in. The delegate will be passed the user's tokens and a cancellation token. Other overloads also receive theIServiceProvider, theITokenCacheand the credentials, andAddWeb<TService>passes an instance ofTServiceas well.The delegate should return the user's tokens.
5. Use the provider in your application
Update
MainPageto include a button that will be used to login.<Page x:Class="Uno.Extensions.Authentication.Sample.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Uno.Extensions.Authentication.Sample" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"> <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <Button x:Name="LoginButton" Content="Login" Command="{x:Bind ViewModel.Authenticate}" /> </Grid> </Page>Update
MainViewModelto include a command that will be used to login.public class MainViewModel : ObservableCollection { private readonly IAuthenticationService _authService; public MainViewModel(IAuthenticationService authService) { _authService = authService; } public ICommand Authenticate => new DelegateCommand(async () => { await _authService.LoginAsync(/* ... */); }); }Finally, we can pass the login credentials to the
LoginAsync()method and authenticate with the identity provider. The user will be prompted to sign in to their account when they tap the button in the application.WebAuthenticationProviderwill then store the user's access token in credential storage. To refresh the token when it expires, register aRefreshcallback withoptions.Refresh(...).