Skip to content

Commit

Permalink
Added MAUI android sample
Browse files Browse the repository at this point in the history
  • Loading branch information
gentledepp committed Oct 7, 2024
1 parent 9f613b1 commit f7a44bf
Show file tree
Hide file tree
Showing 14 changed files with 180 additions and 81 deletions.
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

0 comments on commit f7a44bf

Please sign in to comment.