You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1513 lines
71 KiB
1513 lines
71 KiB
using System.Globalization;
|
|
using System.Text;
|
|
using System.Xml.Linq;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.Text;
|
|
using Scriban;
|
|
using Scriban.Runtime;
|
|
|
|
namespace OpenIddict.Client.WebIntegration.Generators;
|
|
|
|
[Generator]
|
|
public sealed class OpenIddictClientWebIntegrationGenerator : IIncrementalGenerator
|
|
{
|
|
public void Initialize(IncrementalGeneratorInitializationContext context)
|
|
{
|
|
var files = context.AdditionalTextsProvider.Where(static file =>
|
|
string.Equals(Path.GetFileName(file.Path), "OpenIddictClientWebIntegrationProviders.xml"));
|
|
|
|
context.RegisterSourceOutput(files, static (context, file) =>
|
|
{
|
|
var document = XDocument.Load(file.Path, LoadOptions.None);
|
|
|
|
context.AddSource(
|
|
"OpenIddictClientWebIntegrationBuilder.generated.cs",
|
|
SourceText.From(GenerateBuilderMethods(document), Encoding.UTF8));
|
|
|
|
context.AddSource(
|
|
"OpenIddictClientWebIntegrationConfiguration.generated.cs",
|
|
SourceText.From(GenerateConfigurationClasses(document), Encoding.UTF8));
|
|
|
|
context.AddSource(
|
|
"OpenIddictClientWebIntegrationConstants.generated.cs",
|
|
SourceText.From(GenerateConstants(document), Encoding.UTF8));
|
|
|
|
context.AddSource(
|
|
"OpenIddictClientWebIntegrationHelpers.generated.cs",
|
|
SourceText.From(GenerateHelpers(document), Encoding.UTF8));
|
|
|
|
context.AddSource(
|
|
"OpenIddictClientWebIntegrationSettings.generated.cs",
|
|
SourceText.From(GenerateSettings(document), Encoding.UTF8));
|
|
});
|
|
|
|
static string GenerateBuilderMethods(XDocument document)
|
|
{
|
|
var template = Template.Parse(@"#nullable enable
|
|
#pragma warning disable CS0618
|
|
|
|
using System.ComponentModel;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.Claims;
|
|
using System.Security.Cryptography;
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
|
using Microsoft.Extensions.Options;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using OpenIddict.Client;
|
|
using OpenIddict.Client.WebIntegration;
|
|
using OpenIddict.Extensions;
|
|
using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants;
|
|
|
|
namespace Microsoft.Extensions.DependencyInjection;
|
|
|
|
public sealed partial class OpenIddictClientWebIntegrationBuilder
|
|
{
|
|
{{~ for provider in providers ~}}
|
|
/// <summary>
|
|
/// Adds a new {{ provider.display_name }} client registration.
|
|
{{~ if provider.documentation ~}}
|
|
/// For more information, read <see href=""{{ provider.documentation }}"">the documentation</see>.
|
|
{{~ end ~}}
|
|
/// </summary>
|
|
/// <param name=""configuration"">The delegate used to configure the OpenIddict/{{ provider.display_name }} options.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder""/> instance.</returns>
|
|
{{~ if provider.obsolete ~}}
|
|
[Obsolete(""This provider is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public OpenIddictClientWebIntegrationBuilder Add{{ provider.name }}(Action<OpenIddictClientWebIntegrationBuilder.{{ provider.name }}> configuration)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(configuration);
|
|
|
|
Services.Configure<OpenIddictClientOptions>(options =>
|
|
{
|
|
var registration = new OpenIddictClientRegistration
|
|
{
|
|
ProviderSettings = new OpenIddictClientWebIntegrationSettings.{{ provider.name }}(),
|
|
ProviderType = ProviderTypes.{{ provider.name }}
|
|
};
|
|
|
|
configuration(new OpenIddictClientWebIntegrationBuilder.{{ provider.name }}(registration));
|
|
|
|
options.Registrations.Add(registration);
|
|
});
|
|
|
|
return this;
|
|
}
|
|
{{~ end ~}}
|
|
|
|
{{~ for provider in providers ~}}
|
|
/// <summary>
|
|
/// Exposes the necessary methods required to configure the {{ provider.display_name }} integration.
|
|
/// </summary>
|
|
{{~ if provider.obsolete ~}}
|
|
[Obsolete(""This provider is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public sealed partial class {{ provider.name }}
|
|
{
|
|
/// <summary>
|
|
/// Initializes a new instance of <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/>.
|
|
/// </summary>
|
|
/// <param name=""registration"">The client registration.</param>
|
|
public {{ provider.name }}(OpenIddictClientRegistration registration)
|
|
=> Registration = registration ?? throw new ArgumentNullException(nameof(registration));
|
|
|
|
/// <summary>
|
|
/// Gets the client registration.
|
|
/// </summary>
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public OpenIddictClientRegistration Registration { get; }
|
|
|
|
/// <summary>
|
|
/// Adds one or more client authentication methods to the list of client authentication methods that can be negotiated for this provider.
|
|
/// </summary>
|
|
/// <param name=""methods"">The client authentication methods.</param>
|
|
/// <remarks>Note: explicitly configuring the allowed client authentication methods is NOT recommended in most cases.</remarks>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
|
public {{ provider.name }} AddClientAuthenticationMethods(params string[] methods)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(methods);
|
|
|
|
return Set(registration => registration.ClientAuthenticationMethods.UnionWith(methods));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds one or more code challenge methods to the list of code challenge methods that can be negotiated for this provider.
|
|
/// </summary>
|
|
/// <param name=""methods"">The code challenge methods.</param>
|
|
/// <remarks>Note: explicitly configuring the allowed code challenge methods is NOT recommended in most cases.</remarks>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
|
public {{ provider.name }} AddCodeChallengeMethods(params string[] methods)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(methods);
|
|
|
|
return Set(registration => registration.CodeChallengeMethods.UnionWith(methods));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds one or more grant types to the list of grant types that can be negotiated for this provider.
|
|
/// </summary>
|
|
/// <param name=""types"">The grant types.</param>
|
|
/// <remarks>Note: explicitly configuring the allowed grant types is NOT recommended in most cases.</remarks>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
|
public {{ provider.name }} AddGrantTypes(params string[] types)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(types);
|
|
|
|
return Set(registration => registration.GrantTypes.UnionWith(types));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds one or more response modes to the list of response modes that can be negotiated for this provider.
|
|
/// </summary>
|
|
/// <param name=""modes"">The response modes.</param>
|
|
/// <remarks>Note: explicitly configuring the allowed response modes is NOT recommended in most cases.</remarks>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
|
public {{ provider.name }} AddResponseModes(params string[] modes)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(modes);
|
|
|
|
return Set(registration => registration.ResponseModes.UnionWith(modes));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds one or more response types to the list of response types that can be negotiated for this provider.
|
|
/// </summary>
|
|
/// <param name=""types"">The response types.</param>
|
|
/// <remarks>Note: explicitly configuring the allowed response types is NOT recommended in most cases.</remarks>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
[EditorBrowsable(EditorBrowsableState.Advanced)]
|
|
public {{ provider.name }} AddResponseTypes(params string[] types)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(types);
|
|
|
|
return Set(registration => registration.ResponseTypes.UnionWith(types));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds one or more scopes to the list of requested scopes, if applicable.
|
|
/// </summary>
|
|
/// <param name=""scopes"">The scopes.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
public {{ provider.name }} AddScopes(params string[] scopes)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(scopes);
|
|
|
|
return Set(registration => registration.Scopes.UnionWith(scopes));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disables pushed authorization requests for this client registration. When pushed authorization
|
|
/// requests are disabled, PAR is not used by the OpenIddict client, even if the remote authorization
|
|
/// server exposes a pushed authorization endpoint. If the authorization server requires using PAR,
|
|
/// an exception is automatically thrown when starting an interactive authentication challenge.
|
|
/// </summary>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
public {{ provider.name }} DisablePushedAuthorizationRequests()
|
|
=> Set(registration => registration.DisablePushedAuthorizationRequests = true);
|
|
|
|
/// <summary>
|
|
/// Sets the issuer that will be attached to the <see cref=""Claim""/>
|
|
/// instances created by the OpenIddict client stack for this provider.
|
|
/// </summary>
|
|
/// <param name=""issuer"">The claims issuer.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
public {{ provider.name }} SetClaimsIssuer(string issuer)
|
|
{
|
|
ArgumentException.ThrowIfNullOrEmpty(issuer);
|
|
|
|
return Set(registration => registration.ClaimsIssuer = issuer);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the provider name.
|
|
/// </summary>
|
|
/// <param name=""name"">The provider name.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
public {{ provider.name }} SetProviderName(string name)
|
|
{
|
|
ArgumentException.ThrowIfNullOrEmpty(name);
|
|
|
|
return Set(registration => registration.ProviderName = name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the provider display name.
|
|
/// </summary>
|
|
/// <param name=""name"">The provider display name.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
public {{ provider.name }} SetProviderDisplayName(string name)
|
|
{
|
|
ArgumentException.ThrowIfNullOrEmpty(name);
|
|
|
|
return Set(registration => registration.ProviderDisplayName = name);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the registration identifier.
|
|
/// </summary>
|
|
/// <param name=""identifier"">The registration identifier.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
public {{ provider.name }} SetRegistrationId(string identifier)
|
|
{
|
|
ArgumentException.ThrowIfNullOrEmpty(identifier);
|
|
|
|
return Set(registration => registration.RegistrationId = identifier);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the client identifier.
|
|
/// </summary>
|
|
/// <param name=""identifier"">The client identifier.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
public {{ provider.name }} SetClientId(string identifier)
|
|
{
|
|
ArgumentException.ThrowIfNullOrEmpty(identifier);
|
|
|
|
return Set(registration => registration.ClientId = identifier);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the client secret, if applicable.
|
|
/// </summary>
|
|
/// <param name=""secret"">The client secret.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
public {{ provider.name }} SetClientSecret(string secret)
|
|
{
|
|
ArgumentException.ThrowIfNullOrEmpty(secret);
|
|
|
|
return Set(registration => registration.ClientSecret = secret);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the post-logout redirection URI, if applicable.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Note: the post-logout redirection URI is automatically added to
|
|
/// <see cref=""OpenIddictClientOptions.PostLogoutRedirectionEndpointUris""/>.
|
|
/// </remarks>
|
|
/// <param name=""uri"">The post-logout redirection URI.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
public {{ provider.name }} SetPostLogoutRedirectUri(Uri uri)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(uri);
|
|
|
|
return Set(registration => registration.PostLogoutRedirectUri = uri);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the post-logout redirection URI, if applicable.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Note: the post-logout redirection URI is automatically added to
|
|
/// <see cref=""OpenIddictClientOptions.PostLogoutRedirectionEndpointUris""/>.
|
|
/// </remarks>
|
|
/// <param name=""uri"">The post-logout redirection URI.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
public {{ provider.name }} SetPostLogoutRedirectUri([StringSyntax(StringSyntaxAttribute.Uri)] string uri)
|
|
{
|
|
ArgumentException.ThrowIfNullOrEmpty(uri);
|
|
|
|
return SetPostLogoutRedirectUri(new Uri(uri, UriKind.RelativeOrAbsolute));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the redirection URI, if applicable.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Note: the redirection URI is automatically added to
|
|
/// <see cref=""OpenIddictClientOptions.RedirectionEndpointUris""/>.
|
|
/// </remarks>
|
|
/// <param name=""uri"">The redirection URI.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
public {{ provider.name }} SetRedirectUri(Uri uri)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(uri);
|
|
|
|
return Set(registration => registration.RedirectUri = uri);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets the redirection URI, if applicable.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Note: the redirection URI is automatically added to
|
|
/// <see cref=""OpenIddictClientOptions.RedirectionEndpointUris""/>.
|
|
/// </remarks>
|
|
/// <param name=""uri"">The redirection URI.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
public {{ provider.name }} SetRedirectUri([StringSyntax(StringSyntaxAttribute.Uri)] string uri)
|
|
{
|
|
ArgumentException.ThrowIfNullOrEmpty(uri);
|
|
|
|
return SetRedirectUri(new Uri(uri, UriKind.RelativeOrAbsolute));
|
|
}
|
|
|
|
{{~ for environment in provider.environments ~}}
|
|
/// <summary>
|
|
/// Configures the provider to use the ""{{ environment.name }}"" environment.
|
|
/// </summary>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
public {{ provider.name }} Use{{ environment.name }}Environment()
|
|
=> Set(registration => registration.Get{{ provider.name }}Settings().Environment = OpenIddictClientWebIntegrationConstants.{{ provider.name }}.Environments.{{ environment.name }});
|
|
{{~ end ~}}
|
|
|
|
{{~ for setting in provider.settings ~}}
|
|
{{~ if setting.collection ~}}
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""{{ setting.parameter_name }}"">{{ setting.description | string.capitalize }}.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Add{{ setting.property_name }}(params {{ setting.clr_type }}[] {{ setting.parameter_name }})
|
|
{
|
|
ArgumentNullException.ThrowIfNull({{ setting.parameter_name }});
|
|
|
|
return Set(registration => registration.Get{{ provider.name }}Settings().{{ setting.property_name }}.UnionWith({{ setting.parameter_name }}));
|
|
}
|
|
{{~ else if setting.clr_type == 'ECDsaSecurityKey' ~}}
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""{{ setting.parameter_name }}"">{{ setting.description | string.capitalize }}.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Set{{ setting.property_name }}(ECDsaSecurityKey {{ setting.parameter_name }})
|
|
{
|
|
ArgumentNullException.ThrowIfNull({{ setting.parameter_name }});
|
|
|
|
if ({{ setting.parameter_name }}.PrivateKeyStatus is PrivateKeyStatus.DoesNotExist)
|
|
{
|
|
throw new ArgumentException(SR.GetResourceString(SR.ID0055), nameof({{ setting.parameter_name }}));
|
|
}
|
|
|
|
return Set(registration => registration.Get{{ provider.name }}Settings().{{ setting.property_name }} = {{ setting.parameter_name }});
|
|
}
|
|
|
|
#if SUPPORTS_PEM_ENCODED_KEY_IMPORT
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""key"">
|
|
/// The PEM-encoded Elliptic Curve Digital Signature Algorithm (ECDSA) signing key.
|
|
/// </param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Set{{ setting.property_name }}(string key)
|
|
=> Set{{ setting.property_name }}(key.AsMemory());
|
|
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""key"">
|
|
/// The PEM-encoded Elliptic Curve Digital Signature Algorithm (ECDSA) signing key.
|
|
/// </param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Set{{ setting.property_name }}(ReadOnlyMemory<char> key)
|
|
=> Set{{ setting.property_name }}(key.Span);
|
|
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""key"">
|
|
/// The PEM-encoded Elliptic Curve Digital Signature Algorithm (ECDSA) signing key.
|
|
/// </param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Set{{ setting.property_name }}(ReadOnlySpan<char> key)
|
|
{
|
|
if (key.IsEmpty)
|
|
{
|
|
throw new ArgumentException(SR.GetResourceString(SR.ID0346), nameof(key));
|
|
}
|
|
|
|
var algorithm = OpenIddictHelpers.CreateEcdsaKey();
|
|
|
|
try
|
|
{
|
|
algorithm.ImportFromPem(key);
|
|
}
|
|
|
|
catch
|
|
{
|
|
algorithm.Dispose();
|
|
|
|
throw;
|
|
}
|
|
|
|
return Set{{ setting.property_name }}(new ECDsaSecurityKey(algorithm));
|
|
}
|
|
#endif
|
|
{{~ else if setting.clr_type == 'Uri' ~}}
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""{{ setting.parameter_name }}"">{{ setting.description | string.capitalize }}.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Set{{ setting.property_name }}(Uri {{ setting.parameter_name }})
|
|
{
|
|
ArgumentNullException.ThrowIfNull({{ setting.parameter_name }});
|
|
|
|
if (!{{ setting.parameter_name }}.IsAbsoluteUri || OpenIddictHelpers.IsImplicitFileUri({{ setting.parameter_name }}))
|
|
{
|
|
throw new ArgumentException(SR.GetResourceString(SR.ID0144), nameof({{ setting.parameter_name }}));
|
|
}
|
|
|
|
return Set(registration => registration.Get{{ provider.name }}Settings().{{ setting.property_name }} = {{ setting.parameter_name }});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""{{ setting.parameter_name }}"">{{ setting.description | string.capitalize }}.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Set{{ setting.property_name }}(string {{ setting.parameter_name }})
|
|
{
|
|
ArgumentException.ThrowIfNullOrEmpty({{ setting.parameter_name }});
|
|
|
|
return Set{{ setting.property_name }}(new Uri({{ setting.parameter_name }}, UriKind.RelativeOrAbsolute));
|
|
}
|
|
{{~ else if setting.clr_type == 'X509Certificate2' ~}}
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""{{ setting.parameter_name }}"">{{ setting.description | string.capitalize }}.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Set{{ setting.property_name }}(X509Certificate2 {{ setting.parameter_name }})
|
|
{
|
|
ArgumentNullException.ThrowIfNull({{ setting.parameter_name }});
|
|
|
|
if (!{{ setting.parameter_name }}.HasPrivateKey)
|
|
{
|
|
throw new ArgumentException(SR.GetResourceString(SR.ID0061), nameof({{ setting.parameter_name }}));
|
|
}
|
|
|
|
return Set(registration => registration.Get{{ provider.name }}Settings().{{ setting.property_name }} = {{ setting.parameter_name }});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""assembly"">The assembly containing the certificate.</param>
|
|
/// <param name=""resource"">The name of the embedded resource.</param>
|
|
/// <param name=""password"">The password used to open the certificate.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Set{{ setting.property_name }}(Assembly assembly, string resource, string? password)
|
|
#if SUPPORTS_EPHEMERAL_KEY_SETS
|
|
// Note: ephemeral key sets are currently not supported on macOS.
|
|
=> Set{{ setting.property_name }}(assembly, resource, password, OperatingSystem.IsMacOS() ?
|
|
X509KeyStorageFlags.MachineKeySet :
|
|
X509KeyStorageFlags.EphemeralKeySet);
|
|
#else
|
|
=> Set{{ setting.property_name }}(assembly, resource, password, X509KeyStorageFlags.MachineKeySet);
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""assembly"">The assembly containing the certificate.</param>
|
|
/// <param name=""resource"">The name of the embedded resource.</param>
|
|
/// <param name=""password"">The password used to open the certificate.</param>
|
|
/// <param name=""flags"">An enumeration of flags indicating how and where to store the private key of the certificate.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Set{{ setting.property_name }}(
|
|
Assembly assembly, string resource,
|
|
string? password, X509KeyStorageFlags flags)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(assembly);
|
|
ArgumentException.ThrowIfNullOrEmpty(resource);
|
|
|
|
using var stream = assembly.GetManifestResourceStream(resource) ??
|
|
throw new InvalidOperationException(SR.GetResourceString(SR.ID0064));
|
|
|
|
return Set{{ setting.property_name }}(stream, password, flags);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""stream"">The stream containing the certificate.</param>
|
|
/// <param name=""password"">The password used to open the certificate.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Set{{ setting.property_name }}(Stream stream, string? password)
|
|
#if SUPPORTS_EPHEMERAL_KEY_SETS
|
|
// Note: ephemeral key sets are currently not supported on macOS.
|
|
=> Set{{ setting.property_name }}(stream, password, OperatingSystem.IsMacOS() ?
|
|
X509KeyStorageFlags.MachineKeySet :
|
|
X509KeyStorageFlags.EphemeralKeySet);
|
|
#else
|
|
=> Set{{ setting.property_name }}(stream, password, X509KeyStorageFlags.MachineKeySet);
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""stream"">The stream containing the certificate.</param>
|
|
/// <param name=""password"">The password used to open the certificate.</param>
|
|
/// <param name=""flags"">An enumeration of flags indicating how and where to store the private key of the certificate.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Set{{ setting.property_name }}(Stream stream, string? password, X509KeyStorageFlags flags)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(stream);
|
|
|
|
using var buffer = new MemoryStream();
|
|
stream.CopyTo(buffer);
|
|
|
|
#if SUPPORTS_CERTIFICATE_LOADER
|
|
var certificate = X509Certificate2.GetCertContentType(buffer.ToArray()) switch
|
|
{
|
|
X509ContentType.Pkcs12 => X509CertificateLoader.LoadPkcs12(buffer.ToArray(), password, flags),
|
|
|
|
_ => throw new InvalidOperationException(SR.GetResourceString(SR.ID0454))
|
|
};
|
|
#else
|
|
var certificate = new X509Certificate2(buffer.ToArray(), password, flags);
|
|
#endif
|
|
return Set{{ setting.property_name }}(certificate);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""thumbprint"">The thumbprint of the certificate used to identify it in the X.509 store.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Set{{ setting.property_name }}(string thumbprint)
|
|
{
|
|
ArgumentException.ThrowIfNullOrEmpty(thumbprint);
|
|
|
|
return Set{{ setting.property_name }}(
|
|
GetCertificate(StoreLocation.CurrentUser, thumbprint) ??
|
|
GetCertificate(StoreLocation.LocalMachine, thumbprint) ??
|
|
throw new InvalidOperationException(SR.GetResourceString(SR.ID0066)));
|
|
|
|
static X509Certificate2? GetCertificate(StoreLocation location, string thumbprint)
|
|
{
|
|
using var store = new X509Store(StoreName.My, location);
|
|
store.Open(OpenFlags.ReadOnly);
|
|
|
|
return store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, validOnly: false)
|
|
.Cast<X509Certificate2>()
|
|
.SingleOrDefault();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""thumbprint"">The thumbprint of the certificate used to identify it in the X.509 store.</param>
|
|
/// <param name=""name"">The name of the X.509 store.</param>
|
|
/// <param name=""location"">The location of the X.509 store.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Set{{ setting.property_name }}(string thumbprint, StoreName name, StoreLocation location)
|
|
{
|
|
ArgumentException.ThrowIfNullOrEmpty(thumbprint);
|
|
|
|
using var store = new X509Store(name, location);
|
|
store.Open(OpenFlags.ReadOnly);
|
|
|
|
return Set{{ setting.property_name }}(
|
|
store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, validOnly: false)
|
|
.Cast<X509Certificate2>()
|
|
.SingleOrDefault() ?? throw new InvalidOperationException(SR.GetResourceString(SR.ID0066)));
|
|
}
|
|
{{~ else if setting.clr_type == 'bool' ~}}
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""{{ setting.parameter_name }}"">{{ setting.description | string.capitalize }}.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Set{{ setting.property_name }}(bool {{ setting.parameter_name }})
|
|
=> Set(registration => registration.Get{{ provider.name }}Settings().{{ setting.property_name }} = {{ setting.parameter_name }});
|
|
{{~ else ~}}
|
|
/// <summary>
|
|
/// Configures {{ setting.description }}.
|
|
/// </summary>
|
|
/// <param name=""{{ setting.parameter_name }}"">{{ setting.description | string.capitalize }}.</param>
|
|
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder.{{ provider.name }}""/> instance.</returns>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
public {{ provider.name }} Set{{ setting.property_name }}({{ setting.clr_type }} {{ setting.parameter_name }})
|
|
{
|
|
ArgumentNullException.ThrowIfNull({{ setting.parameter_name }});
|
|
|
|
return Set(registration => registration.Get{{ provider.name }}Settings().{{ setting.property_name }} = {{ setting.parameter_name }});
|
|
}
|
|
{{~ end ~}}
|
|
|
|
{{~ end ~}}
|
|
|
|
/// <inheritdoc/>
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public override bool Equals(object? obj) => base.Equals(obj);
|
|
|
|
/// <inheritdoc/>
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public override int GetHashCode() => base.GetHashCode();
|
|
|
|
/// <inheritdoc/>
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
public override string? ToString() => base.ToString();
|
|
|
|
/// <summary>
|
|
/// Amends the client registration created by the {{ provider.display_name }} integration.
|
|
/// </summary>
|
|
/// <param name=""configuration"">The delegate used to configure the {{ provider.display_name }} client registration.</param>
|
|
/// <remarks>This extension can be safely called multiple times.</remarks>
|
|
/// <returns>The <see cref=""OpenIddictClientRegistration""/> instance.</returns>
|
|
private {{ provider.name }} Set(Action<OpenIddictClientRegistration> configuration)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(configuration);
|
|
|
|
configuration(Registration);
|
|
|
|
return this;
|
|
}
|
|
}
|
|
{{~ end ~}}
|
|
}
|
|
");
|
|
var context = CreateTemplateContext(new
|
|
{
|
|
Providers = document.Root.Elements("Provider")
|
|
.Select(static provider => new
|
|
{
|
|
Name = (string) provider.Attribute("Name"),
|
|
DisplayName = (string?) provider.Attribute("DisplayName") ?? (string) provider.Attribute("Name"),
|
|
Documentation = (string?) provider.Attribute("Documentation"),
|
|
|
|
Obsolete = (bool?) provider.Attribute("Obsolete") ?? false,
|
|
|
|
Environments = provider.Elements("Environment").Select(static environment => new
|
|
{
|
|
Name = (string?) environment.Attribute("Name") ?? "Production"
|
|
})
|
|
.ToList(),
|
|
|
|
Settings = provider.Elements("Setting").Select(static setting => new
|
|
{
|
|
PropertyName = (string) setting.Attribute("PropertyName"),
|
|
ParameterName = (string) setting.Attribute("ParameterName"),
|
|
|
|
Collection = (bool?) setting.Attribute("Collection") ?? false,
|
|
Obsolete = (bool?) setting.Attribute("Obsolete") ?? false,
|
|
|
|
Description = (string) setting.Attribute("Description") is string description ?
|
|
char.ToLower(description[0], CultureInfo.GetCultureInfo("en-US")) + description[1..] : null,
|
|
ClrType = (string) setting.Attribute("Type") switch
|
|
{
|
|
"Boolean" => "bool",
|
|
"EncryptionKey" => (string?) setting.Element("EncryptionAlgorithm")?.Attribute("Value") switch
|
|
{
|
|
"RS256" or "RS384" or "RS512" => "RsaSecurityKey",
|
|
|
|
_ => "SecurityKey"
|
|
},
|
|
|
|
"SigningCertificate" => "X509Certificate2",
|
|
|
|
"SigningKey" => (string?) setting.Element("SigningAlgorithm")?.Attribute("Value") switch
|
|
{
|
|
"ES256" or "ES384" or "ES512" => "ECDsaSecurityKey",
|
|
"PS256" or "PS384" or "PS512" or "RS256" or "RS384" or "RS512" => "RsaSecurityKey",
|
|
|
|
_ => "SecurityKey"
|
|
},
|
|
|
|
"String" => "string",
|
|
"StringHashSet" => "HashSet<string>",
|
|
"Uri" => "Uri",
|
|
|
|
string value => value
|
|
}
|
|
})
|
|
.ToList()
|
|
})
|
|
.ToList()
|
|
});
|
|
|
|
return template.Render(context);
|
|
}
|
|
|
|
static string GenerateConstants(XDocument document)
|
|
{
|
|
var template = Template.Parse(@"#nullable enable
|
|
|
|
namespace OpenIddict.Client.WebIntegration;
|
|
|
|
public static partial class OpenIddictClientWebIntegrationConstants
|
|
{
|
|
{{~ for provider in providers ~}}
|
|
public static class {{ provider.name }}
|
|
{
|
|
public static class Environments
|
|
{
|
|
{{~ for environment in provider.environments ~}}
|
|
public const string {{ environment.name }} = ""{{ environment.name }}"";
|
|
{{~ end ~}}
|
|
}
|
|
|
|
public static class Properties
|
|
{
|
|
{{~ for property in provider.properties ~}}
|
|
public const string {{ property.name }} = ""{{ property.dictionary_key }}"";
|
|
{{~ end ~}}
|
|
}
|
|
|
|
{{~ for group in provider.constants ~}}
|
|
public static class {{ group.key }}
|
|
{
|
|
{{~ for constant in group ~}}
|
|
public const string {{ constant.name }} = ""{{ constant.value }}"";
|
|
{{~ end ~}}
|
|
}
|
|
{{~ end ~}}
|
|
}
|
|
{{~ end ~}}
|
|
|
|
public static class Providers
|
|
{
|
|
{{~ for provider in providers ~}}
|
|
public const string {{ provider.name }} = ""{{ provider.name }}"";
|
|
{{~ end ~}}
|
|
}
|
|
|
|
public static class ProviderTypes
|
|
{
|
|
{{~ for provider in providers ~}}
|
|
public const string {{ provider.name }} = ""{{ provider.id }}"";
|
|
{{~ end ~}}
|
|
}
|
|
}
|
|
");
|
|
var context = CreateTemplateContext(new
|
|
{
|
|
Providers = document.Root.Elements("Provider")
|
|
.Select(static provider => new
|
|
{
|
|
Name = (string) provider.Attribute("Name"),
|
|
Id = (string) provider.Attribute("Id"),
|
|
|
|
Environments = provider.Elements("Environment").Select(static environment => new
|
|
{
|
|
Name = (string?) environment.Attribute("Name") ?? "Production"
|
|
})
|
|
.ToList(),
|
|
|
|
Properties = provider.Elements("Property").Select(static property => new
|
|
{
|
|
Name = (string) property.Attribute("Name"),
|
|
DictionaryKey = (string) property.Attribute("DictionaryKey")
|
|
})
|
|
.ToList(),
|
|
|
|
Constants = provider.Elements("Constant")
|
|
.Select(static constant => new
|
|
{
|
|
Class = (string) constant.Attribute("Class"),
|
|
Name = (string) constant.Attribute("Name"),
|
|
Value = (string) constant.Attribute("Value")
|
|
})
|
|
.GroupBy(static constant => constant.Class)
|
|
.ToList(),
|
|
})
|
|
.ToList()
|
|
});
|
|
|
|
return template.Render(context);
|
|
}
|
|
|
|
static string GenerateConfigurationClasses(XDocument document)
|
|
{
|
|
var template = Template.Parse(@"#nullable enable
|
|
#pragma warning disable CS0618
|
|
|
|
using System.Text.RegularExpressions;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Options;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using OpenIddict.Client;
|
|
using OpenIddict.Extensions;
|
|
using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants;
|
|
using static OpenIddict.Extensions.OpenIddictHelpers;
|
|
|
|
namespace OpenIddict.Client.WebIntegration;
|
|
|
|
public sealed partial class OpenIddictClientWebIntegrationConfiguration
|
|
{
|
|
public static partial void ConfigureProvider(OpenIddictClientRegistration registration)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(registration);
|
|
|
|
{{~ for provider in providers ~}}
|
|
{{~ if for.index == 0 ~}}
|
|
if (registration.ProviderType is ProviderTypes.{{ provider.name }})
|
|
{{~ else ~}}
|
|
else if (registration.ProviderType is ProviderTypes.{{ provider.name }})
|
|
{{~ end ~}}
|
|
{
|
|
if (registration.ProviderSettings is not OpenIddictClientWebIntegrationSettings.{{ provider.name }} settings)
|
|
{
|
|
throw new InvalidOperationException(SR.GetResourceString(SR.ID0406));
|
|
}
|
|
|
|
{{~ for setting in provider.settings ~}}
|
|
{{~ if setting.default_value ~}}
|
|
{{~ if setting.type == 'String' ~}}
|
|
if (string.IsNullOrEmpty(settings.{{ setting.property_name }}))
|
|
{
|
|
settings.{{ setting.property_name }} = ""{{ setting.default_value }}"";
|
|
}
|
|
{{~ else if setting.type == 'Uri' ~}}
|
|
if (settings.{{ setting.property_name }} is null)
|
|
{
|
|
settings.{{ setting.property_name }} = new Uri(""{{ setting.default_value }}"", UriKind.RelativeOrAbsolute);
|
|
}
|
|
{{~ else if setting.type == 'Boolean' ~}}
|
|
if (settings.{{ setting.property_name }} is null)
|
|
{
|
|
settings.{{ setting.property_name }} = {{ setting.default_value }};
|
|
}
|
|
{{~ end ~}}
|
|
{{~ end ~}}
|
|
|
|
{{~ if setting.collection ~}}
|
|
if (settings.{{ setting.property_name }}.Count is 0)
|
|
{
|
|
{{~ for item in setting.items ~}}
|
|
{{~ if item.default && !item.required ~}}
|
|
settings.{{ setting.property_name }}.Add(""{{ item.value }}"");
|
|
{{~ end ~}}
|
|
{{~ end ~}}
|
|
}
|
|
{{~ end ~}}
|
|
|
|
{{~ for item in setting.items ~}}
|
|
{{~ if item.required ~}}
|
|
settings.{{ setting.property_name }}.Add(""{{ item.value }}"");
|
|
{{~ end ~}}
|
|
{{~ end ~}}
|
|
{{~ end ~}}
|
|
|
|
{{~ for environment in provider.environments ~}}
|
|
if (settings.Environment is OpenIddictClientWebIntegrationConstants.{{ provider.name }}.Environments.{{ environment.name }})
|
|
{
|
|
if (registration.Scopes.Count is 0)
|
|
{
|
|
{{~ for scope in environment.scopes ~}}
|
|
{{~ if scope.default && !scope.required ~}}
|
|
registration.Scopes.Add(""{{ scope.name }}"");
|
|
{{~ end ~}}
|
|
{{~ end ~}}
|
|
}
|
|
|
|
{{~ for scope in environment.scopes ~}}
|
|
{{~ if scope.required ~}}
|
|
registration.Scopes.Add(""{{ scope.name }}"");
|
|
{{~ end ~}}
|
|
{{~ end ~}}
|
|
}
|
|
{{~ end ~}}
|
|
|
|
{{~ for setting in provider.settings ~}}
|
|
{{~ if setting.required ~}}
|
|
{{~ if setting.type == 'String' ~}}
|
|
if (string.IsNullOrEmpty(settings.{{ setting.property_name }}))
|
|
{{~ else ~}}
|
|
if (settings.{{ setting.property_name }} is null)
|
|
{{~ end ~}}
|
|
{
|
|
throw new InvalidOperationException(SR.FormatID0332(nameof(settings.{{ setting.property_name }}), Providers.{{ provider.name }}));
|
|
}
|
|
{{~ end ~}}
|
|
|
|
{{~ if setting.type == 'Uri' ~}}
|
|
if (!settings.{{ setting.property_name }}.IsAbsoluteUri || OpenIddictHelpers.IsImplicitFileUri(settings.{{ setting.property_name }}))
|
|
{
|
|
throw new InvalidOperationException(SR.FormatID0350(nameof(settings.{{ setting.property_name }}), Providers.{{ provider.name }}));
|
|
}
|
|
{{~ end ~}}
|
|
{{~ end ~}}
|
|
|
|
registration.ProviderName ??= Providers.{{ provider.name }};
|
|
registration.ProviderDisplayName ??= ""{{ provider.display_name }}"";
|
|
|
|
registration.Issuer ??= settings.Environment switch
|
|
{
|
|
{{~ for environment in provider.environments ~}}
|
|
OpenIddictClientWebIntegrationConstants.{{ provider.name }}.Environments.{{ environment.name }}
|
|
=> new Uri($""{{ environment.issuer | string.replace '\'' '""' }}"", UriKind.Absolute),
|
|
{{~ end ~}}
|
|
|
|
_ => throw new InvalidOperationException(SR.FormatID0194(nameof(settings.Environment)))
|
|
};
|
|
|
|
registration.ConfigurationEndpoint ??= settings.Environment switch
|
|
{
|
|
{{~ for environment in provider.environments ~}}
|
|
OpenIddictClientWebIntegrationConstants.{{ provider.name }}.Environments.{{ environment.name }}
|
|
{{~ if environment.configuration_endpoint ~}}
|
|
=> new Uri($""{{ environment.configuration_endpoint | string.replace '\'' '""' }}"", UriKind.Absolute),
|
|
{{~ else ~}}
|
|
=> null,
|
|
{{~ end ~}}
|
|
{{~ end ~}}
|
|
|
|
_ => throw new InvalidOperationException(SR.FormatID0194(nameof(settings.Environment)))
|
|
};
|
|
|
|
registration.Configuration ??= settings.Environment switch
|
|
{
|
|
{{~ for environment in provider.environments ~}}
|
|
{{~ if environment.configuration ~}}
|
|
OpenIddictClientWebIntegrationConstants.{{ provider.name }}.Environments.{{ environment.name }} => new OpenIddictConfiguration
|
|
{
|
|
{{~ if environment.configuration.authorization_endpoint ~}}
|
|
AuthorizationEndpoint = new Uri($""{{ environment.configuration.authorization_endpoint | string.replace '\'' '""' }}"", UriKind.Absolute),
|
|
{{~ end ~}}
|
|
|
|
{{~ if environment.configuration.device_authorization_endpoint ~}}
|
|
DeviceAuthorizationEndpoint = new Uri($""{{ environment.configuration.device_authorization_endpoint | string.replace '\'' '""' }}"", UriKind.Absolute),
|
|
{{~ end ~}}
|
|
|
|
{{~ if environment.configuration.introspection_endpoint ~}}
|
|
IntrospectionEndpoint = new Uri($""{{ environment.configuration.introspection_endpoint | string.replace '\'' '""' }}"", UriKind.Absolute),
|
|
{{~ end ~}}
|
|
|
|
{{~ if environment.configuration.revocation_endpoint ~}}
|
|
RevocationEndpoint = new Uri($""{{ environment.configuration.revocation_endpoint | string.replace '\'' '""' }}"", UriKind.Absolute),
|
|
{{~ end ~}}
|
|
|
|
{{~ if environment.configuration.token_endpoint ~}}
|
|
TokenEndpoint = new Uri($""{{ environment.configuration.token_endpoint | string.replace '\'' '""' }}"", UriKind.Absolute),
|
|
{{~ end ~}}
|
|
|
|
{{~ if environment.configuration.user_info_endpoint ~}}
|
|
UserInfoEndpoint = new Uri($""{{ environment.configuration.user_info_endpoint | string.replace '\'' '""' }}"", UriKind.Absolute),
|
|
{{~ end ~}}
|
|
|
|
CodeChallengeMethodsSupported =
|
|
{
|
|
{{~ for method in environment.configuration.code_challenge_methods_supported ~}}
|
|
""{{ method }}"",
|
|
{{~ end ~}}
|
|
},
|
|
|
|
GrantTypesSupported =
|
|
{
|
|
{{~ for type in environment.configuration.grant_types_supported ~}}
|
|
""{{ type }}"",
|
|
{{~ end ~}}
|
|
},
|
|
|
|
ResponseModesSupported =
|
|
{
|
|
{{~ for mode in environment.configuration.response_modes_supported ~}}
|
|
""{{ mode }}"",
|
|
{{~ end ~}}
|
|
},
|
|
|
|
ResponseTypesSupported =
|
|
{
|
|
{{~ for type in environment.configuration.response_types_supported ~}}
|
|
""{{ type }}"",
|
|
{{~ end ~}}
|
|
},
|
|
|
|
ScopesSupported =
|
|
{
|
|
{{~ for scope in environment.configuration.scopes_supported ~}}
|
|
""{{ scope }}"",
|
|
{{~ end ~}}
|
|
},
|
|
|
|
DeviceAuthorizationEndpointAuthMethodsSupported =
|
|
{
|
|
{{~ for method in environment.configuration.device_authorization_endpoint_auth_methods_supported ~}}
|
|
""{{ method }}"",
|
|
{{~ end ~}}
|
|
},
|
|
|
|
IntrospectionEndpointAuthMethodsSupported =
|
|
{
|
|
{{~ for method in environment.configuration.introspection_endpoint_auth_methods_supported ~}}
|
|
""{{ method }}"",
|
|
{{~ end ~}}
|
|
},
|
|
|
|
RevocationEndpointAuthMethodsSupported =
|
|
{
|
|
{{~ for method in environment.configuration.revocation_endpoint_auth_methods_supported ~}}
|
|
""{{ method }}"",
|
|
{{~ end ~}}
|
|
},
|
|
|
|
TokenEndpointAuthMethodsSupported =
|
|
{
|
|
{{~ for method in environment.configuration.token_endpoint_auth_methods_supported ~}}
|
|
""{{ method }}"",
|
|
{{~ end ~}}
|
|
}
|
|
},
|
|
{{~ else ~}}
|
|
OpenIddictClientWebIntegrationConstants.{{ provider.name }}.Environments.{{ environment.name }} => null,
|
|
{{~ end ~}}
|
|
{{~ end ~}}
|
|
|
|
_ => throw new InvalidOperationException(SR.FormatID0194(nameof(settings.Environment)))
|
|
};
|
|
|
|
{{~ for setting in provider.settings ~}}
|
|
{{~ if setting.type == 'EncryptionKey' ~}}
|
|
if (settings.{{ setting.property_name }} is not null)
|
|
{
|
|
registration.EncryptionCredentials.Add(new EncryptingCredentials(settings.{{ setting.property_name }}, ""{{ setting.encryption_algorithm }}"", SecurityAlgorithms.Aes256CbcHmacSha512));
|
|
}
|
|
{{~ end ~}}
|
|
{{~ end ~}}
|
|
|
|
{{~ for setting in provider.settings ~}}
|
|
{{~ if setting.type == 'SigningCertificate' ~}}
|
|
if (settings.{{ setting.property_name }} is not null)
|
|
{
|
|
var key = new X509SecurityKey(settings.{{ setting.property_name }});
|
|
if (key.IsSupportedAlgorithm(SecurityAlgorithms.RsaSha256))
|
|
{
|
|
registration.SigningCredentials.Add(new SigningCredentials(key, SecurityAlgorithms.RsaSha256));
|
|
}
|
|
|
|
else if (key.IsSupportedAlgorithm(SecurityAlgorithms.HmacSha256))
|
|
{
|
|
registration.SigningCredentials.Add(new SigningCredentials(key, SecurityAlgorithms.HmacSha256));
|
|
}
|
|
|
|
#if SUPPORTS_ECDSA
|
|
// Note: ECDSA algorithms are bound to specific curves and must be treated separately.
|
|
else if (key.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha256))
|
|
{
|
|
registration.SigningCredentials.Add(new SigningCredentials(key, SecurityAlgorithms.EcdsaSha256));
|
|
}
|
|
|
|
else if (key.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha384))
|
|
{
|
|
registration.SigningCredentials.Add(new SigningCredentials(key, SecurityAlgorithms.EcdsaSha384));
|
|
}
|
|
|
|
else if (key.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha512))
|
|
{
|
|
registration.SigningCredentials.Add(new SigningCredentials(key, SecurityAlgorithms.EcdsaSha512));
|
|
}
|
|
#else
|
|
else if (key.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha256) ||
|
|
key.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha384) ||
|
|
key.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha512))
|
|
{
|
|
throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0069));
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
throw new InvalidOperationException(SR.GetResourceString(SR.ID0068));
|
|
}
|
|
}
|
|
{{~ end ~}}
|
|
{{~ if setting.type == 'SigningKey' ~}}
|
|
if (settings.{{ setting.property_name }} is not null)
|
|
{
|
|
// If the signing key is an asymmetric security key, ensure it has a private key.
|
|
if (settings.{{ setting.property_name }} is AsymmetricSecurityKey { PrivateKeyStatus: PrivateKeyStatus.DoesNotExist })
|
|
{
|
|
throw new InvalidOperationException(SR.GetResourceString(SR.ID0067));
|
|
}
|
|
|
|
{{~ if setting.signing_algorithm ~}}
|
|
registration.SigningCredentials.Add(new SigningCredentials(settings.{{ setting.property_name }}, ""{{ setting.signing_algorithm }}""));
|
|
{{~ else ~}}
|
|
if (settings.{{ setting.property_name }}.IsSupportedAlgorithm(SecurityAlgorithms.RsaSha256))
|
|
{
|
|
registration.SigningCredentials.Add(new SigningCredentials(settings.{{ setting.property_name }}, SecurityAlgorithms.RsaSha256));
|
|
}
|
|
|
|
else if (settings.{{ setting.property_name }}.IsSupportedAlgorithm(SecurityAlgorithms.HmacSha256))
|
|
{
|
|
registration.SigningCredentials.Add(new SigningCredentials(settings.{{ setting.property_name }}, SecurityAlgorithms.HmacSha256));
|
|
}
|
|
|
|
#if SUPPORTS_ECDSA
|
|
// Note: ECDSA algorithms are bound to specific curves and must be treated separately.
|
|
else if (settings.{{ setting.property_name }}.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha256))
|
|
{
|
|
registration.SigningCredentials.Add(new SigningCredentials(settings.{{ setting.property_name }}, SecurityAlgorithms.EcdsaSha256));
|
|
}
|
|
|
|
else if (settings.{{ setting.property_name }}.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha384))
|
|
{
|
|
registration.SigningCredentials.Add(new SigningCredentials(settings.{{ setting.property_name }}, SecurityAlgorithms.EcdsaSha384));
|
|
}
|
|
|
|
else if (settings.{{ setting.property_name }}.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha512))
|
|
{
|
|
registration.SigningCredentials.Add(new SigningCredentials(settings.{{ setting.property_name }}, SecurityAlgorithms.EcdsaSha512));
|
|
}
|
|
#else
|
|
else if (settings.{{ setting.property_name }}.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha256) ||
|
|
settings.{{ setting.property_name }}.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha384) ||
|
|
settings.{{ setting.property_name }}.IsSupportedAlgorithm(SecurityAlgorithms.EcdsaSha512))
|
|
{
|
|
throw new PlatformNotSupportedException(SR.GetResourceString(SR.ID0069));
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
throw new InvalidOperationException(SR.GetResourceString(SR.ID0068));
|
|
}
|
|
{{~ end ~}}
|
|
}
|
|
{{~ end ~}}
|
|
{{~ end ~}}
|
|
}
|
|
{{~ end ~}}
|
|
|
|
else
|
|
{
|
|
throw new InvalidOperationException(SR.GetResourceString(SR.ID0407));
|
|
}
|
|
}
|
|
}
|
|
");
|
|
var context = CreateTemplateContext(new
|
|
{
|
|
Providers = document.Root.Elements("Provider")
|
|
.Select(static provider => new
|
|
{
|
|
Name = (string) provider.Attribute("Name"),
|
|
DisplayName = (string?) provider.Attribute("DisplayName") ?? (string) provider.Attribute("Name"),
|
|
|
|
Environments = provider.Elements("Environment").Select(static environment => new
|
|
{
|
|
Name = (string?) environment.Attribute("Name") ?? "Production",
|
|
|
|
Issuer = (string) environment.Attribute("Issuer"),
|
|
ConfigurationEndpoint = (string?) environment.Attribute("ConfigurationEndpoint"),
|
|
|
|
Configuration = environment.Element("Configuration") switch
|
|
{
|
|
XElement configuration => new
|
|
{
|
|
AuthorizationEndpoint = (string?) configuration.Attribute("AuthorizationEndpoint"),
|
|
DeviceAuthorizationEndpoint = (string?) configuration.Attribute("DeviceAuthorizationEndpoint"),
|
|
IntrospectionEndpoint = (string?) configuration.Attribute("IntrospectionEndpoint"),
|
|
RevocationEndpoint = (string?) configuration.Attribute("RevocationEndpoint"),
|
|
TokenEndpoint = (string?) configuration.Attribute("TokenEndpoint"),
|
|
UserInfoEndpoint = (string?) configuration.Attribute("UserInfoEndpoint"),
|
|
|
|
CodeChallengeMethodsSupported = configuration.Elements("CodeChallengeMethod").ToList() switch
|
|
{
|
|
{ Count: > 0 } methods => methods.Select(static type => (string?) type.Attribute("Value")).ToList(),
|
|
|
|
_ => []
|
|
},
|
|
|
|
GrantTypesSupported = configuration.Elements("GrantType").ToList() switch
|
|
{
|
|
{ Count: > 0 } types => types.Select(static type => (string?) type.Attribute("Value")).ToList(),
|
|
|
|
// If no explicit grant type was set, assume the provider only supports the code flow.
|
|
_ => [GrantTypes.AuthorizationCode]
|
|
},
|
|
|
|
ResponseModesSupported = configuration.Elements("ResponseMode").ToList() switch
|
|
{
|
|
{ Count: > 0 } modes => modes.Select(static type => (string?) type.Attribute("Value")).ToList(),
|
|
|
|
// If no explicit response mode was set, assume the provider only supports the query response mode.
|
|
_ => [ResponseModes.Query]
|
|
},
|
|
|
|
ResponseTypesSupported = configuration.Elements("ResponseType").ToList() switch
|
|
{
|
|
{ Count: > 0 } types => types.Select(static type => (string?) type.Attribute("Value")).ToList(),
|
|
|
|
// If no explicit response type was set, assume the provider only supports the code flow.
|
|
_ => [ResponseTypes.Code]
|
|
},
|
|
|
|
ScopesSupported = configuration.Elements("Scope").ToList() switch
|
|
{
|
|
{ Count: > 0 } types => types.Select(static type => (string?) type.Attribute("Value")).ToList(),
|
|
|
|
_ => []
|
|
},
|
|
|
|
DeviceAuthorizationEndpointAuthMethodsSupported = configuration.Elements("DeviceAuthorizationEndpointAuthMethod").ToList() switch
|
|
{
|
|
{ Count: > 0 } methods => methods.Select(static type => (string?) type.Attribute("Value")).ToList(),
|
|
|
|
// If no explicit client authentication method was set, assume the provider only supports
|
|
// flowing the client credentials as part of the device authorization request payload.
|
|
_ => [ClientAuthenticationMethods.ClientSecretPost]
|
|
},
|
|
|
|
IntrospectionEndpointAuthMethodsSupported = configuration.Elements("IntrospectionEndpointAuthMethod").ToList() switch
|
|
{
|
|
{ Count: > 0 } methods => methods.Select(static type => (string?) type.Attribute("Value")).ToList(),
|
|
|
|
// If no explicit client authentication method was set, assume the provider only
|
|
// supports flowing the client credentials as part of the introspection request payload.
|
|
_ => [ClientAuthenticationMethods.ClientSecretPost]
|
|
},
|
|
|
|
RevocationEndpointAuthMethodsSupported = configuration.Elements("RevocationEndpointAuthMethod").ToList() switch
|
|
{
|
|
{ Count: > 0 } methods => methods.Select(static type => (string?) type.Attribute("Value")).ToList(),
|
|
|
|
// If no explicit client authentication method was set, assume the provider only
|
|
// supports flowing the client credentials as part of the revocation request payload.
|
|
_ => [ClientAuthenticationMethods.ClientSecretPost]
|
|
},
|
|
|
|
TokenEndpointAuthMethodsSupported = configuration.Elements("TokenEndpointAuthMethod").ToList() switch
|
|
{
|
|
{ Count: > 0 } methods => methods.Select(static type => (string?) type.Attribute("Value")).ToList(),
|
|
|
|
// If no explicit client authentication method was set, assume the provider only
|
|
// supports flowing the client credentials as part of the token request payload.
|
|
_ => [ClientAuthenticationMethods.ClientSecretPost]
|
|
}
|
|
},
|
|
|
|
_ => null
|
|
},
|
|
|
|
Scopes = environment.Elements("Scope").Select(static setting => new
|
|
{
|
|
Name = (string) setting.Attribute("Name"),
|
|
Default = (bool?) setting.Attribute("Default") ?? false,
|
|
Required = (bool?) setting.Attribute("Required") ?? false
|
|
})
|
|
})
|
|
.ToList(),
|
|
|
|
Settings = provider.Elements("Setting").Select(static setting => new
|
|
{
|
|
PropertyName = (string) setting.Attribute("PropertyName"),
|
|
|
|
Type = (string) setting.Attribute("Type"),
|
|
Required = (bool?) setting.Attribute("Required") ?? false,
|
|
Collection = (bool?) setting.Attribute("Collection") ?? false,
|
|
|
|
EncryptionAlgorithm = (string?) setting.Element("EncryptionAlgorithm")?.Attribute("Value"),
|
|
SigningAlgorithm = (string?) setting.Element("SigningAlgorithm")?.Attribute("Value"),
|
|
|
|
DefaultValue = (string?) setting.Attribute("DefaultValue"),
|
|
|
|
Items = setting.Elements("Item").Select(static item => new
|
|
{
|
|
Value = (string) item.Attribute("Value"),
|
|
Default = (bool?) item.Attribute("Default") ?? false,
|
|
Required = (bool?) item.Attribute("Required") ?? false
|
|
})
|
|
.ToList()
|
|
})
|
|
.ToList()
|
|
})
|
|
.ToList()
|
|
});
|
|
|
|
return template.Render(context);
|
|
}
|
|
|
|
static string GenerateHelpers(XDocument document)
|
|
{
|
|
var template = Template.Parse(@"#nullable enable
|
|
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using OpenIddict.Client;
|
|
using OpenIddict.Client.WebIntegration;
|
|
using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants;
|
|
|
|
namespace OpenIddict.Client.WebIntegration;
|
|
|
|
public static partial class OpenIddictClientWebIntegrationHelpers
|
|
{
|
|
{{~ for provider in providers ~}}
|
|
/// <summary>
|
|
/// Resolves the {{ provider.display_name }} provider settings from the specified registration.
|
|
/// </summary>
|
|
/// <param name=""registration"">The client registration.</param>
|
|
/// <returns>The {{ provider.display_name }} provider settings.</returns>
|
|
/// <exception cref=""InvalidOperationException"">The provider options cannot be resolved.</exception>
|
|
public static OpenIddictClientWebIntegrationSettings.{{ provider.name }} Get{{ provider.name }}Settings(this OpenIddictClientRegistration registration)
|
|
=> registration.ProviderSettings is OpenIddictClientWebIntegrationSettings.{{ provider.name }} settings ? settings :
|
|
throw new InvalidOperationException(SR.FormatID0333(Providers.{{ provider.name }}));
|
|
|
|
{{~ end ~}}
|
|
}
|
|
");
|
|
var context = CreateTemplateContext(new
|
|
{
|
|
Providers = document.Root.Elements("Provider")
|
|
.Select(static provider => new
|
|
{
|
|
Name = (string) provider.Attribute("Name"),
|
|
DisplayName = (string?) provider.Attribute("DisplayName") ?? (string) provider.Attribute("Name")
|
|
})
|
|
.ToList()
|
|
});
|
|
|
|
return template.Render(context);
|
|
}
|
|
|
|
static string GenerateSettings(XDocument document)
|
|
{
|
|
var template = Template.Parse(@"#nullable enable
|
|
|
|
using System.Security.Cryptography.X509Certificates;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
|
|
namespace OpenIddict.Client.WebIntegration;
|
|
|
|
public sealed partial class OpenIddictClientWebIntegrationSettings
|
|
{
|
|
{{~ for provider in providers ~}}
|
|
/// <summary>
|
|
/// Provides various options needed to configure the {{ provider.display_name }} integration.
|
|
/// </summary>
|
|
public sealed class {{ provider.name }}
|
|
{
|
|
/// <summary>
|
|
/// Gets or sets the environment that determines the endpoints to use (by default, ""Production"").
|
|
/// </summary>
|
|
public string? Environment { get; set; } = OpenIddictClientWebIntegrationConstants.{{ provider.name }}.Environments.Production;
|
|
|
|
{{~ for setting in provider.settings ~}}
|
|
/// <summary>
|
|
/// Gets or sets {{ setting.description }}.
|
|
/// </summary>
|
|
{{~ if setting.obsolete ~}}
|
|
[Obsolete(""This option is no longer supported and will be removed in a future version."")]
|
|
{{~ end ~}}
|
|
{{~ if setting.collection ~}}
|
|
public HashSet<{{ setting.clr_type }}> {{ setting.property_name }} { get; } = new();
|
|
{{~ else ~}}
|
|
public {{ setting.clr_type }}? {{ setting.property_name }} { get; set; }
|
|
{{~ end ~}}
|
|
|
|
{{~ end ~}}
|
|
}
|
|
{{~ end ~}}
|
|
}
|
|
");
|
|
var context = CreateTemplateContext(new
|
|
{
|
|
Providers = document.Root.Elements("Provider")
|
|
.Select(static provider => new
|
|
{
|
|
Name = (string) provider.Attribute("Name"),
|
|
DisplayName = (string?) provider.Attribute("DisplayName") ?? (string) provider.Attribute("Name"),
|
|
|
|
Settings = provider.Elements("Setting").Select(static setting => new
|
|
{
|
|
PropertyName = (string) setting.Attribute("PropertyName"),
|
|
|
|
Collection = (bool?) setting.Attribute("Collection") ?? false,
|
|
Obsolete = (bool?) setting.Attribute("Obsolete") ?? false,
|
|
|
|
Description = (string) setting.Attribute("Description") is string description ?
|
|
char.ToLower(description[0], CultureInfo.GetCultureInfo("en-US")) + description[1..] : null,
|
|
ClrType = (string) setting.Attribute("Type") switch
|
|
{
|
|
"Boolean" => "bool",
|
|
"EncryptionKey" => (string?) setting.Element("EncryptionAlgorithm")?.Attribute("Value") switch
|
|
{
|
|
"RS256" or "RS384" or "RS512" => "RsaSecurityKey",
|
|
|
|
_ => "SecurityKey"
|
|
},
|
|
|
|
"SigningCertificate" => "X509Certificate2",
|
|
|
|
"SigningKey" => (string?) setting.Element("SigningAlgorithm")?.Attribute("Value") switch
|
|
{
|
|
"ES256" or "ES384" or "ES512" => "ECDsaSecurityKey",
|
|
"PS256" or "PS384" or "PS512" or "RS256" or "RS384" or "RS512" => "RsaSecurityKey",
|
|
|
|
_ => "SecurityKey"
|
|
},
|
|
|
|
"String" => "string",
|
|
"StringHashSet" => "HashSet<string>",
|
|
"Uri" => "Uri",
|
|
|
|
string value => value
|
|
}
|
|
})
|
|
.ToList()
|
|
})
|
|
.ToList()
|
|
});
|
|
|
|
return template.Render(context);
|
|
}
|
|
|
|
static TemplateContext CreateTemplateContext(object model)
|
|
{
|
|
var context = new TemplateContext
|
|
{
|
|
LoopLimit = 100_000
|
|
};
|
|
|
|
context.PushGlobal(ScriptObject.From(model));
|
|
|
|
return context;
|
|
}
|
|
}
|
|
}
|
|
|