// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; using System.Net.Http; using System.Text; using Microsoft.AspNet.Builder; using Microsoft.AspNet.DataProtection; using Microsoft.AspNet.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.OptionsModel; using Microsoft.Extensions.WebEncoders; using Microsoft.IdentityModel.Protocols; using Microsoft.IdentityModel.Protocols.OpenIdConnect; namespace Microsoft.AspNet.Authentication.OpenIdConnect { /// /// ASP.NET middleware for obtaining identities using OpenIdConnect protocol. /// public class OpenIdConnectMiddleware : AuthenticationMiddleware { /// /// Initializes a /// /// The next middleware in the ASP.NET pipeline to invoke. /// provider for creating a data protector. /// factory for creating a . /// /// /// /// [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Managed by caller")] public OpenIdConnectMiddleware( RequestDelegate next, IDataProtectionProvider dataProtectionProvider, ILoggerFactory loggerFactory, IUrlEncoder encoder, IServiceProvider services, IOptions sharedOptions, OpenIdConnectOptions options) : base(next, options, loggerFactory, encoder) { if (next == null) { throw new ArgumentNullException(nameof(next)); } if (dataProtectionProvider == null) { throw new ArgumentNullException(nameof(dataProtectionProvider)); } if (loggerFactory == null) { throw new ArgumentNullException(nameof(loggerFactory)); } if (encoder == null) { throw new ArgumentNullException(nameof(encoder)); } if (services == null) { throw new ArgumentNullException(nameof(services)); } if (sharedOptions == null) { throw new ArgumentNullException(nameof(sharedOptions)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } if (!Options.CallbackPath.HasValue) { throw new ArgumentException("Options.CallbackPath must be provided."); } if (string.IsNullOrEmpty(Options.SignInScheme)) { Options.SignInScheme = sharedOptions.Value.SignInScheme; } if (string.IsNullOrEmpty(Options.SignInScheme)) { throw new ArgumentException("Options.SignInScheme is required."); } if (Options.HtmlEncoder == null) { Options.HtmlEncoder = services.GetHtmlEncoder(); } if (Options.StateDataFormat == null) { var dataProtector = dataProtectionProvider.CreateProtector( typeof(OpenIdConnectMiddleware).FullName, typeof(string).FullName, Options.AuthenticationScheme, "v1"); Options.StateDataFormat = new PropertiesDataFormat(dataProtector); } if (Options.StringDataFormat == null) { var dataProtector = dataProtectionProvider.CreateProtector( typeof(OpenIdConnectMiddleware).FullName, typeof(string).FullName, Options.AuthenticationScheme, "v1"); Options.StringDataFormat = new SecureDataFormat(new StringSerializer(), dataProtector); } // if the user has not set the AuthorizeCallback, set it from the redirect_uri if (!Options.CallbackPath.HasValue) { Uri redirectUri; if (!string.IsNullOrEmpty(Options.RedirectUri) && Uri.TryCreate(Options.RedirectUri, UriKind.Absolute, out redirectUri)) { // Redirect_Uri must be a very specific, case sensitive value, so we can't generate it. Instead we generate AuthorizeCallback from it. Options.CallbackPath = PathString.FromUriComponent(redirectUri); } } if (Options.Events == null) { Options.Events = new OpenIdConnectEvents(); } if (string.IsNullOrEmpty(Options.TokenValidationParameters.ValidAudience) && !string.IsNullOrEmpty(Options.ClientId)) { Options.TokenValidationParameters.ValidAudience = Options.ClientId; } Backchannel = new HttpClient(Options.BackchannelHttpHandler ?? new HttpClientHandler()); Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET OpenIdConnect middleware"); Backchannel.Timeout = Options.BackchannelTimeout; Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB if (Options.ConfigurationManager == null) { if (Options.Configuration != null) { Options.ConfigurationManager = new StaticConfigurationManager(Options.Configuration); } else if (!(string.IsNullOrEmpty(Options.MetadataAddress) && string.IsNullOrEmpty(Options.Authority))) { if (string.IsNullOrEmpty(Options.MetadataAddress) && !string.IsNullOrEmpty(Options.Authority)) { Options.MetadataAddress = Options.Authority; if (!Options.MetadataAddress.EndsWith("/", StringComparison.Ordinal)) { Options.MetadataAddress += "/"; } Options.MetadataAddress += ".well-known/openid-configuration"; } Options.ConfigurationManager = new ConfigurationManager(Options.MetadataAddress, new OpenIdConnectConfigurationRetriever(), Backchannel); } } } protected HttpClient Backchannel { get; private set; } /// /// Provides the object for processing authentication-related requests. /// /// An configured with the supplied to the constructor. protected override AuthenticationHandler CreateHandler() { return new OpenIdConnectHandler(Backchannel); } private class StringSerializer : IDataSerializer { public string Deserialize(byte[] data) { return Encoding.UTF8.GetString(data); } public byte[] Serialize(string model) { return Encoding.UTF8.GetBytes(model); } } } }