Browse Source

Update the Microsoft provider to support custom tenants and force the use of Proof Key for Code Exchange

pull/1467/head
Kévin Chalet 4 years ago
parent
commit
f1fd1d9d45
  1. 75
      gen/OpenIddict.Client.WebIntegration.Generators/OpenIddictClientWebIntegrationGenerator.cs
  2. 51
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs
  3. 13
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Protection.cs
  4. 12
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml
  5. 10
      src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xsd

75
gen/OpenIddict.Client.WebIntegration.Generators/OpenIddictClientWebIntegrationGenerator.cs

@ -237,6 +237,46 @@ public partial class OpenIddictClientWebIntegrationConfiguration
{{~ end ~}}
{{~ end ~}}
{{~ for environment in provider.environments ~}}
if (settings.Environment is OpenIddictClientWebIntegrationEnvironments.{{ provider.name }}.{{ environment.name }})
{
{{~ for scope in environment.scopes ~}}
{{~ if scope.required ~}}
settings.Scopes.Add(""{{ scope.name }}"");
{{~ end ~}}
{{~ if scope.default ~}}
if (settings.Scopes.Count is 0)
{
settings.Scopes.Add(""{{ scope.name }}"");
}
{{~ end ~}}
{{~ end ~}}
}
{{~ end ~}}
{{~ for setting in provider.settings ~}}
{{~ if setting.default_value ~}}
if (string.IsNullOrEmpty(settings.{{ setting.name }}))
{
settings.{{ setting.name }} = ""{{ setting.default_value }}"";
}
{{~ end ~}}
{{~ for item in setting.collection_items ~}}
{{~ if item.required ~}}
settings.{{ setting.name }}.Add(""{{ item.value }}"");
{{~ end ~}}
{{~ if item.default ~}}
if (settings.{{ setting.name }}.Count is 0)
{
settings.{{ setting.name }}.Add(""{{ item.value }}"");
}
{{~ end ~}}
{{~ end ~}}
{{~ end ~}}
var formatter = Smart.CreateDefaultSmartFormat(new SmartSettings
{
CaseSensitivity = CaseSensitivityType.CaseInsensitive
@ -353,39 +393,6 @@ public partial class OpenIddictClientWebIntegrationConfiguration
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 ~}}
{{~ for setting in provider.settings ~}}
{{~ for item in setting.collection_items ~}}
{{~ if item.required ~}}
settings.{{ setting.name }}.Add(""{{ item.value }}"");
{{~ end ~}}
{{~ if item.default ~}}
if (settings.{{ setting.name }}.Count is 0)
{
settings.{{ setting.name }}.Add(""{{ item.value }}"");
}
{{~ end ~}}
{{~ end ~}}
{{~ end ~}}
options.Registrations.Add(registration);
}
}
@ -481,6 +488,8 @@ public partial class OpenIddictClientWebIntegrationConfiguration
EncryptionAlgorithm = (string?) setting.Element("EncryptionAlgorithm")?.Attribute("Value"),
SigningAlgorithm = (string?) setting.Element("SigningAlgorithm")?.Attribute("Value"),
DefaultValue = (string?) setting.Attribute("DefaultValue"),
CollectionItems = setting.Elements("CollectionItem").Select(item => new
{
Value = (string) item.Attribute("Value"),

51
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Discovery.cs

@ -19,7 +19,8 @@ public static partial class OpenIddictClientWebIntegrationHandlers
* Configuration response handling:
*/
AmendIssuer.Descriptor,
AmendClientAuthenticationMethods.Descriptor);
AmendClientAuthenticationMethods.Descriptor,
AmendCodeChallengeMethods.Descriptor);
/// <summary>
/// Contains the logic responsible for amending the issuer for the providers that require it.
@ -52,7 +53,11 @@ public static partial class OpenIddictClientWebIntegrationHandlers
// is replaced by this handler to always use "https://login.microsoftonline.com/common/v2.0".
if (context.Registration.GetProviderName() is Providers.Microsoft)
{
context.Response[Metadata.Issuer] = "https://login.microsoftonline.com/common/v2.0";
var settings = context.Registration.GetMicrosoftSettings();
if (string.Equals(settings.Tenant, "common", StringComparison.OrdinalIgnoreCase))
{
context.Response[Metadata.Issuer] = "https://login.microsoftonline.com/common/v2.0";
}
}
return default;
@ -60,8 +65,8 @@ public static partial class OpenIddictClientWebIntegrationHandlers
}
/// <summary>
/// Contains the logic responsible for amending the client
/// authentication methods for the providers that require it.
/// Contains the logic responsible for amending the supported
/// client authentication methods for the providers that require it.
/// </summary>
public class AmendClientAuthenticationMethods : IOpenIddictClientHandler<HandleConfigurationResponseContext>
{
@ -99,5 +104,43 @@ public static partial class OpenIddictClientWebIntegrationHandlers
return default;
}
}
/// <summary>
/// Contains the logic responsible for amending the supported
/// code challenge methods for the providers that require it.
/// </summary>
public class AmendCodeChallengeMethods : IOpenIddictClientHandler<HandleConfigurationResponseContext>
{
/// <summary>
/// Gets the default descriptor definition assigned to this handler.
/// </summary>
public static OpenIddictClientHandlerDescriptor Descriptor { get; }
= OpenIddictClientHandlerDescriptor.CreateBuilder<HandleConfigurationResponseContext>()
.UseSingletonHandler<AmendCodeChallengeMethods>()
.SetOrder(ExtractCodeChallengeMethods.Descriptor.Order + 500)
.SetType(OpenIddictClientHandlerType.BuiltIn)
.Build();
/// <inheritdoc/>
public ValueTask HandleAsync(HandleConfigurationResponseContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
// Microsoft Account supports both "plain" and "S256" code challenge methods but
// don't list them in the server configuration metadata. To ensure the OpenIddict
// client uses Proof Key for Code Exchange for the Microsoft provider, the 2 methods
// are manually added to the list of supported code challenge methods by this handler.
if (context.Registration.GetProviderName() is Providers.Microsoft)
{
context.Configuration.CodeChallengeMethodsSupported.Add(CodeChallengeMethods.Plain);
context.Configuration.CodeChallengeMethodsSupported.Add(CodeChallengeMethods.Sha256);
}
return default;
}
}
}
}

13
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationHandlers.Protection.cs

@ -52,11 +52,14 @@ public static partial class OpenIddictClientWebIntegrationHandlers
context.TokenValidationParameters.ValidateIssuer = context.Registration.GetProviderName() switch
{
// While the Microsoft Account provider uses the "common" tenant, the issued tokens include
// a dynamic issuer claim corresponding to the tenant instance that is associated with
// the client application. Since the tenant cannot be inferred when targeting the common
// tenant, issuer validation is manually disabled for the Microsoft Account provider.
Providers.Microsoft => false,
// When the Microsoft Account provider is configured to use the "common" tenant,
// the returned tokens include a dynamic issuer claim corresponding to the tenant
// that is associated with the client application. Since the tenant cannot be
// inferred when targeting the common tenant instance, issuer validation is disabled.
Providers.Microsoft when string.Equals(
context.Registration.GetMicrosoftSettings().Tenant,
"common", StringComparison.OrdinalIgnoreCase)
=> false,
_ => context.TokenValidationParameters.ValidateIssuer
};

12
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xml

@ -26,7 +26,17 @@
</Provider>
<Provider Name="Microsoft" Documentation="https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-protocols-oidc">
<Environment Issuer="https://login.microsoftonline.com/common/v2.0" />
<!--
Note: Microsoft is a multitenant provider that relies on virtual paths to identify instances.
As such, the issuer includes a {tenant} placeholder that will be dynamically replaced
by OpenIddict at runtime by the tenant configured in the Microsoft Account settings.
If no tenant is explicitly configured, the "common" tenant will be automatically used.
-->
<Environment Issuer="https://login.microsoftonline.com/{tenant}/v2.0" />
<Setting Name="Tenant" Type="String" Required="false" DefaultValue="common"
Description="Gets or sets the tenant used to identify the Azure AD instance (by default, the common tenant is used)." />
</Provider>
<Provider Name="Reddit" Documentation="https://github.com/reddit-archive/reddit/wiki/OAuth2">

10
src/OpenIddict.Client.WebIntegration/OpenIddictClientWebIntegrationProviders.xsd

@ -375,6 +375,16 @@
</xs:simpleType>
</xs:attribute>
<xs:attribute name="DefaultValue" use="optional">
<xs:annotation>
<xs:documentation>The default value used if no value was explictly set by the user.</xs:documentation>
</xs:annotation>
<xs:simpleType>
<xs:restriction base="xs:string" />
</xs:simpleType>
</xs:attribute>
<xs:attribute name="Description" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>The setting description.</xs:documentation>

Loading…
Cancel
Save