using System; using System.Linq; using System.Threading.Tasks; using CryptoHelper; using Microsoft.AspNet.Http; using Microsoft.AspNet.Identity; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Internal; using Microsoft.Extensions.Logging; using Microsoft.Extensions.OptionsModel; namespace OpenIddict { public class OpenIddictManager : UserManager where TUser : class where TApplication : class { public OpenIddictManager([NotNull] IServiceProvider services) : base(services: services, store: services.GetService>(), optionsAccessor: services.GetService>(), passwordHasher: services.GetService>(), userValidators: services.GetServices>(), passwordValidators: services.GetServices>(), keyNormalizer: services.GetService(), errors: services.GetService(), logger: services.GetService>>(), contextAccessor: services.GetService()) { Context = services.GetRequiredService().HttpContext; } /// /// Gets the HTTP context associated with the current manager. /// public virtual HttpContext Context { get; } /// /// Gets the store associated with the current manager. /// public virtual new IOpenIddictStore Store { get { return base.Store as IOpenIddictStore; } } public virtual Task FindApplicationByIdAsync(string identifier) { return Store.FindApplicationByIdAsync(identifier, Context.RequestAborted); } public virtual Task FindApplicationByLogoutRedirectUri(string url) { return Store.FindApplicationByLogoutRedirectUri(url, Context.RequestAborted); } public virtual async Task FindClaimAsync(TUser user, string type) { // Note: GetClaimsAsync will automatically throw an exception // if the underlying store doesn't support custom claims. return (from claim in await GetClaimsAsync(user) where string.Equals(claim.Type, type, StringComparison.Ordinal) select claim.Value).FirstOrDefault(); } public virtual async Task GetApplicationTypeAsync(TApplication application) { if (application == null) { throw new ArgumentNullException(nameof(application)); } var type = await Store.GetApplicationTypeAsync(application, Context.RequestAborted); // Ensure the application type returned by the store is supported by the manager. if (!string.Equals(type, OpenIddictConstants.ApplicationTypes.Confidential, StringComparison.OrdinalIgnoreCase) && !string.Equals(type, OpenIddictConstants.ApplicationTypes.Public, StringComparison.OrdinalIgnoreCase)) { throw new InvalidOperationException("Only 'confidential' or 'public' applications are " + "supported by the default OpenIddict manager."); } return type; } public virtual Task GetDisplayNameAsync(TApplication application) { if (application == null) { throw new ArgumentNullException(nameof(application)); } return Store.GetDisplayNameAsync(application, Context.RequestAborted); } public virtual async Task ValidateRedirectUriAsync(TApplication application, string address) { if (application == null) { throw new ArgumentNullException(nameof(application)); } if (!string.Equals(address, await Store.GetRedirectUriAsync(application, Context.RequestAborted), StringComparison.Ordinal)) { Logger.LogWarning("Client validation failed because {RedirectUri} was not a valid redirect_uri " + "for {Client}", address, await GetDisplayNameAsync(application)); return false; } return true; } public virtual async Task ValidateSecretAsync(TApplication application, string secret) { if (application == null) { throw new ArgumentNullException(nameof(application)); } if (!await this.IsConfidentialApplicationAsync(application)) { Logger.LogWarning("Client authentication cannot be enforced for non-confidential applications."); return false; } var hash = await Store.GetHashedSecretAsync(application, Context.RequestAborted); if (string.IsNullOrEmpty(hash)) { Logger.LogError("Client authentication failed for {Client} because " + "no client secret was associated with the application."); return false; } if (!Crypto.VerifyHashedPassword(hash, secret)) { Logger.LogWarning("Client authentication failed for {Client}.", await GetDisplayNameAsync(application)); return false; } return true; } } }