diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs index cc132e9bb..61a9f87e5 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationHandler.cs @@ -1,11 +1,14 @@ -// 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; +using System.Collections.Generic; using System.Globalization; using System.IdentityModel.Tokens; 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; @@ -13,6 +16,7 @@ using Microsoft.AspNet.Http.Authentication; using Microsoft.Framework.Logging; using Microsoft.IdentityModel.Protocols; +using Newtonsoft.Json.Linq; namespace Microsoft.AspNet.Authentication.OpenIdConnect { @@ -38,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(); @@ -153,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); @@ -183,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(); @@ -238,6 +254,7 @@ protected override AuthenticationTicket AuthenticateCore() /// Uses log id's OIDCH-0000 - OIDCH-0025 protected override async Task AuthenticateCoreAsync() { + 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. @@ -299,12 +316,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.Code) : false; + // devs will need to hook AuthenticationFailedNotification to avoid having 'raw' runtime errors displayed to users. if (!string.IsNullOrWhiteSpace(message.Error)) { @@ -321,6 +342,21 @@ protected override async Task AuthenticateCoreAsync() _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); } + // Redeeming authorization code for tokens + if (string.IsNullOrWhiteSpace(message.IdToken) && isCodeOnlyFlow) + { + Logger.LogDebug(Resources.OIDCH_0037_Redeeming_Auth_Code, 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; + } + } + // OpenIdConnect protocol allows a Code to be received without the id_token if (!string.IsNullOrWhiteSpace(message.IdToken)) { @@ -409,6 +445,12 @@ protected override async Task AuthenticateCoreAsync() } } + if (Options.GetClaimsFromUserInfoEndpoint) + { + Logger.LogDebug(Resources.OIDCH_0039_Sending_Request_UIEndpoint); + ticket = await GetUserInformationAsync(properties, tokens, ticket); + } + var securityTokenValidatedNotification = new SecurityTokenValidatedNotification(Context, Options) { @@ -429,27 +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) + { + // if the nonce cannot be removed, it was used + if (!Options.NonceCache.TryRemoveNonce(nonce)) + { + nonce = null; + } + } + else { - nonce = null; + nonce = ReadNonceCookie(nonce); } - } - else - { - nonce = ReadNonceCookie(nonce); - } - var protocolValidationContext = new OpenIdConnectProtocolValidationContext - { - AuthorizationCode = message.Code, - Nonce = nonce, - }; + var protocolValidationContext = new OpenIdConnectProtocolValidationContext + { + AuthorizationCode = message.Code, + Nonce = nonce, + }; - Options.ProtocolValidator.Validate(jwt, protocolValidationContext); + Options.ProtocolValidator.Validate(jwt, protocolValidationContext); + } } if (message.Code != null) @@ -520,6 +566,90 @@ protected override async Task AuthenticateCoreAsync() } } + protected virtual async Task RedeemAuthorizationCode(string authorizationCode, string redirectUri) + { + var openIdMessage = new OpenIdConnectMessage() + { + ClientId = Options.ClientId, + ClientSecret = Options.ClientSecret, + Code = authorizationCode, + GrantType = "authorization_code", + RedirectUri = redirectUri + }; + + var requestMessage = new HttpRequestMessage(HttpMethod.Post, _configuration.TokenEndpoint); + requestMessage.Content = new FormUrlEncodedContent(openIdMessage.Parameters); + var responseMessage = await Backchannel.SendAsync(requestMessage); + responseMessage.EnsureSuccessStatusCode(); + var tokenResonse = await responseMessage.Content.ReadAsStringAsync(); + var jsonTokenResponse = JObject.Parse(tokenResonse); + return new OpenIdConnectMessage() + { + 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) + { + 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, 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).Value; + + // check if the sub claim matches + var userInfoSubject = user.Value("sub"); + 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 (var pair in user) + { + JToken value; + var claimValue = user.TryGetValue(pair.Key, out value) ? value.ToString() : null; + + var 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; + } + /// /// Adds the nonce to . /// diff --git a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationMiddleware.cs index 9353f385c..a507dee49 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; @@ -13,11 +13,11 @@ 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; +using Microsoft.IdentityModel.Protocols; namespace Microsoft.AspNet.Authentication.OpenIdConnect { @@ -100,6 +100,11 @@ public OpenIdConnectAuthenticationMiddleware( Options.TokenValidationParameters.ValidAudience = Options.ClientId; } + Backchannel = new HttpClient(ResolveHttpMessageHandler(Options)); + Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET OpenIdConnect middleware"); + Backchannel.Timeout = Options.BackchannelTimeout; + Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB + if (Options.ConfigurationManager == null) { if (Options.Configuration != null) @@ -119,21 +124,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 9e4bfa55d..d77dd87fb 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/OpenIdConnectAuthenticationOptions.cs @@ -55,6 +55,7 @@ public OpenIdConnectAuthenticationOptions(string authenticationScheme) AuthenticationScheme = authenticationScheme; BackchannelTimeout = TimeSpan.FromMinutes(1); Caption = OpenIdConnectAuthenticationDefaults.Caption; + GetClaimsFromUserInfoEndpoint = false; ProtocolValidator = new OpenIdConnectProtocolValidator(); RefreshOnIssuerKeyNotFound = true; ResponseMode = OpenIdConnectResponseModes.FormPost; @@ -163,6 +164,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 b95de8318..ad1702f04 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.Designer.cs @@ -172,6 +172,30 @@ 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_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_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 454dae209..0084347f2 100644 --- a/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx +++ b/src/Microsoft.AspNet.Authentication.OpenIdConnect/Resources.resx @@ -222,4 +222,13 @@ OIDCH_0020: 'id_token' received: '{0}' - + + 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. + + + OIDCH_0039: Sending request to user info endpoint for retrieving claims. + + \ 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 183ae6018..6cc2afd4a 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. @@ -7,9 +7,13 @@ 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; using Microsoft.AspNet.Authentication.Notifications; +using Microsoft.AspNet.Authentication.OAuth; using Microsoft.AspNet.Authentication.OpenIdConnect; using Microsoft.AspNet.Builder; using Microsoft.AspNet.DataProtection; @@ -62,6 +66,8 @@ static OpenIdConnectHandlerTests() { "OIDCH_0019:", LogLevel.Debug }, { "OIDCH_0020:", LogLevel.Debug }, { "OIDCH_0026:", LogLevel.Error }, + { "OIDCH_0037:", LogLevel.Debug }, + { "OIDCH_0039:", LogLevel.Debug } }; BuildLogEntryList(); @@ -157,6 +163,13 @@ public async Task AuthenticateCore() 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, 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); @@ -317,6 +330,22 @@ private static void CodeReceivedHandledOptions(OpenIdConnectAuthenticationOption }; } + private static void CodeReceivedAndRedeemedHandledOptions(OpenIdConnectAuthenticationOptions options) + { + DefaultOptions(options); + options.ResponseType = OpenIdConnectResponseTypes.Code; + options.StateDataFormat = new CodeOnlyAuthenticationPropertiesFormater(); + options.Notifications = + new OpenIdConnectAuthenticationNotifications + { + SecurityTokenReceived = (notification) => + { + notification.HandleResponse(); + return Task.FromResult(null); + } + }; + } + private static void CodeReceivedSkippedOptions(OpenIdConnectAuthenticationOptions options) { DefaultOptions(options); @@ -336,6 +365,30 @@ private static void DefaultOptions(OpenIdConnectAuthenticationOptions options) options.AuthenticationScheme = "OpenIdConnectHandlerTest"; options.ConfigurationManager = ConfigurationManager.DefaultStaticConfigurationManager; options.StateDataFormat = new AuthenticationPropertiesFormater(); + + } + + 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) @@ -397,6 +450,7 @@ private static void SecurityTokenReceivedSkippedOptions(OpenIdConnectAuthenticat private static void SecurityTokenValidatedHandledOptions(OpenIdConnectAuthenticationOptions options) { DefaultOptions(options); + options.GetClaimsFromUserInfoEndpoint = true; options.Notifications = new OpenIdConnectAuthenticationNotifications { @@ -544,6 +598,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); @@ -570,6 +626,28 @@ protected override async Task ApplyResponseChallengeAsync() await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification); } + + protected override async Task RedeemAuthorizationCode(string authorizationCode, string redirectUri) + { + return new OpenIdConnectMessage() + { + IdToken = "test token" + }; + } + + 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; + } + + } /// @@ -723,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 /// @@ -736,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(); + } + } }