Skip to content

Added MAUI android sample #2200

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions sandbox/OpenIddict.Sandbox.Maui.Client/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
#if IOS || MACCATALYST || WINDOWS
namespace OpenIddict.Sandbox.Maui.Client;
namespace OpenIddict.Sandbox.Maui.Client;

public partial class App : Application
{
public App() => InitializeComponent();

protected override Window CreateWindow(IActivationState? activationState) => new(new AppShell());
}
#endif
4 changes: 1 addition & 3 deletions sandbox/OpenIddict.Sandbox.Maui.Client/AppShell.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#if IOS || MACCATALYST || WINDOWS
namespace OpenIddict.Sandbox.Maui.Client;
namespace OpenIddict.Sandbox.Maui.Client;

public partial class AppShell : Shell
{
public AppShell() => InitializeComponent();
}
#endif
119 changes: 64 additions & 55 deletions sandbox/OpenIddict.Sandbox.Maui.Client/MainPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,62 +2,71 @@
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="OpenIddict.Sandbox.Maui.Client.MainPage">

<ScrollView>
<VerticalStackLayout
Spacing="25"
Padding="30,0"
VerticalOptions="Center">

<Image
Source="dotnet_bot.png"
SemanticProperties.Description="Cute dot net bot waving hi to you!"
HeightRequest="200"
HorizontalOptions="Center" />

<Label
Text="Hello, World!"
SemanticProperties.HeadingLevel="Level1"
FontSize="32"
HorizontalOptions="Center" />

<Label
x:Name="Message"
Text="Welcome to .NET Multi-platform App UI"
SemanticProperties.HeadingLevel="Level2"
SemanticProperties.Description="Welcome to dot net Multi platform App U I"
FontSize="18"
HorizontalOptions="Center" />

<Button
x:Name="LocalLogin"
Text="Log in using the local server"
SemanticProperties.Hint="Starts a new authentication flow"
Clicked="OnLocalLoginButtonClicked"
HorizontalOptions="Center" />

<Button
x:Name="LocalLoginWithGitHub"
Text="Log in using the local server (preferred service: GitHub)"
SemanticProperties.Hint="Starts a new authentication flow"
Clicked="OnLocalLoginWithGitHubButtonClicked"
HorizontalOptions="Center" />

<Button
x:Name="TwitterLogin"
Text="Log in using Twitter"
SemanticProperties.Hint="Starts a new authentication flow"
Clicked="OnTwitterLoginButtonClicked"
HorizontalOptions="Center" />

<Button
x:Name="LocalLogout"
Text="Log out from the local server"
SemanticProperties.Hint="Starts a new logout flow"
Clicked="OnLocalLogoutButtonClicked"
HorizontalOptions="Center" />

</VerticalStackLayout>
<Grid>
<VerticalStackLayout Spacing="25"
Padding="30,0"
VerticalOptions="Center"
x:Name="LoginForm">

<Image Source="dotnet_bot.png"
SemanticProperties.Description="Cute dot net bot waving hi to you!"
HeightRequest="200"
HorizontalOptions="Center" />

<Label Text="Hello, World!"
SemanticProperties.HeadingLevel="Level1"
FontSize="32"
HorizontalOptions="Center" />

<Label x:Name="Message"
Text="Welcome to .NET Multi-platform App UI"
SemanticProperties.HeadingLevel="Level2"
SemanticProperties.Description="Welcome to dot net Multi platform App U I"
FontSize="18"
HorizontalOptions="Center" />

<Button x:Name="LocalLogin"
Text="Log in using the local server"
SemanticProperties.Hint="Starts a new authentication flow"
Clicked="OnLocalLoginButtonClicked"
HorizontalOptions="Center" />

<Button x:Name="LocalLoginWithGitHub"
Text="Log in using the local server (preferred service: GitHub)"
SemanticProperties.Hint="Starts a new authentication flow"
Clicked="OnLocalLoginWithGitHubButtonClicked"
HorizontalOptions="Center" />

<Button x:Name="TwitterLogin"
Text="Log in using Twitter"
SemanticProperties.Hint="Starts a new authentication flow"
Clicked="OnTwitterLoginButtonClicked"
HorizontalOptions="Center" />

<Button x:Name="LocalLogout"
Text="Log out from the local server"
SemanticProperties.Hint="Starts a new logout flow"
Clicked="OnLocalLogoutButtonClicked"
HorizontalOptions="Center" />

</VerticalStackLayout>

<VerticalStackLayout x:Name="ProgressView"
Spacing="25"
VerticalOptions="Center"
IsVisible="False">
<Label Text="Logging in..."
FontSize="32"
HorizontalOptions="Center" />
<ProgressBar Progress="0.5"
WidthRequest="200"
HorizontalOptions="Center" />
<Button Text="Cancel"
Clicked="OnCancelButtonClicked"
HorizontalOptions="Center" />
</VerticalStackLayout>
</Grid>
</ScrollView>

</ContentPage>
23 changes: 17 additions & 6 deletions sandbox/OpenIddict.Sandbox.Maui.Client/MainPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
#if IOS || MACCATALYST || WINDOWS
using System.Globalization;
using OpenIddict.Abstractions;
using OpenIddict.Client;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Abstractions.OpenIddictExceptions;
using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants;

namespace OpenIddict.Sandbox.Maui.Client;

public partial class MainPage : ContentPage
{
private readonly OpenIddictClientService _service;
Expand All @@ -33,32 +32,42 @@ private async void OnLocalLogoutButtonClicked(object sender, EventArgs e)
private async void OnTwitterLoginButtonClicked(object sender, EventArgs e)
=> await LogInAsync(Providers.Twitter);


private void OnCancelButtonClicked(object sender, EventArgs e)
{
_source?.Cancel();
}

private CancellationTokenSource? _source = null;

private async Task LogInAsync(string provider, Dictionary<string, OpenIddictParameter>? parameters = null)
{
// Disable the buttons to prevent concurrent operations.
LocalLogin.IsEnabled = false;
LocalLoginWithGitHub.IsEnabled = false;
LocalLogout.IsEnabled = false;
TwitterLogin.IsEnabled = false;
LoginForm.Opacity = 0.2d;
ProgressView.IsVisible = true;

try
{
using var source = new CancellationTokenSource(delay: TimeSpan.FromSeconds(90));
_source = new CancellationTokenSource();

try
{
// Ask OpenIddict to initiate the authentication flow (typically, by starting the system browser).
var result = await _service.ChallengeInteractivelyAsync(new()
{
AdditionalAuthorizationRequestParameters = parameters,
CancellationToken = source.Token,
CancellationToken = _source.Token,
ProviderName = provider
});

// Wait for the user to complete the authorization process.
var principal = (await _service.AuthenticateInteractivelyAsync(new()
{
CancellationToken = source.Token,
CancellationToken = _source.Token,
Nonce = result.Nonce
})).Principal;

Expand Down Expand Up @@ -88,6 +97,9 @@ private async Task LogInAsync(string provider, Dictionary<string, OpenIddictPara
LocalLoginWithGitHub.IsEnabled = true;
LocalLogout.IsEnabled = true;
TwitterLogin.IsEnabled = true;
LoginForm.Opacity = 1d;
ProgressView.IsVisible = false;
_source?.Dispose();
}
}

Expand Down Expand Up @@ -147,4 +159,3 @@ await _service.AuthenticateInteractivelyAsync(new()
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#if IOS || MACCATALYST || WINDOWS
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting;

namespace OpenIddict.Sandbox.Maui.Client;

Expand All @@ -19,4 +18,3 @@ public void StopApplication()
Environment.Exit(0);
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#if IOS || MACCATALYST || WINDOWS
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting;

namespace OpenIddict.Sandbox.Maui.Client;

Expand All @@ -13,4 +12,3 @@ public MauiHostedServiceAdapter(IHostedService service)
public void Initialize(IServiceProvider services)
=> Task.Run(() => _service.StartAsync(CancellationToken.None)).GetAwaiter().GetResult();
}
#endif
37 changes: 33 additions & 4 deletions sandbox/OpenIddict.Sandbox.Maui.Client/MauiProgram.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
#if IOS || MACCATALYST || WINDOWS
using System.Net.Http;
using System.Net.Http;
using System.Security.Cryptography;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Hosting.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Client;
using OpenIddict.Client.SystemIntegration;
using static OpenIddict.Abstractions.OpenIddictConstants;
Expand Down Expand Up @@ -48,6 +49,9 @@ public static MauiApp CreateMauiApp()
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();

// remove DevelopmentSigning/Encryption certificates and uncomment the following line if you run on iOS or Android
// options.AddCertificatesForMobileApps();

// Register the operating system integration.
options.UseSystemIntegration();

Expand All @@ -67,7 +71,7 @@ public static MauiApp CreateMauiApp()
// Add a client registration matching the client application definition in the server project.
options.AddRegistration(new OpenIddictClientRegistration
{
Issuer = new Uri("https://localhost:44395/", UriKind.Absolute),
Issuer = new Uri("https://vsr1d2md-44349.euw.devtunnels.ms/", UriKind.Absolute),
ProviderName = "Local",

ClientId = "maui",
Expand Down Expand Up @@ -135,5 +139,30 @@ public static MauiApp CreateMauiApp()

return builder.Build();
}

/// <summary>
/// Note: Do not use the following keys in production!
/// there just added so we can authenticate from Android (Maui/Avalonia), iOS (Maui/Avalonia) and Macos, since on these platforms the following does not work
///
/// options.AddDevelopmentEncryptionCertificate()
/// .AddDevelopmentSigningCertificate();
///
/// this is because
/// 1. Adding an X509 certificate to the system store is not supported (throws on iOS and Android)
/// 2. and even if so, it would be a different machine - therefore different store - and client and server would not use the same certificate!
/// </summary>
/// <param name="options"></param>
/// <returns></returns>
private static OpenIddictClientBuilder AddCertificatesForMobileApps(this OpenIddictClientBuilder options)
{
var privateKeyXml = "<RSAKeyValue><Modulus>uSQBwbidg8/lAw3N3xeWmc9uYQPMHH5fODGmER6uXRzzJaL8upFWXanwts7ILNFOFAWogxQuWaTqu4dUFDVuXhJsdxpT4YZy0+k8QEMyBi6VIenQtKhYgiCgx9RK6cAuXRN1X6iQ2F+3MaenUGxztEOSQ1iJarV7E5od0o0doDl0TcW/wVqnwpAc5j8K/06kICuy1Pb1glHZsF8vzCgTPwdBTAYLGbzJWWxpLNiEFDuvJR6lopSSxKpurvzYXgpZHMZuOUlmQM/XGXjCYctHldAmr+gp8/xtufx3w2/V3gApLS6kWdkA9xazLOt7Xqb2QBGNGbunVzhtGg2rBYdBXQ==</Modulus><Exponent>AQAB</Exponent><P>wiiY1qCfHaiO+FoVpB3OocUYtqI9WvXUV2tk/JIOVuBth5oRg01GMN1cMA085YcwlV1d2RQVqGXdhAKHUwyi73luFQ/yt5ehemPUQPau03Pv8GkySLSGsbwuK+FKpDQ9kdupG1eW6dBt91um4Q1Gtu+GAJ2LkucYRHA2yx6osIs=</P><Q>9BwZ5gtnMw70n/h8NvULco5RxxpfoQ++2D7iQ6rc7i27/k53E0is2L03PP/LR8bV/14z+ixMW6rH7G2d475NIzFTrR4HjZdf+i05Fq7N/xvNCLrUvAd0CWqxYrume0t9zfw62JQtp5IYQ3g9K7DxUwfY9qVwYlZByLkgrUz26rc=</Q><DP>m2n5pVte4lOpVXxudDbzzqPA+3f0WtoKBYvOgym6VqpAolmeCRcSx0x5XXFLPIMxTW42D+w2xdv8K44GmmC0D7KIfk2MwI6cUCaWoQWUvWfBORRLjs0KQDzcTH2CzNuQKS/GNj+vaitPyr9PXjfNUeN6xQVW0tkuoKGeCorZBq8=</DP><DQ>HOd26ZZQEeuja42wp5E8WcQgSsMEr719i31mrTx+DHW93M7NqqrgTImbEM348/bHQAWXgffc0r3WDlisaVsPJyugDM+RdWKHKshQCi+IlLxl+rKknd8EDlljx50QiWjW7J0BGsPw4/aYiOSj2ZiJ+prjRdExDXPJNks1Y0/JrOE=</DQ><InverseQ>g+JNJBZbKFIY5jWZxCeX7TW25KpR498x+0yGJlzCwy23JbBGDupt2tsBnhXr8KuTxSfMOGWtazQeipI//XyLCvV7BohkL6PhzMKKHwAoM/0xNaqA0d5t9Q32OqEn6I+deu4SF4OwMXkQ96xGp0zLlsWnw3HdG2rVtx5KYARMmGA=</InverseQ><D>YA+CqdT0RXQUyyTacKp4hY3PI58oxI/9L9by52cX6VAgCKMsplDKkwad0vwveLGQ5WqaKIjME88xy+NHiMTAYycECDgs1ZNA+RrHHEDBL9vznQkINPQ0GDB9u7E2vVnttHVoLR31KY9gKe9nLJ9Y2WtF9JN3mVpYZa9NUfXOLVc+zs6ChwqfryfrkgQGHZXNFtwYhG4KuOLkrQy2S4etJEWn+NMbJVYEmy1Sg99BZs4eyi0666B30ofUsx6GwyCa9IXgDm4cJnUDQu0ZEGNU7LX+p9lFym13DkWt4z9TuE3QeOSr7jHEQz1CdE8a4zsqdf3TKP2Fl05+URL35kr/MQ==</D></RSAKeyValue>";
var rsa = RSA.Create(2048);
rsa.FromXmlString(privateKeyXml);

options.AddEncryptionKey(new SymmetricSecurityKey(Convert.FromBase64String("DRjd/GnduI3Efzen9V9BvbNUfc/VKgXltV7Kbk9sMkY=")));
options.AddSigningKey(new RsaSecurityKey(rsa));

return options;
}

}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<OutputType>Exe</OutputType>
<TargetFrameworks Condition=" '$(SupportsWindowsTargeting)' == 'true' ">net9.0-windows10.0.19041</TargetFrameworks>
<TargetFrameworks Condition=" '$(SupportsIOSTargeting)' == 'true' ">$(TargetFrameworks);net9.0-ios17.5</TargetFrameworks>
<TargetFrameworks Condition=" '$(SupportsAndroidTargeting)' == 'true' ">$(TargetFrameworks);net9.0-android35</TargetFrameworks>
<TargetFrameworks Condition=" '$(SupportsMacCatalystTargeting)' == 'true' ">$(TargetFrameworks);net9.0-maccatalyst17.5</TargetFrameworks>
<UseMaui Condition=" '$(TargetFrameworks)' != '' ">true</UseMaui>
<TargetFrameworks Condition=" '$(TargetFrameworks)' == '' ">net9.0</TargetFrameworks>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Android.App;
using Android.Content.PM;
using OpenIddict.Client.SystemIntegration;
using Intent = Android.Content.Intent;

namespace OpenIddict.Sandbox.Maui.Client
{
[Activity(Theme = "@style/Maui.SplashTheme", MainLauncher = true,
LaunchMode = LaunchMode.SingleTop,
ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize | ConfigChanges.Density)]
// Intent filter for custom URI scheme
[IntentFilter(new[] { Intent.ActionView },
Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
DataScheme = "com.openiddict.sandbox.maui.client")]
public class MainActivity : MauiAppCompatActivity
{

protected override async void OnNewIntent(Intent? intent)
{
base.OnNewIntent(intent);

// Handle the custom URL scheme
if (intent?.Data is not null &&
IPlatformApplication.Current?.Services is IServiceProvider provider)
{
var scheme = intent?.Data?.Scheme;
await provider.GetRequiredService<OpenIddictClientSystemIntegrationService>().HandleCustomTabsIntentAsync(intent!);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Android.App;
using Android.Runtime;

namespace OpenIddict.Sandbox.Maui.Client
{
[Application]
public class MainApplication : MauiApplication
{
public MainApplication(IntPtr handle, JniHandleOwnership ownership)
: base(handle, ownership)
{
}

protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#512BD4</color>
<color name="colorPrimaryDark">#2B0B98</color>
<color name="colorAccent">#2B0B98</color>
</resources>
2 changes: 1 addition & 1 deletion sandbox/OpenIddict.Sandbox.Maui.Client/Program.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#if !IOS && !MACCATALYST && !WINDOWS
#if !IOS && !MACCATALYST && !WINDOWS && !ANDROID
Console.Error.WriteLine("This sample is only supported on iOS, Mac Catalyst and Windows.");
#endif
Loading