Browse Source

Update the ASP.NET 4.8 samples to use file-scoped namespaces

pull/2087/head
Kévin Chalet 2 years ago
parent
commit
63db215afc
  1. 541
      gen/OpenIddict.Client.WebIntegration.Generators/OpenIddictClientWebIntegrationGenerator.cs
  2. 37
      sandbox/OpenIddict.Sandbox.AspNet.Client/App_Start/BundleConfig.cs
  3. 11
      sandbox/OpenIddict.Sandbox.AspNet.Client/App_Start/FilterConfig.cs
  4. 23
      sandbox/OpenIddict.Sandbox.AspNet.Client/App_Start/RouteConfig.cs
  5. 353
      sandbox/OpenIddict.Sandbox.AspNet.Client/Controllers/AuthenticationController.cs
  6. 145
      sandbox/OpenIddict.Sandbox.AspNet.Client/Controllers/HomeController.cs
  7. 17
      sandbox/OpenIddict.Sandbox.AspNet.Client/Global.asax.cs
  8. 27
      sandbox/OpenIddict.Sandbox.AspNet.Client/Models/ApplicationDbContext.cs
  9. 213
      sandbox/OpenIddict.Sandbox.AspNet.Client/Startup.cs
  10. 37
      sandbox/OpenIddict.Sandbox.AspNet.Server/App_Start/BundleConfig.cs
  11. 11
      sandbox/OpenIddict.Sandbox.AspNet.Server/App_Start/FilterConfig.cs
  12. 147
      sandbox/OpenIddict.Sandbox.AspNet.Server/App_Start/IdentityConfig.cs
  13. 23
      sandbox/OpenIddict.Sandbox.AspNet.Server/App_Start/RouteConfig.cs
  14. 735
      sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AccountController.cs
  15. 139
      sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthenticationController.cs
  16. 681
      sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthorizationController.cs
  17. 31
      sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/HomeController.cs
  18. 567
      sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/ManageController.cs
  19. 83
      sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/ResourceController.cs
  20. 17
      sandbox/OpenIddict.Sandbox.AspNet.Server/Global.asax.cs
  21. 33
      sandbox/OpenIddict.Sandbox.AspNet.Server/Helpers/AsyncEnumerableExtensions.cs
  22. 47
      sandbox/OpenIddict.Sandbox.AspNet.Server/Helpers/FormValueRequiredAttribute.cs
  23. 213
      sandbox/OpenIddict.Sandbox.AspNet.Server/Models/AccountViewModels.cs
  24. 47
      sandbox/OpenIddict.Sandbox.AspNet.Server/Models/IdentityModels.cs
  25. 135
      sandbox/OpenIddict.Sandbox.AspNet.Server/Models/ManageViewModels.cs
  26. 455
      sandbox/OpenIddict.Sandbox.AspNet.Server/Startup.cs
  27. 11
      sandbox/OpenIddict.Sandbox.AspNet.Server/ViewModels/Authorization/AuthorizeViewModel.cs
  28. 19
      sandbox/OpenIddict.Sandbox.AspNet.Server/ViewModels/Authorization/LogoutViewModel.cs

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

@ -5,47 +5,47 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Scriban;
namespace OpenIddict.Client.WebIntegration.Generators
namespace OpenIddict.Client.WebIntegration.Generators;
[Generator]
public sealed class OpenIddictClientWebIntegrationGenerator : ISourceGenerator
{
[Generator]
public sealed class OpenIddictClientWebIntegrationGenerator : ISourceGenerator
public void Execute(GeneratorExecutionContext context)
{
public void Execute(GeneratorExecutionContext context)
{
var file = context.AdditionalFiles.Select(file => file.Path)
.Where(path => string.Equals(Path.GetFileName(path), "OpenIddictClientWebIntegrationProviders.xml"))
.SingleOrDefault();
var file = context.AdditionalFiles.Select(file => file.Path)
.Where(path => string.Equals(Path.GetFileName(path), "OpenIddictClientWebIntegrationProviders.xml"))
.SingleOrDefault();
if (string.IsNullOrEmpty(file))
{
return;
}
if (string.IsNullOrEmpty(file))
{
return;
}
var document = XDocument.Load(file, LoadOptions.None);
var document = XDocument.Load(file, LoadOptions.None);
context.AddSource(
"OpenIddictClientWebIntegrationBuilder.generated.cs",
SourceText.From(GenerateBuilderMethods(document), Encoding.UTF8));
context.AddSource(
"OpenIddictClientWebIntegrationBuilder.generated.cs",
SourceText.From(GenerateBuilderMethods(document), Encoding.UTF8));
context.AddSource(
"OpenIddictClientWebIntegrationConfiguration.generated.cs",
SourceText.From(GenerateConfigurationClasses(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(
"OpenIddictClientWebIntegrationConstants.generated.cs",
SourceText.From(GenerateConstants(document), Encoding.UTF8));
context.AddSource(
"OpenIddictClientWebIntegrationHelpers.generated.cs",
SourceText.From(GenerateHelpers(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));
context.AddSource(
"OpenIddictClientWebIntegrationSettings.generated.cs",
SourceText.From(GenerateSettings(document), Encoding.UTF8));
static string GenerateBuilderMethods(XDocument document)
{
var template = Template.Parse(@"#nullable enable
static string GenerateBuilderMethods(XDocument document)
{
var template = Template.Parse(@"#nullable enable
#pragma warning disable CS0618
using System.ComponentModel;
@ -743,62 +743,62 @@ public sealed partial class OpenIddictClientWebIntegrationBuilder
{{~ end ~}}
}
");
return template.Render(new
{
Providers = document.Root.Elements("Provider")
.Select(provider => new
{
Name = (string) provider.Attribute("Name"),
DisplayName = (string?) provider.Attribute("DisplayName") ?? (string) provider.Attribute("Name"),
Documentation = (string?) provider.Attribute("Documentation"),
return template.Render(new
{
Providers = document.Root.Elements("Provider")
.Select(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,
Obsolete = (bool?) provider.Attribute("Obsolete") ?? false,
Environments = provider.Elements("Environment").Select(environment => new
{
Name = (string?) environment.Attribute("Name") ?? "Production"
})
.ToList(),
Environments = provider.Elements("Environment").Select(environment => new
{
Name = (string?) environment.Attribute("Name") ?? "Production"
})
.ToList(),
Settings = provider.Elements("Setting").Select(setting => new
{
PropertyName = (string) setting.Attribute("PropertyName"),
ParameterName = (string) setting.Attribute("ParameterName"),
Settings = provider.Elements("Setting").Select(setting => new
{
PropertyName = (string) setting.Attribute("PropertyName"),
ParameterName = (string) setting.Attribute("ParameterName"),
Collection = (bool?) setting.Attribute("Collection") ?? false,
Obsolete = (bool?) setting.Attribute("Obsolete") ?? false,
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
{
"EncryptionKey" when (string) setting.Element("EncryptionAlgorithm").Attribute("Value")
is "RS256" or "RS384" or "RS512" => "RsaSecurityKey",
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
{
"EncryptionKey" when (string) setting.Element("EncryptionAlgorithm").Attribute("Value")
is "RS256" or "RS384" or "RS512" => "RsaSecurityKey",
"SigningKey" when (string) setting.Element("SigningAlgorithm").Attribute("Value")
is "ES256" or "ES384" or "ES512" => "ECDsaSecurityKey",
"SigningKey" when (string) setting.Element("SigningAlgorithm").Attribute("Value")
is "ES256" or "ES384" or "ES512" => "ECDsaSecurityKey",
"SigningKey" when (string) setting.Element("SigningAlgorithm").Attribute("Value")
is "PS256" or "PS384" or "PS512" or
"RS256" or "RS384" or "RS512" => "RsaSecurityKey",
"SigningKey" when (string) setting.Element("SigningAlgorithm").Attribute("Value")
is "PS256" or "PS384" or "PS512" or
"RS256" or "RS384" or "RS512" => "RsaSecurityKey",
"Certificate" => "X509Certificate2",
"String" => "string",
"StringHashSet" => "HashSet<string>",
"Uri" => "Uri",
"Certificate" => "X509Certificate2",
"String" => "string",
"StringHashSet" => "HashSet<string>",
"Uri" => "Uri",
string value => value
}
})
.ToList()
string value => value
}
})
.ToList()
});
}
})
.ToList()
});
}
static string GenerateConstants(XDocument document)
{
var template = Template.Parse(@"#nullable enable
static string GenerateConstants(XDocument document)
{
var template = Template.Parse(@"#nullable enable
namespace OpenIddict.Client.WebIntegration;
@ -838,34 +838,34 @@ public static partial class OpenIddictClientWebIntegrationConstants
}
}
");
return template.Render(new
{
Providers = document.Root.Elements("Provider")
.Select(provider => new
{
Name = (string) provider.Attribute("Name"),
Id = (string) provider.Attribute("Id"),
return template.Render(new
{
Providers = document.Root.Elements("Provider")
.Select(provider => new
{
Name = (string) provider.Attribute("Name"),
Id = (string) provider.Attribute("Id"),
Environments = provider.Elements("Environment").Select(environment => new
{
Name = (string?) environment.Attribute("Name") ?? "Production"
})
.ToList(),
Environments = provider.Elements("Environment").Select(environment => new
{
Name = (string?) environment.Attribute("Name") ?? "Production"
})
.ToList(),
Properties = provider.Elements("Property").Select(property => new
{
Name = (string) property.Attribute("Name"),
DictionaryKey = (string) property.Attribute("DictionaryKey")
})
.ToList(),
Properties = provider.Elements("Property").Select(property => new
{
Name = (string) property.Attribute("Name"),
DictionaryKey = (string) property.Attribute("DictionaryKey")
})
.ToList()
});
}
.ToList(),
})
.ToList()
});
}
static string GenerateConfigurationClasses(XDocument document)
{
var template = Template.Parse(@"#nullable enable
static string GenerateConfigurationClasses(XDocument document)
{
var template = Template.Parse(@"#nullable enable
#pragma warning disable CS0618
using Microsoft.Extensions.DependencyInjection;
@ -1116,149 +1116,149 @@ public sealed partial class OpenIddictClientWebIntegrationConfiguration
}
}
");
return template.Render(new
{
Providers = document.Root.Elements("Provider")
.Select(provider => new
return template.Render(new
{
Providers = document.Root.Elements("Provider")
.Select(provider => new
{
Name = (string) provider.Attribute("Name"),
DisplayName = (string?) provider.Attribute("DisplayName") ?? (string) provider.Attribute("Name"),
Environments = provider.Elements("Environment").Select(environment => new
{
Name = (string) provider.Attribute("Name"),
DisplayName = (string?) provider.Attribute("DisplayName") ?? (string) provider.Attribute("Name"),
Name = (string?) environment.Attribute("Name") ?? "Production",
Environments = provider.Elements("Environment").Select(environment => new
Issuer = (string) environment.Attribute("Issuer"),
ConfigurationEndpoint = (string?) environment.Attribute("ConfigurationEndpoint"),
Configuration = environment.Element("Configuration") switch
{
Name = (string?) environment.Attribute("Name") ?? "Production",
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(type => (string?) type.Attribute("Value")).ToList(),
Issuer = (string) environment.Attribute("Issuer"),
ConfigurationEndpoint = (string?) environment.Attribute("ConfigurationEndpoint"),
_ => []
},
Configuration = environment.Element("Configuration") switch
{
XElement configuration => new
GrantTypesSupported = configuration.Elements("GrantType").ToList() switch
{
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(type => (string?) type.Attribute("Value")).ToList(),
_ => []
},
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.
_ => [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.
_ => [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.
_ => [ResponseTypes.Code]
},
ScopesSupported = configuration.Elements("Scope").ToList() switch
{
{ Count: > 0 } types => types.Select(type => (string?) type.Attribute("Value")).ToList(),
_ => []
},
DeviceAuthorizationEndpointAuthMethodsSupported = configuration.Elements("DeviceAuthorizationEndpointAuthMethod").ToList() switch
{
{ Count: > 0 } methods => methods.Select(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(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(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(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]
}
{ 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.
_ => [GrantTypes.AuthorizationCode]
},
_ => null
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.
_ => [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.
_ => [ResponseTypes.Code]
},
ScopesSupported = configuration.Elements("Scope").ToList() switch
{
{ Count: > 0 } types => types.Select(type => (string?) type.Attribute("Value")).ToList(),
_ => []
},
DeviceAuthorizationEndpointAuthMethodsSupported = configuration.Elements("DeviceAuthorizationEndpointAuthMethod").ToList() switch
{
{ Count: > 0 } methods => methods.Select(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(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(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(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]
}
},
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(),
_ => null
},
Settings = provider.Elements("Setting").Select(setting => new
Scopes = environment.Elements("Scope").Select(setting => new
{
PropertyName = (string) setting.Attribute("PropertyName"),
Name = (string) setting.Attribute("Name"),
Default = (bool?) setting.Attribute("Default") ?? false,
Required = (bool?) setting.Attribute("Required") ?? false
})
})
.ToList(),
Type = (string) setting.Attribute("Type"),
Required = (bool?) setting.Attribute("Required") ?? false,
Collection = (bool?) setting.Attribute("Collection") ?? false,
Settings = provider.Elements("Setting").Select(setting => new
{
PropertyName = (string) setting.Attribute("PropertyName"),
EncryptionAlgorithm = (string?) setting.Element("EncryptionAlgorithm")?.Attribute("Value"),
SigningAlgorithm = (string?) setting.Element("SigningAlgorithm")?.Attribute("Value"),
Type = (string) setting.Attribute("Type"),
Required = (bool?) setting.Attribute("Required") ?? false,
Collection = (bool?) setting.Attribute("Collection") ?? false,
DefaultValue = (string?) setting.Attribute("DefaultValue"),
EncryptionAlgorithm = (string?) setting.Element("EncryptionAlgorithm")?.Attribute("Value"),
SigningAlgorithm = (string?) setting.Element("SigningAlgorithm")?.Attribute("Value"),
Items = setting.Elements("Item").Select(item => new
{
Value = (string) item.Attribute("Value"),
Default = (bool?) item.Attribute("Default") ?? false,
Required = (bool?) item.Attribute("Required") ?? false
})
.ToList()
DefaultValue = (string?) setting.Attribute("DefaultValue"),
Items = setting.Elements("Item").Select(item => new
{
Value = (string) item.Attribute("Value"),
Default = (bool?) item.Attribute("Default") ?? false,
Required = (bool?) item.Attribute("Required") ?? false
})
.ToList()
})
.ToList()
});
}
})
.ToList()
});
}
static string GenerateHelpers(XDocument document)
{
var template = Template.Parse(@"#nullable enable
static string GenerateHelpers(XDocument document)
{
var template = Template.Parse(@"#nullable enable
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Client;
@ -1283,21 +1283,21 @@ public static partial class OpenIddictClientWebIntegrationHelpers
{{~ end ~}}
}
");
return template.Render(new
{
Providers = document.Root.Elements("Provider")
.Select(provider => new
{
Name = (string) provider.Attribute("Name"),
DisplayName = (string?) provider.Attribute("DisplayName") ?? (string) provider.Attribute("Name")
})
.ToList()
});
}
static string GenerateSettings(XDocument document)
return template.Render(new
{
var template = Template.Parse(@"#nullable enable
Providers = document.Root.Elements("Provider")
.Select(provider => new
{
Name = (string) provider.Attribute("Name"),
DisplayName = (string?) provider.Attribute("DisplayName") ?? (string) provider.Attribute("Name")
})
.ToList()
});
}
static string GenerateSettings(XDocument document)
{
var template = Template.Parse(@"#nullable enable
using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.Tokens;
@ -1335,52 +1335,51 @@ public sealed partial class OpenIddictClientWebIntegrationSettings
{{~ end ~}}
}
");
return template.Render(new
{
Providers = document.Root.Elements("Provider")
.Select(provider => new
{
Name = (string) provider.Attribute("Name"),
DisplayName = (string?) provider.Attribute("DisplayName") ?? (string) provider.Attribute("Name"),
return template.Render(new
{
Providers = document.Root.Elements("Provider")
.Select(provider => new
{
Name = (string) provider.Attribute("Name"),
DisplayName = (string?) provider.Attribute("DisplayName") ?? (string) provider.Attribute("Name"),
Settings = provider.Elements("Setting").Select(setting => new
{
PropertyName = (string) setting.Attribute("PropertyName"),
Settings = provider.Elements("Setting").Select(setting => new
{
PropertyName = (string) setting.Attribute("PropertyName"),
Collection = (bool?) setting.Attribute("Collection") ?? false,
Obsolete = (bool?) setting.Attribute("Obsolete") ?? false,
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
{
"EncryptionKey" when (string) setting.Element("EncryptionAlgorithm").Attribute("Value")
is "RS256" or "RS384" or "RS512" => "RsaSecurityKey",
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
{
"EncryptionKey" when (string) setting.Element("EncryptionAlgorithm").Attribute("Value")
is "RS256" or "RS384" or "RS512" => "RsaSecurityKey",
"SigningKey" when (string) setting.Element("SigningAlgorithm").Attribute("Value")
is "ES256" or "ES384" or "ES512" => "ECDsaSecurityKey",
"SigningKey" when (string) setting.Element("SigningAlgorithm").Attribute("Value")
is "ES256" or "ES384" or "ES512" => "ECDsaSecurityKey",
"SigningKey" when (string) setting.Element("SigningAlgorithm").Attribute("Value")
is "PS256" or "PS384" or "PS512" or
"RS256" or "RS384" or "RS512" => "RsaSecurityKey",
"SigningKey" when (string) setting.Element("SigningAlgorithm").Attribute("Value")
is "PS256" or "PS384" or "PS512" or
"RS256" or "RS384" or "RS512" => "RsaSecurityKey",
"Certificate" => "X509Certificate2",
"String" => "string",
"StringHashSet" => "HashSet<string>",
"Uri" => "Uri",
"Certificate" => "X509Certificate2",
"String" => "string",
"StringHashSet" => "HashSet<string>",
"Uri" => "Uri",
string value => value
}
})
.ToList()
string value => value
}
})
.ToList()
});
}
})
.ToList()
});
}
}
public void Initialize(GeneratorInitializationContext context)
{
}
public void Initialize(GeneratorInitializationContext context)
{
}
}

37
sandbox/OpenIddict.Sandbox.AspNet.Client/App_Start/BundleConfig.cs

@ -1,29 +1,28 @@
using System.Web.Optimization;
namespace OpenIddict.Sandbox.AspNet.Client
namespace OpenIddict.Sandbox.AspNet.Client;
public class BundleConfig
{
public class BundleConfig
// Pour plus d'informations sur le regroupement, visitez https://go.microsoft.com/fwlink/?LinkId=301862
public static void RegisterBundles(BundleCollection bundles)
{
// Pour plus d'informations sur le regroupement, visitez https://go.microsoft.com/fwlink/?LinkId=301862
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.validate*"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.validate*"));
// Utilisez la version de développement de Modernizr pour développer et apprendre. Puis, lorsque vous êtes
// prêt pour la production, utilisez l'outil de génération à l'adresse https://modernizr.com pour sélectionner uniquement les tests dont vous avez besoin.
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));
// Utilisez la version de développement de Modernizr pour développer et apprendre. Puis, lorsque vous êtes
// prêt pour la production, utilisez l'outil de génération à l'adresse https://modernizr.com pour sélectionner uniquement les tests dont vous avez besoin.
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));
bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
"~/Scripts/bootstrap.js"));
bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
"~/Scripts/bootstrap.js"));
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/bootstrap.css",
"~/Content/site.css"));
}
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/bootstrap.css",
"~/Content/site.css"));
}
}

11
sandbox/OpenIddict.Sandbox.AspNet.Client/App_Start/FilterConfig.cs

@ -1,12 +1,11 @@
using System.Web.Mvc;
namespace OpenIddict.Sandbox.AspNet.Client
namespace OpenIddict.Sandbox.AspNet.Client;
public class FilterConfig
{
public class FilterConfig
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
filters.Add(new HandleErrorAttribute());
}
}

23
sandbox/OpenIddict.Sandbox.AspNet.Client/App_Start/RouteConfig.cs

@ -1,21 +1,20 @@
using System.Web.Mvc;
using System.Web.Routing;
namespace OpenIddict.Sandbox.AspNet.Client
namespace OpenIddict.Sandbox.AspNet.Client;
public class RouteConfig
{
public class RouteConfig
public static void RegisterRoutes(RouteCollection routes)
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}

353
sandbox/OpenIddict.Sandbox.AspNet.Client/Controllers/AuthenticationController.cs

@ -11,212 +11,211 @@ using OpenIddict.Client;
using OpenIddict.Client.Owin;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace OpenIddict.Sandbox.AspNet.Client.Controllers
namespace OpenIddict.Sandbox.AspNet.Client.Controllers;
public class AuthenticationController : Controller
{
public class AuthenticationController : Controller
{
private readonly OpenIddictClientService _service;
private readonly OpenIddictClientService _service;
public AuthenticationController(OpenIddictClientService service)
=> _service = service;
public AuthenticationController(OpenIddictClientService service)
=> _service = service;
[HttpPost, Route("~/login"), ValidateAntiForgeryToken]
public async Task<ActionResult> LogIn(string provider, string returnUrl)
{
var context = HttpContext.GetOwinContext();
[HttpPost, Route("~/login"), ValidateAntiForgeryToken]
public async Task<ActionResult> LogIn(string provider, string returnUrl)
{
var context = HttpContext.GetOwinContext();
// The local authorization server sample allows the client to select the external
// identity provider that will be used to eventually authenticate the user. For that,
// a custom "identity_provider" parameter is sent to the authorization server so that
// the user is directly redirected to GitHub (in this case, no login page is shown).
if (string.Equals(provider, "Local+GitHub", StringComparison.Ordinal))
// The local authorization server sample allows the client to select the external
// identity provider that will be used to eventually authenticate the user. For that,
// a custom "identity_provider" parameter is sent to the authorization server so that
// the user is directly redirected to GitHub (in this case, no login page is shown).
if (string.Equals(provider, "Local+GitHub", StringComparison.Ordinal))
{
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
// Note: when only one client is registered in the client options,
// specifying the issuer URI or the provider name is not required.
[OpenIddictClientOwinConstants.Properties.ProviderName] = "Local",
// Note: the OWIN host requires appending the #string suffix to indicate
// that the "identity_provider" property is a public string parameter.
[Parameters.IdentityProvider + OpenIddictClientOwinConstants.PropertyTypes.String] = "GitHub"
})
{
// Only allow local return URLs to prevent open redirect attacks.
RedirectUri = Url.IsLocalUrl(returnUrl) ? returnUrl : "/"
};
// Ask the OpenIddict client middleware to redirect the user agent to the identity provider.
context.Authentication.Challenge(properties, OpenIddictClientOwinDefaults.AuthenticationType);
return new EmptyResult();
}
else
// Note: when only one client is registered in the client options,
// specifying the issuer URI or the provider name is not required.
[OpenIddictClientOwinConstants.Properties.ProviderName] = "Local",
// Note: the OWIN host requires appending the #string suffix to indicate
// that the "identity_provider" property is a public string parameter.
[Parameters.IdentityProvider + OpenIddictClientOwinConstants.PropertyTypes.String] = "GitHub"
})
{
// Note: OpenIddict always validates the specified provider name when handling the challenge operation,
// but the provider can also be validated earlier to return an error page or a special HTTP error code.
var registrations = await _service.GetClientRegistrationsAsync();
if (!registrations.Any(registration => string.Equals(registration.ProviderName, provider, StringComparison.Ordinal)))
{
return new HttpStatusCodeResult(400);
}
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
// Note: when only one client is registered in the client options,
// specifying the issuer URI or the provider name is not required.
[OpenIddictClientOwinConstants.Properties.ProviderName] = provider
})
{
// Only allow local return URLs to prevent open redirect attacks.
RedirectUri = Url.IsLocalUrl(returnUrl) ? returnUrl : "/"
};
// Ask the OpenIddict client middleware to redirect the user agent to the identity provider.
context.Authentication.Challenge(properties, OpenIddictClientOwinDefaults.AuthenticationType);
return new EmptyResult();
}
// Only allow local return URLs to prevent open redirect attacks.
RedirectUri = Url.IsLocalUrl(returnUrl) ? returnUrl : "/"
};
// Ask the OpenIddict client middleware to redirect the user agent to the identity provider.
context.Authentication.Challenge(properties, OpenIddictClientOwinDefaults.AuthenticationType);
return new EmptyResult();
}
[HttpPost, Route("~/logout"), ValidateAntiForgeryToken]
public async Task<ActionResult> LogOut(string returnUrl)
else
{
var context = HttpContext.GetOwinContext();
// Note: OpenIddict always validates the specified provider name when handling the challenge operation,
// but the provider can also be validated earlier to return an error page or a special HTTP error code.
var registrations = await _service.GetClientRegistrationsAsync();
if (!registrations.Any(registration => string.Equals(registration.ProviderName, provider, StringComparison.Ordinal)))
{
return new HttpStatusCodeResult(400);
}
// Retrieve the identity stored in the local authentication cookie. If it's not available,
// this indicate that the user is already logged out locally (or has not logged in yet).
var result = await context.Authentication.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationType);
if (result is not { Identity: ClaimsIdentity identity })
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
// Note: when only one client is registered in the client options,
// specifying the issuer URI or the provider name is not required.
[OpenIddictClientOwinConstants.Properties.ProviderName] = provider
})
{
// Only allow local return URLs to prevent open redirect attacks.
return Redirect(Url.IsLocalUrl(returnUrl) ? returnUrl : "/");
}
RedirectUri = Url.IsLocalUrl(returnUrl) ? returnUrl : "/"
};
// Remove the local authentication cookie before triggering a redirection to the remote server.
context.Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
// Ask the OpenIddict client middleware to redirect the user agent to the identity provider.
context.Authentication.Challenge(properties, OpenIddictClientOwinDefaults.AuthenticationType);
return new EmptyResult();
}
}
// Extract the client registration identifier and retrieve the associated server configuration.
// If the provider is known to support remote sign-out, ask OpenIddict to initiate a logout request.
if (identity.FindFirst(Claims.Private.RegistrationId)?.Value is string identifier &&
await _service.GetServerConfigurationByRegistrationIdAsync(identifier) is { EndSessionEndpoint: Uri })
{
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictClientOwinConstants.Properties.RegistrationId] = identifier,
// While not required, the specification encourages sending an id_token_hint
// parameter containing an identity token returned by the server for this user.
[OpenIddictClientOwinConstants.Properties.IdentityTokenHint] =
result.Properties.Dictionary[OpenIddictClientOwinConstants.Tokens.BackchannelIdentityToken]
})
{
// Only allow local return URLs to prevent open redirect attacks.
RedirectUri = Url.IsLocalUrl(returnUrl) ? returnUrl : "/"
};
// Ask the OpenIddict client middleware to redirect the user agent to the identity provider.
context.Authentication.SignOut(properties, OpenIddictClientOwinDefaults.AuthenticationType);
return new EmptyResult();
}
[HttpPost, Route("~/logout"), ValidateAntiForgeryToken]
public async Task<ActionResult> LogOut(string returnUrl)
{
var context = HttpContext.GetOwinContext();
// Retrieve the identity stored in the local authentication cookie. If it's not available,
// this indicate that the user is already logged out locally (or has not logged in yet).
var result = await context.Authentication.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationType);
if (result is not { Identity: ClaimsIdentity identity })
{
// Only allow local return URLs to prevent open redirect attacks.
return Redirect(Url.IsLocalUrl(returnUrl) ? returnUrl : "/");
}
// Note: this controller uses the same callback action for all providers
// but for users who prefer using a different action per provider,
// the following action can be split into separate actions.
[AcceptVerbs("GET", "POST"), Route("~/callback/login/{provider}")]
public async Task<ActionResult> LogInCallback()
// Remove the local authentication cookie before triggering a redirection to the remote server.
context.Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType);
// Extract the client registration identifier and retrieve the associated server configuration.
// If the provider is known to support remote sign-out, ask OpenIddict to initiate a logout request.
if (identity.FindFirst(Claims.Private.RegistrationId)?.Value is string identifier &&
await _service.GetServerConfigurationByRegistrationIdAsync(identifier) is { EndSessionEndpoint: Uri })
{
var context = HttpContext.GetOwinContext();
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictClientOwinConstants.Properties.RegistrationId] = identifier,
// Retrieve the authorization data validated by OpenIddict as part of the callback handling.
var result = await context.Authentication.AuthenticateAsync(OpenIddictClientOwinDefaults.AuthenticationType);
// While not required, the specification encourages sending an id_token_hint
// parameter containing an identity token returned by the server for this user.
[OpenIddictClientOwinConstants.Properties.IdentityTokenHint] =
result.Properties.Dictionary[OpenIddictClientOwinConstants.Tokens.BackchannelIdentityToken]
})
{
// Only allow local return URLs to prevent open redirect attacks.
RedirectUri = Url.IsLocalUrl(returnUrl) ? returnUrl : "/"
};
// Multiple strategies exist to handle OAuth 2.0/OpenID Connect callbacks, each with their pros and cons:
//
// * Directly using the tokens to perform the necessary action(s) on behalf of the user, which is suitable
// for applications that don't need a long-term access to the user's resources or don't want to store
// access/refresh tokens in a database or in an authentication cookie (which has security implications).
// It is also suitable for applications that don't need to authenticate users but only need to perform
// action(s) on their behalf by making API calls using the access token returned by the remote server.
//
// * Storing the external claims/tokens in a database (and optionally keeping the essential claims in an
// authentication cookie so that cookie size limits are not hit). For the applications that use ASP.NET
// Core Identity, the UserManager.SetAuthenticationTokenAsync() API can be used to store external tokens.
//
// Note: in this case, it's recommended to use column encryption to protect the tokens in the database.
// Ask the OpenIddict client middleware to redirect the user agent to the identity provider.
context.Authentication.SignOut(properties, OpenIddictClientOwinDefaults.AuthenticationType);
return new EmptyResult();
}
// Only allow local return URLs to prevent open redirect attacks.
return Redirect(Url.IsLocalUrl(returnUrl) ? returnUrl : "/");
}
// Note: this controller uses the same callback action for all providers
// but for users who prefer using a different action per provider,
// the following action can be split into separate actions.
[AcceptVerbs("GET", "POST"), Route("~/callback/login/{provider}")]
public async Task<ActionResult> LogInCallback()
{
var context = HttpContext.GetOwinContext();
// Retrieve the authorization data validated by OpenIddict as part of the callback handling.
var result = await context.Authentication.AuthenticateAsync(OpenIddictClientOwinDefaults.AuthenticationType);
// Multiple strategies exist to handle OAuth 2.0/OpenID Connect callbacks, each with their pros and cons:
//
// * Directly using the tokens to perform the necessary action(s) on behalf of the user, which is suitable
// for applications that don't need a long-term access to the user's resources or don't want to store
// access/refresh tokens in a database or in an authentication cookie (which has security implications).
// It is also suitable for applications that don't need to authenticate users but only need to perform
// action(s) on their behalf by making API calls using the access token returned by the remote server.
//
// * Storing the external claims/tokens in a database (and optionally keeping the essential claims in an
// authentication cookie so that cookie size limits are not hit). For the applications that use ASP.NET
// Core Identity, the UserManager.SetAuthenticationTokenAsync() API can be used to store external tokens.
//
// Note: in this case, it's recommended to use column encryption to protect the tokens in the database.
//
// * Storing the external claims/tokens in an authentication cookie, which doesn't require having
// a user database but may be affected by the cookie size limits enforced by most browser vendors
// (e.g Safari for macOS and Safari for iOS/iPadOS enforce a per-domain 4KB limit for all cookies).
//
// Note: this is the approach used here, but the external claims are first filtered to only persist
// a few claims like the user identifier. The same approach is used to store the access/refresh tokens.
// Important: if the remote server doesn't support OpenID Connect and doesn't expose a userinfo endpoint,
// result.Principal.Identity will represent an unauthenticated identity and won't contain any user claim.
//
// Such identities cannot be used as-is to build an authentication cookie in ASP.NET (as the
// antiforgery stack requires at least a name claim to bind CSRF cookies to the user's identity) but
// the access/refresh tokens can be retrieved using result.Properties.GetTokens() to make API calls.
if (result.Identity is not ClaimsIdentity { IsAuthenticated: true })
{
throw new InvalidOperationException("The external authorization data cannot be used for authentication.");
}
// Build an identity based on the external claims and that will be used to create the authentication cookie.
//
// By default, all claims extracted during the authorization dance are available. The claims collection stored
// in the cookie can be filtered out or mapped to different names depending the claim name or its issuer.
var claims = result.Identity.Claims.Where(claim => claim.Type is ClaimTypes.NameIdentifier or ClaimTypes.Name
//
// * Storing the external claims/tokens in an authentication cookie, which doesn't require having
// a user database but may be affected by the cookie size limits enforced by most browser vendors
// (e.g Safari for macOS and Safari for iOS/iPadOS enforce a per-domain 4KB limit for all cookies).
// Preserve the registration details to be able to resolve them later.
//
// Note: this is the approach used here, but the external claims are first filtered to only persist
// a few claims like the user identifier. The same approach is used to store the access/refresh tokens.
// Important: if the remote server doesn't support OpenID Connect and doesn't expose a userinfo endpoint,
// result.Principal.Identity will represent an unauthenticated identity and won't contain any user claim.
or Claims.Private.RegistrationId or Claims.Private.ProviderName
//
// Such identities cannot be used as-is to build an authentication cookie in ASP.NET (as the
// antiforgery stack requires at least a name claim to bind CSRF cookies to the user's identity) but
// the access/refresh tokens can be retrieved using result.Properties.GetTokens() to make API calls.
if (result.Identity is not ClaimsIdentity { IsAuthenticated: true })
{
throw new InvalidOperationException("The external authorization data cannot be used for authentication.");
}
// Build an identity based on the external claims and that will be used to create the authentication cookie.
// The ASP.NET 4.x antiforgery module requires preserving the "identityprovider" claim.
//
// By default, all claims extracted during the authorization dance are available. The claims collection stored
// in the cookie can be filtered out or mapped to different names depending the claim name or its issuer.
var claims = result.Identity.Claims.Where(claim => claim.Type is ClaimTypes.NameIdentifier or ClaimTypes.Name
//
// Preserve the registration details to be able to resolve them later.
//
or Claims.Private.RegistrationId or Claims.Private.ProviderName
//
// The ASP.NET 4.x antiforgery module requires preserving the "identityprovider" claim.
//
or "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider");
var identity = new ClaimsIdentity(claims,
authenticationType: CookieAuthenticationDefaults.AuthenticationType,
nameType: ClaimTypes.Name,
roleType: ClaimTypes.Role);
// Build the authentication properties based on the properties that were added when the challenge was triggered.
var properties = new AuthenticationProperties(result.Properties.Dictionary
.Where(item => item.Key is
// Preserve the return URL.
".redirect" or
// If needed, the tokens returned by the authorization server can be stored in the authentication cookie.
OpenIddictClientOwinConstants.Tokens.BackchannelAccessToken or
OpenIddictClientOwinConstants.Tokens.BackchannelIdentityToken or
OpenIddictClientOwinConstants.Tokens.RefreshToken)
.ToDictionary(pair => pair.Key, pair => pair.Value));
context.Authentication.SignIn(properties, identity);
return Redirect(properties.RedirectUri ?? "/");
}
or "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider");
var identity = new ClaimsIdentity(claims,
authenticationType: CookieAuthenticationDefaults.AuthenticationType,
nameType: ClaimTypes.Name,
roleType: ClaimTypes.Role);
// Build the authentication properties based on the properties that were added when the challenge was triggered.
var properties = new AuthenticationProperties(result.Properties.Dictionary
.Where(item => item.Key is
// Preserve the return URL.
".redirect" or
// If needed, the tokens returned by the authorization server can be stored in the authentication cookie.
OpenIddictClientOwinConstants.Tokens.BackchannelAccessToken or
OpenIddictClientOwinConstants.Tokens.BackchannelIdentityToken or
OpenIddictClientOwinConstants.Tokens.RefreshToken)
.ToDictionary(pair => pair.Key, pair => pair.Value));
context.Authentication.SignIn(properties, identity);
return Redirect(properties.RedirectUri ?? "/");
}
// Note: this controller uses the same callback action for all providers
// but for users who prefer using a different action per provider,
// the following action can be split into separate actions.
[AcceptVerbs("GET", "POST"), Route("~/callback/logout/{provider}")]
public async Task<ActionResult> LogOutCallback()
{
var context = HttpContext.GetOwinContext();
// Note: this controller uses the same callback action for all providers
// but for users who prefer using a different action per provider,
// the following action can be split into separate actions.
[AcceptVerbs("GET", "POST"), Route("~/callback/logout/{provider}")]
public async Task<ActionResult> LogOutCallback()
{
var context = HttpContext.GetOwinContext();
// Retrieve the data stored by OpenIddict in the state token created when the logout was triggered.
var result = await context.Authentication.AuthenticateAsync(OpenIddictClientOwinDefaults.AuthenticationType);
// Retrieve the data stored by OpenIddict in the state token created when the logout was triggered.
var result = await context.Authentication.AuthenticateAsync(OpenIddictClientOwinDefaults.AuthenticationType);
// In this sample, the local authentication cookie is always removed before the user agent is redirected
// to the authorization server. Applications that prefer delaying the removal of the local cookie can
// remove the corresponding code from the logout action and remove the authentication cookie in this action.
// In this sample, the local authentication cookie is always removed before the user agent is redirected
// to the authorization server. Applications that prefer delaying the removal of the local cookie can
// remove the corresponding code from the logout action and remove the authentication cookie in this action.
return Redirect(result.Properties.RedirectUri ?? "/");
}
return Redirect(result.Properties.RedirectUri ?? "/");
}
}

145
sandbox/OpenIddict.Sandbox.AspNet.Client/Controllers/HomeController.cs

@ -13,97 +13,96 @@ using OpenIddict.Sandbox.AspNet.Client.ViewModels.Home;
using static OpenIddict.Abstractions.OpenIddictConstants;
using static OpenIddict.Client.Owin.OpenIddictClientOwinConstants;
namespace OpenIddict.Sandbox.AspNet.Client.Controllers
namespace OpenIddict.Sandbox.AspNet.Client.Controllers;
public class HomeController : Controller
{
public class HomeController : Controller
private readonly IHttpClientFactory _httpClientFactory;
private readonly OpenIddictClientService _service;
public HomeController(
IHttpClientFactory httpClientFactory,
OpenIddictClientService service)
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly OpenIddictClientService _service;
_httpClientFactory = httpClientFactory;
_service = service;
}
public HomeController(
IHttpClientFactory httpClientFactory,
OpenIddictClientService service)
{
_httpClientFactory = httpClientFactory;
_service = service;
}
[HttpGet, Route("~/")]
public async Task<ActionResult> Index(CancellationToken cancellationToken) => View(new IndexViewModel
{
Providers = from registration in await _service.GetClientRegistrationsAsync(cancellationToken)
where !string.IsNullOrEmpty(registration.ProviderName)
where !string.IsNullOrEmpty(registration.ProviderDisplayName)
select registration
});
[Authorize, HttpPost, Route("~/message"), ValidateAntiForgeryToken]
public async Task<ActionResult> GetMessage(CancellationToken cancellationToken)
{
var context = HttpContext.GetOwinContext();
[HttpGet, Route("~/")]
public async Task<ActionResult> Index(CancellationToken cancellationToken) => View(new IndexViewModel
var result = await context.Authentication.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationType);
var token = result.Properties.Dictionary[Tokens.BackchannelAccessToken];
using var client = _httpClientFactory.CreateClient();
using var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44349/api/message");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
using var response = await client.SendAsync(request, cancellationToken);
response.EnsureSuccessStatusCode();
return View("Index", new IndexViewModel
{
Message = await response.Content.ReadAsStringAsync(),
Providers = from registration in await _service.GetClientRegistrationsAsync(cancellationToken)
where !string.IsNullOrEmpty(registration.ProviderName)
where !string.IsNullOrEmpty(registration.ProviderDisplayName)
select registration
});
}
[Authorize, HttpPost, Route("~/message"), ValidateAntiForgeryToken]
public async Task<ActionResult> GetMessage(CancellationToken cancellationToken)
{
var context = HttpContext.GetOwinContext();
[Authorize, HttpPost, Route("~/refresh-token")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RefreshToken(CancellationToken cancellationToken)
{
var context = HttpContext.GetOwinContext();
var result = await context.Authentication.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationType);
var token = result.Properties.Dictionary[Tokens.BackchannelAccessToken];
var ticket = await context.Authentication.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationType);
if (!ticket.Properties.Dictionary.TryGetValue(Tokens.RefreshToken, out string token))
{
return new HttpStatusCodeResult(400);
}
using var client = _httpClientFactory.CreateClient();
var result = await _service.AuthenticateWithRefreshTokenAsync(new()
{
CancellationToken = cancellationToken,
RefreshToken = token,
RegistrationId = ticket.Identity.FindFirst(Claims.Private.RegistrationId)?.Value
});
using var request = new HttpRequestMessage(HttpMethod.Get, "https://localhost:44349/api/message");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
var properties = new AuthenticationProperties(ticket.Properties.Dictionary)
{
RedirectUri = null
};
using var response = await client.SendAsync(request, cancellationToken);
response.EnsureSuccessStatusCode();
properties.Dictionary[Tokens.BackchannelAccessToken] = result.AccessToken;
return View("Index", new IndexViewModel
{
Message = await response.Content.ReadAsStringAsync(),
Providers = from registration in await _service.GetClientRegistrationsAsync(cancellationToken)
where !string.IsNullOrEmpty(registration.ProviderName)
where !string.IsNullOrEmpty(registration.ProviderDisplayName)
select registration
});
if (!string.IsNullOrEmpty(result.RefreshToken))
{
properties.Dictionary[Tokens.RefreshToken] = result.RefreshToken;
}
[Authorize, HttpPost, Route("~/refresh-token")]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RefreshToken(CancellationToken cancellationToken)
context.Authentication.SignIn(properties, ticket.Identity);
return View("Index", new IndexViewModel
{
var context = HttpContext.GetOwinContext();
var ticket = await context.Authentication.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationType);
if (!ticket.Properties.Dictionary.TryGetValue(Tokens.RefreshToken, out string token))
{
return new HttpStatusCodeResult(400);
}
var result = await _service.AuthenticateWithRefreshTokenAsync(new()
{
CancellationToken = cancellationToken,
RefreshToken = token,
RegistrationId = ticket.Identity.FindFirst(Claims.Private.RegistrationId)?.Value
});
var properties = new AuthenticationProperties(ticket.Properties.Dictionary)
{
RedirectUri = null
};
properties.Dictionary[Tokens.BackchannelAccessToken] = result.AccessToken;
if (!string.IsNullOrEmpty(result.RefreshToken))
{
properties.Dictionary[Tokens.RefreshToken] = result.RefreshToken;
}
context.Authentication.SignIn(properties, ticket.Identity);
return View("Index", new IndexViewModel
{
Message = result.AccessToken,
Providers = from registration in await _service.GetClientRegistrationsAsync(cancellationToken)
where !string.IsNullOrEmpty(registration.ProviderName)
where !string.IsNullOrEmpty(registration.ProviderDisplayName)
select registration
});
}
Message = result.AccessToken,
Providers = from registration in await _service.GetClientRegistrationsAsync(cancellationToken)
where !string.IsNullOrEmpty(registration.ProviderName)
where !string.IsNullOrEmpty(registration.ProviderDisplayName)
select registration
});
}
}

17
sandbox/OpenIddict.Sandbox.AspNet.Client/Global.asax.cs

@ -2,16 +2,15 @@
using System.Web.Optimization;
using System.Web.Routing;
namespace OpenIddict.Sandbox.AspNet.Client
namespace OpenIddict.Sandbox.AspNet.Client;
public class MvcApplication : System.Web.HttpApplication
{
public class MvcApplication : System.Web.HttpApplication
protected void Application_Start()
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}

27
sandbox/OpenIddict.Sandbox.AspNet.Client/Models/ApplicationDbContext.cs

@ -1,23 +1,22 @@
using System.Data.Entity;
namespace OpenIddict.Sandbox.AspNetCore.Server.Models
namespace OpenIddict.Sandbox.AspNetCore.Server.Models;
public class ApplicationDbContext : DbContext
{
public class ApplicationDbContext : DbContext
public ApplicationDbContext()
: base("DefaultConnection")
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.UseOpenIddict();
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.UseOpenIddict();
base.OnModelCreating(modelBuilder);
base.OnModelCreating(modelBuilder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}

213
sandbox/OpenIddict.Sandbox.AspNet.Client/Startup.cs

@ -13,127 +13,126 @@ using OpenIddict.Sandbox.AspNetCore.Server.Models;
using Owin;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace OpenIddict.Sandbox.AspNet.Client
namespace OpenIddict.Sandbox.AspNet.Client;
public class Startup
{
public class Startup
public void Configuration(IAppBuilder app)
{
public void Configuration(IAppBuilder app)
{
var services = new ServiceCollection();
var services = new ServiceCollection();
services.AddOpenIddict()
services.AddOpenIddict()
// Register the OpenIddict core components.
.AddCore(options =>
{
// Configure OpenIddict to use the Entity Framework 6.x stores and models.
// Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities.
options.UseEntityFramework()
.UseDbContext<ApplicationDbContext>();
// Developers who prefer using MongoDB can remove the previous lines
// and configure OpenIddict to use the specified MongoDB database:
// options.UseMongoDb()
// .UseDatabase(new MongoClient().GetDatabase("openiddict"));
})
// Register the OpenIddict client components.
.AddClient(options =>
// Register the OpenIddict core components.
.AddCore(options =>
{
// Configure OpenIddict to use the Entity Framework 6.x stores and models.
// Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities.
options.UseEntityFramework()
.UseDbContext<ApplicationDbContext>();
// Developers who prefer using MongoDB can remove the previous lines
// and configure OpenIddict to use the specified MongoDB database:
// options.UseMongoDb()
// .UseDatabase(new MongoClient().GetDatabase("openiddict"));
})
// Register the OpenIddict client components.
.AddClient(options =>
{
// Note: this sample uses the authorization code and refresh token
// flows, but you can enable the other flows if necessary.
options.AllowAuthorizationCodeFlow()
.AllowRefreshTokenFlow();
// Register the signing and encryption credentials used to protect
// sensitive data like the state tokens produced by OpenIddict.
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
// Register the OWIN host and configure the OWIN-specific options.
options.UseOwin()
.EnableRedirectionEndpointPassthrough()
.EnablePostLogoutRedirectionEndpointPassthrough()
.SetCookieManager(new SystemWebCookieManager());
// Register the System.Net.Http integration and use the identity of the current
// assembly as a more specific user agent, which can be useful when dealing with
// providers that use the user agent as a way to throttle requests (e.g Reddit).
options.UseSystemNetHttp()
.SetProductInformation(typeof(Startup).Assembly);
// Add a client registration matching the client application definition in the server project.
options.AddRegistration(new OpenIddictClientRegistration
{
// Note: this sample uses the authorization code and refresh token
// flows, but you can enable the other flows if necessary.
options.AllowAuthorizationCodeFlow()
.AllowRefreshTokenFlow();
// Register the signing and encryption credentials used to protect
// sensitive data like the state tokens produced by OpenIddict.
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
// Register the OWIN host and configure the OWIN-specific options.
options.UseOwin()
.EnableRedirectionEndpointPassthrough()
.EnablePostLogoutRedirectionEndpointPassthrough()
.SetCookieManager(new SystemWebCookieManager());
// Register the System.Net.Http integration and use the identity of the current
// assembly as a more specific user agent, which can be useful when dealing with
// providers that use the user agent as a way to throttle requests (e.g Reddit).
options.UseSystemNetHttp()
.SetProductInformation(typeof(Startup).Assembly);
// Add a client registration matching the client application definition in the server project.
options.AddRegistration(new OpenIddictClientRegistration
{
Issuer = new Uri("https://localhost:44349/", UriKind.Absolute),
ProviderName = "Local",
ProviderDisplayName = "Local OIDC server",
ClientId = "mvc",
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654",
Scopes = { Scopes.Email, Scopes.Profile, Scopes.OfflineAccess, "demo_api" },
RedirectUri = new Uri("callback/login/local", UriKind.Relative),
PostLogoutRedirectUri = new Uri("callback/logout/local", UriKind.Relative)
});
// Register the Web providers integrations.
//
// Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint
// URI per provider, unless all the registered providers support returning a special "iss"
// parameter containing their URL as part of authorization responses. For more information,
// see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4.
options.UseWebProviders()
.AddGitHub(options =>
{
options.SetClientId("c4ade52327b01ddacff3")
.SetClientSecret("da6bed851b75e317bf6b2cb67013679d9467c122")
.SetRedirectUri("callback/login/github");
})
.AddGoogle(options =>
{
options.SetClientId("1016114395689-kgtgq2p6dj27d7v6e2kjkoj54dgrrckh.apps.googleusercontent.com")
.SetClientSecret("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")
.SetRedirectUri("callback/login/google")
.SetAccessType("offline")
.AddScopes(Scopes.Profile);
});
Issuer = new Uri("https://localhost:44349/", UriKind.Absolute),
ProviderName = "Local",
ProviderDisplayName = "Local OIDC server",
ClientId = "mvc",
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654",
Scopes = { Scopes.Email, Scopes.Profile, Scopes.OfflineAccess, "demo_api" },
RedirectUri = new Uri("callback/login/local", UriKind.Relative),
PostLogoutRedirectUri = new Uri("callback/logout/local", UriKind.Relative)
});
// Create a new Autofac container and import the OpenIddict services.
var builder = new ContainerBuilder();
builder.Populate(services);
// Register the Web providers integrations.
//
// Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint
// URI per provider, unless all the registered providers support returning a special "iss"
// parameter containing their URL as part of authorization responses. For more information,
// see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4.
options.UseWebProviders()
.AddGitHub(options =>
{
options.SetClientId("c4ade52327b01ddacff3")
.SetClientSecret("da6bed851b75e317bf6b2cb67013679d9467c122")
.SetRedirectUri("callback/login/github");
})
.AddGoogle(options =>
{
options.SetClientId("1016114395689-kgtgq2p6dj27d7v6e2kjkoj54dgrrckh.apps.googleusercontent.com")
.SetClientSecret("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")
.SetRedirectUri("callback/login/google")
.SetAccessType("offline")
.AddScopes(Scopes.Profile);
});
});
// Register the MVC controllers.
builder.RegisterControllers(typeof(Startup).Assembly);
// Create a new Autofac container and import the OpenIddict services.
var builder = new ContainerBuilder();
builder.Populate(services);
var container = builder.Build();
// Register the MVC controllers.
builder.RegisterControllers(typeof(Startup).Assembly);
// Register the Autofac scope injector middleware.
app.UseAutofacLifetimeScopeInjector(container);
var container = builder.Build();
// Register the cookie middleware responsible for storing the user sessions.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
ExpireTimeSpan = TimeSpan.FromMinutes(50),
SlidingExpiration = false
});
// Register the Autofac scope injector middleware.
app.UseAutofacLifetimeScopeInjector(container);
// Register the OpenIddict middleware.
app.UseMiddlewareFromContainer<OpenIddictClientOwinMiddleware>();
// Register the cookie middleware responsible for storing the user sessions.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
ExpireTimeSpan = TimeSpan.FromMinutes(50),
SlidingExpiration = false
});
// Configure ASP.NET MVC 5.2 to use Autofac when activating controller instances.
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// Register the OpenIddict middleware.
app.UseMiddlewareFromContainer<OpenIddictClientOwinMiddleware>();
// Create the database used by the OpenIddict client stack to store tokens.
// Note: in a real world application, this step should be part of a setup script.
Task.Run(async delegate
{
await using var scope = container.BeginLifetimeScope();
// Configure ASP.NET MVC 5.2 to use Autofac when activating controller instances.
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// Create the database used by the OpenIddict client stack to store tokens.
// Note: in a real world application, this step should be part of a setup script.
Task.Run(async delegate
{
await using var scope = container.BeginLifetimeScope();
var context = scope.Resolve<ApplicationDbContext>();
context.Database.CreateIfNotExists();
}).GetAwaiter().GetResult();
}
var context = scope.Resolve<ApplicationDbContext>();
context.Database.CreateIfNotExists();
}).GetAwaiter().GetResult();
}
}

37
sandbox/OpenIddict.Sandbox.AspNet.Server/App_Start/BundleConfig.cs

@ -1,29 +1,28 @@
using System.Web.Optimization;
namespace OpenIddict.Sandbox.AspNet.Server
namespace OpenIddict.Sandbox.AspNet.Server;
public class BundleConfig
{
public class BundleConfig
// Pour plus d'informations sur le regroupement, visitez https://go.microsoft.com/fwlink/?LinkId=301862
public static void RegisterBundles(BundleCollection bundles)
{
// Pour plus d'informations sur le regroupement, visitez https://go.microsoft.com/fwlink/?LinkId=301862
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jquery").Include(
"~/Scripts/jquery-{version}.js"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.validate*"));
bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
"~/Scripts/jquery.validate*"));
// Utilisez la version de développement de Modernizr pour développer et apprendre. Puis, lorsque vous êtes
// prêt pour la production, utilisez l'outil de génération à l'adresse https://modernizr.com pour sélectionner uniquement les tests dont vous avez besoin.
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));
// Utilisez la version de développement de Modernizr pour développer et apprendre. Puis, lorsque vous êtes
// prêt pour la production, utilisez l'outil de génération à l'adresse https://modernizr.com pour sélectionner uniquement les tests dont vous avez besoin.
bundles.Add(new ScriptBundle("~/bundles/modernizr").Include(
"~/Scripts/modernizr-*"));
bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
"~/Scripts/bootstrap.js"));
bundles.Add(new ScriptBundle("~/bundles/bootstrap").Include(
"~/Scripts/bootstrap.js"));
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/bootstrap.css",
"~/Content/site.css"));
}
bundles.Add(new StyleBundle("~/Content/css").Include(
"~/Content/bootstrap.css",
"~/Content/site.css"));
}
}

11
sandbox/OpenIddict.Sandbox.AspNet.Server/App_Start/FilterConfig.cs

@ -1,12 +1,11 @@
using System.Web.Mvc;
namespace OpenIddict.Sandbox.AspNet.Server
namespace OpenIddict.Sandbox.AspNet.Server;
public class FilterConfig
{
public class FilterConfig
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
filters.Add(new HandleErrorAttribute());
}
}

147
sandbox/OpenIddict.Sandbox.AspNet.Server/App_Start/IdentityConfig.cs

@ -8,98 +8,97 @@ using Microsoft.Owin;
using Microsoft.Owin.Security;
using OpenIddict.Sandbox.AspNet.Server.Models;
namespace OpenIddict.Sandbox.AspNet.Server
namespace OpenIddict.Sandbox.AspNet.Server;
public class EmailService : IIdentityMessageService
{
public class EmailService : IIdentityMessageService
public Task SendAsync(IdentityMessage message)
{
public Task SendAsync(IdentityMessage message)
{
// Connectez votre service e-mail ici pour envoyer un e-mail.
return Task.FromResult(0);
}
// Connectez votre service e-mail ici pour envoyer un e-mail.
return Task.FromResult(0);
}
}
public class SmsService : IIdentityMessageService
public class SmsService : IIdentityMessageService
{
public Task SendAsync(IdentityMessage message)
{
public Task SendAsync(IdentityMessage message)
{
// Connectez votre service SMS ici pour envoyer un message texte.
return Task.FromResult(0);
}
// Connectez votre service SMS ici pour envoyer un message texte.
return Task.FromResult(0);
}
}
// Configurer l'application que le gestionnaire des utilisateurs a utilisée dans cette application. UserManager est défini dans ASP.NET Identity et est utilisé par l'application.
public class ApplicationUserManager : UserManager<ApplicationUser>
// Configurer l'application que le gestionnaire des utilisateurs a utilisée dans cette application. UserManager est défini dans ASP.NET Identity et est utilisé par l'application.
public class ApplicationUserManager : UserManager<ApplicationUser>
{
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
public ApplicationUserManager(IUserStore<ApplicationUser> store)
: base(store)
{
}
}
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configurer la logique de validation pour les noms d'utilisateur
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
var manager = new ApplicationUserManager(new UserStore<ApplicationUser>(context.Get<ApplicationDbContext>()));
// Configurer la logique de validation pour les noms d'utilisateur
manager.UserValidator = new UserValidator<ApplicationUser>(manager)
{
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
AllowOnlyAlphanumericUserNames = false,
RequireUniqueEmail = true
};
// Configurer la logique de validation pour les mots de passe
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// Configurer la logique de validation pour les mots de passe
manager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
// Configurer les valeurs par défaut du verrouillage de l'utilisateur
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Configurer les valeurs par défaut du verrouillage de l'utilisateur
manager.UserLockoutEnabledByDefault = true;
manager.DefaultAccountLockoutTimeSpan = TimeSpan.FromMinutes(5);
manager.MaxFailedAccessAttemptsBeforeLockout = 5;
// Inscrire les fournisseurs d'authentification à 2 facteurs. Cette application utilise le téléphone et l'e-mail comme procédure de réception d'un code de vérification de l'utilisateur
// Vous pouvez écrire votre propre fournisseur et le connecter ici.
manager.RegisterTwoFactorProvider("Code téléphonique ", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Votre code de sécurité est {0}"
});
manager.RegisterTwoFactorProvider("Code d'e-mail", new EmailTokenProvider<ApplicationUser>
{
Subject = "Code de sécurité",
BodyFormat = "Votre code de sécurité est {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
// Inscrire les fournisseurs d'authentification à 2 facteurs. Cette application utilise le téléphone et l'e-mail comme procédure de réception d'un code de vérification de l'utilisateur
// Vous pouvez écrire votre propre fournisseur et le connecter ici.
manager.RegisterTwoFactorProvider("Code téléphonique ", new PhoneNumberTokenProvider<ApplicationUser>
{
MessageFormat = "Votre code de sécurité est {0}"
});
manager.RegisterTwoFactorProvider("Code d'e-mail", new EmailTokenProvider<ApplicationUser>
{
Subject = "Code de sécurité",
BodyFormat = "Votre code de sécurité est {0}"
});
manager.EmailService = new EmailService();
manager.SmsService = new SmsService();
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider =
new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
return manager;
}
}
// Configurer le gestionnaire de connexion d'application qui est utilisé dans cette application.
public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
// Configurer le gestionnaire de connexion d'application qui est utilisé dans cette application.
public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
{
public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
public ApplicationSignInManager(ApplicationUserManager userManager, IAuthenticationManager authenticationManager)
: base(userManager, authenticationManager)
{
}
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
public override Task<ClaimsIdentity> CreateUserIdentityAsync(ApplicationUser user)
{
return user.GenerateUserIdentityAsync((ApplicationUserManager)UserManager);
}
public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
{
return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
}
public static ApplicationSignInManager Create(IdentityFactoryOptions<ApplicationSignInManager> options, IOwinContext context)
{
return new ApplicationSignInManager(context.GetUserManager<ApplicationUserManager>(), context.Authentication);
}
}

23
sandbox/OpenIddict.Sandbox.AspNet.Server/App_Start/RouteConfig.cs

@ -1,21 +1,20 @@
using System.Web.Mvc;
using System.Web.Routing;
namespace OpenIddict.Sandbox.AspNet.Server
namespace OpenIddict.Sandbox.AspNet.Server;
public class RouteConfig
{
public class RouteConfig
public static void RegisterRoutes(RouteCollection routes)
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}

735
sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AccountController.cs

@ -7,476 +7,475 @@ using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using OpenIddict.Sandbox.AspNet.Server.Models;
namespace OpenIddict.Sandbox.AspNet.Server.Controllers
namespace OpenIddict.Sandbox.AspNet.Server.Controllers;
[Authorize]
public class AccountController : Controller
{
[Authorize]
public class AccountController : Controller
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
public AccountController()
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
}
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager )
{
UserManager = userManager;
SignInManager = signInManager;
}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager )
public ApplicationSignInManager SignInManager
{
get
{
UserManager = userManager;
SignInManager = signInManager;
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
}
public ApplicationSignInManager SignInManager
public ApplicationUserManager UserManager
{
get
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
public ApplicationUserManager UserManager
private set
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
_userManager = value;
}
}
//
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
{
ViewBag.ReturnUrl = returnUrl;
return View();
}
//
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(string returnUrl)
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
if (!ModelState.IsValid)
{
ViewBag.ReturnUrl = returnUrl;
return View();
return View(model);
}
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
// Ceci ne comptabilise pas les échecs de connexion pour le verrouillage du compte
// Pour que les échecs de mot de passe déclenchent le verrouillage du compte, utilisez shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
if (!ModelState.IsValid)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Tentative de connexion non valide.");
return View(model);
}
// Ceci ne comptabilise pas les échecs de connexion pour le verrouillage du compte
// Pour que les échecs de mot de passe déclenchent le verrouillage du compte, utilisez shouldLockout: true
var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Tentative de connexion non valide.");
return View(model);
}
}
}
//
// GET: /Account/VerifyCode
[AllowAnonymous]
public async Task<ActionResult> VerifyCode(string provider, string returnUrl, bool rememberMe)
//
// GET: /Account/VerifyCode
[AllowAnonymous]
public async Task<ActionResult> VerifyCode(string provider, string returnUrl, bool rememberMe)
{
// Nécessiter que l'utilisateur soit déjà connecté via un nom d'utilisateur/mot de passe ou une connexte externe
if (!await SignInManager.HasBeenVerifiedAsync())
{
// Nécessiter que l'utilisateur soit déjà connecté via un nom d'utilisateur/mot de passe ou une connexte externe
if (!await SignInManager.HasBeenVerifiedAsync())
{
return View("Error");
}
return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
return View("Error");
}
return View(new VerifyCodeViewModel { Provider = provider, ReturnUrl = returnUrl, RememberMe = rememberMe });
}
//
// POST: /Account/VerifyCode
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model)
//
// POST: /Account/VerifyCode
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> VerifyCode(VerifyCodeViewModel model)
{
if (!ModelState.IsValid)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Le code suivant protège des attaques par force brute contre les codes à 2 facteurs.
// Si un utilisateur entre des codes incorrects pendant un certain intervalle, le compte de cet utilisateur
// est alors verrouillé pendant une durée spécifiée.
// Vous pouvez configurer les paramètres de verrouillage du compte dans IdentityConfig
var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: model.RememberMe, rememberBrowser: model.RememberBrowser);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(model.ReturnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Code non valide.");
return View(model);
}
return View(model);
}
//
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
// Le code suivant protège des attaques par force brute contre les codes à 2 facteurs.
// Si un utilisateur entre des codes incorrects pendant un certain intervalle, le compte de cet utilisateur
// est alors verrouillé pendant une durée spécifiée.
// Vous pouvez configurer les paramètres de verrouillage du compte dans IdentityConfig
var result = await SignInManager.TwoFactorSignInAsync(model.Provider, model.Code, isPersistent: model.RememberMe, rememberBrowser: model.RememberBrowser);
switch (result)
{
return View();
case SignInStatus.Success:
return RedirectToLocal(model.ReturnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.Failure:
default:
ModelState.AddModelError("", "Code non valide.");
return View(model);
}
}
//
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
return View();
}
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
//
// POST: /Account/Register
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(RegisterViewModel model)
{
if (ModelState.IsValid)
{
if (ModelState.IsValid)
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
// Pour plus d'informations sur l'activation de la confirmation de compte et de la réinitialisation de mot de passe, visitez https://go.microsoft.com/fwlink/?LinkID=320771
// Envoyer un e-mail avec ce lien
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirmer votre compte", "Confirmez votre compte en cliquant <a href=\"" + callbackUrl + "\">ici</a>");
return RedirectToAction("Index", "Home");
}
AddErrors(result);
await SignInManager.SignInAsync(user, isPersistent:false, rememberBrowser:false);
// Pour plus d'informations sur l'activation de la confirmation de compte et de la réinitialisation de mot de passe, visitez https://go.microsoft.com/fwlink/?LinkID=320771
// Envoyer un e-mail avec ce lien
// string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
// var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Confirmer votre compte", "Confirmez votre compte en cliquant <a href=\"" + callbackUrl + "\">ici</a>");
return RedirectToAction("Index", "Home");
}
AddErrors(result);
}
// Si nous sommes arrivés là, quelque chose a échoué, réafficher le formulaire
return View(model);
// Si nous sommes arrivés là, quelque chose a échoué, réafficher le formulaire
return View(model);
}
//
// GET: /Account/ConfirmEmail
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
if (userId == null || code == null)
{
return View("Error");
}
var result = await UserManager.ConfirmEmailAsync(userId, code);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
//
// GET: /Account/ForgotPassword
[AllowAnonymous]
public ActionResult ForgotPassword()
{
return View();
}
//
// GET: /Account/ConfirmEmail
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
//
// POST: /Account/ForgotPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
if (userId == null || code == null)
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
{
return View("Error");
// Ne révélez pas que l'utilisateur n'existe pas ou qu'il n'est pas confirmé
return View("ForgotPasswordConfirmation");
}
var result = await UserManager.ConfirmEmailAsync(userId, code);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
//
// GET: /Account/ForgotPassword
[AllowAnonymous]
public ActionResult ForgotPassword()
{
return View();
// Pour plus d'informations sur l'activation de la confirmation de compte et de la réinitialisation de mot de passe, visitez https://go.microsoft.com/fwlink/?LinkID=320771
// Envoyer un e-mail avec ce lien
// string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
// var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Réinitialiser le mot de passe", "Réinitialisez votre mot de passe en cliquant <a href=\"" + callbackUrl + "\">ici</a>");
// return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
//
// POST: /Account/ForgotPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ForgotPassword(ForgotPasswordViewModel model)
{
if (ModelState.IsValid)
{
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null || !(await UserManager.IsEmailConfirmedAsync(user.Id)))
{
// Ne révélez pas que l'utilisateur n'existe pas ou qu'il n'est pas confirmé
return View("ForgotPasswordConfirmation");
}
// Si nous sommes arrivés là, quelque chose a échoué, réafficher le formulaire
return View(model);
}
// Pour plus d'informations sur l'activation de la confirmation de compte et de la réinitialisation de mot de passe, visitez https://go.microsoft.com/fwlink/?LinkID=320771
// Envoyer un e-mail avec ce lien
// string code = await UserManager.GeneratePasswordResetTokenAsync(user.Id);
// var callbackUrl = Url.Action("ResetPassword", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
// await UserManager.SendEmailAsync(user.Id, "Réinitialiser le mot de passe", "Réinitialisez votre mot de passe en cliquant <a href=\"" + callbackUrl + "\">ici</a>");
// return RedirectToAction("ForgotPasswordConfirmation", "Account");
}
//
// GET: /Account/ForgotPasswordConfirmation
[AllowAnonymous]
public ActionResult ForgotPasswordConfirmation()
{
return View();
}
// Si nous sommes arrivés là, quelque chose a échoué, réafficher le formulaire
//
// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code)
{
return code == null ? View("Error") : View();
}
//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
//
// GET: /Account/ForgotPasswordConfirmation
[AllowAnonymous]
public ActionResult ForgotPasswordConfirmation()
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null)
{
return View();
// Ne révélez pas que l'utilisateur n'existe pas
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
//
// GET: /Account/ResetPassword
[AllowAnonymous]
public ActionResult ResetPassword(string code)
var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded)
{
return code == null ? View("Error") : View();
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
AddErrors(result);
return View();
}
//
// GET: /Account/ResetPasswordConfirmation
[AllowAnonymous]
public ActionResult ResetPasswordConfirmation()
{
return View();
}
//
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
// Demander une redirection vers le fournisseur de connexion externe
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
//
// POST: /Account/ResetPassword
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ResetPassword(ResetPasswordViewModel model)
//
// GET: /Account/SendCode
[AllowAnonymous]
public async Task<ActionResult> SendCode(string returnUrl, bool rememberMe)
{
var userId = await SignInManager.GetVerifiedUserIdAsync();
if (userId == null)
{
if (!ModelState.IsValid)
{
return View(model);
}
var user = await UserManager.FindByNameAsync(model.Email);
if (user == null)
{
// Ne révélez pas que l'utilisateur n'existe pas
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
var result = await UserManager.ResetPasswordAsync(user.Id, model.Code, model.Password);
if (result.Succeeded)
{
return RedirectToAction("ResetPasswordConfirmation", "Account");
}
AddErrors(result);
return View();
return View("Error");
}
var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(userId);
var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
}
//
// GET: /Account/ResetPasswordConfirmation
[AllowAnonymous]
public ActionResult ResetPasswordConfirmation()
//
// POST: /Account/SendCode
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SendCode(SendCodeViewModel model)
{
if (!ModelState.IsValid)
{
return View();
}
//
// POST: /Account/ExternalLogin
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
// Générer le jeton et l'envoyer
if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider))
{
// Demander une redirection vers le fournisseur de connexion externe
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
return View("Error");
}
return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
}
//
// GET: /Account/SendCode
[AllowAnonymous]
public async Task<ActionResult> SendCode(string returnUrl, bool rememberMe)
//
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
var userId = await SignInManager.GetVerifiedUserIdAsync();
if (userId == null)
{
return View("Error");
}
var userFactors = await UserManager.GetValidTwoFactorProvidersAsync(userId);
var factorOptions = userFactors.Select(purpose => new SelectListItem { Text = purpose, Value = purpose }).ToList();
return View(new SendCodeViewModel { Providers = factorOptions, ReturnUrl = returnUrl, RememberMe = rememberMe });
return RedirectToAction("Login");
}
//
// POST: /Account/SendCode
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SendCode(SendCodeViewModel model)
// Connecter cet utilisateur à ce fournisseur de connexion externe si l'utilisateur possède déjà une connexion
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
switch (result)
{
if (!ModelState.IsValid)
{
return View();
}
// Générer le jeton et l'envoyer
if (!await SignInManager.SendTwoFactorCodeAsync(model.SelectedProvider))
{
return View("Error");
}
return RedirectToAction("VerifyCode", new { Provider = model.SelectedProvider, ReturnUrl = model.ReturnUrl, RememberMe = model.RememberMe });
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
case SignInStatus.Failure:
default:
// Si l'utilisateur n'a pas de compte, invitez alors celui-ci à créer un compte
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
}
}
//
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
//
// POST: /Account/ExternalLoginConfirmation
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
{
if (User.Identity.IsAuthenticated)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
// Connecter cet utilisateur à ce fournisseur de connexion externe si l'utilisateur possède déjà une connexion
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
switch (result)
{
case SignInStatus.Success:
return RedirectToLocal(returnUrl);
case SignInStatus.LockedOut:
return View("Lockout");
case SignInStatus.RequiresVerification:
return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = false });
case SignInStatus.Failure:
default:
// Si l'utilisateur n'a pas de compte, invitez alors celui-ci à créer un compte
ViewBag.ReturnUrl = returnUrl;
ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = loginInfo.Email });
}
return RedirectToAction("Index", "Manage");
}
//
// POST: /Account/ExternalLoginConfirmation
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
if (ModelState.IsValid)
{
if (User.Identity.IsAuthenticated)
// Obtenir des informations sur l'utilisateur à partir du fournisseur de connexion externe
var info = await AuthenticationManager.GetExternalLoginInfoAsync();
if (info == null)
{
return RedirectToAction("Index", "Manage");
return View("ExternalLoginFailure");
}
if (ModelState.IsValid)
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user);
if (result.Succeeded)
{
// Obtenir des informations sur l'utilisateur à partir du fournisseur de connexion externe
var info = await AuthenticationManager.GetExternalLoginInfoAsync();
if (info == null)
{
return View("ExternalLoginFailure");
}
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user);
result = await UserManager.AddLoginAsync(user.Id, info.Login);
if (result.Succeeded)
{
result = await UserManager.AddLoginAsync(user.Id, info.Login);
if (result.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
return RedirectToLocal(returnUrl);
}
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
return RedirectToLocal(returnUrl);
}
AddErrors(result);
}
ViewBag.ReturnUrl = returnUrl;
return View(model);
AddErrors(result);
}
//
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return RedirectToAction("Index", "Home");
}
ViewBag.ReturnUrl = returnUrl;
return View(model);
}
//
// GET: /Account/ExternalLoginFailure
[AllowAnonymous]
public ActionResult ExternalLoginFailure()
{
return View();
}
//
// POST: /Account/LogOff
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
return RedirectToAction("Index", "Home");
}
//
// GET: /Account/ExternalLoginFailure
[AllowAnonymous]
public ActionResult ExternalLoginFailure()
{
return View();
}
protected override void Dispose(bool disposing)
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (disposing)
if (_userManager != null)
{
if (_userManager != null)
{
_userManager.Dispose();
_userManager = null;
}
if (_signInManager != null)
{
_signInManager.Dispose();
_signInManager = null;
}
_userManager.Dispose();
_userManager = null;
}
base.Dispose(disposing);
if (_signInManager != null)
{
_signInManager.Dispose();
_signInManager = null;
}
}
#region Applications auxiliaires
// Utilisé(e) pour la protection XSRF lors de l'ajout de connexions externes
private const string XsrfKey = "XsrfId";
base.Dispose(disposing);
}
private IAuthenticationManager AuthenticationManager
#region Applications auxiliaires
// Utilisé(e) pour la protection XSRF lors de l'ajout de connexions externes
private const string XsrfKey = "XsrfId";
private IAuthenticationManager AuthenticationManager
{
get
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
return HttpContext.GetOwinContext().Authentication;
}
}
private void AddErrors(IdentityResult result)
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
}
ModelState.AddModelError("", error);
}
}
private ActionResult RedirectToLocal(string returnUrl)
private ActionResult RedirectToLocal(string returnUrl)
{
if (Url.IsLocalUrl(returnUrl))
{
if (Url.IsLocalUrl(returnUrl))
{
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
return Redirect(returnUrl);
}
return RedirectToAction("Index", "Home");
}
internal class ChallengeResult : HttpUnauthorizedResult
internal class ChallengeResult : HttpUnauthorizedResult
{
public ChallengeResult(string provider, string redirectUri)
: this(provider, redirectUri, null)
{
public ChallengeResult(string provider, string redirectUri)
: this(provider, redirectUri, null)
{
}
}
public ChallengeResult(string provider, string redirectUri, string userId)
{
LoginProvider = provider;
RedirectUri = redirectUri;
UserId = userId;
}
public ChallengeResult(string provider, string redirectUri, string userId)
{
LoginProvider = provider;
RedirectUri = redirectUri;
UserId = userId;
}
public string LoginProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public string LoginProvider { get; set; }
public string RedirectUri { get; set; }
public string UserId { get; set; }
public override void ExecuteResult(ControllerContext context)
public override void ExecuteResult(ControllerContext context)
{
var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
if (UserId != null)
{
var properties = new AuthenticationProperties { RedirectUri = RedirectUri };
if (UserId != null)
{
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
properties.Dictionary[XsrfKey] = UserId;
}
context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
}
#endregion
}
#endregion
}

139
sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthenticationController.cs

@ -9,87 +9,86 @@ using Microsoft.Owin.Security;
using OpenIddict.Client.Owin;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace OpenIddict.Sandbox.AspNet.Server.Controllers
namespace OpenIddict.Sandbox.AspNet.Server.Controllers;
public class AuthenticationController : Controller
{
public class AuthenticationController : Controller
// Note: this controller uses the same callback action for all providers
// but for users who prefer using a different action per provider,
// the following action can be split into separate actions.
[AcceptVerbs("GET", "POST"), Route("~/callback/login/{provider}")]
public async Task<ActionResult> LogInCallback()
{
// Note: this controller uses the same callback action for all providers
// but for users who prefer using a different action per provider,
// the following action can be split into separate actions.
[AcceptVerbs("GET", "POST"), Route("~/callback/login/{provider}")]
public async Task<ActionResult> LogInCallback()
{
var context = HttpContext.GetOwinContext();
var context = HttpContext.GetOwinContext();
// Retrieve the authorization data validated by OpenIddict as part of the callback handling.
var result = await context.Authentication.AuthenticateAsync(OpenIddictClientOwinDefaults.AuthenticationType);
// Retrieve the authorization data validated by OpenIddict as part of the callback handling.
var result = await context.Authentication.AuthenticateAsync(OpenIddictClientOwinDefaults.AuthenticationType);
// Multiple strategies exist to handle OAuth 2.0/OpenID Connect callbacks, each with their pros and cons:
//
// * Directly using the tokens to perform the necessary action(s) on behalf of the user, which is suitable
// for applications that don't need a long-term access to the user's resources or don't want to store
// access/refresh tokens in a database or in an authentication cookie (which has security implications).
// It is also suitable for applications that don't need to authenticate users but only need to perform
// action(s) on their behalf by making API calls using the access token returned by the remote server.
//
// * Storing the external claims/tokens in a database (and optionally keeping the essential claims in an
// authentication cookie so that cookie size limits are not hit). For the applications that use ASP.NET
// Core Identity, the UserManager.SetAuthenticationTokenAsync() API can be used to store external tokens.
//
// Note: in this case, it's recommended to use column encryption to protect the tokens in the database.
// Multiple strategies exist to handle OAuth 2.0/OpenID Connect callbacks, each with their pros and cons:
//
// * Directly using the tokens to perform the necessary action(s) on behalf of the user, which is suitable
// for applications that don't need a long-term access to the user's resources or don't want to store
// access/refresh tokens in a database or in an authentication cookie (which has security implications).
// It is also suitable for applications that don't need to authenticate users but only need to perform
// action(s) on their behalf by making API calls using the access token returned by the remote server.
//
// * Storing the external claims/tokens in a database (and optionally keeping the essential claims in an
// authentication cookie so that cookie size limits are not hit). For the applications that use ASP.NET
// Core Identity, the UserManager.SetAuthenticationTokenAsync() API can be used to store external tokens.
//
// Note: in this case, it's recommended to use column encryption to protect the tokens in the database.
//
// * Storing the external claims/tokens in an authentication cookie, which doesn't require having
// a user database but may be affected by the cookie size limits enforced by most browser vendors
// (e.g Safari for macOS and Safari for iOS/iPadOS enforce a per-domain 4KB limit for all cookies).
//
// Note: this is the approach used here, but the external claims are first filtered to only persist
// a few claims like the user identifier. The same approach is used to store the access/refresh tokens.
// Important: if the remote server doesn't support OpenID Connect and doesn't expose a userinfo endpoint,
// result.Principal.Identity will represent an unauthenticated identity and won't contain any user claim.
//
// Such identities cannot be used as-is to build an authentication cookie in ASP.NET (as the
// antiforgery stack requires at least a name claim to bind CSRF cookies to the user's identity) but
// the access/refresh tokens can be retrieved using result.Properties.GetTokens() to make API calls.
if (result.Identity is not ClaimsIdentity { IsAuthenticated: true })
{
throw new InvalidOperationException("The external authorization data cannot be used for authentication.");
}
// Build an identity based on the external claims and that will be used to create the authentication cookie.
//
// By default, all claims extracted during the authorization dance are available. The claims collection stored
// in the cookie can be filtered out or mapped to different names depending the claim name or its issuer.
var claims = result.Identity.Claims.Where(claim => claim.Type is ClaimTypes.NameIdentifier or ClaimTypes.Name
//
// * Storing the external claims/tokens in an authentication cookie, which doesn't require having
// a user database but may be affected by the cookie size limits enforced by most browser vendors
// (e.g Safari for macOS and Safari for iOS/iPadOS enforce a per-domain 4KB limit for all cookies).
// Preserve the registration details to be able to resolve them later.
//
// Note: this is the approach used here, but the external claims are first filtered to only persist
// a few claims like the user identifier. The same approach is used to store the access/refresh tokens.
// Important: if the remote server doesn't support OpenID Connect and doesn't expose a userinfo endpoint,
// result.Principal.Identity will represent an unauthenticated identity and won't contain any user claim.
or Claims.Private.RegistrationId or Claims.Private.ProviderName
//
// Such identities cannot be used as-is to build an authentication cookie in ASP.NET (as the
// antiforgery stack requires at least a name claim to bind CSRF cookies to the user's identity) but
// the access/refresh tokens can be retrieved using result.Properties.GetTokens() to make API calls.
if (result.Identity is not ClaimsIdentity { IsAuthenticated: true })
{
throw new InvalidOperationException("The external authorization data cannot be used for authentication.");
}
// Build an identity based on the external claims and that will be used to create the authentication cookie.
// The ASP.NET 4.x antiforgery module requires preserving the "identityprovider" claim.
//
// By default, all claims extracted during the authorization dance are available. The claims collection stored
// in the cookie can be filtered out or mapped to different names depending the claim name or its issuer.
var claims = result.Identity.Claims.Where(claim => claim.Type is ClaimTypes.NameIdentifier or ClaimTypes.Name
//
// Preserve the registration details to be able to resolve them later.
//
or Claims.Private.RegistrationId or Claims.Private.ProviderName
//
// The ASP.NET 4.x antiforgery module requires preserving the "identityprovider" claim.
//
or "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider");
or "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider");
// Note: when using external authentication providers with ASP.NET Identity,
// the user identity MUST be added to the external authentication cookie scheme.
var identity = new ClaimsIdentity(claims,
authenticationType: DefaultAuthenticationTypes.ExternalCookie,
nameType: ClaimTypes.Name,
roleType: ClaimTypes.Role);
// Note: when using external authentication providers with ASP.NET Identity,
// the user identity MUST be added to the external authentication cookie scheme.
var identity = new ClaimsIdentity(claims,
authenticationType: DefaultAuthenticationTypes.ExternalCookie,
nameType: ClaimTypes.Name,
roleType: ClaimTypes.Role);
// Build the authentication properties based on the properties that were added when the challenge was triggered.
var properties = new AuthenticationProperties(result.Properties.Dictionary
.Where(item => item.Key is
// Preserve the return URL.
".redirect" or
// Build the authentication properties based on the properties that were added when the challenge was triggered.
var properties = new AuthenticationProperties(result.Properties.Dictionary
.Where(item => item.Key is
// Preserve the return URL.
".redirect" or
// If needed, the tokens returned by the authorization server can be stored in the authentication cookie.
OpenIddictClientOwinConstants.Tokens.BackchannelAccessToken or
OpenIddictClientOwinConstants.Tokens.RefreshToken)
.ToDictionary(pair => pair.Key, pair => pair.Value));
// If needed, the tokens returned by the authorization server can be stored in the authentication cookie.
OpenIddictClientOwinConstants.Tokens.BackchannelAccessToken or
OpenIddictClientOwinConstants.Tokens.RefreshToken)
.ToDictionary(pair => pair.Key, pair => pair.Value));
context.Authentication.SignIn(properties, identity);
return Redirect(properties.RedirectUri ?? "/");
}
context.Authentication.SignIn(properties, identity);
return Redirect(properties.RedirectUri ?? "/");
}
}

681
sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/AuthorizationController.cs

@ -24,229 +24,106 @@ using OpenIddict.Server.Owin;
using Owin;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace OpenIddict.Sandbox.AspNet.Server.Controllers
namespace OpenIddict.Sandbox.AspNet.Server.Controllers;
public class AuthorizationController : Controller
{
public class AuthorizationController : Controller
private readonly IOpenIddictApplicationManager _applicationManager;
private readonly IOpenIddictAuthorizationManager _authorizationManager;
private readonly OpenIddictClientService _clientService;
private readonly IOpenIddictScopeManager _scopeManager;
public AuthorizationController(
IOpenIddictApplicationManager applicationManager,
IOpenIddictAuthorizationManager authorizationManager,
OpenIddictClientService clientService,
IOpenIddictScopeManager scopeManager)
{
private readonly IOpenIddictApplicationManager _applicationManager;
private readonly IOpenIddictAuthorizationManager _authorizationManager;
private readonly OpenIddictClientService _clientService;
private readonly IOpenIddictScopeManager _scopeManager;
public AuthorizationController(
IOpenIddictApplicationManager applicationManager,
IOpenIddictAuthorizationManager authorizationManager,
OpenIddictClientService clientService,
IOpenIddictScopeManager scopeManager)
{
_applicationManager = applicationManager;
_authorizationManager = authorizationManager;
_clientService = clientService;
_scopeManager = scopeManager;
}
_applicationManager = applicationManager;
_authorizationManager = authorizationManager;
_clientService = clientService;
_scopeManager = scopeManager;
}
[HttpGet, Route("~/connect/authorize")]
public async Task<ActionResult> Authorize()
[HttpGet, Route("~/connect/authorize")]
public async Task<ActionResult> Authorize()
{
var context = HttpContext.GetOwinContext();
var request = context.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
// Retrieve the user principal stored in the authentication cookie.
// If a max_age parameter was provided, ensure that the cookie is not too old.
// If the user principal can't be extracted or the cookie is too old, redirect the user to the login page.
var result = await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCookie);
if (result?.Identity == null || (request.MaxAge != null && result.Properties?.IssuedUtc != null &&
DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value)))
{
var context = HttpContext.GetOwinContext();
var request = context.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
// Retrieve the user principal stored in the authentication cookie.
// If a max_age parameter was provided, ensure that the cookie is not too old.
// If the user principal can't be extracted or the cookie is too old, redirect the user to the login page.
var result = await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCookie);
if (result?.Identity == null || (request.MaxAge != null && result.Properties?.IssuedUtc != null &&
DateTimeOffset.UtcNow - result.Properties.IssuedUtc > TimeSpan.FromSeconds(request.MaxAge.Value)))
// For applications that want to allow the client to select the external authentication provider
// that will be used to authenticate the user, the identity_provider parameter can be used for that.
if (!string.IsNullOrEmpty(request.IdentityProvider))
{
// For applications that want to allow the client to select the external authentication provider
// that will be used to authenticate the user, the identity_provider parameter can be used for that.
if (!string.IsNullOrEmpty(request.IdentityProvider))
var registrations = await _clientService.GetClientRegistrationsAsync();
if (!registrations.Any(registration => string.Equals(registration.ProviderName,
request.IdentityProvider, StringComparison.Ordinal)))
{
var registrations = await _clientService.GetClientRegistrationsAsync();
if (!registrations.Any(registration => string.Equals(registration.ProviderName,
request.IdentityProvider, StringComparison.Ordinal)))
{
context.Authentication.Challenge(
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerOwinConstants.Properties.Error] = Errors.InvalidRequest,
[OpenIddictServerOwinConstants.Properties.ErrorDescription] =
"The specified identity provider is not valid."
}));
return new EmptyResult();
}
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
// Note: when only one client is registered in the client options,
// specifying the issuer URI or the provider name is not required.
[OpenIddictClientOwinConstants.Properties.ProviderName] = request.IdentityProvider
})
{
// Once the callback is handled, redirect the user agent to the ASP.NET Identity
// page responsible for showing the external login confirmation form if necessary.
RedirectUri = Url.Action("ExternalLoginCallback", "Account", new
{
ReturnUrl = Request.RawUrl
})
};
// Ask the OpenIddict client middleware to redirect the user agent to the identity provider.
context.Authentication.Challenge(properties, OpenIddictClientOwinDefaults.AuthenticationType);
return new EmptyResult();
}
context.Authentication.Challenge(DefaultAuthenticationTypes.ApplicationCookie);
return new EmptyResult();
}
// Retrieve the profile of the logged in user.
var user = await context.GetUserManager<ApplicationUserManager>().FindByIdAsync(result.Identity.GetUserId()) ??
throw new InvalidOperationException("The user details cannot be retrieved.");
// Retrieve the application details from the database.
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ??
throw new InvalidOperationException("Details concerning the calling client application cannot be found.");
// Retrieve the permanent authorizations associated with the user and the calling client application.
var authorizations = await _authorizationManager.FindAsync(
subject: user.Id,
client : await _applicationManager.GetIdAsync(application),
status : Statuses.Valid,
type : AuthorizationTypes.Permanent,
scopes : request.GetScopes()).ToListAsync();
switch (await _applicationManager.GetConsentTypeAsync(application))
{
// If the consent is external (e.g when authorizations are granted by a sysadmin),
// immediately return an error if no authorization can be found in the database.
case ConsentTypes.External when authorizations.Count is 0:
context.Authentication.Challenge(
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerOwinConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerOwinConstants.Properties.Error] = Errors.InvalidRequest,
[OpenIddictServerOwinConstants.Properties.ErrorDescription] =
"The logged in user is not allowed to access this client application."
}));
return new EmptyResult();
// If the consent is implicit or if an authorization was found,
// return an authorization response without displaying the consent form.
case ConsentTypes.Implicit:
case ConsentTypes.External when authorizations.Count is not 0:
case ConsentTypes.Explicit when authorizations.Count is not 0 && !request.HasPrompt(Prompts.Consent):
// Create the claims-based identity that will be used by OpenIddict to generate tokens.
var identity = new ClaimsIdentity(
authenticationType: OpenIddictServerOwinDefaults.AuthenticationType,
nameType: Claims.Name,
roleType: Claims.Role);
// Add the claims that will be persisted in the tokens.
identity.SetClaim(Claims.Subject, user.Id)
.SetClaim(Claims.Email, user.Email)
.SetClaim(Claims.Name, user.UserName)
.SetClaim(Claims.PreferredUsername, user.UserName)
.SetClaims(Claims.Role, (await context.Get<ApplicationUserManager>().GetRolesAsync(user.Id)).ToImmutableArray());
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
// For that, simply restrict the list of scopes before calling SetScopes.
identity.SetScopes(request.GetScopes());
identity.SetResources(await _scopeManager.ListResourcesAsync(identity.GetScopes()).ToListAsync());
// Automatically create a permanent authorization to avoid requiring explicit consent
// for future authorization or token requests containing the same scopes.
var authorization = authorizations.LastOrDefault();
authorization ??= await _authorizationManager.CreateAsync(
identity: identity,
subject : user.Id,
client : await _applicationManager.GetIdAsync(application),
type : AuthorizationTypes.Permanent,
scopes : identity.GetScopes());
identity.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization));
identity.SetDestinations(GetDestinations);
context.Authentication.SignIn(new AuthenticationProperties(), identity);
return new EmptyResult();
// At this point, no authorization was found in the database and an error must be returned
// if the client application specified prompt=none in the authorization request.
case ConsentTypes.Explicit when request.HasPrompt(Prompts.None):
case ConsentTypes.Systematic when request.HasPrompt(Prompts.None):
context.Authentication.Challenge(
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerOwinConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerOwinConstants.Properties.ErrorDescription] =
"Interactive user consent is required."
"The specified identity provider is not valid."
}));
return new EmptyResult();
}
// In every other case, render the consent form.
default: return View(new AuthorizeViewModel
var properties = new AuthenticationProperties(new Dictionary<string, string>
{
ApplicationName = await _applicationManager.GetDisplayNameAsync(application),
Scope = request.Scope,
// Flow the request parameters so they can be received by the Accept/Reject actions.
Parameters = string.Equals(Request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase) ?
from name in Request.Form.AllKeys
from value in Request.Form.GetValues(name)
select new KeyValuePair<string, string>(name, value) :
from name in Request.QueryString.AllKeys
from value in Request.QueryString.GetValues(name)
select new KeyValuePair<string, string>(name, value)
});
// Note: when only one client is registered in the client options,
// specifying the issuer URI or the provider name is not required.
[OpenIddictClientOwinConstants.Properties.ProviderName] = request.IdentityProvider
})
{
// Once the callback is handled, redirect the user agent to the ASP.NET Identity
// page responsible for showing the external login confirmation form if necessary.
RedirectUri = Url.Action("ExternalLoginCallback", "Account", new
{
ReturnUrl = Request.RawUrl
})
};
// Ask the OpenIddict client middleware to redirect the user agent to the identity provider.
context.Authentication.Challenge(properties, OpenIddictClientOwinDefaults.AuthenticationType);
return new EmptyResult();
}
context.Authentication.Challenge(DefaultAuthenticationTypes.ApplicationCookie);
return new EmptyResult();
}
[Authorize, FormValueRequired("submit.Accept")]
[HttpPost, Route("~/connect/authorize"), ValidateAntiForgeryToken]
public async Task<ActionResult> Accept()
{
var context = HttpContext.GetOwinContext();
var request = context.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
// Retrieve the profile of the logged in user.
var user = await context.GetUserManager<ApplicationUserManager>().FindByIdAsync(result.Identity.GetUserId()) ??
throw new InvalidOperationException("The user details cannot be retrieved.");
// Retrieve the user principal stored in the authentication cookie.
var result = await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCookie);
if (result == null || result.Identity == null)
{
context.Authentication.Challenge(DefaultAuthenticationTypes.ApplicationCookie);
// Retrieve the application details from the database.
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ??
throw new InvalidOperationException("Details concerning the calling client application cannot be found.");
return new EmptyResult();
}
// Retrieve the permanent authorizations associated with the user and the calling client application.
var authorizations = await _authorizationManager.FindAsync(
subject: user.Id,
client : await _applicationManager.GetIdAsync(application),
status : Statuses.Valid,
type : AuthorizationTypes.Permanent,
scopes : request.GetScopes()).ToListAsync();
// Retrieve the profile of the logged in user.
var user = await context.GetUserManager<ApplicationUserManager>().FindByIdAsync(result.Identity.GetUserId()) ??
throw new InvalidOperationException("The user details cannot be retrieved.");
// Retrieve the application details from the database.
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ??
throw new InvalidOperationException("Details concerning the calling client application cannot be found.");
// Retrieve the permanent authorizations associated with the user and the calling client application.
var authorizations = await _authorizationManager.FindAsync(
subject: user.Id,
client : await _applicationManager.GetIdAsync(application),
status : Statuses.Valid,
type : AuthorizationTypes.Permanent,
scopes : request.GetScopes()).ToListAsync();
// Note: the same check is already made in the other action but is repeated
// here to ensure a malicious user can't abuse this POST-only endpoint and
// force it to return a valid response without the external authorization.
if (authorizations.Count is 0 && await _applicationManager.HasConsentTypeAsync(application, ConsentTypes.External))
{
switch (await _applicationManager.GetConsentTypeAsync(application))
{
// If the consent is external (e.g when authorizations are granted by a sysadmin),
// immediately return an error if no authorization can be found in the database.
case ConsentTypes.External when authorizations.Count is 0:
context.Authentication.Challenge(
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType,
properties: new AuthenticationProperties(new Dictionary<string, string>
@ -257,191 +134,313 @@ namespace OpenIddict.Sandbox.AspNet.Server.Controllers
}));
return new EmptyResult();
}
// Create the claims-based identity that will be used by OpenIddict to generate tokens.
var identity = new ClaimsIdentity(
authenticationType: OpenIddictServerOwinDefaults.AuthenticationType,
nameType: Claims.Name,
roleType: Claims.Role);
// If the consent is implicit or if an authorization was found,
// return an authorization response without displaying the consent form.
case ConsentTypes.Implicit:
case ConsentTypes.External when authorizations.Count is not 0:
case ConsentTypes.Explicit when authorizations.Count is not 0 && !request.HasPrompt(Prompts.Consent):
// Create the claims-based identity that will be used by OpenIddict to generate tokens.
var identity = new ClaimsIdentity(
authenticationType: OpenIddictServerOwinDefaults.AuthenticationType,
nameType: Claims.Name,
roleType: Claims.Role);
// Add the claims that will be persisted in the tokens.
identity.SetClaim(Claims.Subject, user.Id)
.SetClaim(Claims.Email, user.Email)
.SetClaim(Claims.Name, user.UserName)
.SetClaim(Claims.PreferredUsername, user.UserName)
.SetClaims(Claims.Role, (await context.Get<ApplicationUserManager>().GetRolesAsync(user.Id)).ToImmutableArray());
// Add the claims that will be persisted in the tokens.
identity.SetClaim(Claims.Subject, user.Id)
.SetClaim(Claims.Email, user.Email)
.SetClaim(Claims.Name, user.UserName)
.SetClaim(Claims.PreferredUsername, user.UserName)
.SetClaims(Claims.Role, (await context.Get<ApplicationUserManager>().GetRolesAsync(user.Id)).ToImmutableArray());
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
// For that, simply restrict the list of scopes before calling SetScopes.
identity.SetScopes(request.GetScopes());
identity.SetResources(await _scopeManager.ListResourcesAsync(identity.GetScopes()).ToListAsync());
// Automatically create a permanent authorization to avoid requiring explicit consent
// for future authorization or token requests containing the same scopes.
var authorization = authorizations.LastOrDefault();
authorization ??= await _authorizationManager.CreateAsync(
identity: identity,
subject : user.Id,
client : await _applicationManager.GetIdAsync(application),
type : AuthorizationTypes.Permanent,
scopes : identity.GetScopes());
identity.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization));
identity.SetDestinations(GetDestinations);
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
// For that, simply restrict the list of scopes before calling SetScopes.
identity.SetScopes(request.GetScopes());
identity.SetResources(await _scopeManager.ListResourcesAsync(identity.GetScopes()).ToListAsync());
// Automatically create a permanent authorization to avoid requiring explicit consent
// for future authorization or token requests containing the same scopes.
var authorization = authorizations.LastOrDefault();
authorization ??= await _authorizationManager.CreateAsync(
identity: identity,
subject : user.Id,
client : await _applicationManager.GetIdAsync(application),
type : AuthorizationTypes.Permanent,
scopes : identity.GetScopes());
identity.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization));
identity.SetDestinations(GetDestinations);
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
context.Authentication.SignIn(new AuthenticationProperties(), identity);
context.Authentication.SignIn(new AuthenticationProperties(), identity);
return new EmptyResult();
return new EmptyResult();
// At this point, no authorization was found in the database and an error must be returned
// if the client application specified prompt=none in the authorization request.
case ConsentTypes.Explicit when request.HasPrompt(Prompts.None):
case ConsentTypes.Systematic when request.HasPrompt(Prompts.None):
context.Authentication.Challenge(
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerOwinConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerOwinConstants.Properties.ErrorDescription] =
"Interactive user consent is required."
}));
return new EmptyResult();
// In every other case, render the consent form.
default: return View(new AuthorizeViewModel
{
ApplicationName = await _applicationManager.GetDisplayNameAsync(application),
Scope = request.Scope,
// Flow the request parameters so they can be received by the Accept/Reject actions.
Parameters = string.Equals(Request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase) ?
from name in Request.Form.AllKeys
from value in Request.Form.GetValues(name)
select new KeyValuePair<string, string>(name, value) :
from name in Request.QueryString.AllKeys
from value in Request.QueryString.GetValues(name)
select new KeyValuePair<string, string>(name, value)
});
}
}
[Authorize, FormValueRequired("submit.Deny")]
[HttpPost, Route("~/connect/authorize"), ValidateAntiForgeryToken]
// Notify OpenIddict that the authorization grant has been denied by the resource owner
// to redirect the user agent to the client application using the appropriate response_mode.
public ActionResult Deny()
[Authorize, FormValueRequired("submit.Accept")]
[HttpPost, Route("~/connect/authorize"), ValidateAntiForgeryToken]
public async Task<ActionResult> Accept()
{
var context = HttpContext.GetOwinContext();
var request = context.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
// Retrieve the user principal stored in the authentication cookie.
var result = await context.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ApplicationCookie);
if (result == null || result.Identity == null)
{
var context = HttpContext.GetOwinContext();
context.Authentication.Challenge(OpenIddictServerOwinDefaults.AuthenticationType);
context.Authentication.Challenge(DefaultAuthenticationTypes.ApplicationCookie);
return new EmptyResult();
}
[HttpGet, Route("~/connect/logout")]
public ActionResult Logout() => View(new AuthorizeViewModel
{
// Flow the request parameters so they can be received by the Accept/Reject actions.
Parameters = string.Equals(Request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase) ?
from name in Request.Form.AllKeys
from value in Request.Form.GetValues(name)
select new KeyValuePair<string, string>(name, value) :
from name in Request.QueryString.AllKeys
from value in Request.QueryString.GetValues(name)
select new KeyValuePair<string, string>(name, value)
});
[ActionName(nameof(Logout)), HttpPost, Route("~/connect/logout"), ValidateAntiForgeryToken]
public ActionResult LogoutPost()
// Retrieve the profile of the logged in user.
var user = await context.GetUserManager<ApplicationUserManager>().FindByIdAsync(result.Identity.GetUserId()) ??
throw new InvalidOperationException("The user details cannot be retrieved.");
// Retrieve the application details from the database.
var application = await _applicationManager.FindByClientIdAsync(request.ClientId) ??
throw new InvalidOperationException("Details concerning the calling client application cannot be found.");
// Retrieve the permanent authorizations associated with the user and the calling client application.
var authorizations = await _authorizationManager.FindAsync(
subject: user.Id,
client : await _applicationManager.GetIdAsync(application),
status : Statuses.Valid,
type : AuthorizationTypes.Permanent,
scopes : request.GetScopes()).ToListAsync();
// Note: the same check is already made in the other action but is repeated
// here to ensure a malicious user can't abuse this POST-only endpoint and
// force it to return a valid response without the external authorization.
if (authorizations.Count is 0 && await _applicationManager.HasConsentTypeAsync(application, ConsentTypes.External))
{
var context = HttpContext.GetOwinContext();
context.Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
context.Authentication.SignOut(
context.Authentication.Challenge(
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType,
properties: new AuthenticationProperties
properties: new AuthenticationProperties(new Dictionary<string, string>
{
RedirectUri = "/"
});
[OpenIddictServerOwinConstants.Properties.Error] = Errors.ConsentRequired,
[OpenIddictServerOwinConstants.Properties.ErrorDescription] =
"The logged in user is not allowed to access this client application."
}));
return new EmptyResult();
}
[HttpPost, Route("~/connect/token")]
public async Task<ActionResult> Exchange()
{
var context = HttpContext.GetOwinContext();
var request = context.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
// Create the claims-based identity that will be used by OpenIddict to generate tokens.
var identity = new ClaimsIdentity(
authenticationType: OpenIddictServerOwinDefaults.AuthenticationType,
nameType: Claims.Name,
roleType: Claims.Role);
// Add the claims that will be persisted in the tokens.
identity.SetClaim(Claims.Subject, user.Id)
.SetClaim(Claims.Email, user.Email)
.SetClaim(Claims.Name, user.UserName)
.SetClaim(Claims.PreferredUsername, user.UserName)
.SetClaims(Claims.Role, (await context.Get<ApplicationUserManager>().GetRolesAsync(user.Id)).ToImmutableArray());
// Note: in this sample, the granted scopes match the requested scope
// but you may want to allow the user to uncheck specific scopes.
// For that, simply restrict the list of scopes before calling SetScopes.
identity.SetScopes(request.GetScopes());
identity.SetResources(await _scopeManager.ListResourcesAsync(identity.GetScopes()).ToListAsync());
// Automatically create a permanent authorization to avoid requiring explicit consent
// for future authorization or token requests containing the same scopes.
var authorization = authorizations.LastOrDefault();
authorization ??= await _authorizationManager.CreateAsync(
identity: identity,
subject : user.Id,
client : await _applicationManager.GetIdAsync(application),
type : AuthorizationTypes.Permanent,
scopes : identity.GetScopes());
identity.SetAuthorizationId(await _authorizationManager.GetIdAsync(authorization));
identity.SetDestinations(GetDestinations);
// Returning a SignInResult will ask OpenIddict to issue the appropriate access/identity tokens.
context.Authentication.SignIn(new AuthenticationProperties(), identity);
return new EmptyResult();
}
if (request.IsAuthorizationCodeGrantType() || request.IsRefreshTokenGrantType())
{
// Retrieve the claims identity stored in the authorization code/device code/refresh token.
var result = await context.Authentication.AuthenticateAsync(OpenIddictServerOwinDefaults.AuthenticationType);
[Authorize, FormValueRequired("submit.Deny")]
[HttpPost, Route("~/connect/authorize"), ValidateAntiForgeryToken]
// Notify OpenIddict that the authorization grant has been denied by the resource owner
// to redirect the user agent to the client application using the appropriate response_mode.
public ActionResult Deny()
{
var context = HttpContext.GetOwinContext();
context.Authentication.Challenge(OpenIddictServerOwinDefaults.AuthenticationType);
// Retrieve the user profile corresponding to the authorization code/refresh token.
var user = await context.GetUserManager<ApplicationUserManager>().FindByIdAsync(result.Identity.GetClaim(Claims.Subject));
if (user == null)
{
context.Authentication.Challenge(
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerOwinConstants.Properties.Error] = Errors.InvalidGrant,
[OpenIddictServerOwinConstants.Properties.ErrorDescription] = "The token is no longer valid."
}));
return new EmptyResult();
}
return new EmptyResult();
}
[HttpGet, Route("~/connect/logout")]
public ActionResult Logout() => View(new AuthorizeViewModel
{
// Flow the request parameters so they can be received by the Accept/Reject actions.
Parameters = string.Equals(Request.HttpMethod, "POST", StringComparison.OrdinalIgnoreCase) ?
from name in Request.Form.AllKeys
from value in Request.Form.GetValues(name)
select new KeyValuePair<string, string>(name, value) :
from name in Request.QueryString.AllKeys
from value in Request.QueryString.GetValues(name)
select new KeyValuePair<string, string>(name, value)
});
[ActionName(nameof(Logout)), HttpPost, Route("~/connect/logout"), ValidateAntiForgeryToken]
public ActionResult LogoutPost()
{
var context = HttpContext.GetOwinContext();
context.Authentication.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
// Ensure the user is still allowed to sign in.
if (context.GetUserManager<ApplicationUserManager>().IsLockedOut(user.Id))
{
context.Authentication.Challenge(
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerOwinConstants.Properties.Error] = Errors.InvalidGrant,
[OpenIddictServerOwinConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in."
}));
context.Authentication.SignOut(
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType,
properties: new AuthenticationProperties
{
RedirectUri = "/"
});
return new EmptyResult();
}
return new EmptyResult();
}
var identity = new ClaimsIdentity(result.Identity.Claims,
authenticationType: OpenIddictServerOwinDefaults.AuthenticationType,
nameType: Claims.Name,
roleType: Claims.Role);
[HttpPost, Route("~/connect/token")]
public async Task<ActionResult> Exchange()
{
var context = HttpContext.GetOwinContext();
var request = context.GetOpenIddictServerRequest() ??
throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");
// Override the user claims present in the principal in case they
// changed since the authorization code/refresh token was issued.
identity.SetClaim(Claims.Subject, user.Id)
.SetClaim(Claims.Email, user.Email)
.SetClaim(Claims.Name, user.UserName)
.SetClaim(Claims.PreferredUsername, user.UserName)
.SetClaims(Claims.Role, (await context.Get<ApplicationUserManager>().GetRolesAsync(user.Id)).ToImmutableArray());
if (request.IsAuthorizationCodeGrantType() || request.IsRefreshTokenGrantType())
{
// Retrieve the claims identity stored in the authorization code/device code/refresh token.
var result = await context.Authentication.AuthenticateAsync(OpenIddictServerOwinDefaults.AuthenticationType);
identity.SetDestinations(GetDestinations);
// Retrieve the user profile corresponding to the authorization code/refresh token.
var user = await context.GetUserManager<ApplicationUserManager>().FindByIdAsync(result.Identity.GetClaim(Claims.Subject));
if (user == null)
{
context.Authentication.Challenge(
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerOwinConstants.Properties.Error] = Errors.InvalidGrant,
[OpenIddictServerOwinConstants.Properties.ErrorDescription] = "The token is no longer valid."
}));
// Ask OpenIddict to issue the appropriate access/identity tokens.
context.Authentication.SignIn(new AuthenticationProperties(), identity);
return new EmptyResult();
}
// Ensure the user is still allowed to sign in.
if (context.GetUserManager<ApplicationUserManager>().IsLockedOut(user.Id))
{
context.Authentication.Challenge(
authenticationTypes: OpenIddictServerOwinDefaults.AuthenticationType,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictServerOwinConstants.Properties.Error] = Errors.InvalidGrant,
[OpenIddictServerOwinConstants.Properties.ErrorDescription] = "The user is no longer allowed to sign in."
}));
return new EmptyResult();
}
throw new InvalidOperationException("The specified grant type is not supported.");
var identity = new ClaimsIdentity(result.Identity.Claims,
authenticationType: OpenIddictServerOwinDefaults.AuthenticationType,
nameType: Claims.Name,
roleType: Claims.Role);
// Override the user claims present in the principal in case they
// changed since the authorization code/refresh token was issued.
identity.SetClaim(Claims.Subject, user.Id)
.SetClaim(Claims.Email, user.Email)
.SetClaim(Claims.Name, user.UserName)
.SetClaim(Claims.PreferredUsername, user.UserName)
.SetClaims(Claims.Role, (await context.Get<ApplicationUserManager>().GetRolesAsync(user.Id)).ToImmutableArray());
identity.SetDestinations(GetDestinations);
// Ask OpenIddict to issue the appropriate access/identity tokens.
context.Authentication.SignIn(new AuthenticationProperties(), identity);
return new EmptyResult();
}
private static IEnumerable<string> GetDestinations(Claim claim)
{
// Note: by default, claims are NOT automatically included in the access and identity tokens.
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
// whether they should be included in access tokens, in identity tokens or in both.
throw new InvalidOperationException("The specified grant type is not supported.");
}
switch (claim.Type)
{
case Claims.Name or Claims.PreferredUsername:
yield return Destinations.AccessToken;
private static IEnumerable<string> GetDestinations(Claim claim)
{
// Note: by default, claims are NOT automatically included in the access and identity tokens.
// To allow OpenIddict to serialize them, you must attach them a destination, that specifies
// whether they should be included in access tokens, in identity tokens or in both.
if (claim.Subject.HasScope(Scopes.Profile))
yield return Destinations.IdentityToken;
switch (claim.Type)
{
case Claims.Name or Claims.PreferredUsername:
yield return Destinations.AccessToken;
yield break;
if (claim.Subject.HasScope(Scopes.Profile))
yield return Destinations.IdentityToken;
case Claims.Email:
yield return Destinations.AccessToken;
yield break;
if (claim.Subject.HasScope(Scopes.Email))
yield return Destinations.IdentityToken;
case Claims.Email:
yield return Destinations.AccessToken;
yield break;
if (claim.Subject.HasScope(Scopes.Email))
yield return Destinations.IdentityToken;
case Claims.Role:
yield return Destinations.AccessToken;
yield break;
if (claim.Subject.HasScope(Scopes.Roles))
yield return Destinations.IdentityToken;
case Claims.Role:
yield return Destinations.AccessToken;
yield break;
if (claim.Subject.HasScope(Scopes.Roles))
yield return Destinations.IdentityToken;
// Never include the security stamp in the access and identity tokens, as it's a secret value.
case "AspNet.Identity.SecurityStamp": yield break;
yield break;
default:
yield return Destinations.AccessToken;
yield break;
}
// Never include the security stamp in the access and identity tokens, as it's a secret value.
case "AspNet.Identity.SecurityStamp": yield break;
default:
yield return Destinations.AccessToken;
yield break;
}
}
}

31
sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/HomeController.cs

@ -1,26 +1,25 @@
using System.Web.Mvc;
namespace OpenIddict.Sandbox.AspNet.Server.Controllers
namespace OpenIddict.Sandbox.AspNet.Server.Controllers;
public class HomeController : Controller
{
public class HomeController : Controller
public ActionResult Index()
{
public ActionResult Index()
{
return View();
}
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
public ActionResult About()
{
ViewBag.Message = "Your application description page.";
return View();
}
return View();
}
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
public ActionResult Contact()
{
ViewBag.Message = "Your contact page.";
return View();
}
return View();
}
}

567
sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/ManageController.cs

@ -7,229 +7,258 @@ using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin.Security;
using OpenIddict.Sandbox.AspNet.Server.Models;
namespace OpenIddict.Sandbox.AspNet.Server.Controllers
namespace OpenIddict.Sandbox.AspNet.Server.Controllers;
[Authorize]
public class ManageController : Controller
{
[Authorize]
public class ManageController : Controller
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
public ManageController()
{
}
public ManageController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
UserManager = userManager;
SignInManager = signInManager;
}
public ManageController()
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
}
public ManageController(ApplicationUserManager userManager, ApplicationSignInManager signInManager)
public ApplicationUserManager UserManager
{
get
{
UserManager = userManager;
SignInManager = signInManager;
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
public ApplicationSignInManager SignInManager
private set
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
_userManager = value;
}
}
public ApplicationUserManager UserManager
//
// GET: /Manage/Index
public async Task<ActionResult> Index(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Votre mot de passe a été changé."
: message == ManageMessageId.SetPasswordSuccess ? "Votre mot de passe a été défini."
: message == ManageMessageId.SetTwoFactorSuccess ? "Votre fournisseur d'authentification à 2 facteurs a été défini."
: message == ManageMessageId.Error ? "Une erreur s'est produite."
: message == ManageMessageId.AddPhoneSuccess ? "Votre numéro de téléphone a été ajouté."
: message == ManageMessageId.RemovePhoneSuccess ? "Votre numéro de téléphone a été supprimé."
: "";
var userId = User.Identity.GetUserId();
var model = new IndexViewModel
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
HasPassword = HasPassword(),
PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
Logins = await UserManager.GetLoginsAsync(userId),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(userId)
};
return View(model);
}
//
// POST: /Manage/RemoveLogin
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RemoveLogin(string loginProvider, string providerKey)
{
ManageMessageId? message;
var result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey));
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
_userManager = value;
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
message = ManageMessageId.RemoveLoginSuccess;
}
else
{
message = ManageMessageId.Error;
}
return RedirectToAction("ManageLogins", new { Message = message });
}
//
// GET: /Manage/Index
public async Task<ActionResult> Index(ManageMessageId? message)
//
// GET: /Manage/AddPhoneNumber
public ActionResult AddPhoneNumber()
{
return View();
}
//
// POST: /Manage/AddPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
{
if (!ModelState.IsValid)
{
ViewBag.StatusMessage =
message == ManageMessageId.ChangePasswordSuccess ? "Votre mot de passe a été changé."
: message == ManageMessageId.SetPasswordSuccess ? "Votre mot de passe a été défini."
: message == ManageMessageId.SetTwoFactorSuccess ? "Votre fournisseur d'authentification à 2 facteurs a été défini."
: message == ManageMessageId.Error ? "Une erreur s'est produite."
: message == ManageMessageId.AddPhoneSuccess ? "Votre numéro de téléphone a été ajouté."
: message == ManageMessageId.RemovePhoneSuccess ? "Votre numéro de téléphone a été supprimé."
: "";
var userId = User.Identity.GetUserId();
var model = new IndexViewModel
{
HasPassword = HasPassword(),
PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
Logins = await UserManager.GetLoginsAsync(userId),
BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(userId)
};
return View(model);
}
//
// POST: /Manage/RemoveLogin
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RemoveLogin(string loginProvider, string providerKey)
// Générer le jeton et l'envoyer
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(User.Identity.GetUserId(), model.Number);
if (UserManager.SmsService != null)
{
ManageMessageId? message;
var result = await UserManager.RemoveLoginAsync(User.Identity.GetUserId(), new UserLoginInfo(loginProvider, providerKey));
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
message = ManageMessageId.RemoveLoginSuccess;
}
else
var message = new IdentityMessage
{
message = ManageMessageId.Error;
}
return RedirectToAction("ManageLogins", new { Message = message });
Destination = model.Number,
Body = "Votre code de sécurité est : " + code
};
await UserManager.SmsService.SendAsync(message);
}
return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
}
//
// GET: /Manage/AddPhoneNumber
public ActionResult AddPhoneNumber()
//
// POST: /Manage/EnableTwoFactorAuthentication
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> EnableTwoFactorAuthentication()
{
await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), true);
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
return View();
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", "Manage");
}
//
// POST: /Manage/AddPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> AddPhoneNumber(AddPhoneNumberViewModel model)
//
// POST: /Manage/DisableTwoFactorAuthentication
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> DisableTwoFactorAuthentication()
{
await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), false);
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
if (!ModelState.IsValid)
{
return View(model);
}
// Générer le jeton et l'envoyer
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(User.Identity.GetUserId(), model.Number);
if (UserManager.SmsService != null)
{
var message = new IdentityMessage
{
Destination = model.Number,
Body = "Votre code de sécurité est : " + code
};
await UserManager.SmsService.SendAsync(message);
}
return RedirectToAction("VerifyPhoneNumber", new { PhoneNumber = model.Number });
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", "Manage");
}
//
// GET: /Manage/VerifyPhoneNumber
public async Task<ActionResult> VerifyPhoneNumber(string phoneNumber)
{
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(User.Identity.GetUserId(), phoneNumber);
// Envoyer un SMS via le fournisseur SMS afin de vérifier le numéro de téléphone
return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber });
}
//
// POST: /Manage/EnableTwoFactorAuthentication
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> EnableTwoFactorAuthentication()
//
// POST: /Manage/VerifyPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model)
{
if (!ModelState.IsValid)
{
await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), true);
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", "Manage");
return View(model);
}
//
// POST: /Manage/DisableTwoFactorAuthentication
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> DisableTwoFactorAuthentication()
var result = await UserManager.ChangePhoneNumberAsync(User.Identity.GetUserId(), model.PhoneNumber, model.Code);
if (result.Succeeded)
{
await UserManager.SetTwoFactorEnabledAsync(User.Identity.GetUserId(), false);
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", "Manage");
return RedirectToAction("Index", new { Message = ManageMessageId.AddPhoneSuccess });
}
// Si nous sommes arrivés là, quelque chose a échoué, réafficher le formulaire
ModelState.AddModelError("", "La vérification du téléphone a échoué");
return View(model);
}
//
// GET: /Manage/VerifyPhoneNumber
public async Task<ActionResult> VerifyPhoneNumber(string phoneNumber)
//
// POST: /Manage/RemovePhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RemovePhoneNumber()
{
var result = await UserManager.SetPhoneNumberAsync(User.Identity.GetUserId(), null);
if (!result.Succeeded)
{
return RedirectToAction("Index", new { Message = ManageMessageId.Error });
}
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
var code = await UserManager.GenerateChangePhoneNumberTokenAsync(User.Identity.GetUserId(), phoneNumber);
// Envoyer un SMS via le fournisseur SMS afin de vérifier le numéro de téléphone
return phoneNumber == null ? View("Error") : View(new VerifyPhoneNumberViewModel { PhoneNumber = phoneNumber });
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.RemovePhoneSuccess });
}
//
// POST: /Manage/VerifyPhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> VerifyPhoneNumber(VerifyPhoneNumberViewModel model)
//
// GET: /Manage/ChangePassword
public ActionResult ChangePassword()
{
return View();
}
//
// POST: /Manage/ChangePassword
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ChangePassword(ChangePasswordViewModel model)
{
if (!ModelState.IsValid)
{
if (!ModelState.IsValid)
{
return View(model);
}
var result = await UserManager.ChangePhoneNumberAsync(User.Identity.GetUserId(), model.PhoneNumber, model.Code);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.AddPhoneSuccess });
}
// Si nous sommes arrivés là, quelque chose a échoué, réafficher le formulaire
ModelState.AddModelError("", "La vérification du téléphone a échoué");
return View(model);
}
//
// POST: /Manage/RemovePhoneNumber
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> RemovePhoneNumber()
var result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
if (result.Succeeded)
{
var result = await UserManager.SetPhoneNumberAsync(User.Identity.GetUserId(), null);
if (!result.Succeeded)
{
return RedirectToAction("Index", new { Message = ManageMessageId.Error });
}
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.RemovePhoneSuccess });
return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess });
}
AddErrors(result);
return View(model);
}
//
// GET: /Manage/ChangePassword
public ActionResult ChangePassword()
{
return View();
}
//
// GET: /Manage/SetPassword
public ActionResult SetPassword()
{
return View();
}
//
// POST: /Manage/ChangePassword
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ChangePassword(ChangePasswordViewModel model)
//
// POST: /Manage/SetPassword
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SetPassword(SetPasswordViewModel model)
{
if (ModelState.IsValid)
{
if (!ModelState.IsValid)
{
return View(model);
}
var result = await UserManager.ChangePasswordAsync(User.Identity.GetUserId(), model.OldPassword, model.NewPassword);
var result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
@ -237,152 +266,122 @@ namespace OpenIddict.Sandbox.AspNet.Server.Controllers
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess });
return RedirectToAction("Index", new { Message = ManageMessageId.SetPasswordSuccess });
}
AddErrors(result);
return View(model);
}
//
// GET: /Manage/SetPassword
public ActionResult SetPassword()
{
return View();
}
// Si nous sommes arrivés là, quelque chose a échoué, réafficher le formulaire
return View(model);
}
//
// POST: /Manage/SetPassword
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> SetPassword(SetPasswordViewModel model)
//
// GET: /Manage/ManageLogins
public async Task<ActionResult> ManageLogins(ManageMessageId? message)
{
ViewBag.StatusMessage =
message == ManageMessageId.RemoveLoginSuccess ? "La connexion externe a été supprimée."
: message == ManageMessageId.Error ? "Une erreur s'est produite."
: "";
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user == null)
{
if (ModelState.IsValid)
{
var result = await UserManager.AddPasswordAsync(User.Identity.GetUserId(), model.NewPassword);
if (result.Succeeded)
{
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user != null)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
}
return RedirectToAction("Index", new { Message = ManageMessageId.SetPasswordSuccess });
}
AddErrors(result);
}
// Si nous sommes arrivés là, quelque chose a échoué, réafficher le formulaire
return View(model);
return View("Error");
}
//
// GET: /Manage/ManageLogins
public async Task<ActionResult> ManageLogins(ManageMessageId? message)
var userLogins = await UserManager.GetLoginsAsync(User.Identity.GetUserId());
var otherLogins = AuthenticationManager.GetExternalAuthenticationTypes().Where(auth => userLogins.All(ul => auth.AuthenticationType != ul.LoginProvider)).ToList();
ViewBag.ShowRemoveButton = user.PasswordHash != null || userLogins.Count > 1;
return View(new ManageLoginsViewModel
{
ViewBag.StatusMessage =
message == ManageMessageId.RemoveLoginSuccess ? "La connexion externe a été supprimée."
: message == ManageMessageId.Error ? "Une erreur s'est produite."
: "";
var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
if (user == null)
{
return View("Error");
}
var userLogins = await UserManager.GetLoginsAsync(User.Identity.GetUserId());
var otherLogins = AuthenticationManager.GetExternalAuthenticationTypes().Where(auth => userLogins.All(ul => auth.AuthenticationType != ul.LoginProvider)).ToList();
ViewBag.ShowRemoveButton = user.PasswordHash != null || userLogins.Count > 1;
return View(new ManageLoginsViewModel
{
CurrentLogins = userLogins,
OtherLogins = otherLogins
});
}
CurrentLogins = userLogins,
OtherLogins = otherLogins
});
}
//
// POST: /Manage/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LinkLogin(string provider)
{
// Demander une redirection vers le fournisseur de connexion externe afin de lier une connexion pour l'utilisateur actuel
return new AccountController.ChallengeResult(provider, Url.Action("LinkLoginCallback", "Manage"), User.Identity.GetUserId());
}
//
// POST: /Manage/LinkLogin
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LinkLogin(string provider)
{
// Demander une redirection vers le fournisseur de connexion externe afin de lier une connexion pour l'utilisateur actuel
return new AccountController.ChallengeResult(provider, Url.Action("LinkLoginCallback", "Manage"), User.Identity.GetUserId());
}
//
// GET: /Manage/LinkLoginCallback
public async Task<ActionResult> LinkLoginCallback()
//
// GET: /Manage/LinkLoginCallback
public async Task<ActionResult> LinkLoginCallback()
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
if (loginInfo == null)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
if (loginInfo == null)
{
return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
return result.Succeeded ? RedirectToAction("ManageLogins") : RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
return RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
return result.Succeeded ? RedirectToAction("ManageLogins") : RedirectToAction("ManageLogins", new { Message = ManageMessageId.Error });
}
protected override void Dispose(bool disposing)
protected override void Dispose(bool disposing)
{
if (disposing && _userManager != null)
{
if (disposing && _userManager != null)
{
_userManager.Dispose();
_userManager = null;
}
base.Dispose(disposing);
_userManager.Dispose();
_userManager = null;
}
base.Dispose(disposing);
}
#region Applications d'assistance
// Utilisé(e) pour la protection XSRF lors de l'ajout de connexions externes
private const string XsrfKey = "XsrfId";
// Utilisé(e) pour la protection XSRF lors de l'ajout de connexions externes
private const string XsrfKey = "XsrfId";
private IAuthenticationManager AuthenticationManager
private IAuthenticationManager AuthenticationManager
{
get
{
get
{
return HttpContext.GetOwinContext().Authentication;
}
return HttpContext.GetOwinContext().Authentication;
}
}
private void AddErrors(IdentityResult result)
private void AddErrors(IdentityResult result)
{
foreach (var error in result.Errors)
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error);
}
ModelState.AddModelError("", error);
}
}
private bool HasPassword()
private bool HasPassword()
{
var user = UserManager.FindById(User.Identity.GetUserId());
if (user != null)
{
var user = UserManager.FindById(User.Identity.GetUserId());
if (user != null)
{
return user.PasswordHash != null;
}
return false;
return user.PasswordHash != null;
}
return false;
}
private bool HasPhoneNumber()
private bool HasPhoneNumber()
{
var user = UserManager.FindById(User.Identity.GetUserId());
if (user != null)
{
var user = UserManager.FindById(User.Identity.GetUserId());
if (user != null)
{
return user.PhoneNumber != null;
}
return false;
return user.PhoneNumber != null;
}
return false;
}
public enum ManageMessageId
{
AddPhoneSuccess,
ChangePasswordSuccess,
SetTwoFactorSuccess,
SetPasswordSuccess,
RemoveLoginSuccess,
RemovePhoneSuccess,
Error
}
public enum ManageMessageId
{
AddPhoneSuccess,
ChangePasswordSuccess,
SetTwoFactorSuccess,
SetPasswordSuccess,
RemoveLoginSuccess,
RemovePhoneSuccess,
Error
}
#endregion
}
}

83
sandbox/OpenIddict.Sandbox.AspNet.Server/Controllers/ResourceController.cs

@ -10,52 +10,51 @@ using OpenIddict.Abstractions;
using OpenIddict.Validation.Owin;
using static OpenIddict.Abstractions.OpenIddictConstants;
namespace OpenIddict.Sandbox.AspNet.Server.Controllers
namespace OpenIddict.Sandbox.AspNet.Server.Controllers;
[HostAuthentication(OpenIddictValidationOwinDefaults.AuthenticationType)]
public class ResourceController : ApiController
{
[HostAuthentication(OpenIddictValidationOwinDefaults.AuthenticationType)]
public class ResourceController : ApiController
[Authorize, HttpGet, Route("~/api/message")]
public async Task<IHttpActionResult> GetMessage()
{
[Authorize, HttpGet, Route("~/api/message")]
public async Task<IHttpActionResult> GetMessage()
{
var context = Request.GetOwinContext();
var context = Request.GetOwinContext();
// This demo action requires that the client application be granted the "demo_api" scope.
// If it was not granted, a detailed error is returned to the client application to inform it
// that the authorization process must be restarted with the specified scope to access this API.
if (User is not ClaimsPrincipal principal || !principal.HasScope("demo_api"))
{
context.Authentication.Challenge(
authenticationTypes: OpenIddictValidationOwinDefaults.AuthenticationType,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictValidationOwinConstants.Properties.Scope] = "demo_api",
[OpenIddictValidationOwinConstants.Properties.Error] = Errors.InsufficientScope,
[OpenIddictValidationOwinConstants.Properties.ErrorDescription] =
"The 'demo_api' scope is required to perform this action."
}));
return Unauthorized();
}
var user = await context.GetUserManager<ApplicationUserManager>().FindByIdAsync(
((ClaimsPrincipal) User).FindFirst(Claims.Subject).Value);
if (user is null)
{
context.Authentication.Challenge(
authenticationTypes: OpenIddictValidationOwinDefaults.AuthenticationType,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictValidationOwinConstants.Properties.Error] = Errors.InvalidToken,
[OpenIddictValidationOwinConstants.Properties.ErrorDescription] =
"The specified access token is bound to an account that no longer exists."
}));
return Unauthorized();
}
// This demo action requires that the client application be granted the "demo_api" scope.
// If it was not granted, a detailed error is returned to the client application to inform it
// that the authorization process must be restarted with the specified scope to access this API.
if (User is not ClaimsPrincipal principal || !principal.HasScope("demo_api"))
{
context.Authentication.Challenge(
authenticationTypes: OpenIddictValidationOwinDefaults.AuthenticationType,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictValidationOwinConstants.Properties.Scope] = "demo_api",
[OpenIddictValidationOwinConstants.Properties.Error] = Errors.InsufficientScope,
[OpenIddictValidationOwinConstants.Properties.ErrorDescription] =
"The 'demo_api' scope is required to perform this action."
}));
return Unauthorized();
}
return ResponseMessage(new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent($"{user.UserName} has been successfully authenticated.")
});
var user = await context.GetUserManager<ApplicationUserManager>().FindByIdAsync(
((ClaimsPrincipal) User).FindFirst(Claims.Subject).Value);
if (user is null)
{
context.Authentication.Challenge(
authenticationTypes: OpenIddictValidationOwinDefaults.AuthenticationType,
properties: new AuthenticationProperties(new Dictionary<string, string>
{
[OpenIddictValidationOwinConstants.Properties.Error] = Errors.InvalidToken,
[OpenIddictValidationOwinConstants.Properties.ErrorDescription] =
"The specified access token is bound to an account that no longer exists."
}));
return Unauthorized();
}
return ResponseMessage(new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent($"{user.UserName} has been successfully authenticated.")
});
}
}

17
sandbox/OpenIddict.Sandbox.AspNet.Server/Global.asax.cs

@ -3,16 +3,15 @@ using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
namespace OpenIddict.Sandbox.AspNet.Server
namespace OpenIddict.Sandbox.AspNet.Server;
public class MvcApplication : HttpApplication
{
public class MvcApplication : HttpApplication
protected void Application_Start()
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}

33
sandbox/OpenIddict.Sandbox.AspNet.Server/Helpers/AsyncEnumerableExtensions.cs

@ -2,30 +2,29 @@ using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace OpenIddict.Sandbox.AspNet.Server.Helpers
namespace OpenIddict.Sandbox.AspNet.Server.Helpers;
public static class AsyncEnumerableExtensions
{
public static class AsyncEnumerableExtensions
public static Task<List<T>> ToListAsync<T>(this IAsyncEnumerable<T> source)
{
public static Task<List<T>> ToListAsync<T>(this IAsyncEnumerable<T> source)
if (source == null)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
throw new ArgumentNullException(nameof(source));
}
return ExecuteAsync();
return ExecuteAsync();
async Task<List<T>> ExecuteAsync()
{
var list = new List<T>();
await foreach (var element in source)
{
list.Add(element);
}
async Task<List<T>> ExecuteAsync()
{
var list = new List<T>();
return list;
await foreach (var element in source)
{
list.Add(element);
}
return list;
}
}
}

47
sandbox/OpenIddict.Sandbox.AspNet.Server/Helpers/FormValueRequiredAttribute.cs

@ -2,38 +2,37 @@ using System;
using System.Reflection;
using System.Web.Mvc;
namespace OpenIddict.Sandbox.AspNet.Server.Helpers
namespace OpenIddict.Sandbox.AspNet.Server.Helpers;
public sealed class FormValueRequiredAttribute : ActionMethodSelectorAttribute
{
public sealed class FormValueRequiredAttribute : ActionMethodSelectorAttribute
private readonly string _name;
public FormValueRequiredAttribute(string name)
{
private readonly string _name;
_name = name;
}
public FormValueRequiredAttribute(string name)
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
if (string.Equals(controllerContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase) ||
string.Equals(controllerContext.HttpContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase) ||
string.Equals(controllerContext.HttpContext.Request.HttpMethod, "DELETE", StringComparison.OrdinalIgnoreCase) ||
string.Equals(controllerContext.HttpContext.Request.HttpMethod, "TRACE", StringComparison.OrdinalIgnoreCase))
{
_name = name;
return false;
}
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
if (string.IsNullOrEmpty(controllerContext.HttpContext.Request.ContentType))
{
if (string.Equals(controllerContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase) ||
string.Equals(controllerContext.HttpContext.Request.HttpMethod, "HEAD", StringComparison.OrdinalIgnoreCase) ||
string.Equals(controllerContext.HttpContext.Request.HttpMethod, "DELETE", StringComparison.OrdinalIgnoreCase) ||
string.Equals(controllerContext.HttpContext.Request.HttpMethod, "TRACE", StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (string.IsNullOrEmpty(controllerContext.HttpContext.Request.ContentType))
{
return false;
}
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return false;
}
return !string.IsNullOrEmpty(controllerContext.HttpContext.Request.Form[_name]);
if (!controllerContext.HttpContext.Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return !string.IsNullOrEmpty(controllerContext.HttpContext.Request.Form[_name]);
}
}

213
sandbox/OpenIddict.Sandbox.AspNet.Server/Models/AccountViewModels.cs

@ -1,112 +1,111 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace OpenIddict.Sandbox.AspNet.Server.Models
namespace OpenIddict.Sandbox.AspNet.Server.Models;
public class ExternalLoginConfirmationViewModel
{
[Required]
[Display(Name = "Courrier électronique")]
public string Email { get; set; }
}
public class ExternalLoginListViewModel
{
public string ReturnUrl { get; set; }
}
public class SendCodeViewModel
{
public string SelectedProvider { get; set; }
public ICollection<System.Web.Mvc.SelectListItem> Providers { get; set; }
public string ReturnUrl { get; set; }
public bool RememberMe { get; set; }
}
public class VerifyCodeViewModel
{
[Required]
public string Provider { get; set; }
[Required]
[Display(Name = "Code")]
public string Code { get; set; }
public string ReturnUrl { get; set; }
[Display(Name = "Mémoriser ce navigateur ?")]
public bool RememberBrowser { get; set; }
public bool RememberMe { get; set; }
}
public class ForgotViewModel
{
[Required]
[Display(Name = "Courrier électronique")]
public string Email { get; set; }
}
public class LoginViewModel
{
[Required]
[Display(Name = "Courrier électronique")]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Mot de passe")]
public string Password { get; set; }
[Display(Name = "Mémoriser mes informations")]
public bool RememberMe { get; set; }
}
public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Courrier électronique")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "{0} doit contenir au moins {2} caractères.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Mot de passe")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirmer le mot de passe")]
[Compare("Password", ErrorMessage = "Le nouveau mot de passe et le mot de passe de confirmation ne correspondent pas.")]
public string ConfirmPassword { get; set; }
}
public class ResetPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Courrier électronique")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "{0} doit contenir au moins {2} caractères.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Mot de passe")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirmer le mot de passe")]
[Compare("Password", ErrorMessage = "Le nouveau mot de passe et le mot de passe de confirmation ne correspondent pas.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
public class ForgotPasswordViewModel
{
public class ExternalLoginConfirmationViewModel
{
[Required]
[Display(Name = "Courrier électronique")]
public string Email { get; set; }
}
public class ExternalLoginListViewModel
{
public string ReturnUrl { get; set; }
}
public class SendCodeViewModel
{
public string SelectedProvider { get; set; }
public ICollection<System.Web.Mvc.SelectListItem> Providers { get; set; }
public string ReturnUrl { get; set; }
public bool RememberMe { get; set; }
}
public class VerifyCodeViewModel
{
[Required]
public string Provider { get; set; }
[Required]
[Display(Name = "Code")]
public string Code { get; set; }
public string ReturnUrl { get; set; }
[Display(Name = "Mémoriser ce navigateur ?")]
public bool RememberBrowser { get; set; }
public bool RememberMe { get; set; }
}
public class ForgotViewModel
{
[Required]
[Display(Name = "Courrier électronique")]
public string Email { get; set; }
}
public class LoginViewModel
{
[Required]
[Display(Name = "Courrier électronique")]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "Mot de passe")]
public string Password { get; set; }
[Display(Name = "Mémoriser mes informations")]
public bool RememberMe { get; set; }
}
public class RegisterViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Courrier électronique")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "{0} doit contenir au moins {2} caractères.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Mot de passe")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirmer le mot de passe")]
[Compare("Password", ErrorMessage = "Le nouveau mot de passe et le mot de passe de confirmation ne correspondent pas.")]
public string ConfirmPassword { get; set; }
}
public class ResetPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "Courrier électronique")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "{0} doit contenir au moins {2} caractères.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Mot de passe")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirmer le mot de passe")]
[Compare("Password", ErrorMessage = "Le nouveau mot de passe et le mot de passe de confirmation ne correspondent pas.")]
public string ConfirmPassword { get; set; }
public string Code { get; set; }
}
public class ForgotPasswordViewModel
{
[Required]
[EmailAddress]
[Display(Name = "E-mail")]
public string Email { get; set; }
}
[Required]
[EmailAddress]
[Display(Name = "E-mail")]
public string Email { get; set; }
}

47
sandbox/OpenIddict.Sandbox.AspNet.Server/Models/IdentityModels.cs

@ -4,37 +4,36 @@ using System.Threading.Tasks;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.EntityFramework;
namespace OpenIddict.Sandbox.AspNet.Server.Models
namespace OpenIddict.Sandbox.AspNet.Server.Models;
// Vous pouvez ajouter des données de profil pour l'utilisateur en ajoutant d'autres propriétés à votre classe ApplicationUser. Pour en savoir plus, consultez https://go.microsoft.com/fwlink/?LinkID=317594.
public class ApplicationUser : IdentityUser
{
// Vous pouvez ajouter des données de profil pour l'utilisateur en ajoutant d'autres propriétés à votre classe ApplicationUser. Pour en savoir plus, consultez https://go.microsoft.com/fwlink/?LinkID=317594.
public class ApplicationUser : IdentityUser
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Notez que l'authenticationType doit correspondre à celui défini dans CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Ajouter des revendications utilisateur personnalisées ici
return userIdentity;
}
// Notez que l'authenticationType doit correspondre à celui défini dans CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Ajouter des revendications utilisateur personnalisées ici
return userIdentity;
}
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.UseOpenIddict();
}
modelBuilder.UseOpenIddict();
}
}

135
sandbox/OpenIddict.Sandbox.AspNet.Server/Models/ManageViewModels.cs

@ -3,84 +3,83 @@ using System.ComponentModel.DataAnnotations;
using Microsoft.AspNet.Identity;
using Microsoft.Owin.Security;
namespace OpenIddict.Sandbox.AspNet.Server.Models
namespace OpenIddict.Sandbox.AspNet.Server.Models;
public class IndexViewModel
{
public class IndexViewModel
{
public bool HasPassword { get; set; }
public IList<UserLoginInfo> Logins { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
}
public bool HasPassword { get; set; }
public IList<UserLoginInfo> Logins { get; set; }
public string PhoneNumber { get; set; }
public bool TwoFactor { get; set; }
public bool BrowserRemembered { get; set; }
}
public class ManageLoginsViewModel
{
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<AuthenticationDescription> OtherLogins { get; set; }
}
public class ManageLoginsViewModel
{
public IList<UserLoginInfo> CurrentLogins { get; set; }
public IList<AuthenticationDescription> OtherLogins { get; set; }
}
public class FactorViewModel
{
public string Purpose { get; set; }
}
public class FactorViewModel
{
public string Purpose { get; set; }
}
public class SetPasswordViewModel
{
[Required]
[StringLength(100, ErrorMessage = "La chaîne {0} doit comporter au moins {2} caractères.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Nouveau mot de passe")]
public string NewPassword { get; set; }
public class SetPasswordViewModel
{
[Required]
[StringLength(100, ErrorMessage = "La chaîne {0} doit comporter au moins {2} caractères.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Nouveau mot de passe")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirmer le nouveau mot de passe")]
[Compare("NewPassword", ErrorMessage = "Le nouveau mot de passe et le mot de passe de confirmation ne correspondent pas.")]
public string ConfirmPassword { get; set; }
}
[DataType(DataType.Password)]
[Display(Name = "Confirmer le nouveau mot de passe")]
[Compare("NewPassword", ErrorMessage = "Le nouveau mot de passe et le mot de passe de confirmation ne correspondent pas.")]
public string ConfirmPassword { get; set; }
}
public class ChangePasswordViewModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Mot de passe actuel")]
public string OldPassword { get; set; }
public class ChangePasswordViewModel
{
[Required]
[DataType(DataType.Password)]
[Display(Name = "Mot de passe actuel")]
public string OldPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "La chaîne {0} doit comporter au moins {2} caractères.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Nouveau mot de passe")]
public string NewPassword { get; set; }
[Required]
[StringLength(100, ErrorMessage = "La chaîne {0} doit comporter au moins {2} caractères.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Nouveau mot de passe")]
public string NewPassword { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirmer le nouveau mot de passe")]
[Compare("NewPassword", ErrorMessage = "Le nouveau mot de passe et le mot de passe de confirmation ne correspondent pas.")]
public string ConfirmPassword { get; set; }
}
[DataType(DataType.Password)]
[Display(Name = "Confirmer le nouveau mot de passe")]
[Compare("NewPassword", ErrorMessage = "Le nouveau mot de passe et le mot de passe de confirmation ne correspondent pas.")]
public string ConfirmPassword { get; set; }
}
public class AddPhoneNumberViewModel
{
[Required]
[Phone]
[Display(Name = "Numéro de téléphone")]
public string Number { get; set; }
}
public class AddPhoneNumberViewModel
{
[Required]
[Phone]
[Display(Name = "Numéro de téléphone")]
public string Number { get; set; }
}
public class VerifyPhoneNumberViewModel
{
[Required]
[Display(Name = "Code")]
public string Code { get; set; }
public class VerifyPhoneNumberViewModel
{
[Required]
[Display(Name = "Code")]
public string Code { get; set; }
[Required]
[Phone]
[Display(Name = "Numéro de téléphone")]
public string PhoneNumber { get; set; }
}
[Required]
[Phone]
[Display(Name = "Numéro de téléphone")]
public string PhoneNumber { get; set; }
}
public class ConfigureTwoFactorViewModel
{
public string SelectedProvider { get; set; }
public ICollection<System.Web.Mvc.SelectListItem> Providers { get; set; }
}
public class ConfigureTwoFactorViewModel
{
public string SelectedProvider { get; set; }
public ICollection<System.Web.Mvc.SelectListItem> Providers { get; set; }
}

455
sandbox/OpenIddict.Sandbox.AspNet.Server/Startup.cs

@ -22,257 +22,256 @@ using Owin;
using static OpenIddict.Abstractions.OpenIddictConstants;
[assembly: OwinStartup(typeof(OpenIddict.Sandbox.AspNet.Server.Startup))]
namespace OpenIddict.Sandbox.AspNet.Server
namespace OpenIddict.Sandbox.AspNet.Server;
public class Startup
{
public class Startup
public void Configuration(IAppBuilder app)
{
public void Configuration(IAppBuilder app)
{
var services = new ServiceCollection();
var services = new ServiceCollection();
services.AddOpenIddict()
services.AddOpenIddict()
// Register the OpenIddict core components.
.AddCore(options =>
{
// Configure OpenIddict to use the Entity Framework 6.x stores and models.
// Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities.
options.UseEntityFramework()
.UseDbContext<ApplicationDbContext>();
// Developers who prefer using MongoDB can remove the previous lines
// and configure OpenIddict to use the specified MongoDB database:
// options.UseMongoDb()
// .UseDatabase(new MongoClient().GetDatabase("openiddict"));
})
// Register the OpenIddict client components.
.AddClient(options =>
{
// Note: this sample uses the code flow, but you can enable the other flows if necessary.
options.AllowAuthorizationCodeFlow();
// Register the signing and encryption credentials used to protect
// sensitive data like the state tokens produced by OpenIddict.
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
// Register the OWIN host and configure the OWIN-specific options.
options.UseOwin()
.EnableRedirectionEndpointPassthrough()
.SetCookieManager(new SystemWebCookieManager());
// Register the System.Net.Http integration and use the identity of the current
// assembly as a more specific user agent, which can be useful when dealing with
// providers that use the user agent as a way to throttle requests (e.g Reddit).
options.UseSystemNetHttp()
.SetProductInformation(typeof(Startup).Assembly);
// Register the Web providers integrations.
//
// Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint
// URI per provider, unless all the registered providers support returning a special "iss"
// parameter containing their URL as part of authorization responses. For more information,
// see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4.
options.UseWebProviders()
.AddGitHub(options =>
{
options.SetClientId("c4ade52327b01ddacff3")
.SetClientSecret("da6bed851b75e317bf6b2cb67013679d9467c122")
.SetRedirectUri("callback/login/github");
});
})
// Register the OpenIddict server components.
.AddServer(options =>
{
// Enable the authorization, device, introspection,
// logout, token, userinfo and verification endpoints.
options.SetAuthorizationEndpointUris("connect/authorize")
.SetDeviceEndpointUris("connect/device")
.SetIntrospectionEndpointUris("connect/introspect")
.SetLogoutEndpointUris("connect/logout")
.SetTokenEndpointUris("connect/token")
.SetUserinfoEndpointUris("connect/userinfo")
.SetVerificationEndpointUris("connect/verify");
// Note: this sample uses the code, device code, password and refresh token flows, but you
// can enable the other flows if you need to support implicit or client credentials.
options.AllowAuthorizationCodeFlow()
.AllowDeviceCodeFlow()
.AllowPasswordFlow()
.AllowRefreshTokenFlow();
// Mark the "email", "profile", "roles" and "demo_api" scopes as supported scopes.
options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles, "demo_api");
// Register the signing and encryption credentials.
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
// Force client applications to use Proof Key for Code Exchange (PKCE).
options.RequireProofKeyForCodeExchange();
// Register the OWIN host and configure the OWIN-specific options.
options.UseOwin()
.EnableAuthorizationEndpointPassthrough()
.EnableLogoutEndpointPassthrough()
.EnableTokenEndpointPassthrough();
})
// Register the OpenIddict validation components.
.AddValidation(options =>
{
// Import the configuration from the local OpenIddict server instance.
options.UseLocalServer();
// Register the OpenIddict core components.
.AddCore(options =>
{
// Configure OpenIddict to use the Entity Framework 6.x stores and models.
// Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities.
options.UseEntityFramework()
.UseDbContext<ApplicationDbContext>();
// Developers who prefer using MongoDB can remove the previous lines
// and configure OpenIddict to use the specified MongoDB database:
// options.UseMongoDb()
// .UseDatabase(new MongoClient().GetDatabase("openiddict"));
})
// Register the OpenIddict client components.
.AddClient(options =>
{
// Note: this sample uses the code flow, but you can enable the other flows if necessary.
options.AllowAuthorizationCodeFlow();
// Register the signing and encryption credentials used to protect
// sensitive data like the state tokens produced by OpenIddict.
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
// Register the OWIN host and configure the OWIN-specific options.
options.UseOwin()
.EnableRedirectionEndpointPassthrough()
.SetCookieManager(new SystemWebCookieManager());
// Register the System.Net.Http integration and use the identity of the current
// assembly as a more specific user agent, which can be useful when dealing with
// providers that use the user agent as a way to throttle requests (e.g Reddit).
options.UseSystemNetHttp()
.SetProductInformation(typeof(Startup).Assembly);
// Register the Web providers integrations.
//
// Note: to mitigate mix-up attacks, it's recommended to use a unique redirection endpoint
// URI per provider, unless all the registered providers support returning a special "iss"
// parameter containing their URL as part of authorization responses. For more information,
// see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#section-4.4.
options.UseWebProviders()
.AddGitHub(options =>
{
options.SetClientId("c4ade52327b01ddacff3")
.SetClientSecret("da6bed851b75e317bf6b2cb67013679d9467c122")
.SetRedirectUri("callback/login/github");
});
})
// Register the OpenIddict server components.
.AddServer(options =>
{
// Enable the authorization, device, introspection,
// logout, token, userinfo and verification endpoints.
options.SetAuthorizationEndpointUris("connect/authorize")
.SetDeviceEndpointUris("connect/device")
.SetIntrospectionEndpointUris("connect/introspect")
.SetLogoutEndpointUris("connect/logout")
.SetTokenEndpointUris("connect/token")
.SetUserinfoEndpointUris("connect/userinfo")
.SetVerificationEndpointUris("connect/verify");
// Note: this sample uses the code, device code, password and refresh token flows, but you
// can enable the other flows if you need to support implicit or client credentials.
options.AllowAuthorizationCodeFlow()
.AllowDeviceCodeFlow()
.AllowPasswordFlow()
.AllowRefreshTokenFlow();
// Mark the "email", "profile", "roles" and "demo_api" scopes as supported scopes.
options.RegisterScopes(Scopes.Email, Scopes.Profile, Scopes.Roles, "demo_api");
// Register the signing and encryption credentials.
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
// Force client applications to use Proof Key for Code Exchange (PKCE).
options.RequireProofKeyForCodeExchange();
// Register the OWIN host and configure the OWIN-specific options.
options.UseOwin()
.EnableAuthorizationEndpointPassthrough()
.EnableLogoutEndpointPassthrough()
.EnableTokenEndpointPassthrough();
})
// Register the OpenIddict validation components.
.AddValidation(options =>
{
// Import the configuration from the local OpenIddict server instance.
options.UseLocalServer();
// Register the OWIN host.
options.UseOwin();
});
// Register the OWIN host.
options.UseOwin();
});
// Create a new Autofac container and import the OpenIddict services.
var builder = new ContainerBuilder();
builder.Populate(services);
// Create a new Autofac container and import the OpenIddict services.
var builder = new ContainerBuilder();
builder.Populate(services);
// Register the MVC controllers.
builder.RegisterControllers(typeof(Startup).Assembly);
// Register the MVC controllers.
builder.RegisterControllers(typeof(Startup).Assembly);
// Register the Web API controllers.
builder.RegisterApiControllers(typeof(Startup).Assembly);
// Register the Web API controllers.
builder.RegisterApiControllers(typeof(Startup).Assembly);
var container = builder.Build();
var container = builder.Build();
// Register the Autofac scope injector middleware.
app.UseAutofacLifetimeScopeInjector(container);
// Register the Autofac scope injector middleware.
app.UseAutofacLifetimeScopeInjector(container);
// Register the Entity Framework context and the user/sign-in managers used by ASP.NET Identity.
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Register the Entity Framework context and the user/sign-in managers used by ASP.NET Identity.
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Register the cookie middleware used by ASP.NET Identity.
app.UseCookieAuthentication(new CookieAuthenticationOptions
// Register the cookie middleware used by ASP.NET Identity.
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
// Register the OpenIddict middleware.
app.UseMiddlewareFromContainer<OpenIddictClientOwinMiddleware>();
app.UseMiddlewareFromContainer<OpenIddictServerOwinMiddleware>();
app.UseMiddlewareFromContainer<OpenIddictValidationOwinMiddleware>();
// Configure ASP.NET MVC 5.2 to use Autofac when activating controller instances.
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// Configure ASP.NET MVC 5.2 to use Autofac when activating controller instances
// and infer the Web API routes using the HTTP attributes used in the controllers.
var configuration = new HttpConfiguration
{
DependencyResolver = new AutofacWebApiDependencyResolver(container)
};
// Register the OpenIddict middleware.
app.UseMiddlewareFromContainer<OpenIddictClientOwinMiddleware>();
app.UseMiddlewareFromContainer<OpenIddictServerOwinMiddleware>();
app.UseMiddlewareFromContainer<OpenIddictValidationOwinMiddleware>();
configuration.MapHttpAttributeRoutes();
configuration.SuppressDefaultHostAuthentication();
// Configure ASP.NET MVC 5.2 to use Autofac when activating controller instances.
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// Register the Autofac Web API integration and Web API middleware.
app.UseAutofacWebApi(configuration);
app.UseWebApi(configuration);
// Configure ASP.NET MVC 5.2 to use Autofac when activating controller instances
// and infer the Web API routes using the HTTP attributes used in the controllers.
var configuration = new HttpConfiguration
{
DependencyResolver = new AutofacWebApiDependencyResolver(container)
};
// Seed the database with the sample client using the OpenIddict application manager.
// Note: in a real world application, this step should be part of a setup script.
Task.Run(async delegate
{
await using var scope = container.BeginLifetimeScope();
configuration.MapHttpAttributeRoutes();
configuration.SuppressDefaultHostAuthentication();
var context = scope.Resolve<ApplicationDbContext>();
context.Database.CreateIfNotExists();
// Register the Autofac Web API integration and Web API middleware.
app.UseAutofacWebApi(configuration);
app.UseWebApi(configuration);
var manager = scope.Resolve<IOpenIddictApplicationManager>();
// Seed the database with the sample client using the OpenIddict application manager.
// Note: in a real world application, this step should be part of a setup script.
Task.Run(async delegate
if (await manager.FindByClientIdAsync("mvc") is null)
{
await using var scope = container.BeginLifetimeScope();
var context = scope.Resolve<ApplicationDbContext>();
context.Database.CreateIfNotExists();
var manager = scope.Resolve<IOpenIddictApplicationManager>();
if (await manager.FindByClientIdAsync("mvc") is null)
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
await manager.CreateAsync(new OpenIddictApplicationDescriptor
ApplicationType = ApplicationTypes.Web,
ClientId = "mvc",
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654",
ClientType = ClientTypes.Confidential,
ConsentType = ConsentTypes.Explicit,
DisplayName = "MVC client application",
RedirectUris =
{
new Uri("https://localhost:44378/callback/login/local")
},
PostLogoutRedirectUris =
{
new Uri("https://localhost:44378/callback/logout/local")
},
Permissions =
{
ApplicationType = ApplicationTypes.Web,
ClientId = "mvc",
ClientSecret = "901564A5-E7FE-42CB-B10D-61EF6A8F3654",
ClientType = ClientTypes.Confidential,
ConsentType = ConsentTypes.Explicit,
DisplayName = "MVC client application",
RedirectUris =
{
new Uri("https://localhost:44378/callback/login/local")
},
PostLogoutRedirectUris =
{
new Uri("https://localhost:44378/callback/logout/local")
},
Permissions =
{
Permissions.Endpoints.Authorization,
Permissions.Endpoints.Logout,
Permissions.Endpoints.Token,
Permissions.GrantTypes.AuthorizationCode,
Permissions.GrantTypes.RefreshToken,
Permissions.ResponseTypes.Code,
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
Permissions.Scopes.Roles,
Permissions.Prefixes.Scope + "demo_api"
},
Requirements =
{
Requirements.Features.ProofKeyForCodeExchange
}
});
}
if (await manager.FindByClientIdAsync("postman") is null)
Permissions.Endpoints.Authorization,
Permissions.Endpoints.Logout,
Permissions.Endpoints.Token,
Permissions.GrantTypes.AuthorizationCode,
Permissions.GrantTypes.RefreshToken,
Permissions.ResponseTypes.Code,
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
Permissions.Scopes.Roles,
Permissions.Prefixes.Scope + "demo_api"
},
Requirements =
{
Requirements.Features.ProofKeyForCodeExchange
}
});
}
if (await manager.FindByClientIdAsync("postman") is null)
{
await manager.CreateAsync(new OpenIddictApplicationDescriptor
{
await manager.CreateAsync(new OpenIddictApplicationDescriptor
ApplicationType = ApplicationTypes.Native,
ClientId = "postman",
ClientType = ClientTypes.Public,
ConsentType = ConsentTypes.Systematic,
DisplayName = "Postman",
RedirectUris =
{
ApplicationType = ApplicationTypes.Native,
ClientId = "postman",
ClientType = ClientTypes.Public,
ConsentType = ConsentTypes.Systematic,
DisplayName = "Postman",
RedirectUris =
{
new Uri("https://oauth.pstmn.io/v1/callback")
},
Permissions =
{
Permissions.Endpoints.Authorization,
Permissions.Endpoints.Device,
Permissions.Endpoints.Token,
Permissions.GrantTypes.AuthorizationCode,
Permissions.GrantTypes.DeviceCode,
Permissions.GrantTypes.Password,
Permissions.GrantTypes.RefreshToken,
Permissions.ResponseTypes.Code,
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
Permissions.Scopes.Roles
},
Settings =
{
// Use a shorter access token lifetime for tokens issued to the Postman application.
[Settings.TokenLifetimes.AccessToken] = TimeSpan.FromMinutes(10).ToString("c", CultureInfo.InvariantCulture)
}
});
}
}).GetAwaiter().GetResult();
}
new Uri("https://oauth.pstmn.io/v1/callback")
},
Permissions =
{
Permissions.Endpoints.Authorization,
Permissions.Endpoints.Device,
Permissions.Endpoints.Token,
Permissions.GrantTypes.AuthorizationCode,
Permissions.GrantTypes.DeviceCode,
Permissions.GrantTypes.Password,
Permissions.GrantTypes.RefreshToken,
Permissions.ResponseTypes.Code,
Permissions.Scopes.Email,
Permissions.Scopes.Profile,
Permissions.Scopes.Roles
},
Settings =
{
// Use a shorter access token lifetime for tokens issued to the Postman application.
[Settings.TokenLifetimes.AccessToken] = TimeSpan.FromMinutes(10).ToString("c", CultureInfo.InvariantCulture)
}
});
}
}).GetAwaiter().GetResult();
}
}

11
sandbox/OpenIddict.Sandbox.AspNet.Server/ViewModels/Authorization/AuthorizeViewModel.cs

@ -1,11 +1,10 @@
using System.Collections.Generic;
using System.Web.Mvc;
namespace OpenIddict.Sandbox.AspNet.Server.ViewModels.Authorization
namespace OpenIddict.Sandbox.AspNet.Server.ViewModels.Authorization;
[Bind(Exclude = nameof(Parameters))]
public class LogoutViewModel
{
[Bind(Exclude = nameof(Parameters))]
public class LogoutViewModel
{
public IEnumerable<KeyValuePair<string, string>> Parameters { get; internal set; }
}
public IEnumerable<KeyValuePair<string, string>> Parameters { get; internal set; }
}

19
sandbox/OpenIddict.Sandbox.AspNet.Server/ViewModels/Authorization/LogoutViewModel.cs

@ -2,17 +2,16 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace OpenIddict.Sandbox.AspNet.Server.ViewModels.Authorization
namespace OpenIddict.Sandbox.AspNet.Server.ViewModels.Authorization;
[Bind(Exclude = nameof(Parameters))]
public class AuthorizeViewModel
{
[Bind(Exclude = nameof(Parameters))]
public class AuthorizeViewModel
{
[Display(Name = "Application")]
public string ApplicationName { get; set; }
[Display(Name = "Application")]
public string ApplicationName { get; set; }
[Display(Name = "Scope")]
public string Scope { get; set; }
[Display(Name = "Scope")]
public string Scope { get; set; }
public IEnumerable<KeyValuePair<string, string>> Parameters { get; internal set; }
}
public IEnumerable<KeyValuePair<string, string>> Parameters { get; internal set; }
}

Loading…
Cancel
Save