Skip to content

Commit

Permalink
Added avalonia sample app and detailed description
Browse files Browse the repository at this point in the history
  • Loading branch information
gentledepp committed Oct 8, 2024
1 parent 69678b0 commit 3f594f0
Show file tree
Hide file tree
Showing 51 changed files with 2,711 additions and 32 deletions.
15 changes: 14 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,20 @@
<PackageVersion Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.70" />
<PackageVersion Include="Quartz.Extensions.Hosting" Version="3.5.0" />
<PackageVersion Include="Spectre.Console" Version="0.48.0" />



<PackageVersion Include="Avalonia" Version="11.1.3" />
<PackageVersion Include="Avalonia.Themes.Fluent" Version="11.1.3" />
<PackageVersion Include="Avalonia.Fonts.Inter" Version="11.1.3" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.1.3" />
<PackageVersion Include="Avalonia.Desktop" Version="11.1.3" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.1.3" />
<PackageVersion Include="Avalonia.Browser" Version="11.1.3" />
<PackageVersion Include="Avalonia.Android" Version="11.1.3" />
<PackageVersion Include="Avalonia.iOS" Version="11.1.3" />

<PackageVersion Include="Xamarin.AndroidX.Core.SplashScreen" Version="1.0.1.1" />

<!--
Note: OpenIddict uses PolySharp to dynamically generate polyfills for types that are not available on
some of the targeted TFMs (e.g Index, Range or nullable attributes on .NET Framework/.NET Standard).
Expand Down
38 changes: 38 additions & 0 deletions OpenIddict.sln
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,19 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Sandbox.Console.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Sandbox.Maui.Client", "sandbox\OpenIddict.Sandbox.Maui.Client\OpenIddict.Sandbox.Maui.Client.csproj", "{CD5EE836-ED56-48E3-B3B6-8D74C7C859B9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Avalonia", "Avalonia", "{77F24A0C-50D9-47DB-92AA-15B514C55DFE}"
ProjectSection(SolutionItems) = preProject
sandbox\OpenIddict.Sandbox.Avalonia.Client\readme.md = sandbox\OpenIddict.Sandbox.Avalonia.Client\readme.md
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Sandbox.Avalonia.Client", "sandbox\OpenIddict.Sandbox.Avalonia.Client\OpenIddict.Sandbox.Avalonia.Client\OpenIddict.Sandbox.Avalonia.Client.csproj", "{83F9656B-A339-4CBA-B1CF-392719217B7E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Sandbox.Avalonia.Client.Android", "sandbox\OpenIddict.Sandbox.Avalonia.Client\OpenIddict.Sandbox.Avalonia.Client.Android\OpenIddict.Sandbox.Avalonia.Client.Android.csproj", "{6AD6AFE9-0971-48A8-84CE-9BEF8022593C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Sandbox.Avalonia.Client.Desktop", "sandbox\OpenIddict.Sandbox.Avalonia.Client\OpenIddict.Sandbox.Avalonia.Client.Desktop\OpenIddict.Sandbox.Avalonia.Client.Desktop.csproj", "{F216E0E6-7BFE-4C98-9C72-D5E3AE965C67}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenIddict.Sandbox.Avalonia.Client.iOS", "sandbox\OpenIddict.Sandbox.Avalonia.Client\OpenIddict.Sandbox.Avalonia.Client.iOS\OpenIddict.Sandbox.Avalonia.Client.iOS.csproj", "{F24FA764-0AF6-4DE2-A88E-77A1A2D42483}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -393,6 +406,26 @@ Global
{CD5EE836-ED56-48E3-B3B6-8D74C7C859B9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CD5EE836-ED56-48E3-B3B6-8D74C7C859B9}.Release|Any CPU.Build.0 = Release|Any CPU
{CD5EE836-ED56-48E3-B3B6-8D74C7C859B9}.Release|Any CPU.Deploy.0 = Release|Any CPU
{83F9656B-A339-4CBA-B1CF-392719217B7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{83F9656B-A339-4CBA-B1CF-392719217B7E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{83F9656B-A339-4CBA-B1CF-392719217B7E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{83F9656B-A339-4CBA-B1CF-392719217B7E}.Release|Any CPU.Build.0 = Release|Any CPU
{6AD6AFE9-0971-48A8-84CE-9BEF8022593C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6AD6AFE9-0971-48A8-84CE-9BEF8022593C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AD6AFE9-0971-48A8-84CE-9BEF8022593C}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{6AD6AFE9-0971-48A8-84CE-9BEF8022593C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6AD6AFE9-0971-48A8-84CE-9BEF8022593C}.Release|Any CPU.Build.0 = Release|Any CPU
{6AD6AFE9-0971-48A8-84CE-9BEF8022593C}.Release|Any CPU.Deploy.0 = Release|Any CPU
{F216E0E6-7BFE-4C98-9C72-D5E3AE965C67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F216E0E6-7BFE-4C98-9C72-D5E3AE965C67}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F216E0E6-7BFE-4C98-9C72-D5E3AE965C67}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F216E0E6-7BFE-4C98-9C72-D5E3AE965C67}.Release|Any CPU.Build.0 = Release|Any CPU
{F24FA764-0AF6-4DE2-A88E-77A1A2D42483}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F24FA764-0AF6-4DE2-A88E-77A1A2D42483}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F24FA764-0AF6-4DE2-A88E-77A1A2D42483}.Debug|Any CPU.Deploy.0 = Debug|Any CPU
{F24FA764-0AF6-4DE2-A88E-77A1A2D42483}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F24FA764-0AF6-4DE2-A88E-77A1A2D42483}.Release|Any CPU.Build.0 = Release|Any CPU
{F24FA764-0AF6-4DE2-A88E-77A1A2D42483}.Release|Any CPU.Deploy.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -454,6 +487,11 @@ Global
{35997586-8AAB-4EE5-A022-3E5BD12746CE} = {F47D1283-0EE9-4728-8026-58405C29B786}
{819CD7AA-01FD-4369-BABF-5DFCB7E94068} = {F47D1283-0EE9-4728-8026-58405C29B786}
{CD5EE836-ED56-48E3-B3B6-8D74C7C859B9} = {F47D1283-0EE9-4728-8026-58405C29B786}
{77F24A0C-50D9-47DB-92AA-15B514C55DFE} = {F47D1283-0EE9-4728-8026-58405C29B786}
{83F9656B-A339-4CBA-B1CF-392719217B7E} = {77F24A0C-50D9-47DB-92AA-15B514C55DFE}
{6AD6AFE9-0971-48A8-84CE-9BEF8022593C} = {77F24A0C-50D9-47DB-92AA-15B514C55DFE}
{F216E0E6-7BFE-4C98-9C72-D5E3AE965C67} = {77F24A0C-50D9-47DB-92AA-15B514C55DFE}
{F24FA764-0AF6-4DE2-A88E-77A1A2D42483} = {77F24A0C-50D9-47DB-92AA-15B514C55DFE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A710059F-0466-4D48-9B3A-0EF4F840B616}
Expand Down
171 changes: 154 additions & 17 deletions sandbox/OpenIddict.Sandbox.AspNet.Server/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
using System;
using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Mvc;
Expand All @@ -10,6 +14,8 @@
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin;
using Microsoft.Owin.Host.SystemWeb;
using Microsoft.Owin.Security.Cookies;
Expand All @@ -19,6 +25,7 @@
using OpenIddict.Server.Owin;
using OpenIddict.Validation.Owin;
using Owin;
using static System.Net.WebRequestMethods;
using static OpenIddict.Abstractions.OpenIddictConstants;

[assembly: OwinStartup(typeof(OpenIddict.Sandbox.AspNet.Server.Startup))]
Expand Down Expand Up @@ -52,10 +59,8 @@ public void Configuration(IAppBuilder app)
// Note: this sample uses the code flow, but you can enable the other flows if necessary.
options.AllowAuthorizationCodeFlow();

// Register the signing and encryption credentials used to protect
// sensitive data like the state tokens produced by OpenIddict.
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
.AddDevelopmentSigningCertificate();

// Register the OWIN host and configure the OWIN-specific options.
options.UseOwin()
Expand All @@ -75,26 +80,43 @@ public void Configuration(IAppBuilder app)
// parameter containing their URL as part of authorization responses. For more information,
// see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4.
options.UseWebProviders()
.AddGitHub(options =>
{
options.SetClientId("c4ade52327b01ddacff3")
.SetClientSecret("da6bed851b75e317bf6b2cb67013679d9467c122")
.SetRedirectUri("callback/login/github");
});
.AddGitHub(options =>
{
options.SetClientId("c4ade52327b01ddacff3")
.SetClientSecret("da6bed851b75e317bf6b2cb67013679d9467c122")
.SetRedirectUri("callback/login/github");
});
})

// Register the OpenIddict server components.
// Register the OpenIddict server components.
.AddServer(options =>
{
// Swap Issuer URL
// swap the following lines if you need a fqdn issuer for your mobile app
Uri uri = new Uri("https://vsr1d2md-44349.euw.devtunnels.ms/");
//Uri uri = null;

if(uri != null)
options.SetIssuer(uri);

// ensures, that, in case an issuer uri is set and is absolute, it is used for all endpoints (for dev tunnels)
// but the relative endpoint must be kept (for local auth via browser)
string[] GenerateUris(Uri uri, string relative) => uri switch {
null => [relative],
Uri {IsAbsoluteUri: false} => [relative],
Uri {IsAbsoluteUri: true} => [new Uri(uri, relative).ToString(), relative]
};

// Enable the authorization, device, introspection,
// logout, token, userinfo and verification endpoints.
options.SetAuthorizationEndpointUris("connect/authorize")
.SetDeviceEndpointUris("connect/device")
.SetIntrospectionEndpointUris("connect/introspect")
.SetLogoutEndpointUris("connect/logout")
.SetTokenEndpointUris("connect/token")
.SetUserinfoEndpointUris("connect/userinfo")
.SetVerificationEndpointUris("connect/verify");
options = options.SetAuthorizationEndpointUris(GenerateUris(uri, "connect/authorize"))
.SetDeviceEndpointUris(GenerateUris(uri, "connect/device"))
.SetIntrospectionEndpointUris(GenerateUris(uri, "connect/introspect"))
.SetLogoutEndpointUris(GenerateUris(uri, "connect/logout"))
.SetTokenEndpointUris(GenerateUris(uri, "connect/token"))
.SetUserinfoEndpointUris(GenerateUris(uri, "connect/userinfo"))
.SetVerificationEndpointUris(GenerateUris(uri, "connect/verify"))
.SetCryptographyEndpointUris(GenerateUris(uri, ".well-known/jwks"));

// Note: this sample uses the code, device code, password and refresh token flows, but you
// can enable the other flows if you need to support implicit or client credentials.
Expand All @@ -110,6 +132,9 @@ public void Configuration(IAppBuilder app)
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();

// Register hard-coded certificates for mobile app support
//options.AddCertificatesForMobileApps();

// Force client applications to use Proof Key for Code Exchange (PKCE).
options.RequireProofKeyForCodeExchange();

Expand All @@ -118,6 +143,7 @@ public void Configuration(IAppBuilder app)
.EnableAuthorizationEndpointPassthrough()
.EnableLogoutEndpointPassthrough()
.EnableTokenEndpointPassthrough();

})

// Register the OpenIddict validation components.
Expand Down Expand Up @@ -272,6 +298,117 @@ await manager.CreateAsync(new OpenIddictApplicationDescriptor
}
});
}

if (await manager.FindByClientIdAsync("maui") is null)
{
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
ApplicationType = ApplicationTypes.Native,
ClientId = "maui",
ClientType = ClientTypes.Public,
ConsentType = ConsentTypes.Systematic,
DisplayName = "MAUI client application",
DisplayNames =
{
[CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente MAUI"
},
PostLogoutRedirectUris =
{
new Uri("com.openiddict.sandbox.maui.client:/callback/logout/local")
},
RedirectUris =
{
new Uri("com.openiddict.sandbox.maui.client:/callback/login/local")
},
Permissions =
{
Permissions.Endpoints.Authorization,
Permissions.Endpoints.Logout,
Permissions.Endpoints.Token,
Permissions.GrantTypes.AuthorizationCode,
Permissions.GrantTypes.RefreshToken,
Permissions.ResponseTypes.Code,
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
Permissions.Scopes.Roles,
Permissions.Prefixes.Scope + "demo_api"
},
Requirements =
{
Requirements.Features.ProofKeyForCodeExchange
}
});
}

if (await manager.FindByClientIdAsync("avalonia") is null)
{
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
ApplicationType = ApplicationTypes.Native,
ClientId = "avalonia",
ClientType = ClientTypes.Public,
ConsentType = ConsentTypes.Systematic,
DisplayName = "Avalonia client application",
DisplayNames =
{
[CultureInfo.GetCultureInfo("fr-FR")] = "Application cliente avalonia"
},
PostLogoutRedirectUris =
{
new Uri("com.openiddict.sandbox.avalonia.client:/callback/logout/local")
},
RedirectUris =
{
new Uri("com.openiddict.sandbox.avalonia.client:/callback/login/local")
},
Permissions =
{
Permissions.Endpoints.Authorization,
Permissions.Endpoints.Logout,
Permissions.Endpoints.Token,
Permissions.GrantTypes.AuthorizationCode,
Permissions.GrantTypes.RefreshToken,
Permissions.ResponseTypes.Code,
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
Permissions.Scopes.Roles,
Permissions.Prefixes.Scope + "demo_api"
},
Requirements =
{
Requirements.Features.ProofKeyForCodeExchange
}
});
}
}).GetAwaiter().GetResult();
}
}

internal static class ServiceCollectionExtensions
{
/// <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>
internal static OpenIddictServerBuilder AddCertificatesForMobileApps(this OpenIddictServerBuilder 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;
}

}
Loading

0 comments on commit 3f594f0

Please sign in to comment.