committed by
GitHub
35 changed files with 2027 additions and 33 deletions
@ -0,0 +1,39 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<SignAssembly>false</SignAssembly> |
|||
<PublicSign>false</PublicSign> |
|||
<IsPackable>false</IsPackable> |
|||
<IsShipping>false</IsShipping> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\src\OpenIddict.Abstractions\OpenIddict.Abstractions.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" PrivateAssets="all" /> |
|||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="all" /> |
|||
<PackageReference Include="Scriban" PrivateAssets="all" GeneratePathProperty="true" /> |
|||
<PackageReference Include="System.Interactive" PrivateAssets="all" GeneratePathProperty="true" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Using Include="OpenIddict.Abstractions" /> |
|||
<Using Include="OpenIddict.Abstractions.OpenIddictConstants" Static="true" /> |
|||
<Using Include="OpenIddict.Abstractions.OpenIddictResources" Alias="SR" /> |
|||
</ItemGroup> |
|||
|
|||
<PropertyGroup> |
|||
<GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn> |
|||
</PropertyGroup> |
|||
|
|||
<Target Name="GetDependencyTargetPaths"> |
|||
<ItemGroup> |
|||
<TargetPathWithTargetPlatformMoniker Include="$(PKGScriban)\lib\netstandard2.0\Scriban.dll" IncludeRuntimeDependency="false" /> |
|||
<TargetPathWithTargetPlatformMoniker Include="$(PKGSystem_Interactive)\lib\netstandard2.0\System.Interactive.dll" IncludeRuntimeDependency="false" /> |
|||
</ItemGroup> |
|||
</Target> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,621 @@ |
|||
using System.Globalization; |
|||
using System.Text; |
|||
using System.Text.RegularExpressions; |
|||
using System.Xml.Linq; |
|||
using Microsoft.CodeAnalysis; |
|||
using Microsoft.CodeAnalysis.Text; |
|||
using Scriban; |
|||
|
|||
namespace OpenIddict.Client.WebIntegration.Generators |
|||
{ |
|||
[Generator] |
|||
public class OpenIddictClientWebIntegrationGenerator : ISourceGenerator |
|||
{ |
|||
public void Execute(GeneratorExecutionContext context) |
|||
{ |
|||
var file = context.AdditionalFiles.Select(file => file.Path) |
|||
.Where(path => string.Equals(Path.GetFileName(path), "OpenIddictClientWebIntegrationProviders.xml")) |
|||
.SingleOrDefault(); |
|||
|
|||
if (string.IsNullOrEmpty(file)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var document = XDocument.Load(file, 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( |
|||
"OpenIddictClientWebIntegrationEnvironments.generated.cs", |
|||
SourceText.From(GenerateEnvironments(document), Encoding.UTF8)); |
|||
|
|||
context.AddSource( |
|||
"OpenIddictClientWebIntegrationHelpers.generated.cs", |
|||
SourceText.From(GenerateHelpers(document), Encoding.UTF8)); |
|||
|
|||
context.AddSource( |
|||
"OpenIddictClientWebIntegrationScopes.generated.cs", |
|||
SourceText.From(GenerateScopes(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
|
|||
|
|||
using Microsoft.Extensions.DependencyInjection.Extensions; |
|||
using Microsoft.Extensions.Options; |
|||
using OpenIddict.Client; |
|||
using OpenIddict.Client.WebIntegration; |
|||
using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants; |
|||
|
|||
namespace Microsoft.Extensions.DependencyInjection; |
|||
|
|||
public partial class OpenIddictClientWebIntegrationBuilder |
|||
{ |
|||
{{~ for provider in providers ~}} |
|||
/// <summary>
|
|||
/// Enables {{ provider.name }} integration using the specified settings.
|
|||
/// </summary>
|
|||
{{~ if provider.documentation ~}} |
|||
/// <remarks>
|
|||
/// For more information about {{ provider.name }} integration, visit <see href=""{{ provider.documentation }}"">the official website</see>.
|
|||
/// </remarks>
|
|||
{{~ end ~}} |
|||
/// <param name=""settings"">The provider settings.</param>
|
|||
/// <returns>The <see cref=""OpenIddictClientWebIntegrationBuilder""/>.</returns>
|
|||
public OpenIddictClientWebIntegrationBuilder Add{{ provider.name }}(OpenIddictClientWebIntegrationSettings.{{ provider.name }} settings) |
|||
{ |
|||
if (settings is null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(settings)); |
|||
} |
|||
|
|||
// Note: TryAddEnumerable() is used here to ensure the initializer is registered only once.
|
|||
Services.TryAddEnumerable(ServiceDescriptor.Singleton< |
|||
IConfigureOptions<OpenIddictClientOptions>, OpenIddictClientWebIntegrationConfiguration.{{ provider.name }}>()); |
|||
|
|||
return Configure(options => options.Providers.Add(new OpenIddictClientWebIntegrationProvider(Providers.{{ provider.name }}, settings))); |
|||
} |
|||
{{~ end ~}} |
|||
} |
|||
");
|
|||
return template.Render(new |
|||
{ |
|||
Providers = document.Root.Elements("Provider") |
|||
.Select(provider => new |
|||
{ |
|||
Name = (string) provider.Attribute("Name"), |
|||
Documentation = (string?) provider.Attribute("Documentation") |
|||
}) |
|||
.ToList() |
|||
}); |
|||
} |
|||
|
|||
static string GenerateConstants(XDocument document) |
|||
{ |
|||
var template = Template.Parse(@"#nullable enable
|
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
public static partial class OpenIddictClientWebIntegrationConstants |
|||
{ |
|||
public static class Providers |
|||
{ |
|||
{{~ for provider in providers ~}} |
|||
public const string {{ provider.name }} = ""{{ provider.name }}""; |
|||
{{~ end ~}} |
|||
} |
|||
} |
|||
");
|
|||
return template.Render(new |
|||
{ |
|||
Providers = document.Root.Elements("Provider") |
|||
.Select(provider => new { Name = (string) provider.Attribute("Name") }) |
|||
.ToList() |
|||
}); |
|||
} |
|||
|
|||
static string GenerateEnvironments(XDocument document) |
|||
{ |
|||
var template = Template.Parse(@"#nullable enable
|
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
public partial class OpenIddictClientWebIntegrationEnvironments |
|||
{ |
|||
{{~ for provider in providers ~}} |
|||
/// <summary>
|
|||
/// Exposes the environments supported by the {{ provider.name }} provider.
|
|||
/// </summary>
|
|||
public enum {{ provider.name }} |
|||
{ |
|||
{{~ for environment in provider.environments ~}} |
|||
{{ environment.name }}, |
|||
{{~ end ~}} |
|||
} |
|||
{{~ end ~}} |
|||
} |
|||
");
|
|||
return template.Render(new |
|||
{ |
|||
Providers = document.Root.Elements("Provider") |
|||
.Select(provider => new |
|||
{ |
|||
Name = (string) provider.Attribute("Name"), |
|||
|
|||
Environments = provider.Elements("Environment").Select(environment => new |
|||
{ |
|||
Name = (string?) environment.Attribute("Name") ?? "Production" |
|||
}) |
|||
.ToList() |
|||
}) |
|||
.ToList() |
|||
}); |
|||
} |
|||
|
|||
static string GenerateConfigurationClasses(XDocument document) |
|||
{ |
|||
var template = Template.Parse(@"#nullable enable
|
|||
|
|||
using Microsoft.Extensions.Options; |
|||
using Microsoft.IdentityModel.Tokens; |
|||
using OpenIddict.Client; |
|||
using SmartFormat; |
|||
using SmartFormat.Core.Settings; |
|||
using Properties = OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants.Properties; |
|||
using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants; |
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
public partial class OpenIddictClientWebIntegrationConfiguration |
|||
{ |
|||
{{~ for provider in providers ~}} |
|||
/// <summary>
|
|||
/// Contains the methods required to register the {{ provider.name }} integration in the OpenIddict client options.
|
|||
/// </summary>
|
|||
public class {{ provider.name }} : IConfigureOptions<OpenIddictClientOptions> |
|||
{ |
|||
private readonly IOptions<OpenIddictClientWebIntegrationOptions> _options; |
|||
|
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref=""OpenIddictClientWebIntegrationConfiguration.{{ provider.name }}"" /> class.
|
|||
/// </summary>
|
|||
/// <param name=""options"">The OpenIddict client web integration options.</param>
|
|||
/// <exception cref=""ArgumentException""><paramref name=""options""/> is null.</exception>
|
|||
public {{ provider.name }}(IOptions<OpenIddictClientWebIntegrationOptions> options) |
|||
=> _options = options ?? throw new ArgumentNullException(nameof(options)); |
|||
|
|||
/// <summary>
|
|||
/// Ensures the {{ provider.name }} configuration is in a consistent and valid state
|
|||
/// and registers the {{ provider.name }} integration in the OpenIddict client options.
|
|||
/// </summary>
|
|||
/// <param name=""options"">The options instance to initialize.</param>
|
|||
public void Configure(OpenIddictClientOptions options) |
|||
{ |
|||
foreach (var provider in _options.Value.Providers) |
|||
{ |
|||
if (provider.Name is not Providers.{{ provider.name }}) |
|||
{ |
|||
continue; |
|||
} |
|||
|
|||
if (provider.Settings is not OpenIddictClientWebIntegrationSettings.{{ provider.name }} settings) |
|||
{ |
|||
throw new InvalidOperationException(SR.FormatID0331(Providers.{{ provider.name }})); |
|||
} |
|||
|
|||
if (string.IsNullOrEmpty(settings.ClientId)) |
|||
{ |
|||
throw new InvalidOperationException(SR.FormatID0332(nameof(settings.ClientId), Providers.{{ provider.name }})); |
|||
} |
|||
|
|||
if (settings.RedirectUri is null) |
|||
{ |
|||
throw new InvalidOperationException(SR.FormatID0332(nameof(settings.RedirectUri), Providers.{{ provider.name }})); |
|||
} |
|||
|
|||
{{~ for setting in provider.settings ~}} |
|||
{{~ if setting.required ~}} |
|||
{{~ if setting.type == 'string' ~}} |
|||
if (string.IsNullOrEmpty(settings.{{ setting.name }})) |
|||
{{~ else ~}} |
|||
if (settings.{{ setting.name }} is null) |
|||
{{~ end ~}} |
|||
{ |
|||
throw new InvalidOperationException(SR.FormatID0332(nameof(settings.{{ setting.name }}), Providers.{{ provider.name }})); |
|||
} |
|||
{{~ end ~}} |
|||
{{~ end ~}} |
|||
|
|||
var formatter = Smart.CreateDefaultSmartFormat(new SmartSettings |
|||
{ |
|||
CaseSensitivity = CaseSensitivityType.CaseInsensitive |
|||
}); |
|||
|
|||
var registration = new OpenIddictClientRegistration |
|||
{ |
|||
Issuer = settings.Environment switch |
|||
{ |
|||
{{~ for environment in provider.environments ~}} |
|||
OpenIddictClientWebIntegrationEnvironments.{{ provider.name }}.{{ environment.name }} |
|||
=> new Uri(formatter.Format(""{{ environment.issuer }}"", settings), UriKind.Absolute), |
|||
{{~ end ~}} |
|||
|
|||
_ => throw new InvalidOperationException(SR.FormatID0194(nameof(settings.Environment))) |
|||
}, |
|||
|
|||
ClientId = settings.ClientId, |
|||
ClientSecret = settings.ClientSecret, |
|||
RedirectUri = settings.RedirectUri, |
|||
|
|||
Configuration = settings.Environment switch |
|||
{ |
|||
{{~ for environment in provider.environments ~}} |
|||
{{~ if environment.configuration ~}} |
|||
OpenIddictClientWebIntegrationEnvironments.{{ provider.name }}.{{ environment.name }} => new OpenIddictConfiguration |
|||
{ |
|||
{{~ if environment.configuration.authorization_endpoint ~}} |
|||
AuthorizationEndpoint = new Uri(formatter.Format(""{{ environment.configuration.authorization_endpoint }}"", settings), UriKind.Absolute), |
|||
{{~ end ~}} |
|||
|
|||
{{~ if environment.configuration.token_endpoint ~}} |
|||
TokenEndpoint = new Uri(formatter.Format(""{{ environment.configuration.token_endpoint }}"", settings), UriKind.Absolute), |
|||
{{~ end ~}} |
|||
|
|||
{{~ if environment.configuration.userinfo_endpoint ~}} |
|||
UserinfoEndpoint = new Uri(formatter.Format(""{{ environment.configuration.userinfo_endpoint }}"", settings), 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 ~}} |
|||
}, |
|||
|
|||
TokenEndpointAuthMethodsSupported = |
|||
{ |
|||
{{~ for method in environment.configuration.token_endpoint_auth_methods_supported ~}} |
|||
""{{ method }}"", |
|||
{{~ end ~}} |
|||
} |
|||
}, |
|||
{{~ else ~}} |
|||
OpenIddictClientWebIntegrationEnvironments.{{ provider.name }}.{{ environment.name }} => null, |
|||
{{~ end ~}} |
|||
{{~ end ~}} |
|||
|
|||
_ => throw new InvalidOperationException(SR.FormatID0194(nameof(settings.Environment))) |
|||
}, |
|||
|
|||
EncryptionCredentials = |
|||
{ |
|||
{{~ for setting in provider.settings ~}} |
|||
{{~ if setting.encryption_algorithm ~}} |
|||
new EncryptingCredentials(settings.{{ setting.name }}, ""{{ setting.encryption_algorithm }}"", SecurityAlgorithms.Aes256CbcHmacSha512), |
|||
{{~ end ~}} |
|||
{{~ end ~}} |
|||
}, |
|||
|
|||
SigningCredentials = |
|||
{ |
|||
{{~ for setting in provider.settings ~}} |
|||
{{~ if setting.signing_algorithm ~}} |
|||
new SigningCredentials(settings.{{ setting.name }}, ""{{ setting.signing_algorithm }}""), |
|||
{{~ end ~}} |
|||
{{~ end ~}} |
|||
}, |
|||
|
|||
Properties = |
|||
{ |
|||
[Properties.ProviderName] = Providers.{{ provider.name }}, |
|||
[Properties.ProviderSettings] = settings |
|||
} |
|||
}; |
|||
|
|||
registration.Scopes.UnionWith(settings.Scopes); |
|||
|
|||
{{~ for environment in provider.environments ~}} |
|||
if (settings.Environment is OpenIddictClientWebIntegrationEnvironments.{{ provider.name }}.{{ environment.name }}) |
|||
{ |
|||
{{~ for scope in environment.scopes ~}} |
|||
{{~ if scope.required ~}} |
|||
registration.Scopes.Add(""{{ scope.name }}""); |
|||
{{~ end ~}} |
|||
|
|||
{{~ if scope.default ~}} |
|||
if (registration.Scopes.Count is 0) |
|||
{ |
|||
registration.Scopes.Add(""{{ scope.name }}""); |
|||
} |
|||
{{~ end ~}} |
|||
{{~ end ~}} |
|||
} |
|||
{{~ end ~}} |
|||
|
|||
options.Registrations.Add(registration); |
|||
} |
|||
} |
|||
} |
|||
{{~ end ~}} |
|||
} |
|||
");
|
|||
return template.Render(new |
|||
{ |
|||
Providers = document.Root.Elements("Provider") |
|||
.Select(provider => new |
|||
{ |
|||
Name = (string) provider.Attribute("Name"), |
|||
|
|||
Environments = provider.Elements("Environment").Select(environment => new |
|||
{ |
|||
Name = (string?) environment.Attribute("Name") ?? "Production", |
|||
Issuer = (string) environment.Attribute("Issuer"), |
|||
Configuration = environment.Element("Configuration") switch |
|||
{ |
|||
XElement configuration => new |
|||
{ |
|||
AuthorizationEndpoint = (string?) configuration.Attribute("AuthorizationEndpoint"), |
|||
TokenEndpoint = (string?) configuration.Attribute("TokenEndpoint"), |
|||
UserinfoEndpoint = (string?) configuration.Attribute("UserinfoEndpoint"), |
|||
|
|||
CodeChallengeMethodsSupported = configuration.Elements("CodeChallengeMethod").ToList() switch |
|||
{ |
|||
{ Count: > 0 } methods => methods.Select(type => (string?) type.Attribute("Value")).ToList(), |
|||
|
|||
_ => (IList<string>) Array.Empty<string>() |
|||
}, |
|||
|
|||
GrantTypesSupported = configuration.Elements("GrantType").ToList() switch |
|||
{ |
|||
{ Count: > 0 } types => types.Select(type => (string?) type.Attribute("Value")).ToList(), |
|||
|
|||
// If no explicit grant type was set, assume the provider only supports the code flow.
|
|||
_ => (IList<string>) new[] { GrantTypes.AuthorizationCode } |
|||
}, |
|||
|
|||
ResponseModesSupported = configuration.Elements("ResponseMode").ToList() switch |
|||
{ |
|||
{ Count: > 0 } modes => modes.Select(type => (string?) type.Attribute("Value")).ToList(), |
|||
|
|||
// If no explicit response mode was set, assume the provider only supports the query response mode.
|
|||
_ => (IList<string>) new[] { ResponseModes.Query } |
|||
}, |
|||
|
|||
ResponseTypesSupported = configuration.Elements("ResponseType").ToList() switch |
|||
{ |
|||
{ Count: > 0 } types => types.Select(type => (string?) type.Attribute("Value")).ToList(), |
|||
|
|||
// If no explicit response type was set, assume the provider only supports the code flow.
|
|||
_ => (IList<string>) new[] { ResponseTypes.Code } |
|||
}, |
|||
|
|||
ScopesSupported = configuration.Elements("Scope").ToList() switch |
|||
{ |
|||
{ Count: > 0 } types => types.Select(type => (string?) type.Attribute("Value")).ToList(), |
|||
|
|||
_ => (IList<string>) Array.Empty<string>() |
|||
}, |
|||
|
|||
TokenEndpointAuthMethodsSupported = configuration.Elements("TokenEndpointAuthMethod").ToList() switch |
|||
{ |
|||
{ Count: > 0 } methods => methods.Select(type => (string?) type.Attribute("Value")).ToList(), |
|||
|
|||
// If no explicit response type was set, assume the provider only supports
|
|||
// flowing the client credentials as part of the token request payload.
|
|||
_ => (IList<string>) new[] { ClientAuthenticationMethods.ClientSecretPost } |
|||
} |
|||
}, |
|||
|
|||
_ => null |
|||
}, |
|||
|
|||
Scopes = environment.Elements("Scope").Select(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(setting => new |
|||
{ |
|||
Name = (string) setting.Attribute("Name"), |
|||
Type = (string) setting.Attribute("Type"), |
|||
Required = (bool?) setting.Attribute("Required") ?? false, |
|||
EncryptionAlgorithm = (string?) setting.Attribute("EncryptionAlgorithm"), |
|||
SigningAlgorithm = (string?) setting.Attribute("SigningAlgorithm") |
|||
}) |
|||
.ToList() |
|||
}) |
|||
.ToList() |
|||
}); |
|||
} |
|||
|
|||
static string GenerateHelpers(XDocument document) |
|||
{ |
|||
var template = Template.Parse(@"#nullable enable
|
|||
|
|||
using Microsoft.IdentityModel.Tokens; |
|||
using OpenIddict.Client; |
|||
using OpenIddict.Client.WebIntegration; |
|||
using SmartFormat; |
|||
using SmartFormat.Core.Settings; |
|||
using Properties = OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants.Properties; |
|||
using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants; |
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
public partial class OpenIddictClientWebIntegrationHelpers |
|||
{ |
|||
{{~ for provider in providers ~}} |
|||
/// <summary>
|
|||
/// Resolves the {{ provider.name }} provider settings from the specified registration.
|
|||
/// </summary>
|
|||
/// <param name=""registration"">The client registration.</param>
|
|||
/// <returns>The {{ provider.name }} provider settings.</returns>
|
|||
/// <exception cref=""InvalidOperationException"">The provider settings cannot be resolved.</exception>
|
|||
public static OpenIddictClientWebIntegrationSettings.{{ provider.name }} Get{{ provider.name }}Settings(this OpenIddictClientRegistration registration) |
|||
=> registration.GetProviderSettings<OpenIddictClientWebIntegrationSettings.{{ provider.name }}>() ?? |
|||
throw new InvalidOperationException(SR.FormatID0333(Providers.{{ provider.name }})); |
|||
{{~ end ~}} |
|||
} |
|||
");
|
|||
return template.Render(new |
|||
{ |
|||
Providers = document.Root.Elements("Provider") |
|||
.Select(provider => new { Name = (string) provider.Attribute("Name") }) |
|||
.ToList() |
|||
}); |
|||
} |
|||
|
|||
static string GenerateScopes(XDocument document) |
|||
{ |
|||
var template = Template.Parse(@"#nullable enable
|
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
public static partial class OpenIddictClientWebIntegrationScopes |
|||
{ |
|||
{{~ for provider in providers ~}} |
|||
/// <summary>
|
|||
/// Exposes the scopes supported by the {{ provider.name }} provider.
|
|||
/// </summary>
|
|||
public static class {{ provider.name }} |
|||
{ |
|||
{{~ for scope in provider.scopes ~}} |
|||
{{~ if scope.description ~}} |
|||
/// <summary>
|
|||
/// {{ scope.description }}
|
|||
/// </summary>
|
|||
{{~ end ~}} |
|||
public const string {{ scope.clr_name }} = ""{{ scope.name }}""; |
|||
{{~ end ~}} |
|||
} |
|||
{{~ end ~}} |
|||
} |
|||
");
|
|||
return template.Render(new |
|||
{ |
|||
Providers = document.Root.Elements("Provider") |
|||
.Select(provider => new |
|||
{ |
|||
Name = (string) provider.Attribute("Name"), |
|||
|
|||
Scopes = provider.Elements("Environment") |
|||
.SelectMany(environment => environment.Elements("Scope")) |
|||
.Select(scope => new |
|||
{ |
|||
Name = (string) scope.Attribute("Name"), |
|||
ClrName = Regex.Replace((string) scope.Attribute("Name"), "(?:^|_| +)(.)", |
|||
match => match.Groups[1].Value.ToUpper(CultureInfo.InvariantCulture)), |
|||
Description = (string?) scope.Attribute("Description") |
|||
}) |
|||
.Distinct(scope => scope.ClrName) |
|||
.ToList() |
|||
}) |
|||
.ToList() |
|||
}); |
|||
} |
|||
|
|||
static string GenerateSettings(XDocument document) |
|||
{ |
|||
var template = Template.Parse(@"#nullable enable
|
|||
|
|||
using Microsoft.IdentityModel.Tokens; |
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
public partial class OpenIddictClientWebIntegrationSettings |
|||
{ |
|||
{{~ for provider in providers ~}} |
|||
/// <summary>
|
|||
/// Provides various settings needed to configure the {{ provider.name }} integration.
|
|||
/// </summary>
|
|||
public class {{ provider.name }} : OpenIddictClientWebIntegrationSettings |
|||
{ |
|||
{{~ for setting in provider.settings ~}} |
|||
{{~ if setting.description ~}} |
|||
/// <summary>
|
|||
/// {{ setting.description }}
|
|||
/// </summary>
|
|||
{{~ end ~}} |
|||
public {{ setting.type }}? {{ setting.name }} { get; set; } |
|||
{{~ end ~}} |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the environment that determines the endpoints to use.
|
|||
/// </summary>
|
|||
public OpenIddictClientWebIntegrationEnvironments.{{ provider.name }} Environment { get; set; } |
|||
} |
|||
{{~ end ~}} |
|||
} |
|||
");
|
|||
return template.Render(new |
|||
{ |
|||
Providers = document.Root.Elements("Provider") |
|||
.Select(provider => new |
|||
{ |
|||
Name = (string) provider.Attribute("Name"), |
|||
|
|||
Settings = provider.Elements("Setting").Select(setting => new |
|||
{ |
|||
Type = (string) setting.Attribute("Type"), |
|||
Name = (string) setting.Attribute("Name"), |
|||
Description = (string) setting.Attribute("Description") |
|||
}) |
|||
.ToList() |
|||
}) |
|||
.ToList() |
|||
}); |
|||
} |
|||
} |
|||
|
|||
public void Initialize(GeneratorInitializationContext context) |
|||
{ |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFrameworks>net461;netcoreapp3.1;net5.0;net6.0;netstandard2.0;netstandard2.1</TargetFrameworks> |
|||
</PropertyGroup> |
|||
|
|||
<PropertyGroup> |
|||
<Description>OpenIddict client integration package for various Web services.</Description> |
|||
<PackageTags>$(PackageTags);providers;social;web</PackageTags> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\OpenIddict.Client\OpenIddict.Client.csproj" /> |
|||
<ProjectReference Include="..\OpenIddict.Client.SystemNetHttp\OpenIddict.Client.SystemNetHttp.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\..\gen\OpenIddict.Client.WebIntegration.Generators\OpenIddict.Client.WebIntegration.Generators.csproj" |
|||
OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="SmartFormat" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<AdditionalFiles Include="**\*.xml" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<Using Include="OpenIddict.Abstractions" /> |
|||
<Using Include="OpenIddict.Abstractions.OpenIddictConstants" Static="true" /> |
|||
<Using Include="OpenIddict.Abstractions.OpenIddictResources" Alias="SR" /> |
|||
<Using Include="OpenIddict.Client.OpenIddictClientEvents" Static="true" /> |
|||
<Using Include="OpenIddict.Client.OpenIddictClientHandlers" Static="true" /> |
|||
<Using Include="OpenIddict.Client.OpenIddictClientHandlerFilters" Static="true" /> |
|||
<Using Include="OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationHandlers" Static="true" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,61 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System.ComponentModel; |
|||
using OpenIddict.Client.WebIntegration; |
|||
|
|||
namespace Microsoft.Extensions.DependencyInjection; |
|||
|
|||
/// <summary>
|
|||
/// Exposes the necessary methods required to configure the OpenIddict client services.
|
|||
/// </summary>
|
|||
public partial class OpenIddictClientWebIntegrationBuilder |
|||
{ |
|||
/// <summary>
|
|||
/// Initializes a new instance of <see cref="OpenIddictClientWebIntegrationBuilder"/>.
|
|||
/// </summary>
|
|||
/// <param name="services">The services collection.</param>
|
|||
public OpenIddictClientWebIntegrationBuilder(IServiceCollection services) |
|||
=> Services = services ?? throw new ArgumentNullException(nameof(services)); |
|||
|
|||
/// <summary>
|
|||
/// Gets the services collection.
|
|||
/// </summary>
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public IServiceCollection Services { get; } |
|||
|
|||
/// <summary>
|
|||
/// Amends the default OpenIddict client Web integration configuration.
|
|||
/// </summary>
|
|||
/// <param name="configuration">The delegate used to configure the OpenIddict options.</param>
|
|||
/// <remarks>This extension can be safely called multiple times.</remarks>
|
|||
/// <returns>The <see cref="OpenIddictClientWebIntegrationBuilder"/>.</returns>
|
|||
public OpenIddictClientWebIntegrationBuilder Configure(Action<OpenIddictClientWebIntegrationOptions> configuration) |
|||
{ |
|||
if (configuration is null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(configuration)); |
|||
} |
|||
|
|||
Services.Configure(configuration); |
|||
|
|||
return this; |
|||
} |
|||
|
|||
// Note: provider registration methods are automatically generated by the source generator.
|
|||
|
|||
/// <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(); |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using Microsoft.Extensions.Options; |
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
/// <summary>
|
|||
/// Contains the methods required to ensure that the OpenIddict client Web integration configuration is valid.
|
|||
/// </summary>
|
|||
public partial class OpenIddictClientWebIntegrationConfiguration : IConfigureOptions<OpenIddictClientOptions> |
|||
{ |
|||
/// <summary>
|
|||
/// Populates the default OpenIddict client Web integration options
|
|||
/// and ensures that the configuration is in a consistent and valid state.
|
|||
/// </summary>
|
|||
/// <param name="options">The options instance to initialize.</param>
|
|||
public void Configure(OpenIddictClientOptions options) |
|||
{ |
|||
if (options is null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(options)); |
|||
} |
|||
|
|||
// Register the built-in event handlers used by the OpenIddict client Web components.
|
|||
options.Handlers.AddRange(OpenIddictClientWebIntegrationHandlers.DefaultHandlers); |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
/// <summary>
|
|||
/// Exposes common constants used by the OpenIddict client Web integration services.
|
|||
/// </summary>
|
|||
public static partial class OpenIddictClientWebIntegrationConstants |
|||
{ |
|||
// Note: provider name constants are automatically generated by the source generator.
|
|||
|
|||
public static class Properties |
|||
{ |
|||
public const string ProviderName = ".provider_name"; |
|||
public const string ProviderSettings = ".provider_settings"; |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
/// <summary>
|
|||
/// Exposes the provider-specific environments supported by the OpenIddict client Web integration services.
|
|||
/// </summary>
|
|||
public static partial class OpenIddictClientWebIntegrationEnvironments |
|||
{ |
|||
// Note: environments are automatically generated by the source generator.
|
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using Microsoft.Extensions.DependencyInjection.Extensions; |
|||
using Microsoft.Extensions.Options; |
|||
using OpenIddict.Client; |
|||
using OpenIddict.Client.WebIntegration; |
|||
|
|||
namespace Microsoft.Extensions.DependencyInjection; |
|||
|
|||
/// <summary>
|
|||
/// Exposes extensions allowing to register the OpenIddict client Web integration services.
|
|||
/// </summary>
|
|||
public static class OpenIddictClientWebIntegrationExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// Registers the OpenIddict client Web integration services in the DI container.
|
|||
/// </summary>
|
|||
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
|
|||
/// <remarks>This extension can be safely called multiple times.</remarks>
|
|||
/// <returns>The <see cref="OpenIddictClientWebIntegrationBuilder"/>.</returns>
|
|||
public static OpenIddictClientWebIntegrationBuilder UseWebProviders(this OpenIddictClientBuilder builder) |
|||
{ |
|||
if (builder is null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(builder)); |
|||
} |
|||
|
|||
// Register the built-in event handlers used by the OpenIddict client Web components.
|
|||
// Note: the order used here is not important, as the actual order is set in the options.
|
|||
builder.Services.TryAdd(OpenIddictClientWebIntegrationHandlers.DefaultHandlers |
|||
.Select(descriptor => descriptor.ServiceDescriptor)); |
|||
|
|||
// Note: TryAddEnumerable() is used here to ensure the initializers are registered only once.
|
|||
builder.Services.TryAddEnumerable(new[] |
|||
{ |
|||
ServiceDescriptor.Singleton<IConfigureOptions<OpenIddictClientOptions>, OpenIddictClientWebIntegrationConfiguration>() |
|||
}); |
|||
|
|||
return new OpenIddictClientWebIntegrationBuilder(builder.Services); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Registers the OpenIddict client Web integration services in the DI container.
|
|||
/// </summary>
|
|||
/// <param name="builder">The services builder used by OpenIddict to register new services.</param>
|
|||
/// <param name="configuration">The configuration delegate used to configure the validation services.</param>
|
|||
/// <remarks>This extension can be safely called multiple times.</remarks>
|
|||
/// <returns>The <see cref="OpenIddictClientBuilder"/>.</returns>
|
|||
public static OpenIddictClientBuilder UseWebProviders( |
|||
this OpenIddictClientBuilder builder, Action<OpenIddictClientWebIntegrationBuilder> configuration) |
|||
{ |
|||
if (builder is null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(builder)); |
|||
} |
|||
|
|||
if (configuration is null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(configuration)); |
|||
} |
|||
|
|||
configuration(builder.UseWebProviders()); |
|||
|
|||
return builder; |
|||
} |
|||
} |
|||
@ -0,0 +1,64 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System.Collections.Immutable; |
|||
using static OpenIddict.Client.OpenIddictClientHandlers.Discovery; |
|||
using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants; |
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
public static partial class OpenIddictClientWebIntegrationHandlers |
|||
{ |
|||
public static class Discovery |
|||
{ |
|||
public static ImmutableArray<OpenIddictClientHandlerDescriptor> DefaultHandlers { get; } = ImmutableArray.Create( |
|||
/* |
|||
* Configuration response handling: |
|||
*/ |
|||
AmendClientAuthenticationMethods.Descriptor); |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible for amending the client
|
|||
/// authentication methods for the providers that require it.
|
|||
/// </summary>
|
|||
public class AmendClientAuthenticationMethods : IOpenIddictClientHandler<HandleConfigurationResponseContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictClientHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictClientHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>() |
|||
.UseSingletonHandler<AmendClientAuthenticationMethods>() |
|||
.SetOrder(ExtractTokenEndpointClientAuthenticationMethods.Descriptor.Order + 500) |
|||
.SetType(OpenIddictClientHandlerType.BuiltIn) |
|||
.Build(); |
|||
|
|||
/// <inheritdoc/>
|
|||
public ValueTask HandleAsync(HandleConfigurationResponseContext context) |
|||
{ |
|||
if (context is null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
// Apple implements a non-standard client authentication method for the token endpoint
|
|||
// that is inspired by the standard private_key_jwt method but doesn't use the standard
|
|||
// client_assertion/client_assertion_type parameters. Instead, the client assertion
|
|||
// must be sent as a "dynamic" client secret using client_secret_post. Since the logic
|
|||
// is the same as private_key_jwt, the configuration is amended to assume Apple supports
|
|||
// private_key_jwt and an event handler is responsible for populating the client_secret
|
|||
// parameter using the client assertion token once it has been generated by OpenIddict.
|
|||
if (context.Registration.GetProviderName() is Providers.Apple) |
|||
{ |
|||
context.Configuration.TokenEndpointAuthMethodsSupported.Add( |
|||
ClientAuthenticationMethods.PrivateKeyJwt); |
|||
} |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System.Collections.Immutable; |
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
public static partial class OpenIddictClientWebIntegrationHandlers |
|||
{ |
|||
public static class Exchange |
|||
{ |
|||
public static ImmutableArray<OpenIddictClientHandlerDescriptor> DefaultHandlers { get; } = ImmutableArray.Create( |
|||
/* |
|||
* Token request preparation: |
|||
*/ |
|||
UseProductNameAsUserAgent<PrepareTokenRequestContext>.Descriptor); |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System.Collections.Immutable; |
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
public static partial class OpenIddictClientWebIntegrationHandlers |
|||
{ |
|||
public static class Userinfo |
|||
{ |
|||
public static ImmutableArray<OpenIddictClientHandlerDescriptor> DefaultHandlers { get; } = ImmutableArray.Create( |
|||
/* |
|||
* Userinfo request preparation: |
|||
*/ |
|||
UseProductNameAsUserAgent<PrepareUserinfoRequestContext>.Descriptor); |
|||
} |
|||
} |
|||
@ -0,0 +1,249 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System.Collections.Immutable; |
|||
using System.ComponentModel; |
|||
using System.Diagnostics; |
|||
using System.Net.Http.Headers; |
|||
using System.Security.Claims; |
|||
using static OpenIddict.Client.SystemNetHttp.OpenIddictClientSystemNetHttpHandlerFilters; |
|||
using static OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants; |
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
[EditorBrowsable(EditorBrowsableState.Never)] |
|||
public static partial class OpenIddictClientWebIntegrationHandlers |
|||
{ |
|||
public static ImmutableArray<OpenIddictClientHandlerDescriptor> DefaultHandlers { get; } = ImmutableArray.Create( |
|||
/* |
|||
* Authentication processing: |
|||
*/ |
|||
AttachNonStandardClientAssertionTokenClaims.Descriptor, |
|||
AttachTokenRequestNonStandardClientCredentials.Descriptor, |
|||
|
|||
/* |
|||
* Challenge processing: |
|||
*/ |
|||
AttachNonDefaultResponseMode.Descriptor, |
|||
FormatNonStandardScopeParameter.Descriptor) |
|||
.AddRange(Discovery.DefaultHandlers) |
|||
.AddRange(Exchange.DefaultHandlers) |
|||
.AddRange(Userinfo.DefaultHandlers); |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible for amending the client
|
|||
/// assertion methods for the providers that require it.
|
|||
/// </summary>
|
|||
public class AttachNonStandardClientAssertionTokenClaims : IOpenIddictClientHandler<ProcessAuthenticationContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictClientHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>() |
|||
.AddFilter<RequireClientAssertionTokenGenerated>() |
|||
.UseSingletonHandler<AttachNonStandardClientAssertionTokenClaims>() |
|||
.SetOrder(PrepareClientAssertionTokenPrincipal.Descriptor.Order + 500) |
|||
.SetType(OpenIddictClientHandlerType.BuiltIn) |
|||
.Build(); |
|||
|
|||
/// <inheritdoc/>
|
|||
public ValueTask HandleAsync(ProcessAuthenticationContext context) |
|||
{ |
|||
if (context is null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
Debug.Assert(context.ClientAssertionTokenPrincipal is { Identity: ClaimsIdentity }, SR.GetResourceString(SR.ID4006)); |
|||
|
|||
// For client assertions to be considered valid by the Apple ID authentication service,
|
|||
// the team identifier associated with the developer account MUST be used as the issuer
|
|||
// and the static "https://appleid.apple.com" URL MUST be used as the token audience.
|
|||
//
|
|||
// For more information about the custom client authentication method implemented by Apple,
|
|||
// see https://developer.apple.com/documentation/sign_in_with_apple/generate_and_validate_tokens.
|
|||
if (context.Registration.GetProviderName() is Providers.Apple) |
|||
{ |
|||
var settings = context.Registration.GetAppleSettings(); |
|||
context.ClientAssertionTokenPrincipal.SetClaim(Claims.Private.Issuer, settings.TeamId); |
|||
context.ClientAssertionTokenPrincipal.SetAudiences("https://appleid.apple.com"); |
|||
} |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible for attaching custom client credentials
|
|||
/// parameters to the token request for the providers that require it.
|
|||
/// </summary>
|
|||
public class AttachTokenRequestNonStandardClientCredentials : IOpenIddictClientHandler<ProcessAuthenticationContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictClientHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessAuthenticationContext>() |
|||
.AddFilter<RequireTokenRequest>() |
|||
.UseSingletonHandler<AttachTokenRequestNonStandardClientCredentials>() |
|||
.SetOrder(AttachTokenRequestClientCredentials.Descriptor.Order + 500) |
|||
.SetType(OpenIddictClientHandlerType.BuiltIn) |
|||
.Build(); |
|||
|
|||
/// <inheritdoc/>
|
|||
public ValueTask HandleAsync(ProcessAuthenticationContext context) |
|||
{ |
|||
if (context is null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
Debug.Assert(context.TokenRequest is not null, SR.GetResourceString(SR.ID4008)); |
|||
|
|||
// Apple implements a non-standard client authentication method for the token endpoint
|
|||
// that is inspired by the standard private_key_jwt method but doesn't use the standard
|
|||
// client_assertion/client_assertion_type parameters. Instead, the client assertion
|
|||
// must be sent as a "dynamic" client secret using client_secret_post. Since the logic
|
|||
// is the same as private_key_jwt, the configuration is amended to assume Apple supports
|
|||
// private_key_jwt and an event handler is responsible for populating the client_secret
|
|||
// parameter using the client assertion token once it has been generated by OpenIddict.
|
|||
if (context.Registration.GetProviderName() is Providers.Apple) |
|||
{ |
|||
context.TokenRequest.ClientSecret = context.TokenRequest.ClientAssertion; |
|||
context.TokenRequest.ClientAssertion = null; |
|||
context.TokenRequest.ClientAssertionType = null; |
|||
} |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible for attaching a specific response mode for providers that require it.
|
|||
/// </summary>
|
|||
public class AttachNonDefaultResponseMode : IOpenIddictClientHandler<ProcessChallengeContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictClientHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessChallengeContext>() |
|||
.UseSingletonHandler<AttachNonDefaultResponseMode>() |
|||
// Note: this handler MUST be invoked after the scopes have been attached to the
|
|||
// context to support overriding the response mode based on the requested scopes.
|
|||
.SetOrder(AttachScopes.Descriptor.Order + 500) |
|||
.SetType(OpenIddictClientHandlerType.BuiltIn) |
|||
.Build(); |
|||
|
|||
/// <inheritdoc/>
|
|||
public ValueTask HandleAsync(ProcessChallengeContext context) |
|||
{ |
|||
if (context is null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
context.ResponseMode = context.Registration.GetProviderName() switch |
|||
{ |
|||
// Note: Apple requires using form_post when the "email" or "name" scopes are requested.
|
|||
Providers.Apple when context.Scopes.Contains(Scopes.Email) || context.Scopes.Contains("name") |
|||
=> ResponseModes.FormPost, |
|||
|
|||
_ => context.ResponseMode |
|||
}; |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible for overriding the standard "scope"
|
|||
/// parameter for providers that are known to use a non-standard format.
|
|||
/// </summary>
|
|||
public class FormatNonStandardScopeParameter : IOpenIddictClientHandler<ProcessChallengeContext> |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictClientHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictClientHandlerDescriptor.CreateBuilder<ProcessChallengeContext>() |
|||
.UseSingletonHandler<FormatNonStandardScopeParameter>() |
|||
.SetOrder(AttachChallengeParameters.Descriptor.Order + 500) |
|||
.SetType(OpenIddictClientHandlerType.BuiltIn) |
|||
.Build(); |
|||
|
|||
/// <inheritdoc/>
|
|||
public ValueTask HandleAsync(ProcessChallengeContext context) |
|||
{ |
|||
if (context is null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
context.Request.Scope = context.Registration.GetProviderName() switch |
|||
{ |
|||
// The following providers are known to use comma-separated scopes instead of
|
|||
// the standard format (that requires using a space as the scope separator):
|
|||
Providers.Reddit |
|||
when context.GrantType is GrantTypes.AuthorizationCode or GrantTypes.Implicit |
|||
=> string.Join(",", context.Scopes), |
|||
|
|||
_ => context.Request.Scope |
|||
}; |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Contains the logic responsible for overriding the user agent for providers
|
|||
/// that are known to require or encourage using custom values (e.g Reddit).
|
|||
/// </summary>
|
|||
public class UseProductNameAsUserAgent<TContext> : IOpenIddictClientHandler<TContext> |
|||
where TContext : BaseExternalContext |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the default descriptor definition assigned to this handler.
|
|||
/// </summary>
|
|||
public static OpenIddictClientHandlerDescriptor Descriptor { get; } |
|||
= OpenIddictClientHandlerDescriptor.CreateBuilder<TContext>() |
|||
.AddFilter<RequireHttpMetadataAddress>() |
|||
.UseSingletonHandler<UseProductNameAsUserAgent<TContext>>() |
|||
.SetOrder(int.MaxValue - 200_000) |
|||
.SetType(OpenIddictClientHandlerType.BuiltIn) |
|||
.Build(); |
|||
|
|||
/// <inheritdoc/>
|
|||
public ValueTask HandleAsync(TContext context) |
|||
{ |
|||
if (context is null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(context)); |
|||
} |
|||
|
|||
// This handler only applies to System.Net.Http requests. If the HTTP request cannot be resolved,
|
|||
// this may indicate that the request was incorrectly processed by another client stack.
|
|||
var request = context.Transaction.GetHttpRequestMessage() ?? |
|||
throw new InvalidOperationException(SR.GetResourceString(SR.ID0173)); |
|||
|
|||
// A few providers (like Reddit) are known to aggressively check user agents and encourage
|
|||
// developers to use unique user agents. While OpenIddict itself always adds a user agent,
|
|||
// the default value doesn't differ accross applications. To reduce the risks of seeing
|
|||
// requests blocked by these providers, the user agent is replaced by a custom value
|
|||
// containing the product name and version set by the user or by the client identifier.
|
|||
if (context.Registration.GetProviderName() is Providers.Reddit) |
|||
{ |
|||
var settings = context.Registration.GetRedditSettings(); |
|||
request.Headers.UserAgent.Add(new ProductInfoHeaderValue( |
|||
productName: settings.ProductName ?? context.Registration.ClientId!, |
|||
productVersion: settings.ProductVersion)); |
|||
} |
|||
|
|||
return default; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,66 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using Properties = OpenIddict.Client.WebIntegration.OpenIddictClientWebIntegrationConstants.Properties; |
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
/// <summary>
|
|||
/// Exposes companion extensions for the OpenIddict client Web integration.
|
|||
/// </summary>
|
|||
public static partial class OpenIddictClientWebIntegrationHelpers |
|||
{ |
|||
/// <summary>
|
|||
/// Resolves the name of the provider associated with the client registration or
|
|||
/// <see langword="null" /> if no provider information is attached to the registration.
|
|||
/// </summary>
|
|||
/// <param name="registration">The client registration.</param>
|
|||
/// <returns>The provider name, if applicable.</returns>
|
|||
/// <exception cref="ArgumentNullException"><paramref name="registration"/> is null.</exception>
|
|||
public static string? GetProviderName(this OpenIddictClientRegistration registration) |
|||
{ |
|||
if (registration is null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(registration)); |
|||
} |
|||
|
|||
return registration.Properties.TryGetValue(Properties.ProviderName, out var provider) |
|||
&& provider is string value ? value : null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Resolves the provider settings associated with the client registration or
|
|||
/// <see langword="null" /> if no provider information is attached to the registration.
|
|||
/// </summary>
|
|||
/// <param name="registration">The client registration.</param>
|
|||
/// <returns>The provider settings, if applicable.</returns>
|
|||
/// <exception cref="ArgumentNullException"><paramref name="registration"/> is null.</exception>
|
|||
public static OpenIddictClientWebIntegrationSettings? GetProviderSettings(this OpenIddictClientRegistration registration) |
|||
{ |
|||
if (registration is null) |
|||
{ |
|||
throw new ArgumentNullException(nameof(registration)); |
|||
} |
|||
|
|||
return registration.Properties.TryGetValue(Properties.ProviderSettings, out var value) |
|||
&& value is OpenIddictClientWebIntegrationSettings settings ? settings : null; |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Resolves the provider settings associated with the client registration or
|
|||
/// <see langword="null" /> if no provider information is attached to the registration or if
|
|||
/// the actual setting information doesn't match the specified <typeparamref name="TSettings"/>.
|
|||
/// </summary>
|
|||
/// <typeparam name="TSettings">The type of the provider settings.</typeparam>
|
|||
/// <param name="registration">The client registration.</param>
|
|||
/// <returns>The provider settings, if applicable.</returns>
|
|||
/// <exception cref="ArgumentNullException"><paramref name="registration"/> is null.</exception>
|
|||
public static TSettings? GetProviderSettings<TSettings>(this OpenIddictClientRegistration registration) |
|||
where TSettings : OpenIddictClientWebIntegrationSettings |
|||
=> registration.GetProviderSettings() is TSettings settings ? settings : null; |
|||
|
|||
// Note: provider-specific helpers are automatically generated by the source generator.
|
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
/// <summary>
|
|||
/// Provides various settings needed to configure the OpenIddict client Web integration.
|
|||
/// </summary>
|
|||
public class OpenIddictClientWebIntegrationOptions |
|||
{ |
|||
/// <summary>
|
|||
/// Gets the list of provider integrations enabled for this application.
|
|||
/// </summary>
|
|||
public List<OpenIddictClientWebIntegrationProvider> Providers { get; } = new(); |
|||
} |
|||
@ -0,0 +1,46 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
using System.Diagnostics; |
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
/// <summary>
|
|||
/// Represents an OpenIddict client web integration provider.
|
|||
/// </summary>
|
|||
[DebuggerDisplay("{Name,nq}")] |
|||
public class OpenIddictClientWebIntegrationProvider |
|||
{ |
|||
/// <summary>
|
|||
/// Creates a new instance of the <see cref="OpenIddictClientWebIntegrationProvider"/> class.
|
|||
/// </summary>
|
|||
/// <param name="name">The provider name.</param>
|
|||
/// <param name="settings">The provider settings.</param>
|
|||
/// <exception cref="ArgumentException"><paramref name="name"/> is null or empty.</exception>
|
|||
/// <exception cref="ArgumentNullException"><paramref name="settings"/> are null.</exception>
|
|||
public OpenIddictClientWebIntegrationProvider( |
|||
string name, |
|||
OpenIddictClientWebIntegrationSettings settings) |
|||
{ |
|||
if (string.IsNullOrEmpty(name)) |
|||
{ |
|||
throw new ArgumentException(SR.GetResourceString(SR.ID0330), nameof(name)); |
|||
} |
|||
|
|||
Name = name; |
|||
Settings = settings ?? throw new ArgumentNullException(nameof(settings)); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Gets the provider name associated with the current instance.
|
|||
/// </summary>
|
|||
public string Name { get; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the provider settings associated with the current instance.
|
|||
/// </summary>
|
|||
public OpenIddictClientWebIntegrationSettings Settings { get; } |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
<Providers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
|||
xsi:noNamespaceSchemaLocation="OpenIddictClientWebIntegrationProviders.xsd"> |
|||
|
|||
<Provider Name="Apple" Documentation="https://developer.apple.com/documentation/sign_in_with_apple/sign_in_with_apple_rest_api"> |
|||
<Environment Issuer="https://appleid.apple.com/" /> |
|||
|
|||
<Setting Name="SigningKey" Type="ECDsaSecurityKey" Required="true" SigningAlgorithm="ES256" |
|||
Description="Gets or sets the ECDSA signing key associated with the developer account." /> |
|||
|
|||
<Setting Name="TeamId" Type="string" Required="true" |
|||
Description="Gets or sets the Team ID associated with the developer account." /> |
|||
</Provider> |
|||
|
|||
<Provider Name="GitHub" Documentation="https://docs.github.com/en/developers/apps/building-oauth-apps/authorizing-oauth-apps"> |
|||
<Environment Issuer="https://github.com/"> |
|||
<Configuration AuthorizationEndpoint="https://github.com/login/oauth/authorize" |
|||
TokenEndpoint="https://github.com/login/oauth/access_token" |
|||
UserinfoEndpoint="https://api.github.com/user" /> |
|||
</Environment> |
|||
</Provider> |
|||
|
|||
<Provider Name="Google" Documentation="https://developers.google.com/identity/protocols/oauth2/openid-connect"> |
|||
<Environment Issuer="https://accounts.google.com/" /> |
|||
</Provider> |
|||
|
|||
<Provider Name="Reddit" Documentation="https://github.com/reddit-archive/reddit/wiki/OAuth2"> |
|||
<Environment Issuer="https://www.reddit.com/"> |
|||
<Configuration AuthorizationEndpoint="https://www.reddit.com/api/v1/authorize" |
|||
TokenEndpoint="https://www.reddit.com/api/v1/access_token" |
|||
UserinfoEndpoint="https://oauth.reddit.com/api/v1/me"> |
|||
<TokenEndpointAuthMethod Value="client_secret_basic" /> |
|||
</Configuration> |
|||
|
|||
<!-- |
|||
Note: Reddit requires sending at least one scope element. If no scope parameter |
|||
is set, a misleading "invalid client identifier" error is returned to the caller. |
|||
To prevent that, the "identity" scope (that is required by the userinfo endpoint) |
|||
is always added even if another scope was explicitly registered by the user. |
|||
--> |
|||
|
|||
<Scope Name="identity" Required="true" Description="Access my reddit username and signup date." /> |
|||
</Environment> |
|||
|
|||
<Setting Name="ProductName" Type="string" Required="false" |
|||
Description="Gets or sets the product name used in the user agent header." /> |
|||
|
|||
<Setting Name="ProductVersion" Type="string" Required="false" |
|||
Description="Gets or sets the product version used in the user agent header." /> |
|||
</Provider> |
|||
|
|||
</Providers> |
|||
@ -0,0 +1,349 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
|
|||
<xs:element name="Providers"> |
|||
<xs:annotation> |
|||
<xs:documentation>The list of providers generated by the OpenIddict generator.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:complexType> |
|||
<xs:sequence> |
|||
<xs:element name="Provider" minOccurs="1" maxOccurs="unbounded"> |
|||
<xs:annotation> |
|||
<xs:documentation>A provider definition used by the OpenIddict generator.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:complexType> |
|||
<xs:sequence> |
|||
<xs:element name="Environment" minOccurs="0" maxOccurs="10"> |
|||
<xs:annotation> |
|||
<xs:documentation>An environment supported by the provider.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:complexType> |
|||
<xs:sequence> |
|||
<xs:element name="Configuration" minOccurs="0" maxOccurs="1"> |
|||
<xs:annotation> |
|||
<xs:documentation>The static configuration used for the environment, if applicable.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:complexType> |
|||
<xs:sequence> |
|||
<xs:element name="CodeChallengeMethod" minOccurs="0" maxOccurs="10"> |
|||
<xs:annotation> |
|||
<xs:documentation>The code challenge methods supported by the environment.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:complexType> |
|||
<xs:attribute name="Value" use="required"> |
|||
<xs:annotation> |
|||
<xs:documentation>The code challenge method name (e.g S256).</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:simpleType> |
|||
<xs:restriction base="xs:string"> |
|||
<xs:enumeration value="plain" /> |
|||
<xs:enumeration value="S256" /> |
|||
</xs:restriction> |
|||
</xs:simpleType> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
|
|||
<xs:element name="GrantType" minOccurs="0" maxOccurs="10"> |
|||
<xs:annotation> |
|||
<xs:documentation>The grant types supported by the environment.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:complexType> |
|||
<xs:attribute name="Value" use="required"> |
|||
<xs:annotation> |
|||
<xs:documentation>The grant type name (e.g authorization_code).</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:simpleType> |
|||
<xs:restriction base="xs:string"> |
|||
<xs:enumeration value="authorization_code" /> |
|||
<xs:enumeration value="implicit" /> |
|||
<xs:enumeration value="refresh_token" /> |
|||
</xs:restriction> |
|||
</xs:simpleType> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
|
|||
<xs:element name="ResponseMode" minOccurs="0" maxOccurs="10"> |
|||
<xs:annotation> |
|||
<xs:documentation>The response modes supported by the environment.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:complexType> |
|||
<xs:attribute name="Value" use="required"> |
|||
<xs:annotation> |
|||
<xs:documentation>The response mode name (e.g form_post).</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:simpleType> |
|||
<xs:restriction base="xs:string"> |
|||
<xs:enumeration value="form_post" /> |
|||
<xs:enumeration value="fragment" /> |
|||
<xs:enumeration value="query" /> |
|||
</xs:restriction> |
|||
</xs:simpleType> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
|
|||
<xs:element name="ResponseType" minOccurs="0" maxOccurs="10"> |
|||
<xs:annotation> |
|||
<xs:documentation>The response types supported by the environment.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:complexType> |
|||
<xs:attribute name="Value" use="required"> |
|||
<xs:annotation> |
|||
<xs:documentation>The response type name (e.g code id_token).</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:simpleType> |
|||
<xs:restriction base="xs:string"> |
|||
<xs:enumeration value="code" /> |
|||
<xs:enumeration value="code id_token" /> |
|||
<xs:enumeration value="code id_token token" /> |
|||
<xs:enumeration value="code token" /> |
|||
<xs:enumeration value="id_token" /> |
|||
<xs:enumeration value="id_token token" /> |
|||
</xs:restriction> |
|||
</xs:simpleType> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
|
|||
<xs:element name="Scope" minOccurs="0" maxOccurs="50"> |
|||
<xs:annotation> |
|||
<xs:documentation>The scopes supported by the environment.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:complexType> |
|||
<xs:annotation> |
|||
<xs:documentation>The scope name (e.g openid).</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:attribute name="Value" type="xs:string" use="required" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
|
|||
<xs:element name="TokenEndpointAuthMethod" minOccurs="0" maxOccurs="10"> |
|||
<xs:annotation> |
|||
<xs:documentation>The token endpoint authentication methods supported by the environment.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:complexType> |
|||
<xs:attribute name="Value" use="required"> |
|||
<xs:annotation> |
|||
<xs:documentation>The token endpoint authentication method name (e.g client_secret_basic).</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:simpleType> |
|||
<xs:restriction base="xs:string"> |
|||
<xs:enumeration value="client_secret_basic" /> |
|||
<xs:enumeration value="client_secret_post" /> |
|||
</xs:restriction> |
|||
</xs:simpleType> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:sequence> |
|||
|
|||
<xs:attribute name="AuthorizationEndpoint" type="xs:string" use="optional"> |
|||
<xs:annotation> |
|||
<xs:documentation>The authorization endpoint offered by the environment.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
|
|||
<xs:attribute name="TokenEndpoint" type="xs:string" use="optional"> |
|||
<xs:annotation> |
|||
<xs:documentation>The token endpoint offered by the environment.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
|
|||
<xs:attribute name="UserinfoEndpoint" type="xs:string" use="optional"> |
|||
<xs:annotation> |
|||
<xs:documentation>The userinfo endpoint offered by the environment.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
|
|||
<xs:element name="Scope" minOccurs="0" maxOccurs="50"> |
|||
<xs:annotation> |
|||
<xs:documentation>A scope supported by the environment.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:complexType> |
|||
<xs:attribute name="Name" use="required"> |
|||
<xs:annotation> |
|||
<xs:documentation>The scope value (e.g address or phone).</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:simpleType> |
|||
<xs:restriction base="xs:string" /> |
|||
</xs:simpleType> |
|||
</xs:attribute> |
|||
|
|||
<xs:attribute name="Default" use="optional"> |
|||
<xs:annotation> |
|||
<xs:documentation>A boolean indicating whether the scope is automatically added if no other scope is added by the user.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:simpleType> |
|||
<xs:restriction base="xs:boolean" /> |
|||
</xs:simpleType> |
|||
</xs:attribute> |
|||
|
|||
<xs:attribute name="Required" use="optional"> |
|||
<xs:annotation> |
|||
<xs:documentation>A boolean indicating whether the scope is always added even if another scope is already added by the user.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:simpleType> |
|||
<xs:restriction base="xs:boolean" /> |
|||
</xs:simpleType> |
|||
</xs:attribute> |
|||
|
|||
<xs:attribute name="Description" type="xs:string" use="required"> |
|||
<xs:annotation> |
|||
<xs:documentation>The scope description.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:sequence> |
|||
|
|||
<xs:attribute name="Issuer" type="xs:string" use="required"> |
|||
<xs:annotation> |
|||
<xs:documentation>The issuer URL corresponding to the environment.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
|
|||
<xs:attribute name="Name" use="optional"> |
|||
<xs:annotation> |
|||
<xs:documentation>The environment name (by default, Production).</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:simpleType> |
|||
<xs:restriction base="xs:string"> |
|||
<xs:pattern value="^[A-Z][a-zA-Z]*$" /> |
|||
</xs:restriction> |
|||
</xs:simpleType> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
|
|||
<xs:element name="Setting" minOccurs="0" maxOccurs="10"> |
|||
<xs:annotation> |
|||
<xs:documentation>A custom setting exposed by the provider integration.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:complexType> |
|||
<xs:attribute name="Name" use="required"> |
|||
<xs:annotation> |
|||
<xs:documentation>The setting name.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:simpleType> |
|||
<xs:restriction base="xs:string"> |
|||
<xs:pattern value="^[A-Z][a-zA-Z0-9]*$" /> |
|||
</xs:restriction> |
|||
</xs:simpleType> |
|||
</xs:attribute> |
|||
|
|||
<xs:attribute name="Type" use="required"> |
|||
<xs:annotation> |
|||
<xs:documentation>The setting type.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:simpleType> |
|||
<xs:restriction base="xs:string"> |
|||
<xs:enumeration value="ECDsaSecurityKey" /> |
|||
<xs:enumeration value="string" /> |
|||
</xs:restriction> |
|||
</xs:simpleType> |
|||
</xs:attribute> |
|||
|
|||
<xs:attribute name="Required" use="required"> |
|||
<xs:annotation> |
|||
<xs:documentation>A boolean indicating whether the setting is required.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:simpleType> |
|||
<xs:restriction base="xs:boolean" /> |
|||
</xs:simpleType> |
|||
</xs:attribute> |
|||
|
|||
<xs:attribute name="EncryptionAlgorithm" use="optional"> |
|||
<xs:annotation> |
|||
<xs:documentation>The encryption algorithm, if applicable.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:simpleType> |
|||
<xs:restriction base="xs:string"> |
|||
<xs:enumeration value="A256KW" /> |
|||
<xs:enumeration value="RSA-OAEP" /> |
|||
</xs:restriction> |
|||
</xs:simpleType> |
|||
</xs:attribute> |
|||
|
|||
<xs:attribute name="SigningAlgorithm" use="optional"> |
|||
<xs:annotation> |
|||
<xs:documentation>The signing algorithm, if applicable.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:simpleType> |
|||
<xs:restriction base="xs:string"> |
|||
<xs:enumeration value="ES256" /> |
|||
<xs:enumeration value="ES384" /> |
|||
<xs:enumeration value="ES512" /> |
|||
<xs:enumeration value="PS256" /> |
|||
<xs:enumeration value="PS384" /> |
|||
<xs:enumeration value="PS512" /> |
|||
<xs:enumeration value="RS256" /> |
|||
<xs:enumeration value="RS384" /> |
|||
<xs:enumeration value="RS512" /> |
|||
</xs:restriction> |
|||
</xs:simpleType> |
|||
</xs:attribute> |
|||
|
|||
<xs:attribute name="Description" type="xs:string" use="required"> |
|||
<xs:annotation> |
|||
<xs:documentation>The setting description.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:sequence> |
|||
|
|||
<xs:attribute name="Name" use="required"> |
|||
<xs:annotation> |
|||
<xs:documentation>The provider name.</xs:documentation> |
|||
</xs:annotation> |
|||
|
|||
<xs:simpleType> |
|||
<xs:restriction base="xs:string"> |
|||
<xs:pattern value="^[A-Z][a-zA-Z0-9]*$" /> |
|||
</xs:restriction> |
|||
</xs:simpleType> |
|||
</xs:attribute> |
|||
|
|||
<xs:attribute name="Documentation" type="xs:anyURI" use="optional"> |
|||
<xs:annotation> |
|||
<xs:documentation>The documentation URL, if applicable.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:sequence> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
|
|||
</xs:schema> |
|||
@ -0,0 +1,15 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
/// <summary>
|
|||
/// Exposes the provider-specific scopes supported by the OpenIddict client Web integration services.
|
|||
/// </summary>
|
|||
public static partial class OpenIddictClientWebIntegrationScopes |
|||
{ |
|||
// Note: scopes are automatically generated by the source generator.
|
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/* |
|||
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
|
|||
* See https://github.com/openiddict/openiddict-core for more information concerning
|
|||
* the license and the contributors participating to this project. |
|||
*/ |
|||
|
|||
namespace OpenIddict.Client.WebIntegration; |
|||
|
|||
/// <summary>
|
|||
/// Provides various settings needed to configure the OpenIddict client Web providers.
|
|||
/// </summary>
|
|||
public abstract partial class OpenIddictClientWebIntegrationSettings |
|||
{ |
|||
/// <summary>
|
|||
/// Gets or sets the client identifier.
|
|||
/// </summary>
|
|||
public virtual string? ClientId { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the client secret, if applicable.
|
|||
/// </summary>
|
|||
public virtual string? ClientSecret { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets or sets the redirection URL.
|
|||
/// </summary>
|
|||
public virtual Uri? RedirectUri { get; set; } |
|||
|
|||
/// <summary>
|
|||
/// Gets the scopes requested to the authorization server.
|
|||
/// </summary>
|
|||
public virtual HashSet<string> Scopes { get; } = new(StringComparer.Ordinal); |
|||
} |
|||
Loading…
Reference in new issue