I have followed the official documentation of the web authenticator for .NET Maui here. It explains what to do efficiently, but they miss some crucial information regarding the callback URL. According to the article, the URL should be myapp://. I assume it could be anything like thisapp://.
The problem with myapp:// is that the WebAuthenticatorActivity does not like this. I get the following error when using myapp:// as the callback URL:
You must subclass the
WebAuthenticatorCallbackActivity
and create an IntentFilter for it which matches yourcallbackUrl
.
At the bottom I will post the code.
I have also had a look at Dan Siegal's helper library and his video on the MAUI web authenticator, but he could not get the app working in the video. I have also viewed his sample on GitHub and he uses "myapp" as the callback URL. The problem with only "myapp" is that the Uri class does not like it when you specify the callback URL for the web authenticator. Here is the error I get:
Invalid URI: The format of the URI could not be determined.
So basically I can't use "myapp://" or "myapp" and decided to do a combination of the two. For the Callback URL, I used "myapp://" and for the WebAuthenticatorActivity I used "myapp". This actually works, but there is still one problem. On the Google console it expects a redirect URL. I have tried "myapp://" or "myapp" but it does not allow this:
After searching the Internet, I found an article that mentions that I should use: https://mysite/signin-google. I have set up the API correctly according to the documentation and by using Dan Siegal's library.
So if I use "myapp://" as the callback URL for the web authenticator and "myapp" for the WebAuthenticatorActivity and https://mysite/signin-google on Google console, all is working until the point where Google tries to redirect back to my app from the browser. This is the error I get:
This mysite page can't by found. No webpage was found for the web address: https://mysite/signin-google
It comes down to:
What value should I use for:
- The callback URL of the web authenticator
- The WebAuthenticatorActivity
- The Google Console redirect URL
Here is my code:
File MauiProgram.cs
using CommunityToolkit.Maui;
using FertilizerFarm.Helpers;
using FertilizerFarm.Services;
using FertilizerFarm.View;
using Refit;
using Telerik.Maui.Controls.Compatibility;
namespace FertilizerFarm;
public static class Constants
{
public const string BaseUrl = "https://mysite/mobileauth/";
public const string CallbackScheme = "myapp://";
}
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseTelerik()
.UseMauiApp<App>()
.UseMauiCommunityToolkit()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
fonts.AddFont("telerikfontexamples.ttf", "TelerikFontExamples");
});
builder.Services.AddSingleton<IConnectivity>(Connectivity.Current)
.AddSingleton(WebAuthenticator.Default)
.AddSingleton(SecureStorage.Default)
.AddRefitClient<IUserProfileService>()
.AddSingleton<AuthService>()
.AddSingleton<JWTService>()
.AddTransient<LoginViewModel>()
.AddTransient<Login>()
return builder.Build();
}
public static IServiceCollection AddRefitClient<T>(this IServiceCollection services)
where T : class
{
services.AddSingleton(sp =>
{
var settings = new RefitSettings
{
AuthorizationHeaderValueGetter = () => sp.GetRequiredService<ISecureStorage>().GetAsync("access_token")
};
return RestService.For<T>(Constants.BaseUrl, settings);
});
return services;
}
}
File WebAuthenticatorCallbackActivity.cs
using Android.App;
using Android.Content;
using Android.Content.PM;
namespace FertilizerFarm;
[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";
}
File LoginViewModel.cs
[RelayCommand]
async Task SocialLogin()
{
try
{
var result = await _webAuthenticator.AuthenticateAsync(new WebAuthenticatorOptions
{
CallbackUrl = new Uri($"{Constants.CallbackScheme}"),
Url = new Uri(Constants.BaseUrl + "google")
});
await _storage.SetAsync("access_token", result.AccessToken);
using var response = await _userProfile.GetProfileClaims();
if (response.IsSuccessStatusCode)
{
Claims.Clear();
var claims = response.Content.Select(x => $"{x.Key}: {x.Value}");
foreach (var claim in claims)
Claims.Add(claim);
}
}
catch (Exception ex)
{
}
}
After conducting extensive research on this topic, I am pleased to share my findings and experiences with you regarding the implementation of Google OAuth 2.0 authentication in both desktop and mobile applications.
Desktop Apps:
When it comes to implementing Google OAuth 2.0 authentication in desktop applications, I would highly recommend referring to the example provided in this link: https://dev.to/jaymalli_programmer/google-oauth-20-authorization-service-implementation-in-net-maui-okl. I have personally tested this implementation, and I can confirm its functionality.
Step 1: Create authorization service & add below code.
Step 2: Load webview with the google login screen on button click.
Step 3: The Google Login screen is visible after clicked on button. Fill the user email id & password, Click "Allow" button on oauth consent scrren.
Google login screen
You will get the code in the redirect URL if user authorized successfully. Url
Step 4: Get access token from the code which was provided by URL after authorizing a user.
Get the access_token & refresh_token which is used to create credentials for access services API such as Drive , Gmail API etc.
Mobile Apps:
If you aspire to implement OAuth 2.0 Google Authentication https://developers.google.com/identity/protocols/oauth2/native-app in your mobile app, be prepared for a more complex process. In this case, you will need to create a backend server that can communicate with Google's APIs. You can refer to a comprehensive example of how this can be achieved by examining the code in the following GitHub repository: https://github.com/dotnet/maui/blob/main/src/Essentials/samples/Sample.Server.WebAuthenticator/Controllers/MobileAuthController.cs. This example demonstrates how to set up the necessary server-side components to handle authentication and redirection in a .NET MAUI mobile application, ensuring that all the required information is processed correctly.
Step 1: Create an ASP.NET Core Web Application
Step 2: Configure Google Authentication
Update appsettings.json:
Add your Google Client ID and Secret:
Modify Startup.cs for Google Authentication:
In the ConfigureServices method, set up the authentication services:
Step 3: Configure Authentication and Authorization
Setup Authentication Middleware:
In the Configure method of Startup.cs, add the authentication middleware:
Create the AuthController: Implement the AuthController to handle the authentication flow:
Step 4: Deployment for OAuth 2.0
Public Server Deployment: Google OAuth requires a public and secure URL for the redirect URI in a production environment. Deploy your application to a cloud service like Azure to get a public URL and HTTPS, which is necessary for OAuth to work properly in production. OAuth 2.0 with Google requires a publicly accessible redirect URI for security reasons. localhost refers to the local machine, and it's not accessible from the internet. Google needs a URL that it can reach to send the authentication response.
Step 6: 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:
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:
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:
The WebAuthenticator API takes care of launching the url in the browser and waiting until the callback is received: WebAuthenticatorFlow