From 42f9a555ceeeab9c91149f4031af1f11c810fc53 Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Tue, 24 Mar 2015 21:59:09 -0700 Subject: [PATCH 01/19] Adding logging draft. --- .vs/config/applicationhost.config | 1025 +++++++++++++++++ samples/OpenIdConnectSample/Startup.cs | 4 +- .../OpenIdConnectAuthenticationMiddleware.cs | 10 +- .../project.json | 4 +- 4 files changed, 1038 insertions(+), 5 deletions(-) create mode 100644 .vs/config/applicationhost.config diff --git a/.vs/config/applicationhost.config b/.vs/config/applicationhost.config new file mode 100644 index 000000000..3474ab937 --- /dev/null +++ b/.vs/config/applicationhost.config @@ -0,0 +1,1025 @@ + + + + + + + + +
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+ +
+
+ +
+
+
+ + +
+
+
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/OpenIdConnectSample/Startup.cs b/samples/OpenIdConnectSample/Startup.cs index 2077d802b..d9e127f2b 100644 --- a/samples/OpenIdConnectSample/Startup.cs +++ b/samples/OpenIdConnectSample/Startup.cs @@ -31,8 +31,8 @@ public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory) app.UseOpenIdConnectAuthentication(options => { - options.ClientId = "fe78e0b4-6fe7-47e6-812c-fb75cee266a4"; - options.Authority = "https://login.windows.net/cyrano.onmicrosoft.com"; + options.ClientId = "ba7651c2-53c2-442a-97c2-3d60ea42f403"; + options.Authority = "https://login.windows.net/tushartest.onmicrosoft.com"; options.RedirectUri = "http://localhost:42023"; }); diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs index 9353f385c..cb19432e7 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -47,7 +47,15 @@ public OpenIdConnectAuthenticationMiddleware( ConfigureOptions configureOptions = null) : base(next, options, loggerFactory, encoder, configureOptions) { +<<<<<<< HEAD if (string.IsNullOrEmpty(Options.SignInScheme) && !string.IsNullOrEmpty(externalOptions.Options.SignInScheme)) +======= + _logger = loggerFactory.CreateLogger(); + DefaultLoggingListener eventListener = new DefaultLoggingListener(_logger); + eventListener.EnableEvents(WilsonEventSource.Logger, EventLevel.Informational); + + if (string.IsNullOrWhiteSpace(Options.TokenValidationParameters.AuthenticationType)) +>>>>>>> Adding logging draft. { Options.SignInScheme = externalOptions.Options.SignInScheme; } diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json index b61834b53..a241852b5 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json @@ -3,8 +3,8 @@ "description": "ASP.NET 5 middleware that enables an application to support OpenIdConnect authentication workflow.", "dependencies": { "Microsoft.AspNet.Authentication": "1.0.0-*", - "Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" }, - "Microsoft.IdentityModel.Protocol.Extensions": "2.0.0-beta4-*" + "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" }, + "Microsoft.IdentityModel.Protocol.Extensions": "2.0.0-beta4" }, "frameworks": { "dnx451": { From 6e32a1910e9a638ac88083fcd0f0f390e450e14e Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Wed, 25 Mar 2015 14:56:22 -0700 Subject: [PATCH 02/19] Hooking up wilson event source to oidc handler. --- .../DefaultLoggingListener.cs | 47 +++++++++++++++++++ .../OpenIdConnectAuthenticationMiddleware.cs | 12 ++++- .../OpenIdConnectAuthenticationOptions.cs | 3 ++ 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 src/Microsoft.AspNet.Authentication.OpenIdConnect/DefaultLoggingListener.cs diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/DefaultLoggingListener.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/DefaultLoggingListener.cs new file mode 100644 index 000000000..b409383d9 --- /dev/null +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/DefaultLoggingListener.cs @@ -0,0 +1,47 @@ +using System; +using System.Diagnostics.Tracing; +using Microsoft.Framework.Logging; + +namespace Microsoft.AspNet.Authentication.OpenIdConnect +{ + /// + /// Default logging event listener for wilson event source. It logs the data to the target where other Asp.Net logs go. + /// + public class DefaultLoggingListener : EventListener + { + private ILogger _logger; + + public DefaultLoggingListener(ILogger logger) + { + _logger = logger; + } + + protected override void OnEventWritten(EventWrittenEventArgs eventData) + { + // calling Asp.Net logger to log events + _logger.Log(MapEventLevelToLogLevel(eventData.Level), eventData.EventId, eventData.Payload[0], null, null); + } + + private LogLevel MapEventLevelToLogLevel(EventLevel level) + { + LogLevel logLevel = LogLevel.Information; + + switch(level) + { + case EventLevel.Critical: logLevel = LogLevel.Critical; + break; + case EventLevel.Error: logLevel = LogLevel.Error; + break; + case EventLevel.Warning: logLevel = LogLevel.Warning; + break; + case EventLevel.Informational: logLevel = LogLevel.Information; + break; + case EventLevel.Verbose: logLevel = LogLevel.Verbose; + break; + case EventLevel.LogAlways: logLevel = LogLevel.Debug; + break; + } + return logLevel; + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs index cb19432e7..c561e5984 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs @@ -51,8 +51,16 @@ public OpenIdConnectAuthenticationMiddleware( if (string.IsNullOrEmpty(Options.SignInScheme) && !string.IsNullOrEmpty(externalOptions.Options.SignInScheme)) ======= _logger = loggerFactory.CreateLogger(); - DefaultLoggingListener eventListener = new DefaultLoggingListener(_logger); - eventListener.EnableEvents(WilsonEventSource.Logger, EventLevel.Informational); + + if (Options.WilsonEventSourceListener != null) + { + Options.WilsonEventSourceListener.EnableEvents(WilsonEventSource.Logger, EventLevel.Informational); + } + else + { + DefaultLoggingListener eventListener = new DefaultLoggingListener(_logger); + eventListener.EnableEvents(WilsonEventSource.Logger, EventLevel.Error); + } if (string.IsNullOrWhiteSpace(Options.TokenValidationParameters.AuthenticationType)) >>>>>>> Adding logging draft. diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs index 9e4bfa55d..1419017ed 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Tracing; using System.IdentityModel.Tokens; using System.Net.Http; using Microsoft.AspNet.Http; @@ -337,5 +338,7 @@ public bool UseTokenLifetime get; set; } + + public EventListener WilsonEventSourceListener { get; set; } } } From 5e076e9c79239f585b5292b89900cf70c3a86aef Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Mon, 30 Mar 2015 17:08:56 -0700 Subject: [PATCH 03/19] Reverting changes to OIDCsample\startup.cs --- samples/OpenIdConnectSample/Startup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/OpenIdConnectSample/Startup.cs b/samples/OpenIdConnectSample/Startup.cs index d9e127f2b..2077d802b 100644 --- a/samples/OpenIdConnectSample/Startup.cs +++ b/samples/OpenIdConnectSample/Startup.cs @@ -31,8 +31,8 @@ public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory) app.UseOpenIdConnectAuthentication(options => { - options.ClientId = "ba7651c2-53c2-442a-97c2-3d60ea42f403"; - options.Authority = "https://login.windows.net/tushartest.onmicrosoft.com"; + options.ClientId = "fe78e0b4-6fe7-47e6-812c-fb75cee266a4"; + options.Authority = "https://login.windows.net/cyrano.onmicrosoft.com"; options.RedirectUri = "http://localhost:42023"; }); From 2b0d0ad8e8d2a9637cc8fa759ff9fe0e57bf7ed8 Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Mon, 30 Mar 2015 17:11:01 -0700 Subject: [PATCH 04/19] Removing the unused file reference --- samples/OpenIdConnectSample/Startup.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/samples/OpenIdConnectSample/Startup.cs b/samples/OpenIdConnectSample/Startup.cs index 2077d802b..3c3119b78 100644 --- a/samples/OpenIdConnectSample/Startup.cs +++ b/samples/OpenIdConnectSample/Startup.cs @@ -5,7 +5,6 @@ using Microsoft.AspNet.Authentication.Cookies; using Microsoft.AspNet.Authentication.OpenIdConnect; using Microsoft.Framework.DependencyInjection; -using Microsoft.Framework.Logging; namespace OpenIdConnectSample { From 9fc2eb7c5f395d0dae5e7c2db52794605e272b0e Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Thu, 9 Apr 2015 18:17:03 -0700 Subject: [PATCH 05/19] changing wilsonEventSource to IdentityModelEventSource --- .../OpenIdConnectAuthenticationMiddleware.cs | 11 +++++++---- .../OpenIdConnectAuthenticationOptions.cs | 2 +- .../project.json | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs index c561e5984..295840376 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs @@ -52,14 +52,15 @@ public OpenIdConnectAuthenticationMiddleware( ======= _logger = loggerFactory.CreateLogger(); - if (Options.WilsonEventSourceListener != null) + if (Options.IdentityModelEventSourceListener != null) { - Options.WilsonEventSourceListener.EnableEvents(WilsonEventSource.Logger, EventLevel.Informational); + Options.IdentityModelEventSourceListener.EnableEvents(IdentityModelEventSource.Logger, EventLevel.Informational); } else { DefaultLoggingListener eventListener = new DefaultLoggingListener(_logger); - eventListener.EnableEvents(WilsonEventSource.Logger, EventLevel.Error); + IdentityModelEventSource.LogLevel = EventLevel.Informational; + eventListener.EnableEvents(IdentityModelEventSource.Logger, EventLevel.Informational); } if (string.IsNullOrWhiteSpace(Options.TokenValidationParameters.AuthenticationType)) @@ -169,7 +170,9 @@ private static HttpMessageHandler ResolveHttpMessageHandler(OpenIdConnectAuthent } webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate; } -#else +#endif + +#if DNXCORE50 new WinHttpHandler(); #endif return handler; diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs index 1419017ed..b030ce62e 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs @@ -339,6 +339,6 @@ public bool UseTokenLifetime set; } - public EventListener WilsonEventSourceListener { get; set; } + public EventListener IdentityModelEventSourceListener { get; set; } } } diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json index a241852b5..5f8e6d67a 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json @@ -4,7 +4,7 @@ "dependencies": { "Microsoft.AspNet.Authentication": "1.0.0-*", "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" }, - "Microsoft.IdentityModel.Protocol.Extensions": "2.0.0-beta4" + "Microsoft.IdentityModel.Protocol.Extensions": "2.0.0-beta4-*" }, "frameworks": { "dnx451": { From 3d0eb386adb75a81c6d56eeff88a997acafb9c59 Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Tue, 12 May 2015 16:03:16 -0700 Subject: [PATCH 06/19] Adding code only sign in flow --- .../OpenIdConnectAuthenticationHandler.cs | 120 +++++++++++------- .../Resources.resx | 5 +- .../project.json | 6 +- .../OpenIdConnectHandlerTests.cs | 30 ++++- .../project.json | 3 +- 5 files changed, 112 insertions(+), 52 deletions(-) diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs index cc132e9bb..33dbeec7e 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs @@ -13,6 +13,11 @@ using Microsoft.AspNet.Http.Authentication; using Microsoft.Framework.Logging; using Microsoft.IdentityModel.Protocols; +using Microsoft.IdentityModel.Clients.ActiveDirectory; +using System.Net.Http; +using System.Net.Http.Headers; +using Newtonsoft.Json; +using System.Collections.Generic; namespace Microsoft.AspNet.Authentication.OpenIdConnect { @@ -238,6 +243,7 @@ protected override AuthenticationTicket AuthenticateCore() /// Uses log id's OIDCH-0000 - OIDCH-0025 protected override async Task AuthenticateCoreAsync() { + bool isCodeOnlyFlow = (Options.ResponseType == "code"); Logger.LogDebug(Resources.OIDCH_0000_AuthenticateCoreAsync, this.GetType()); // Allow login to be constrained to a specific path. Need to make this runtime configurable. @@ -321,6 +327,45 @@ protected override async Task AuthenticateCoreAsync() _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); } + if (!string.IsNullOrWhiteSpace(message.Code)) + { + Logger.LogDebug(Resources.OIDCH_0014_CodeReceived, message.Code); + + var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options) + { + Code = message.Code, + ProtocolMessage = message + //RedirectUri = ticket.Properties.Items.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ? + //ticket.Properties.Items[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty, + }; + + await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification); + if (authorizationCodeReceivedNotification.HandledResponse) + { + Logger.LogInformation(Resources.OIDCH_0015_CodeReceivedNotificationHandledResponse); + return authorizationCodeReceivedNotification.AuthenticationTicket; + } + + if (authorizationCodeReceivedNotification.Skipped) + { + Logger.LogInformation(Resources.OIDCH_0016_CodeReceivedNotificationSkipped); + return null; + } + + // Redeeming authorization code for tokens + if (string.IsNullOrWhiteSpace(message.IdToken) && isCodeOnlyFlow) + { + Logger.LogDebug("OIDCH_0037: Id Token is null. Redeeming code : {0} for tokens.", message.Code); + + var tokens = RedeemAuthorizationCode(message.Code); + // Exchange code for tokens + if (tokens != null) + { + message.IdToken = tokens.IdToken; + } + } + } + // OpenIdConnect protocol allows a Code to be received without the id_token if (!string.IsNullOrWhiteSpace(message.IdToken)) { @@ -429,58 +474,31 @@ protected override async Task AuthenticateCoreAsync() return null; } - string nonce = jwt.Payload.Nonce; - if (Options.NonceCache != null) + // If id_token is received using code only flow, no need to validate nonce and chash. + if (!isCodeOnlyFlow) { - // if the nonce cannot be removed, it was used - if (!Options.NonceCache.TryRemoveNonce(nonce)) + + string nonce = jwt.Payload.Nonce; + if (Options.NonceCache != null) { - nonce = null; + // if the nonce cannot be removed, it was used + if (!Options.NonceCache.TryRemoveNonce(nonce)) + { + nonce = null; + } + } + else + { + nonce = ReadNonceCookie(nonce); } - } - else - { - nonce = ReadNonceCookie(nonce); - } - - var protocolValidationContext = new OpenIdConnectProtocolValidationContext - { - AuthorizationCode = message.Code, - Nonce = nonce, - }; - - Options.ProtocolValidator.Validate(jwt, protocolValidationContext); - } - - if (message.Code != null) - { - Logger.LogDebug(Resources.OIDCH_0014_CodeReceived, message.Code); - if (ticket == null) - { - ticket = new AuthenticationTicket(properties, Options.AuthenticationScheme); - } - - var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options) - { - AuthenticationTicket = ticket, - Code = message.Code, - JwtSecurityToken = jwt, - ProtocolMessage = message, - RedirectUri = ticket.Properties.Items.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ? - ticket.Properties.Items[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty, - }; - await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification); - if (authorizationCodeReceivedNotification.HandledResponse) - { - Logger.LogInformation(Resources.OIDCH_0015_CodeReceivedNotificationHandledResponse); - return authorizationCodeReceivedNotification.AuthenticationTicket; - } + var protocolValidationContext = new OpenIdConnectProtocolValidationContext + { + AuthorizationCode = message.Code, + Nonce = nonce, + }; - if (authorizationCodeReceivedNotification.Skipped) - { - Logger.LogInformation(Resources.OIDCH_0016_CodeReceivedNotificationSkipped); - return null; + Options.ProtocolValidator.Validate(jwt, protocolValidationContext); } } @@ -520,6 +538,14 @@ protected override async Task AuthenticateCoreAsync() } } + protected virtual IdentityModel.Clients.ActiveDirectory.AuthenticationResult RedeemAuthorizationCode(string authorizationCode) + { + AuthenticationContext authContext = new AuthenticationContext(Options.Authority, false); + ClientCredential credential = new ClientCredential(Options.ClientId, Options.ClientSecret); + var tokens = authContext.AcquireTokenByAuthorizationCodeAsync(authorizationCode, new Uri(Options.RedirectUri), credential).GetAwaiter().GetResult(); + return tokens; + } + /// /// Adds the nonce to . /// diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx index 454dae209..11a859cec 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx @@ -222,4 +222,7 @@ OIDCH_0020: 'id_token' received: '{0}' - + + OIDCH_0037: Id Token is null. Redeeming code : {0} for tokens. + + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json index 5f8e6d67a..f7bd6d351 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json @@ -4,7 +4,11 @@ "dependencies": { "Microsoft.AspNet.Authentication": "1.0.0-*", "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" }, - "Microsoft.IdentityModel.Protocol.Extensions": "2.0.0-beta4-*" + "Microsoft.IdentityModel.Protocol.Extensions": "2.0.0-beta4-*", + "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.1.204222344-alpha", + "System.Threading.Tasks": "4.0.10-beta-*", + "System.Runtime": "4.0.20-beta-*" + }, "frameworks": { "dnx451": { diff --git a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs index 183ae6018..29e5ef3cd 100644 --- a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs +++ b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs @@ -62,6 +62,7 @@ static OpenIdConnectHandlerTests() { "OIDCH_0019:", LogLevel.Debug }, { "OIDCH_0020:", LogLevel.Debug }, { "OIDCH_0026:", LogLevel.Error }, + { "OIDCH_0037:", LogLevel.Debug } }; BuildLogEntryList(); @@ -151,12 +152,16 @@ public async Task AuthenticateCore() logsEntriesExpected = new int[] { 3 }; await RunVariation(LogLevel.Information, message, MessageReceivedSkippedOptions, errors, logsEntriesExpected); - logsEntriesExpected = new int[] {0, 1, 7, 20, 8 }; + logsEntriesExpected = new int[] {0, 1, 7, 14, 20, 8 }; await RunVariation(LogLevel.Debug, message, SecurityTokenReceivedHandledOptions, errors, logsEntriesExpected); - logsEntriesExpected = new int[] {0, 1, 7, 20, 9 }; + logsEntriesExpected = new int[] {0, 1, 7, 14, 20, 9 }; await RunVariation(LogLevel.Debug, message, SecurityTokenReceivedSkippedOptions, errors, logsEntriesExpected); + message.IdToken = null; + logsEntriesExpected = new int[] { 0, 1, 7, 14, 22, 20, 8}; + await RunVariation(LogLevel.Debug, message, CodeReceivedAndRedeemedHandledOptions, errors, logsEntriesExpected); + #if _Verbose Console.WriteLine("\n ===== \n"); DisplayErrors(errors); @@ -317,6 +322,21 @@ private static void CodeReceivedHandledOptions(OpenIdConnectAuthenticationOption }; } + private static void CodeReceivedAndRedeemedHandledOptions(OpenIdConnectAuthenticationOptions options) + { + DefaultOptions(options); + options.ResponseType = "code"; + options.Notifications = + new OpenIdConnectAuthenticationNotifications + { + SecurityTokenReceived = (notification) => + { + notification.HandleResponse(); + return Task.FromResult(null); + } + }; + } + private static void CodeReceivedSkippedOptions(OpenIdConnectAuthenticationOptions options) { DefaultOptions(options); @@ -336,6 +356,7 @@ private static void DefaultOptions(OpenIdConnectAuthenticationOptions options) options.AuthenticationScheme = "OpenIdConnectHandlerTest"; options.ConfigurationManager = ConfigurationManager.DefaultStaticConfigurationManager; options.StateDataFormat = new AuthenticationPropertiesFormater(); + } private static void MessageReceivedHandledOptions(OpenIdConnectAuthenticationOptions options) @@ -570,6 +591,11 @@ protected override async Task ApplyResponseChallengeAsync() await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification); } + + protected override IdentityModel.Clients.ActiveDirectory.AuthenticationResult RedeemAuthorizationCode(string authorizationCode) + { + return null; + } } /// diff --git a/test/Microsoft.AspNet.Authentication.Test/project.json b/test/Microsoft.AspNet.Authentication.Test/project.json index c7f594c4f..e91c82ef9 100644 --- a/test/Microsoft.AspNet.Authentication.Test/project.json +++ b/test/Microsoft.AspNet.Authentication.Test/project.json @@ -13,7 +13,8 @@ "Microsoft.AspNet.DataProtection": "1.0.0-*", "Microsoft.AspNet.TestHost": "1.0.0-*", "Moq": "4.2.1312.1622", - "xunit.runner.aspnet": "2.0.0-aspnet-*" + "xunit.runner.aspnet": "2.0.0-aspnet-*", + "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.1.204222344-alpha" }, "commands": { "test": "xunit.runner.aspnet" From 719e66e3f97bf8653bd46223d62e5f80b036bf55 Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Wed, 13 May 2015 09:57:36 -0700 Subject: [PATCH 07/19] Fixing the test case for code redemption --- .../OpenIdConnectAuthenticationHandler.cs | 10 +++++----- .../OpenIdConnect/OpenIdConnectHandlerTests.cs | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs index 33dbeec7e..34d9329c5 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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; @@ -334,9 +334,9 @@ protected override async Task AuthenticateCoreAsync() var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options) { Code = message.Code, - ProtocolMessage = message - //RedirectUri = ticket.Properties.Items.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ? - //ticket.Properties.Items[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty, + ProtocolMessage = message, + RedirectUri = properties.Items.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ? + properties.Items[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty, }; await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification); @@ -355,7 +355,7 @@ protected override async Task AuthenticateCoreAsync() // Redeeming authorization code for tokens if (string.IsNullOrWhiteSpace(message.IdToken) && isCodeOnlyFlow) { - Logger.LogDebug("OIDCH_0037: Id Token is null. Redeeming code : {0} for tokens.", message.Code); + Logger.LogDebug(Resources.OIDCH_0037_Redeeming_Auth_Code, message.Code); var tokens = RedeemAuthorizationCode(message.Code); // Exchange code for tokens diff --git a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs index 29e5ef3cd..88ca9c7d0 100644 --- a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs +++ b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// 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. // this controls if the logs are written to the console. @@ -159,7 +159,7 @@ public async Task AuthenticateCore() await RunVariation(LogLevel.Debug, message, SecurityTokenReceivedSkippedOptions, errors, logsEntriesExpected); message.IdToken = null; - logsEntriesExpected = new int[] { 0, 1, 7, 14, 22, 20, 8}; + logsEntriesExpected = new int[] { 0, 1, 7, 14, 22}; await RunVariation(LogLevel.Debug, message, CodeReceivedAndRedeemedHandledOptions, errors, logsEntriesExpected); #if _Verbose From f3d44a946876c6b6df05a8fd19968bfc151bc808 Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Wed, 13 May 2015 10:17:48 -0700 Subject: [PATCH 08/19] Adding code only response type constant --- .../OpenIdConnectAuthenticationHandler.cs | 2 +- .../OpenIdConnectAuthenticationMiddleware.cs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs index 34d9329c5..1d5a168d5 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs @@ -243,7 +243,7 @@ protected override AuthenticationTicket AuthenticateCore() /// Uses log id's OIDCH-0000 - OIDCH-0025 protected override async Task AuthenticateCoreAsync() { - bool isCodeOnlyFlow = (Options.ResponseType == "code"); + bool isCodeOnlyFlow = (Options.ResponseType == OpenIdConnectConstants.Code); Logger.LogDebug(Resources.OIDCH_0000_AuthenticateCoreAsync, this.GetType()); // Allow login to be constrained to a specific path. Need to make this runtime configurable. diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs index 295840376..d25827700 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs @@ -170,9 +170,7 @@ private static HttpMessageHandler ResolveHttpMessageHandler(OpenIdConnectAuthent } webRequestHandler.ServerCertificateValidationCallback = options.BackchannelCertificateValidator.Validate; } -#endif - -#if DNXCORE50 +#else new WinHttpHandler(); #endif return handler; From 8e5c49a9e9c18bbc8de379902d667c737462eeea Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Wed, 13 May 2015 13:26:24 -0700 Subject: [PATCH 09/19] Correcting the constant name --- .../OpenIdConnectAuthenticationHandler.cs | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs index 1d5a168d5..4e1072313 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs @@ -243,7 +243,7 @@ protected override AuthenticationTicket AuthenticateCore() /// Uses log id's OIDCH-0000 - OIDCH-0025 protected override async Task AuthenticateCoreAsync() { - bool isCodeOnlyFlow = (Options.ResponseType == OpenIdConnectConstants.Code); + bool isCodeOnlyFlow = (Options.ResponseType == OpenIdConnectResponseTypes.CodeOnly); Logger.LogDebug(Resources.OIDCH_0000_AuthenticateCoreAsync, this.GetType()); // Allow login to be constrained to a specific path. Need to make this runtime configurable. @@ -474,32 +474,34 @@ protected override async Task AuthenticateCoreAsync() return null; } - // If id_token is received using code only flow, no need to validate nonce and chash. - if (!isCodeOnlyFlow) - { - string nonce = jwt.Payload.Nonce; - if (Options.NonceCache != null) - { - // if the nonce cannot be removed, it was used - if (!Options.NonceCache.TryRemoveNonce(nonce)) - { - nonce = null; - } - } - else + string nonce = jwt.Payload.Nonce; + if (Options.NonceCache != null) + { + // if the nonce cannot be removed, it was used + if (!Options.NonceCache.TryRemoveNonce(nonce)) { - nonce = ReadNonceCookie(nonce); + nonce = null; } + } + else + { + nonce = ReadNonceCookie(nonce); + } - var protocolValidationContext = new OpenIdConnectProtocolValidationContext - { - AuthorizationCode = message.Code, - Nonce = nonce, - }; - - Options.ProtocolValidator.Validate(jwt, protocolValidationContext); + var protocolValidationContext = new OpenIdConnectProtocolValidationContext + { + AuthorizationCode = message.Code, + Nonce = nonce, + }; + + // If id_token is received using code only flow, no need to validate chash and also it is not returned in the response. Setting authorizationCode to null will skip the validation of chash. + if (isCodeOnlyFlow) + { + protocolValidationContext.AuthorizationCode = null; } + + Options.ProtocolValidator.Validate(jwt, protocolValidationContext); } return ticket; From 9c94b490dcf9453267a2461dee5605a52f4372e9 Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Wed, 13 May 2015 13:29:48 -0700 Subject: [PATCH 10/19] replacing string with constant variable for code only flow reponse type value --- .../OpenIdConnect/OpenIdConnectHandlerTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs index 88ca9c7d0..900906d24 100644 --- a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs +++ b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs @@ -325,7 +325,7 @@ private static void CodeReceivedHandledOptions(OpenIdConnectAuthenticationOption private static void CodeReceivedAndRedeemedHandledOptions(OpenIdConnectAuthenticationOptions options) { DefaultOptions(options); - options.ResponseType = "code"; + options.ResponseType = OpenIdConnectResponseTypes.CodeOnly; options.Notifications = new OpenIdConnectAuthenticationNotifications { From d430b5d50db1b4ccb01bb189c1d99b1b6bb3d395 Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Thu, 14 May 2015 08:35:05 -0700 Subject: [PATCH 11/19] Adding .designer.cs file --- .../Resources.Designer.cs | 8 ++++++++ .../project.json | 2 +- test/Microsoft.AspNet.Authentication.Test/project.json | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs index b95de8318..26841458e 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs @@ -172,6 +172,14 @@ internal static string OIDCH_0036_UriIsNotWellFormed get { return ResourceManager.GetString("OIDCH_0036_UriIsNotWellFormed"); } } + /// + /// OIDCH_0037: Id Token is null. Redeeming code : {0} for tokens. + /// + internal static string OIDCH_0037_Redeeming_Auth_Code + { + get { return ResourceManager.GetString("OIDCH_0037_Redeeming_Auth_Code"); } + } + /// /// OIDCH_0000: Entering: '{0}'. /// diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json index f7bd6d351..6cef4d281 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json @@ -5,7 +5,7 @@ "Microsoft.AspNet.Authentication": "1.0.0-*", "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" }, "Microsoft.IdentityModel.Protocol.Extensions": "2.0.0-beta4-*", - "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.1.204222344-alpha", + "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.3.205132343-alpha", "System.Threading.Tasks": "4.0.10-beta-*", "System.Runtime": "4.0.20-beta-*" diff --git a/test/Microsoft.AspNet.Authentication.Test/project.json b/test/Microsoft.AspNet.Authentication.Test/project.json index e91c82ef9..4a549dd04 100644 --- a/test/Microsoft.AspNet.Authentication.Test/project.json +++ b/test/Microsoft.AspNet.Authentication.Test/project.json @@ -14,7 +14,7 @@ "Microsoft.AspNet.TestHost": "1.0.0-*", "Moq": "4.2.1312.1622", "xunit.runner.aspnet": "2.0.0-aspnet-*", - "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.1.204222344-alpha" + "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.3.205132343-alpha" }, "commands": { "test": "xunit.runner.aspnet" From c3b28a92f87f20ba93c6d6c3e1631bfdbe4f3fcf Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Mon, 18 May 2015 23:45:08 -0700 Subject: [PATCH 12/19] Removing adal dependency and addressing some github comments --- .../TokenResponse.cs | 2 + .../OpenIdConnectAuthenticationHandler.cs | 42 +++++++++++++------ .../OpenIdConnectAuthenticationMiddleware.cs | 22 +++++++--- .../OpenIdConnectAuthenticationOptions.cs | 2 - .../project.json | 7 +++- .../OpenIdConnectHandlerTests.cs | 3 +- 6 files changed, 55 insertions(+), 23 deletions(-) diff --git a/src/Microsoft.AspNet.Authentication.OAuth/TokenResponse.cs b/src/Microsoft.AspNet.Authentication.OAuth/TokenResponse.cs index 5bcb80ad8..04dffa0fc 100644 --- a/src/Microsoft.AspNet.Authentication.OAuth/TokenResponse.cs +++ b/src/Microsoft.AspNet.Authentication.OAuth/TokenResponse.cs @@ -14,6 +14,7 @@ public TokenResponse(JObject response) TokenType = response.Value("token_type"); RefreshToken = response.Value("refresh_token"); ExpiresIn = response.Value("expires_in"); + IdToken = response.Value("id_token"); } public JObject Response { get; set; } @@ -21,5 +22,6 @@ public TokenResponse(JObject response) public string TokenType { get; set; } public string RefreshToken { get; set; } public string ExpiresIn { get; set; } + public string IdToken { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs index 4e1072313..213d493b8 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs @@ -2,22 +2,21 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Globalization; using System.IdentityModel.Tokens; using System.IO; using System.Linq; +using System.Net.Http; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNet.Authentication.Notifications; +using Microsoft.AspNet.Authentication.OAuth; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Authentication; using Microsoft.Framework.Logging; using Microsoft.IdentityModel.Protocols; -using Microsoft.IdentityModel.Clients.ActiveDirectory; -using System.Net.Http; -using System.Net.Http.Headers; -using Newtonsoft.Json; -using System.Collections.Generic; +using Newtonsoft.Json.Linq; namespace Microsoft.AspNet.Authentication.OpenIdConnect { @@ -43,6 +42,13 @@ private string CurrentUri } } + protected HttpClient Backchannel { get; private set; } + + public OpenIdConnectAuthenticationHandler(HttpClient backchannel) + { + Backchannel = backchannel; + } + protected override void ApplyResponseGrant() { ApplyResponseGrantAsync().GetAwaiter().GetResult(); @@ -243,7 +249,7 @@ protected override AuthenticationTicket AuthenticateCore() /// Uses log id's OIDCH-0000 - OIDCH-0025 protected override async Task AuthenticateCoreAsync() { - bool isCodeOnlyFlow = (Options.ResponseType == OpenIdConnectResponseTypes.CodeOnly); + var isCodeOnlyFlow = (Options.ResponseType == OpenIdConnectResponseTypes.CodeOnly); Logger.LogDebug(Resources.OIDCH_0000_AuthenticateCoreAsync, this.GetType()); // Allow login to be constrained to a specific path. Need to make this runtime configurable. @@ -357,7 +363,7 @@ protected override async Task AuthenticateCoreAsync() { Logger.LogDebug(Resources.OIDCH_0037_Redeeming_Auth_Code, message.Code); - var tokens = RedeemAuthorizationCode(message.Code); + var tokens = await RedeemAuthorizationCode(message.Code); // Exchange code for tokens if (tokens != null) { @@ -474,7 +480,6 @@ protected override async Task AuthenticateCoreAsync() return null; } - string nonce = jwt.Payload.Nonce; if (Options.NonceCache != null) { @@ -540,12 +545,23 @@ protected override async Task AuthenticateCoreAsync() } } - protected virtual IdentityModel.Clients.ActiveDirectory.AuthenticationResult RedeemAuthorizationCode(string authorizationCode) + protected virtual async Task RedeemAuthorizationCode(string authorizationCode) { - AuthenticationContext authContext = new AuthenticationContext(Options.Authority, false); - ClientCredential credential = new ClientCredential(Options.ClientId, Options.ClientSecret); - var tokens = authContext.AcquireTokenByAuthorizationCodeAsync(authorizationCode, new Uri(Options.RedirectUri), credential).GetAwaiter().GetResult(); - return tokens; + var tokenRequestParameters = new Dictionary() + { + { "client_id", Options.ClientId }, + { "redirect_uri", Options.RedirectUri }, + { "client_secret", Options.ClientSecret }, + { "code", authorizationCode }, + { "grant_type", "authorization_code" }, + }; + var requestMessage = new HttpRequestMessage(HttpMethod.Post, _configuration.TokenEndpoint); + requestMessage.Content = new FormUrlEncodedContent(tokenRequestParameters); + var responseMessage = await Backchannel.SendAsync(requestMessage); + responseMessage.EnsureSuccessStatusCode(); + var tokenResonse = await responseMessage.Content.ReadAsStringAsync(); + JObject jsonTokenResponse = JObject.Parse(tokenResonse); + return new TokenResponse(jsonTokenResponse); } /// diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs index d25827700..27a0b5d96 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs @@ -4,6 +4,7 @@ using System; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.Tracing; using System.IdentityModel.Tokens; using System.Net.Http; using System.Text; @@ -13,10 +14,10 @@ using Microsoft.AspNet.Builder; using Microsoft.AspNet.DataProtection; using Microsoft.AspNet.Http; +using Microsoft.Framework.Internal; using Microsoft.Framework.Logging; using Microsoft.Framework.OptionsModel; using Microsoft.IdentityModel.Protocols; -using Microsoft.Framework.Internal; using Microsoft.Framework.WebEncoders; namespace Microsoft.AspNet.Authentication.OpenIdConnect @@ -47,6 +48,7 @@ public OpenIdConnectAuthenticationMiddleware( ConfigureOptions configureOptions = null) : base(next, options, loggerFactory, encoder, configureOptions) { +<<<<<<< HEAD <<<<<<< HEAD if (string.IsNullOrEmpty(Options.SignInScheme) && !string.IsNullOrEmpty(externalOptions.Options.SignInScheme)) ======= @@ -62,6 +64,10 @@ public OpenIdConnectAuthenticationMiddleware( IdentityModelEventSource.LogLevel = EventLevel.Informational; eventListener.EnableEvents(IdentityModelEventSource.Logger, EventLevel.Informational); } +======= + var eventListener = new DefaultIdentityModelLoggingListener(Logger); + eventListener.EnableEvents(IdentityModelEventSource.Logger, EventLevel.Informational); +>>>>>>> Removing adal dependency and addressing some github comments if (string.IsNullOrWhiteSpace(Options.TokenValidationParameters.AuthenticationType)) >>>>>>> Adding logging draft. @@ -117,6 +123,11 @@ public OpenIdConnectAuthenticationMiddleware( Options.TokenValidationParameters.ValidAudience = Options.ClientId; } + Backchannel = new HttpClient(ResolveHttpMessageHandler(Options)); + Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET OAuth middleware"); + Backchannel.Timeout = Options.BackchannelTimeout; + Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB + if (Options.ConfigurationManager == null) { if (Options.Configuration != null) @@ -136,21 +147,20 @@ public OpenIdConnectAuthenticationMiddleware( Options.MetadataAddress += ".well-known/openid-configuration"; } - var httpClient = new HttpClient(ResolveHttpMessageHandler(Options)); - httpClient.Timeout = Options.BackchannelTimeout; - httpClient.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB - Options.ConfigurationManager = new ConfigurationManager(Options.MetadataAddress, httpClient); + Options.ConfigurationManager = new ConfigurationManager(Options.MetadataAddress, 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 OpenIdConnectAuthenticationHandler(); + return new OpenIdConnectAuthenticationHandler(Backchannel); } [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "Managed by caller")] diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs index b030ce62e..1a96cdc9f 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs @@ -338,7 +338,5 @@ public bool UseTokenLifetime get; set; } - - public EventListener IdentityModelEventSourceListener { get; set; } } } diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json index 6cef4d281..b15f7bc89 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json @@ -5,10 +5,15 @@ "Microsoft.AspNet.Authentication": "1.0.0-*", "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" }, "Microsoft.IdentityModel.Protocol.Extensions": "2.0.0-beta4-*", - "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.3.205132343-alpha", "System.Threading.Tasks": "4.0.10-beta-*", +<<<<<<< HEAD "System.Runtime": "4.0.20-beta-*" +======= + "System.Runtime": "4.0.20-beta-*", + "Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" }, + "Microsoft.AspNet.Authentication.OAuth": "1.0.0-*" +>>>>>>> Removing adal dependency and addressing some github comments }, "frameworks": { "dnx451": { diff --git a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs index 900906d24..eabd94af6 100644 --- a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs +++ b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs @@ -10,6 +10,7 @@ using System.Net.Http; using System.Threading.Tasks; using Microsoft.AspNet.Authentication.Notifications; +using Microsoft.AspNet.Authentication.OAuth; using Microsoft.AspNet.Authentication.OpenIdConnect; using Microsoft.AspNet.Builder; using Microsoft.AspNet.DataProtection; @@ -592,7 +593,7 @@ protected override async Task ApplyResponseChallengeAsync() await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification); } - protected override IdentityModel.Clients.ActiveDirectory.AuthenticationResult RedeemAuthorizationCode(string authorizationCode) + protected override async Task RedeemAuthorizationCode(string authorizationCode) { return null; } From c6f45baa9262bea8440e1752c6d64377e67c7f2c Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Fri, 22 May 2015 10:19:30 -0700 Subject: [PATCH 13/19] retrieving claims from userinfo endpoint --- .../OpenIdConnectAuthenticationHandler.cs | 150 ++++++++++++------ .../OpenIdConnectAuthenticationOptions.cs | 6 + .../Resources.Designer.cs | 8 + .../Resources.resx | 3 + .../OpenIdConnectHandlerTests.cs | 4 +- .../project.json | 3 +- 6 files changed, 126 insertions(+), 48 deletions(-) diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs index 213d493b8..cc7b1ec4f 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs @@ -8,6 +8,7 @@ using System.IO; using System.Linq; using System.Net.Http; +using System.Net.Http.Headers; using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNet.Authentication.Notifications; @@ -250,6 +251,7 @@ protected override AuthenticationTicket AuthenticateCore() protected override async Task AuthenticateCoreAsync() { var isCodeOnlyFlow = (Options.ResponseType == OpenIdConnectResponseTypes.CodeOnly); + OpenIdConnectMessage tokens = null; Logger.LogDebug(Resources.OIDCH_0000_AuthenticateCoreAsync, this.GetType()); // Allow login to be constrained to a specific path. Need to make this runtime configurable. @@ -333,42 +335,15 @@ protected override async Task AuthenticateCoreAsync() _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); } - if (!string.IsNullOrWhiteSpace(message.Code)) + // Redeeming authorization code for tokens + if (string.IsNullOrWhiteSpace(message.IdToken) && isCodeOnlyFlow) { - Logger.LogDebug(Resources.OIDCH_0014_CodeReceived, message.Code); - - var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options) - { - Code = message.Code, - ProtocolMessage = message, - RedirectUri = properties.Items.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ? - properties.Items[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty, - }; - - await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification); - if (authorizationCodeReceivedNotification.HandledResponse) - { - Logger.LogInformation(Resources.OIDCH_0015_CodeReceivedNotificationHandledResponse); - return authorizationCodeReceivedNotification.AuthenticationTicket; - } - - if (authorizationCodeReceivedNotification.Skipped) - { - Logger.LogInformation(Resources.OIDCH_0016_CodeReceivedNotificationSkipped); - return null; - } + Logger.LogDebug(Resources.OIDCH_0037_Redeeming_Auth_Code, message.Code); - // Redeeming authorization code for tokens - if (string.IsNullOrWhiteSpace(message.IdToken) && isCodeOnlyFlow) + tokens = await RedeemAuthorizationCode(message.Code); + if (tokens != null) { - Logger.LogDebug(Resources.OIDCH_0037_Redeeming_Auth_Code, message.Code); - - var tokens = await RedeemAuthorizationCode(message.Code); - // Exchange code for tokens - if (tokens != null) - { - message.IdToken = tokens.IdToken; - } + message.IdToken = tokens.IdToken; } } @@ -460,6 +435,11 @@ protected override async Task AuthenticateCoreAsync() } } + if (Options.GetClaimsFromUserInfoEndpoint) + { + ticket = await GetUserInformationAsync(properties, tokens, ticket); + } + var securityTokenValidatedNotification = new SecurityTokenValidatedNotification(Context, Options) { @@ -500,7 +480,7 @@ protected override async Task AuthenticateCoreAsync() Nonce = nonce, }; - // If id_token is received using code only flow, no need to validate chash and also it is not returned in the response. Setting authorizationCode to null will skip the validation of chash. + // If id_token is received using code only flow, no need to validate chash as it is not returned in the response. Setting authorizationCode to null will skip the validation of chash. if (isCodeOnlyFlow) { protocolValidationContext.AuthorizationCode = null; @@ -509,7 +489,39 @@ protected override async Task AuthenticateCoreAsync() Options.ProtocolValidator.Validate(jwt, protocolValidationContext); } - return ticket; + if (message.Code != null) + { + Logger.LogDebug(Resources.OIDCH_0014_CodeReceived, message.Code); + if (ticket == null) + { + ticket = new AuthenticationTicket(properties, Options.AuthenticationScheme); + } + + var authorizationCodeReceivedNotification = new AuthorizationCodeReceivedNotification(Context, Options) + { + AuthenticationTicket = ticket, + Code = message.Code, + JwtSecurityToken = jwt, + ProtocolMessage = message, + RedirectUri = ticket.Properties.Items.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ? + ticket.Properties.Items[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : string.Empty, + }; + + await Options.Notifications.AuthorizationCodeReceived(authorizationCodeReceivedNotification); + if (authorizationCodeReceivedNotification.HandledResponse) + { + Logger.LogInformation(Resources.OIDCH_0015_CodeReceivedNotificationHandledResponse); + return authorizationCodeReceivedNotification.AuthenticationTicket; + } + + if (authorizationCodeReceivedNotification.Skipped) + { + Logger.LogInformation(Resources.OIDCH_0016_CodeReceivedNotificationSkipped); + return null; + } + } + + return ticket; } catch (Exception exception) { @@ -545,23 +557,71 @@ protected override async Task AuthenticateCoreAsync() } } - protected virtual async Task RedeemAuthorizationCode(string authorizationCode) + protected virtual async Task RedeemAuthorizationCode(string authorizationCode) { - var tokenRequestParameters = new Dictionary() + var openIdMessage = new OpenIdConnectMessage() { - { "client_id", Options.ClientId }, - { "redirect_uri", Options.RedirectUri }, - { "client_secret", Options.ClientSecret }, - { "code", authorizationCode }, - { "grant_type", "authorization_code" }, + ClientId = Options.ClientId, + ClientSecret = Options.ClientSecret, + Code = authorizationCode, + GrantType = "authorization_code", + RedirectUri = Options.RedirectUri }; + var requestMessage = new HttpRequestMessage(HttpMethod.Post, _configuration.TokenEndpoint); - requestMessage.Content = new FormUrlEncodedContent(tokenRequestParameters); + requestMessage.Content = new FormUrlEncodedContent(openIdMessage.Parameters); var responseMessage = await Backchannel.SendAsync(requestMessage); responseMessage.EnsureSuccessStatusCode(); var tokenResonse = await responseMessage.Content.ReadAsStringAsync(); - JObject jsonTokenResponse = JObject.Parse(tokenResonse); - return new TokenResponse(jsonTokenResponse); + var jsonTokenResponse = JObject.Parse(tokenResonse); + return new OpenIdConnectMessage() + { + AccessToken = jsonTokenResponse.Value("access_token"), + IdToken = jsonTokenResponse.Value("id_token"), + TokenType = jsonTokenResponse.Value("token_type"), + ExpiresIn = jsonTokenResponse.Value("expires_in") + }; + } + + protected virtual async Task GetUserInformationAsync(AuthenticationProperties properties, OpenIdConnectMessage message, AuthenticationTicket ticket) + { + var requestMessage = new HttpRequestMessage(HttpMethod.Get, _configuration.UserInfoEndpoint); + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", message.AccessToken); + var responseMessage = await Backchannel.SendAsync(requestMessage); + responseMessage.EnsureSuccessStatusCode(); + var userInfoResponse = await responseMessage.Content.ReadAsStringAsync(); + var user = JObject.Parse(userInfoResponse); + + var identity = (ClaimsIdentity)ticket.Principal.Identity; + var subject = identity.FindFirst(ClaimTypes.NameIdentifier).ToString(); + + // check if the sub claim matches + var userInfoSubject = user.Value("sub"); + if (userInfoSubject == null || !string.Equals(userInfoSubject.ToString(), subject, StringComparison.OrdinalIgnoreCase)) + { + Logger.LogError(Resources.OIDCH_0038_Subject_Claim_Mismatch); + throw new ArgumentException(Resources.OIDCH_0038_Subject_Claim_Mismatch); + } + + var userInfoIdentity = new ClaimsIdentity(identity); + foreach (KeyValuePair pair in user) + { + JToken value; + var claimValue = user.TryGetValue(pair.Key, out value) ? value.ToString() : null; + + IDictionary inboundClaimTypeMap = new Dictionary(JwtSecurityTokenHandler.InboundClaimTypeMap); + string longClaimTypeName; + inboundClaimTypeMap.TryGetValue(pair.Key, out longClaimTypeName); + + // checking if claim exist with either short or long name + if (!(identity.HasClaim(pair.Key, claimValue) || identity.HasClaim(longClaimTypeName, claimValue))) + { + userInfoIdentity.AddClaim(new Claim(pair.Key, claimValue, ClaimValueTypes.String, Options.ClaimsIssuer)); + } + } + + ticket.Principal.AddIdentity(userInfoIdentity); + return ticket; } /// diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs index 1a96cdc9f..38b8f78b5 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs @@ -56,6 +56,7 @@ public OpenIdConnectAuthenticationOptions(string authenticationScheme) AuthenticationScheme = authenticationScheme; BackchannelTimeout = TimeSpan.FromMinutes(1); Caption = OpenIdConnectAuthenticationDefaults.Caption; + GetClaimsFromUserInfoEndpoint = false; ProtocolValidator = new OpenIdConnectProtocolValidator(); RefreshOnIssuerKeyNotFound = true; ResponseMode = OpenIdConnectResponseModes.FormPost; @@ -164,6 +165,11 @@ public string Caption /// public bool DefaultToCurrentUriOnRedirect { get; set; } + /// + /// Boolean to set whether the middleware should go to user info endpoint to retrieve claims or not. + /// + public bool GetClaimsFromUserInfoEndpoint { get; set; } + /// /// Gets or sets the discovery endpoint for obtaining metadata /// diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs index 26841458e..dcecd152d 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs @@ -180,6 +180,14 @@ internal static string OIDCH_0037_Redeeming_Auth_Code get { return ResourceManager.GetString("OIDCH_0037_Redeeming_Auth_Code"); } } + /// + /// OIDCH_0038: Subject claim received from userinfo endpoint does not match the one in the id token. + /// + internal static string OIDCH_0038_Subject_Claim_Mismatch + { + get { return ResourceManager.GetString("OIDCH_0038_Subject_Claim_Mismatch"); } + } + /// /// OIDCH_0000: Entering: '{0}'. /// diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx index 11a859cec..058505786 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx @@ -225,4 +225,7 @@ OIDCH_0037: Id Token is null. Redeeming code : {0} for tokens. + + OIDCH_0038: Subject claim received from userinfo endpoint does not match the one in the id token. + \ No newline at end of file diff --git a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs index eabd94af6..746c42a69 100644 --- a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs +++ b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs @@ -566,6 +566,8 @@ public override void Configure(OpenIdConnectAuthenticationOptions options, strin /// public class CustomOpenIdConnectAuthenticationHandler : OpenIdConnectAuthenticationHandler { + public CustomOpenIdConnectAuthenticationHandler() : base(null) { } + public async Task BaseInitializeAsyncPublic(AuthenticationOptions options, HttpContext context, ILogger logger, IUrlEncoder encoder) { await base.BaseInitializeAsync(options, context, logger, encoder); @@ -593,7 +595,7 @@ protected override async Task ApplyResponseChallengeAsync() await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification); } - protected override async Task RedeemAuthorizationCode(string authorizationCode) + protected override async Task RedeemAuthorizationCode(string authorizationCode) { return null; } diff --git a/test/Microsoft.AspNet.Authentication.Test/project.json b/test/Microsoft.AspNet.Authentication.Test/project.json index 4a549dd04..c7f594c4f 100644 --- a/test/Microsoft.AspNet.Authentication.Test/project.json +++ b/test/Microsoft.AspNet.Authentication.Test/project.json @@ -13,8 +13,7 @@ "Microsoft.AspNet.DataProtection": "1.0.0-*", "Microsoft.AspNet.TestHost": "1.0.0-*", "Moq": "4.2.1312.1622", - "xunit.runner.aspnet": "2.0.0-aspnet-*", - "Microsoft.IdentityModel.Clients.ActiveDirectory": "3.3.205132343-alpha" + "xunit.runner.aspnet": "2.0.0-aspnet-*" }, "commands": { "test": "xunit.runner.aspnet" From 081ee8f9067b69498b6d977f6120af9b1aad08b2 Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Fri, 22 May 2015 11:42:36 -0700 Subject: [PATCH 14/19] removing changes to tokenresponse and cleanup --- .../TokenResponse.cs | 2 -- .../OpenIdConnectAuthenticationHandler.cs | 4 ++-- .../Resources.Designer.cs | 8 ++++++++ .../Resources.resx | 3 +++ .../project.json | 11 +---------- .../OpenIdConnect/OpenIdConnectHandlerTests.cs | 14 ++++++++++---- 6 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/Microsoft.AspNet.Authentication.OAuth/TokenResponse.cs b/src/Microsoft.AspNet.Authentication.OAuth/TokenResponse.cs index 04dffa0fc..5bcb80ad8 100644 --- a/src/Microsoft.AspNet.Authentication.OAuth/TokenResponse.cs +++ b/src/Microsoft.AspNet.Authentication.OAuth/TokenResponse.cs @@ -14,7 +14,6 @@ public TokenResponse(JObject response) TokenType = response.Value("token_type"); RefreshToken = response.Value("refresh_token"); ExpiresIn = response.Value("expires_in"); - IdToken = response.Value("id_token"); } public JObject Response { get; set; } @@ -22,6 +21,5 @@ public TokenResponse(JObject response) public string TokenType { get; set; } public string RefreshToken { get; set; } public string ExpiresIn { get; set; } - public string IdToken { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs index cc7b1ec4f..46d680bbf 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs @@ -12,7 +12,6 @@ using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNet.Authentication.Notifications; -using Microsoft.AspNet.Authentication.OAuth; using Microsoft.AspNet.Http; using Microsoft.AspNet.Http.Authentication; using Microsoft.Framework.Logging; @@ -437,6 +436,7 @@ protected override async Task AuthenticateCoreAsync() if (Options.GetClaimsFromUserInfoEndpoint) { + Logger.LogDebug(Resources.OIDCH_0039_Sending_Request_UIEndpoint); ticket = await GetUserInformationAsync(properties, tokens, ticket); } @@ -521,7 +521,7 @@ protected override async Task AuthenticateCoreAsync() } } - return ticket; + return ticket; } catch (Exception exception) { diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs index dcecd152d..ad1702f04 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs @@ -188,6 +188,14 @@ internal static string OIDCH_0038_Subject_Claim_Mismatch get { return ResourceManager.GetString("OIDCH_0038_Subject_Claim_Mismatch"); } } + /// + /// OIDCH_0038: Subject claim received from userinfo endpoint does not match the one in the id token. + /// + internal static string OIDCH_0039_Sending_Request_UIEndpoint + { + get { return ResourceManager.GetString("OIDCH_0039_Sending_Request_UIEndpoint"); } + } + /// /// OIDCH_0000: Entering: '{0}'. /// diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx index 058505786..0084347f2 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx @@ -228,4 +228,7 @@ OIDCH_0038: Subject claim received from userinfo endpoint does not match the one in the id token. + + OIDCH_0039: Sending request to user info endpoint for retrieving claims. + \ No newline at end of file diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json index b15f7bc89..5f8e6d67a 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json @@ -4,16 +4,7 @@ "dependencies": { "Microsoft.AspNet.Authentication": "1.0.0-*", "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" }, - "Microsoft.IdentityModel.Protocol.Extensions": "2.0.0-beta4-*", - "System.Threading.Tasks": "4.0.10-beta-*", -<<<<<<< HEAD - "System.Runtime": "4.0.20-beta-*" - -======= - "System.Runtime": "4.0.20-beta-*", - "Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" }, - "Microsoft.AspNet.Authentication.OAuth": "1.0.0-*" ->>>>>>> Removing adal dependency and addressing some github comments + "Microsoft.IdentityModel.Protocol.Extensions": "2.0.0-beta4-*" }, "frameworks": { "dnx451": { diff --git a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs index 746c42a69..6cf85f8ce 100644 --- a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs +++ b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Net.Http; +using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNet.Authentication.Notifications; using Microsoft.AspNet.Authentication.OAuth; @@ -153,14 +154,14 @@ public async Task AuthenticateCore() logsEntriesExpected = new int[] { 3 }; await RunVariation(LogLevel.Information, message, MessageReceivedSkippedOptions, errors, logsEntriesExpected); - logsEntriesExpected = new int[] {0, 1, 7, 14, 20, 8 }; + logsEntriesExpected = new int[] {0, 1, 7, 20, 8 }; await RunVariation(LogLevel.Debug, message, SecurityTokenReceivedHandledOptions, errors, logsEntriesExpected); - logsEntriesExpected = new int[] {0, 1, 7, 14, 20, 9 }; + logsEntriesExpected = new int[] {0, 1, 7, 20, 9 }; await RunVariation(LogLevel.Debug, message, SecurityTokenReceivedSkippedOptions, errors, logsEntriesExpected); message.IdToken = null; - logsEntriesExpected = new int[] { 0, 1, 7, 14, 22}; + logsEntriesExpected = new int[] { 0, 1, 7, 22, 20, 8}; await RunVariation(LogLevel.Debug, message, CodeReceivedAndRedeemedHandledOptions, errors, logsEntriesExpected); #if _Verbose @@ -419,6 +420,7 @@ private static void SecurityTokenReceivedSkippedOptions(OpenIdConnectAuthenticat private static void SecurityTokenValidatedHandledOptions(OpenIdConnectAuthenticationOptions options) { DefaultOptions(options); + options.GetClaimsFromUserInfoEndpoint = true; options.Notifications = new OpenIdConnectAuthenticationNotifications { @@ -597,8 +599,12 @@ protected override async Task ApplyResponseChallengeAsync() protected override async Task RedeemAuthorizationCode(string authorizationCode) { - return null; + return new OpenIdConnectMessage() + { + IdToken = "test token" + }; } + } /// From a34370ac9e28e54373b9c19bd12f5c0278967f07 Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Tue, 26 May 2015 10:44:58 -0700 Subject: [PATCH 15/19] Addressing github comments - removing system dependencies and code cleanup --- .../OpenIdConnectAuthenticationHandler.cs | 17 +++++++++-------- .../OpenIdConnectAuthenticationMiddleware.cs | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs index 46d680bbf..5461404b0 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs @@ -576,15 +576,16 @@ protected virtual async Task RedeemAuthorizationCode(strin var jsonTokenResponse = JObject.Parse(tokenResonse); return new OpenIdConnectMessage() { - AccessToken = jsonTokenResponse.Value("access_token"), - IdToken = jsonTokenResponse.Value("id_token"), - TokenType = jsonTokenResponse.Value("token_type"), - ExpiresIn = jsonTokenResponse.Value("expires_in") + AccessToken = jsonTokenResponse.Value(OpenIdConnectParameterNames.AccessToken), + IdToken = jsonTokenResponse.Value(OpenIdConnectParameterNames.IdToken), + TokenType = jsonTokenResponse.Value(OpenIdConnectParameterNames.TokenType), + ExpiresIn = jsonTokenResponse.Value(OpenIdConnectParameterNames.ExpiresIn) }; } protected virtual async Task GetUserInformationAsync(AuthenticationProperties properties, OpenIdConnectMessage message, AuthenticationTicket ticket) { + var requestMessage = new HttpRequestMessage(HttpMethod.Get, _configuration.UserInfoEndpoint); requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", message.AccessToken); var responseMessage = await Backchannel.SendAsync(requestMessage); @@ -593,23 +594,23 @@ protected virtual async Task GetUserInformationAsync(Authe var user = JObject.Parse(userInfoResponse); var identity = (ClaimsIdentity)ticket.Principal.Identity; - var subject = identity.FindFirst(ClaimTypes.NameIdentifier).ToString(); + var subject = identity.FindFirst(ClaimTypes.NameIdentifier).Value; // check if the sub claim matches var userInfoSubject = user.Value("sub"); - if (userInfoSubject == null || !string.Equals(userInfoSubject.ToString(), subject, StringComparison.OrdinalIgnoreCase)) + if (userInfoSubject == null || !string.Equals(userInfoSubject, subject, StringComparison.OrdinalIgnoreCase)) { Logger.LogError(Resources.OIDCH_0038_Subject_Claim_Mismatch); throw new ArgumentException(Resources.OIDCH_0038_Subject_Claim_Mismatch); } var userInfoIdentity = new ClaimsIdentity(identity); - foreach (KeyValuePair pair in user) + foreach (var pair in user) { JToken value; var claimValue = user.TryGetValue(pair.Key, out value) ? value.ToString() : null; - IDictionary inboundClaimTypeMap = new Dictionary(JwtSecurityTokenHandler.InboundClaimTypeMap); + var inboundClaimTypeMap = new Dictionary(JwtSecurityTokenHandler.InboundClaimTypeMap); string longClaimTypeName; inboundClaimTypeMap.TryGetValue(pair.Key, out longClaimTypeName); diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs index 27a0b5d96..e5757095b 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs @@ -124,7 +124,7 @@ public OpenIdConnectAuthenticationMiddleware( } Backchannel = new HttpClient(ResolveHttpMessageHandler(Options)); - Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET OAuth middleware"); + Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET OpenIdConnect middleware"); Backchannel.Timeout = Options.BackchannelTimeout; Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB From 334e66f5f7b6e6441bcc084f2a4cf68523728fcb Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Sun, 14 Jun 2015 15:44:00 -0700 Subject: [PATCH 16/19] Taking off the wilson logging adapter, adding checks for userinfo endpoint and some cleanup --- .../DefaultLoggingListener.cs | 47 ------------------- .../OpenIdConnectAuthenticationHandler.cs | 32 ++++++++++--- .../OpenIdConnectAuthenticationMiddleware.cs | 25 +--------- .../OpenIdConnectAuthenticationOptions.cs | 1 - .../project.json | 2 +- 5 files changed, 27 insertions(+), 80 deletions(-) delete mode 100644 src/Microsoft.AspNet.Authentication.OpenIdConnect/DefaultLoggingListener.cs diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/DefaultLoggingListener.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/DefaultLoggingListener.cs deleted file mode 100644 index b409383d9..000000000 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/DefaultLoggingListener.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System; -using System.Diagnostics.Tracing; -using Microsoft.Framework.Logging; - -namespace Microsoft.AspNet.Authentication.OpenIdConnect -{ - /// - /// Default logging event listener for wilson event source. It logs the data to the target where other Asp.Net logs go. - /// - public class DefaultLoggingListener : EventListener - { - private ILogger _logger; - - public DefaultLoggingListener(ILogger logger) - { - _logger = logger; - } - - protected override void OnEventWritten(EventWrittenEventArgs eventData) - { - // calling Asp.Net logger to log events - _logger.Log(MapEventLevelToLogLevel(eventData.Level), eventData.EventId, eventData.Payload[0], null, null); - } - - private LogLevel MapEventLevelToLogLevel(EventLevel level) - { - LogLevel logLevel = LogLevel.Information; - - switch(level) - { - case EventLevel.Critical: logLevel = LogLevel.Critical; - break; - case EventLevel.Error: logLevel = LogLevel.Error; - break; - case EventLevel.Warning: logLevel = LogLevel.Warning; - break; - case EventLevel.Informational: logLevel = LogLevel.Information; - break; - case EventLevel.Verbose: logLevel = LogLevel.Verbose; - break; - case EventLevel.LogAlways: logLevel = LogLevel.Debug; - break; - } - return logLevel; - } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs index 5461404b0..ebde50eeb 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs @@ -164,17 +164,16 @@ protected override async Task ApplyResponseChallengeAsync() properties.RedirectUri = CurrentUri; } - if (!string.IsNullOrWhiteSpace(Options.RedirectUri)) - { - Logger.LogDebug(Resources.OIDCH_0031_Using_Options_RedirectUri, Options.RedirectUri); - } - // When redeeming a 'code' for an AccessToken, this value is needed if (!string.IsNullOrWhiteSpace(Options.RedirectUri)) { + Logger.LogDebug(Resources.OIDCH_0031_Using_Options_RedirectUri, Options.RedirectUri); properties.Items.Add(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey, Options.RedirectUri); } + // adding response type to properties so that we can determine the response type of an incoming message. + properties.Items.Add(OpenIdConnectParameterNames.ResponseType, Options.ResponseType); + if (_configuration == null && Options.ConfigurationManager != null) { _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); @@ -249,7 +248,6 @@ protected override AuthenticationTicket AuthenticateCore() /// Uses log id's OIDCH-0000 - OIDCH-0025 protected override async Task AuthenticateCoreAsync() { - var isCodeOnlyFlow = (Options.ResponseType == OpenIdConnectResponseTypes.CodeOnly); OpenIdConnectMessage tokens = null; Logger.LogDebug(Resources.OIDCH_0000_AuthenticateCoreAsync, this.GetType()); @@ -312,12 +310,16 @@ protected override async Task AuthenticateCoreAsync() } var properties = GetPropertiesFromState(message.State); + if (properties == null) { Logger.LogError(Resources.OIDCH_0005_MessageStateIsInvalid); return null; } + var isCodeOnlyFlow = (properties.Items.ContainsKey(OpenIdConnectParameterNames.ResponseType) ? + (properties.Items[OpenIdConnectParameterNames.ResponseType] == OpenIdConnectResponseTypes.CodeOnly) : false); + // devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users. if (!string.IsNullOrWhiteSpace(message.Error)) { @@ -585,8 +587,24 @@ protected virtual async Task RedeemAuthorizationCode(strin protected virtual async Task GetUserInformationAsync(AuthenticationProperties properties, OpenIdConnectMessage message, AuthenticationTicket ticket) { + string userInfoEndpoint = null; + if (_configuration != null) + { + userInfoEndpoint = _configuration.UserInfoEndpoint; + } + + if (string.IsNullOrEmpty(userInfoEndpoint)) + { + userInfoEndpoint = Options.Configuration != null ? Options.Configuration.UserInfoEndpoint : null; + } + + if (string.IsNullOrEmpty(userInfoEndpoint)) + { + Logger.LogError("UserInfo endpoint is not set. Request to retrieve claims from userinfo endpoint cannot be completed."); + return ticket; + } - var requestMessage = new HttpRequestMessage(HttpMethod.Get, _configuration.UserInfoEndpoint); + var requestMessage = new HttpRequestMessage(HttpMethod.Get, userInfoEndpoint); requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", message.AccessToken); var responseMessage = await Backchannel.SendAsync(requestMessage); responseMessage.EnsureSuccessStatusCode(); diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs index e5757095b..a507dee49 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs @@ -4,7 +4,6 @@ using System; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Tracing; using System.IdentityModel.Tokens; using System.Net.Http; using System.Text; @@ -17,8 +16,8 @@ using Microsoft.Framework.Internal; using Microsoft.Framework.Logging; using Microsoft.Framework.OptionsModel; -using Microsoft.IdentityModel.Protocols; using Microsoft.Framework.WebEncoders; +using Microsoft.IdentityModel.Protocols; namespace Microsoft.AspNet.Authentication.OpenIdConnect { @@ -48,29 +47,7 @@ public OpenIdConnectAuthenticationMiddleware( ConfigureOptions configureOptions = null) : base(next, options, loggerFactory, encoder, configureOptions) { -<<<<<<< HEAD -<<<<<<< HEAD if (string.IsNullOrEmpty(Options.SignInScheme) && !string.IsNullOrEmpty(externalOptions.Options.SignInScheme)) -======= - _logger = loggerFactory.CreateLogger(); - - if (Options.IdentityModelEventSourceListener != null) - { - Options.IdentityModelEventSourceListener.EnableEvents(IdentityModelEventSource.Logger, EventLevel.Informational); - } - else - { - DefaultLoggingListener eventListener = new DefaultLoggingListener(_logger); - IdentityModelEventSource.LogLevel = EventLevel.Informational; - eventListener.EnableEvents(IdentityModelEventSource.Logger, EventLevel.Informational); - } -======= - var eventListener = new DefaultIdentityModelLoggingListener(Logger); - eventListener.EnableEvents(IdentityModelEventSource.Logger, EventLevel.Informational); ->>>>>>> Removing adal dependency and addressing some github comments - - if (string.IsNullOrWhiteSpace(Options.TokenValidationParameters.AuthenticationType)) ->>>>>>> Adding logging draft. { Options.SignInScheme = externalOptions.Options.SignInScheme; } diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs index 38b8f78b5..d77dd87fb 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Tracing; using System.IdentityModel.Tokens; using System.Net.Http; using Microsoft.AspNet.Http; diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json index 5f8e6d67a..b61834b53 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/project.json @@ -3,7 +3,7 @@ "description": "ASP.NET 5 middleware that enables an application to support OpenIdConnect authentication workflow.", "dependencies": { "Microsoft.AspNet.Authentication": "1.0.0-*", - "Microsoft.Framework.NotNullAttribute.Internal": { "type": "build", "version": "1.0.0-*" }, + "Microsoft.Framework.NotNullAttribute.Sources": { "type": "build", "version": "1.0.0-*" }, "Microsoft.IdentityModel.Protocol.Extensions": "2.0.0-beta4-*" }, "frameworks": { From d35032966943706b22230061c62af4fa5faf25ea Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Sun, 14 Jun 2015 16:10:27 -0700 Subject: [PATCH 17/19] Removing the .vs folder that was pushed by mistake --- .vs/config/applicationhost.config | 1025 ----------------------------- 1 file changed, 1025 deletions(-) delete mode 100644 .vs/config/applicationhost.config diff --git a/.vs/config/applicationhost.config b/.vs/config/applicationhost.config deleted file mode 100644 index 3474ab937..000000000 --- a/.vs/config/applicationhost.config +++ /dev/null @@ -1,1025 +0,0 @@ - - - - - - - - -
-
-
-
-
-
-
-
- - - -
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- -
-
- -
-
-
-
-
-
- -
-
-
-
-
- -
-
-
- -
-
- -
-
- -
-
-
- - -
-
-
-
-
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From abd67c19bde17f6786ba07f200f162878defe7db Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Sun, 14 Jun 2015 16:16:19 -0700 Subject: [PATCH 18/19] Cleanup --- samples/OpenIdConnectSample/Startup.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/OpenIdConnectSample/Startup.cs b/samples/OpenIdConnectSample/Startup.cs index 3c3119b78..2077d802b 100644 --- a/samples/OpenIdConnectSample/Startup.cs +++ b/samples/OpenIdConnectSample/Startup.cs @@ -5,6 +5,7 @@ using Microsoft.AspNet.Authentication.Cookies; using Microsoft.AspNet.Authentication.OpenIdConnect; using Microsoft.Framework.DependencyInjection; +using Microsoft.Framework.Logging; namespace OpenIdConnectSample { From 37208ce93f2b749e549363b60e4d0a08e4e18f4c Mon Sep 17 00:00:00 2001 From: tushar gupta Date: Tue, 16 Jun 2015 18:24:27 -0700 Subject: [PATCH 19/19] updating the redirect uri, adding test for getuserInfo and some other minor changes. --- .../OpenIdConnectAuthenticationHandler.cs | 61 ++++++----- .../OpenIdConnectHandlerTests.cs | 103 +++++++++++++++++- 2 files changed, 134 insertions(+), 30 deletions(-) diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs index ebde50eeb..61a9f87e5 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs @@ -193,6 +193,12 @@ protected override async Task ApplyResponseChallengeAsync() State = OpenIdConnectAuthenticationDefaults.AuthenticationPropertiesKey + "=" + UrlEncoder.UrlEncode(Options.StateDataFormat.Protect(properties)) }; + // if response_type=code, nonce is not required. + if (Options.ResponseType.Equals(OpenIdConnectResponseTypes.Code)) + { + Options.ProtocolValidator.RequireNonce = false; + } + if (Options.ProtocolValidator.RequireNonce) { message.Nonce = Options.ProtocolValidator.GenerateNonce(); @@ -317,8 +323,8 @@ protected override async Task AuthenticateCoreAsync() return null; } - var isCodeOnlyFlow = (properties.Items.ContainsKey(OpenIdConnectParameterNames.ResponseType) ? - (properties.Items[OpenIdConnectParameterNames.ResponseType] == OpenIdConnectResponseTypes.CodeOnly) : false); + var isCodeOnlyFlow = properties.Items.ContainsKey(OpenIdConnectParameterNames.ResponseType) ? + (properties.Items[OpenIdConnectParameterNames.ResponseType] == OpenIdConnectResponseTypes.Code) : false; // devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users. if (!string.IsNullOrWhiteSpace(message.Error)) @@ -341,7 +347,10 @@ protected override async Task AuthenticateCoreAsync() { Logger.LogDebug(Resources.OIDCH_0037_Redeeming_Auth_Code, message.Code); - tokens = await RedeemAuthorizationCode(message.Code); + var redirectUri = properties.Items.ContainsKey(OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey) ? + properties.Items[OpenIdConnectAuthenticationDefaults.RedirectUriUsedForCodeKey] : Options.RedirectUri; + + tokens = await RedeemAuthorizationCode(message.Code, redirectUri); if (tokens != null) { message.IdToken = tokens.IdToken; @@ -462,33 +471,31 @@ protected override async Task AuthenticateCoreAsync() return null; } - string nonce = jwt.Payload.Nonce; - if (Options.NonceCache != null) + // If id_token is received using code only flow, no need to validate chash and nonce. + if (!isCodeOnlyFlow) { - // if the nonce cannot be removed, it was used - if (!Options.NonceCache.TryRemoveNonce(nonce)) + string nonce = jwt.Payload.Nonce; + if (Options.NonceCache != null) { - nonce = null; + // if the nonce cannot be removed, it was used + if (!Options.NonceCache.TryRemoveNonce(nonce)) + { + nonce = null; + } + } + else + { + nonce = ReadNonceCookie(nonce); } - } - else - { - nonce = ReadNonceCookie(nonce); - } - var protocolValidationContext = new OpenIdConnectProtocolValidationContext - { - AuthorizationCode = message.Code, - Nonce = nonce, - }; - - // If id_token is received using code only flow, no need to validate chash as it is not returned in the response. Setting authorizationCode to null will skip the validation of chash. - if (isCodeOnlyFlow) - { - protocolValidationContext.AuthorizationCode = null; - } + var protocolValidationContext = new OpenIdConnectProtocolValidationContext + { + AuthorizationCode = message.Code, + Nonce = nonce, + }; - Options.ProtocolValidator.Validate(jwt, protocolValidationContext); + Options.ProtocolValidator.Validate(jwt, protocolValidationContext); + } } if (message.Code != null) @@ -559,7 +566,7 @@ protected override async Task AuthenticateCoreAsync() } } - protected virtual async Task RedeemAuthorizationCode(string authorizationCode) + protected virtual async Task RedeemAuthorizationCode(string authorizationCode, string redirectUri) { var openIdMessage = new OpenIdConnectMessage() { @@ -567,7 +574,7 @@ protected virtual async Task RedeemAuthorizationCode(strin ClientSecret = Options.ClientSecret, Code = authorizationCode, GrantType = "authorization_code", - RedirectUri = Options.RedirectUri + RedirectUri = redirectUri }; var requestMessage = new HttpRequestMessage(HttpMethod.Post, _configuration.TokenEndpoint); diff --git a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs index 6cf85f8ce..6cc2afd4a 100644 --- a/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs +++ b/test/Microsoft.AspNet.Authentication.Test/OpenIdConnect/OpenIdConnectHandlerTests.cs @@ -7,6 +7,8 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IdentityModel.Tokens; using System.Net.Http; using System.Security.Claims; using System.Threading.Tasks; @@ -64,7 +66,8 @@ static OpenIdConnectHandlerTests() { "OIDCH_0019:", LogLevel.Debug }, { "OIDCH_0020:", LogLevel.Debug }, { "OIDCH_0026:", LogLevel.Error }, - { "OIDCH_0037:", LogLevel.Debug } + { "OIDCH_0037:", LogLevel.Debug }, + { "OIDCH_0039:", LogLevel.Debug } }; BuildLogEntryList(); @@ -164,6 +167,9 @@ public async Task AuthenticateCore() logsEntriesExpected = new int[] { 0, 1, 7, 22, 20, 8}; await RunVariation(LogLevel.Debug, message, CodeReceivedAndRedeemedHandledOptions, errors, logsEntriesExpected); + logsEntriesExpected = new int[] { 0, 1, 7, 22, 20, 23, 12 }; + await RunVariation(LogLevel.Debug, message, GetUserInfoFromUIEndpoint, errors, logsEntriesExpected); + #if _Verbose Console.WriteLine("\n ===== \n"); DisplayErrors(errors); @@ -327,7 +333,8 @@ private static void CodeReceivedHandledOptions(OpenIdConnectAuthenticationOption private static void CodeReceivedAndRedeemedHandledOptions(OpenIdConnectAuthenticationOptions options) { DefaultOptions(options); - options.ResponseType = OpenIdConnectResponseTypes.CodeOnly; + options.ResponseType = OpenIdConnectResponseTypes.Code; + options.StateDataFormat = new CodeOnlyAuthenticationPropertiesFormater(); options.Notifications = new OpenIdConnectAuthenticationNotifications { @@ -361,6 +368,29 @@ private static void DefaultOptions(OpenIdConnectAuthenticationOptions options) } + private static void GetUserInfoFromUIEndpoint(OpenIdConnectAuthenticationOptions options) + { + DefaultOptions(options); + options.ResponseType = OpenIdConnectResponseTypes.Code; + options.StateDataFormat = new CodeOnlyAuthenticationPropertiesFormater(); + options.GetClaimsFromUserInfoEndpoint = true; + options.SecurityTokenValidators = new Collection { new CustomSecurityTokenValidator() }; + options.Notifications = + new OpenIdConnectAuthenticationNotifications + { + SecurityTokenValidated = (notification) => + { + var claimValue = notification.AuthenticationTicket.Principal.FindFirst("test claim"); + if (!claimValue.Value.Equals("test value")) + { + throw new Exception("GetUserInformationAsync: claim added from UI endpoint does not match."); + } + notification.HandleResponse(); + return Task.FromResult(null); + } + }; + } + private static void MessageReceivedHandledOptions(OpenIdConnectAuthenticationOptions options) { DefaultOptions(options); @@ -597,7 +627,7 @@ protected override async Task ApplyResponseChallengeAsync() await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification); } - protected override async Task RedeemAuthorizationCode(string authorizationCode) + protected override async Task RedeemAuthorizationCode(string authorizationCode, string redirectUri) { return new OpenIdConnectMessage() { @@ -605,6 +635,19 @@ protected override async Task RedeemAuthorizationCode(stri }; } + protected override async Task GetUserInformationAsync(AuthenticationProperties properties, OpenIdConnectMessage message, AuthenticationTicket ticket) + { + var claimsIdentity = (ClaimsIdentity)ticket.Principal.Identity; + if (claimsIdentity == null) + { + claimsIdentity = new ClaimsIdentity(); + } + claimsIdentity.AddClaim(new Claim("test claim", "test value")); + ticket.Principal.AddIdentity(claimsIdentity); + return ticket; + } + + } /// @@ -758,6 +801,25 @@ AuthenticationProperties ISecureDataFormat.Unprotect(s } } + /// + /// Processing a requires 'unprotecting' the state. + /// This class side-steps that process. + /// + public class CodeOnlyAuthenticationPropertiesFormater : ISecureDataFormat + { + public string Protect(AuthenticationProperties data) + { + return "protectedData"; + } + + AuthenticationProperties ISecureDataFormat.Unprotect(string protectedText) + { + var properties = new AuthenticationProperties(); + properties.Items.Add(OpenIdConnectParameterNames.ResponseType, OpenIdConnectResponseTypes.Code); + return properties; + } + } + /// /// Used to set up different configurations of metadata for different tests /// @@ -771,4 +833,39 @@ static public IConfigurationManager DefaultStaticCon get { return new StaticConfigurationManager(new OpenIdConnectConfiguration()); } } } + + public class CustomSecurityTokenValidator : ISecurityTokenValidator + { + public bool CanValidateToken + { + get + { + return true; + } + } + + public int MaximumTokenSizeInBytes + { + get + { + return TokenValidationParameters.DefaultMaximumTokenSizeInBytes; + } + + set + { + MaximumTokenSizeInBytes = value; + } + } + + public bool CanReadToken(string securityToken) + { + return true; + } + + public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken) + { + validatedToken = new JwtSecurityToken(); + return new ClaimsPrincipal(); + } + } }