#690 Enable custom OIDC authorization code redemption.

This commit is contained in:
Chris R 2016-02-19 12:24:40 -08:00
parent 0372daeebf
commit 9bbbe535f2
9 changed files with 423 additions and 48 deletions

View File

@ -1,7 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.23107.0
VisualStudioVersion = 14.0.24720.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4D2B6A51-2F9F-44F5-8131-EA5CAC053652}"
EndProject
@ -56,6 +55,8 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Owin.Security.Int
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.Owin.Security.Interop.Test", "test\Microsoft.Owin.Security.Interop.Test\Microsoft.Owin.Security.Interop.Test.xproj", "{A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "OpenIdConnect.AzureAdSample", "samples\OpenIdConnect.AzureAdSample\OpenIdConnect.AzureAdSample.xproj", "{3A7AD414-EBDE-4F92-B307-4E8F19B6117E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -312,6 +313,18 @@ Global
{A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Release|x86.ActiveCfg = Release|Any CPU
{A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24}.Release|x86.Build.0 = Release|Any CPU
{3A7AD414-EBDE-4F92-B307-4E8F19B6117E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3A7AD414-EBDE-4F92-B307-4E8F19B6117E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3A7AD414-EBDE-4F92-B307-4E8F19B6117E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{3A7AD414-EBDE-4F92-B307-4E8F19B6117E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{3A7AD414-EBDE-4F92-B307-4E8F19B6117E}.Debug|x86.ActiveCfg = Debug|Any CPU
{3A7AD414-EBDE-4F92-B307-4E8F19B6117E}.Debug|x86.Build.0 = Debug|Any CPU
{3A7AD414-EBDE-4F92-B307-4E8F19B6117E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3A7AD414-EBDE-4F92-B307-4E8F19B6117E}.Release|Any CPU.Build.0 = Release|Any CPU
{3A7AD414-EBDE-4F92-B307-4E8F19B6117E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{3A7AD414-EBDE-4F92-B307-4E8F19B6117E}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{3A7AD414-EBDE-4F92-B307-4E8F19B6117E}.Release|x86.ActiveCfg = Release|Any CPU
{3A7AD414-EBDE-4F92-B307-4E8F19B6117E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -338,5 +351,6 @@ Global
{D399B84F-591B-4E98-92BA-B0F63E7B6957} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF}
{A7922DD8-09F1-43E4-938B-CC523EA08898} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652}
{A2B5DC39-68D5-4145-A8CC-6AEAB7D33A24} = {7BF11F3A-60B6-4796-B504-579C67FFBA34}
{3A7AD414-EBDE-4F92-B307-4E8F19B6117E} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,56 @@
using System;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
namespace OpenIdConnect.AzureAdSample
{
public class AuthPropertiesTokenCache : TokenCache
{
private const string TokenCacheKey = ".TokenCache";
private AuthenticationProperties _authProperties;
public bool HasCacheChanged { get; internal set; }
public AuthPropertiesTokenCache(AuthenticationProperties authProperties) : base()
{
_authProperties = authProperties;
BeforeAccess = BeforeAccessNotification;
AfterAccess = AfterAccessNotification;
BeforeWrite = BeforeWriteNotification;
string cachedTokensText;
if (authProperties.Items.TryGetValue(TokenCacheKey, out cachedTokensText))
{
var cachedTokens = Convert.FromBase64String(cachedTokensText);
Deserialize(cachedTokens);
}
}
// Notification raised before ADAL accesses the cache.
// This is your chance to update the in-memory copy from the DB, if the in-memory version is stale
private void BeforeAccessNotification(TokenCacheNotificationArgs args)
{
}
// Notification raised after ADAL accessed the cache.
// If the HasStateChanged flag is set, ADAL changed the content of the cache
private void AfterAccessNotification(TokenCacheNotificationArgs args)
{
// if state changed
if (HasStateChanged)
{
HasCacheChanged = true;
var cachedTokens = Serialize();
var cachedTokensText = Convert.ToBase64String(cachedTokens);
_authProperties.Items[TokenCacheKey] = cachedTokensText;
}
}
private void BeforeWriteNotification(TokenCacheNotificationArgs args)
{
// if you want to ensure that no concurrent write take place, use this notification to place a lock on the entry
}
}
}

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>3a7ad414-ebde-4f92-b307-4e8f19b6117e</ProjectGuid>
<RootNamespace>OpenIdConnect.AzureAdSample</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">..\..\artifacts\obj\$(MSBuildProjectName)</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">..\..\artifacts\bin\$(MSBuildProjectName)\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<DnxInvisibleContent Include="bower.json" />
<DnxInvisibleContent Include=".bowerrc" />
<DnxInvisibleContent Include="package.json" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,25 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:42023",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"Hosting:Environment": "Development"
}
},
"web": {
"commandName": "web",
"environmentVariables": {
"Hosting:Environment": "Development"
}
}
}
}

View File

@ -0,0 +1,171 @@
using System;
using System.Linq;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Http.Features.Authentication;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace OpenIdConnect.AzureAdSample
{
public class Startup
{
private const string GraphResourceID = "https://graph.windows.net";
public Startup()
{
Configuration = new ConfigurationBuilder()
.AddEnvironmentVariables()
.AddUserSecrets()
.Build();
}
public IConfiguration Configuration { get; set; }
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(sharedOptions =>
sharedOptions.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme);
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerfactory)
{
loggerfactory.AddConsole(LogLevel.Information);
// Simple error page
app.Use(async (context, next) =>
{
try
{
await next();
}
catch (Exception ex)
{
if (!context.Response.HasStarted)
{
context.Response.Clear();
context.Response.StatusCode = 500;
await context.Response.WriteAsync(ex.ToString());
}
else
{
throw;
}
}
});
app.UseIISPlatformHandler();
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AutomaticAuthenticate = true
});
var clientId = Configuration["oidc:clientid"];
var clientSecret = Configuration["oidc:clientsecret"];
var authority = Configuration["oidc:authority"];
var resource = "https://graph.windows.net";
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
ClientId = clientId,
ClientSecret = clientSecret, // for code flow
Authority = authority,
ResponseType = OpenIdConnectResponseTypes.CodeIdToken,
// GetClaimsFromUserInfoEndpoint = true,
Events = new OpenIdConnectEvents()
{
OnAuthorizationCodeReceived = async context =>
{
var request = context.HttpContext.Request;
var currentUri = UriHelper.Encode(request.Scheme, request.Host, request.PathBase, request.Path);
var credential = new ClientCredential(clientId, clientSecret);
var authContext = new AuthenticationContext(authority, new AuthPropertiesTokenCache(context.Properties));
var result = await authContext.AcquireTokenByAuthorizationCodeAsync(
context.ProtocolMessage.Code, new Uri(currentUri), credential, resource);
context.HandleCodeRedemption(result.AccessToken, result.IdToken);
}
}
});
app.Run(async context =>
{
if (context.Request.Path.Equals("/signout"))
{
await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
context.Response.ContentType = "text/html";
await context.Response.WriteAsync($"<html><body>Signing out {context.User.Identity.Name}<br>{Environment.NewLine}");
await context.Response.WriteAsync("<a href=\"/\">Sign In</a>");
await context.Response.WriteAsync($"</body></html>");
return;
}
if (!context.User.Identities.Any(identity => identity.IsAuthenticated))
{
await context.Authentication.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" });
return;
}
context.Response.ContentType = "text/html";
await context.Response.WriteAsync($"<html><body>Hello Authenticated User {context.User.Identity.Name}<br>{Environment.NewLine}");
await context.Response.WriteAsync("Claims:<br>" + Environment.NewLine);
foreach (var claim in context.User.Claims)
{
await context.Response.WriteAsync($"{claim.Type}: {claim.Value}<br>{Environment.NewLine}");
}
await context.Response.WriteAsync("Tokens:<br>" + Environment.NewLine);
try
{
// Retrieve the auth session with the cached tokens
var authenticateContext = new AuthenticateContext(CookieAuthenticationDefaults.AuthenticationScheme);
await context.Authentication.AuthenticateAsync(authenticateContext);
var authProperties = new AuthenticationProperties(authenticateContext.Properties);
var tokenCache = new AuthPropertiesTokenCache(authProperties);
// Use ADAL to get the right token
var authContext = new AuthenticationContext(authority, tokenCache);
var credential = new ClientCredential(clientId, clientSecret);
string userObjectID = context.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var result = authContext.AcquireTokenSilent(resource, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
// Update the cookie with the modified tokens
if (tokenCache.HasCacheChanged)
{
await context.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, authenticateContext.Principal, authProperties);
}
await context.Response.WriteAsync($"access_token: {result.AccessToken}<br>{Environment.NewLine}");
}
catch (Exception ex)
{
await context.Response.WriteAsync($"AquireToken error: {ex.Message}<br>{Environment.NewLine}");
}
await context.Response.WriteAsync("<a href=\"/signout\">Sign Out</a>");
await context.Response.WriteAsync($"</body></html>");
});
}
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseDefaultConfiguration(args)
.UseServer("Microsoft.AspNetCore.Server.Kestrel")
.UseIISPlatformHandlerUrl()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
}

View File

@ -0,0 +1,23 @@
{
"dependencies": {
"Microsoft.AspNetCore.Authentication.Cookies": "1.0.0-*",
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "0.1.0-*",
"Microsoft.AspNetCore.Http.Extensions": "1.0.0-*",
"Microsoft.AspNetCore.IISPlatformHandler": "1.0.0-*",
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0-*",
"Microsoft.Extensions.Configuration.UserSecrets": "1.0.0-*",
"Microsoft.Extensions.Logging.Console": "1.0.0-*",
"Microsoft.IdentityModel.Clients.ActiveDirectory": "2.22.302111727",
"Microsoft.NETCore.Platforms": "1.0.1-*"
},
"frameworks": {
"dnx451": { }
},
"compilationOptions": {
"emitEntryPoint": true
},
"commands": {
"web": "OpenIdConnect.AzureAdSample"
},
"userSecretsId": "aspnet5-OpenIdConnectSample-20151210110318"
}

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="httpPlatformHandler" path="*" verb="*" modules="httpPlatformHandler" resourceType="Unspecified"/>
</handlers>
<httpPlatform processPath="%DNX_PATH%" arguments="%DNX_ARGS%" stdoutLogEnabled="false" startupTimeLimit="3600"/>
</system.webServer>
</configuration>

View File

@ -1,11 +1,12 @@
// 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.Diagnostics.CodeAnalysis;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
{
@ -17,29 +18,76 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// <summary>
/// Creates a <see cref="AuthorizationCodeReceivedContext"/>
/// </summary>
public AuthorizationCodeReceivedContext(HttpContext context, OpenIdConnectOptions options, AuthenticationProperties properties)
public AuthorizationCodeReceivedContext(HttpContext context, OpenIdConnectOptions options)
: base(context, options)
{
Properties = properties;
}
public AuthenticationProperties Properties { get; set; }
/// <summary>
/// Gets or sets the 'code'.
/// </summary>
public string Code { get; set; }
/// <summary>
/// Gets or sets the <see cref="JwtSecurityToken"/> that was received in the id_token + code OpenIdConnectRequest.
/// Gets or sets the <see cref="JwtSecurityToken"/> that was received in the authentication response, if any.
/// </summary>
public JwtSecurityToken JwtSecurityToken { get; set; }
/// <summary>
/// Gets or sets the 'redirect_uri'.
/// The request that will be sent to the token endpoint and is available for customization.
/// </summary>
/// <remarks>This is the redirect_uri that was sent in the id_token + code OpenIdConnectRequest.</remarks>
[SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings", Justification = "user controlled, not necessarily a URI")]
public string RedirectUri { get; set; }
public OpenIdConnectMessage TokenEndpointRequest { get; set; }
/// <summary>
/// The configured communication channel to the identity provider for use when making custom requests to the token endpoint.
/// </summary>
public HttpClient Backchannel { get; internal set; }
/// <summary>
/// If the developer chooses to redeem the code themselves then they can provide the resulting tokens here. This is the
/// same as calling HandleCodeRedemption. If set then the middleware will not attempt to redeem the code. An IdToken
/// is required if one had not been previously received in the authorization response. An access token is optional
/// if the middleware is to contact the user-info endpoint.
/// </summary>
public OpenIdConnectMessage TokenEndpointResponse { get; set; }
/// <summary>
/// Indicates if the developer choose to handle (or skip) the code redemption. If true then the middleware will not attempt
/// to redeem the code. See HandleCodeRedemption and TokenEndpointResponse.
/// </summary>
public bool HandledCodeRedemption => TokenEndpointResponse != null;
/// <summary>
/// Tells the middleware to skip the code redemption process. The developer may have redeemed the code themselves, or
/// decided that the redemption was not required. If tokens were retrieved that are needed for further processing then
/// call one of the overloads that allows providing tokens. An IdToken is required if one had not been previously received
/// in the authorization response. An access token can optionally be provided for the middleware to contact the
/// user-info endpoint. Calling this is the same as setting TokenEndpointResponse.
/// </summary>
public void HandleCodeRedemption()
{
TokenEndpointResponse = new OpenIdConnectMessage();
}
/// <summary>
/// Tells the middleware to skip the code redemption process. The developer may have redeemed the code themselves, or
/// decided that the redemption was not required. If tokens were retrieved that are needed for further processing then
/// call one of the overloads that allows providing tokens. An IdToken is required if one had not been previously received
/// in the authorization response. An access token can optionally be provided for the middleware to contact the
/// user-info endpoint. Calling this is the same as setting TokenEndpointResponse.
/// </summary>
public void HandleCodeRedemption(string accessToken, string idToken)
{
TokenEndpointResponse = new OpenIdConnectMessage() { AccessToken = accessToken, IdToken = idToken };
}
/// <summary>
/// Tells the middleware to skip the code redemption process. The developer may have redeemed the code themselves, or
/// decided that the redemption was not required. If tokens were retrieved that are needed for further processing then
/// call one of the overloads that allows providing tokens. An IdToken is required if one had not been previously received
/// in the authorization response. An access token can optionally be provided for the middleware to contact the
/// user-info endpoint. Calling this is the same as setting TokenEndpointResponse.
/// </summary>
public void HandleCodeRedemption(OpenIdConnectMessage tokenEndpointResponse)
{
TokenEndpointResponse = tokenEndpointResponse;
}
}
}

View File

@ -303,7 +303,6 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// Invoked to process incoming OpenIdConnect messages.
/// </summary>
/// <returns>An <see cref="AuthenticationTicket"/> if successful.</returns>
/// <remarks>Uses log id's OIDCH-0000 - OIDCH-0025</remarks>
protected override async Task<AuthenticateResult> HandleRemoteAuthenticateAsync()
{
Logger.LogTrace(10, "Entering: {0}." + nameof(HandleRemoteAuthenticateAsync), GetType());
@ -450,16 +449,23 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
// Authorization Code or Hybrid flow
if (!string.IsNullOrEmpty(authorizationResponse.Code))
{
// TODO: Does this event provide any value over AuthorizationResponseReceived or AuthorizationResponseValidated?
var authorizationCodeReceivedContext = await RunAuthorizationCodeReceivedEventAsync(authorizationResponse, properties, ticket, jwt);
if (CheckEventResult(authorizationCodeReceivedContext, out result))
{
return result;
}
authorizationResponse = authorizationCodeReceivedContext.ProtocolMessage;
var code = authorizationCodeReceivedContext.Code;
properties = authorizationCodeReceivedContext.Properties;
var tokenEndpointRequest = authorizationCodeReceivedContext.TokenEndpointRequest;
// If the developer redeemed the code themselves...
tokenEndpointResponse = authorizationCodeReceivedContext.TokenEndpointResponse;
ticket = authorizationCodeReceivedContext.Ticket;
jwt = authorizationCodeReceivedContext.JwtSecurityToken;
tokenEndpointResponse = await RedeemAuthorizationCodeAsync(code, authorizationCodeReceivedContext.RedirectUri);
if (!authorizationCodeReceivedContext.HandledCodeRedemption)
{
tokenEndpointResponse = await RedeemAuthorizationCodeAsync(tokenEndpointRequest);
}
var authorizationCodeRedeemedContext = await RunTokenResponseReceivedEventAsync(authorizationResponse, tokenEndpointResponse, properties);
if (CheckEventResult(authorizationCodeRedeemedContext, out result))
@ -485,13 +491,17 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
}
}
Options.ProtocolValidator.ValidateTokenResponse(new OpenIdConnectProtocolValidationContext()
// Validate the token response if it wasn't provided manually
if (!authorizationCodeReceivedContext.HandledCodeRedemption)
{
ClientId = Options.ClientId,
ProtocolMessage = tokenEndpointResponse,
ValidatedIdToken = jwt,
Nonce = nonce
});
Options.ProtocolValidator.ValidateTokenResponse(new OpenIdConnectProtocolValidationContext()
{
ClientId = Options.ClientId,
ProtocolMessage = tokenEndpointResponse,
ValidatedIdToken = jwt,
Nonce = nonce
});
}
}
var authenticationValidatedContext = await RunAuthenticationValidatedEventAsync(authorizationResponse, ticket, properties, tokenEndpointResponse);
@ -574,23 +584,11 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
/// <param name="authorizationCode">The authorization code to redeem.</param>
/// <param name="redirectUri">Uri that was passed in the request sent for the authorization code.</param>
/// <returns>OpenIdConnect message that has tokens inside it.</returns>
protected virtual async Task<OpenIdConnectMessage> RedeemAuthorizationCodeAsync(string authorizationCode, string redirectUri)
protected virtual async Task<OpenIdConnectMessage> RedeemAuthorizationCodeAsync(OpenIdConnectMessage tokenEndpointRequest)
{
Logger.LogDebug(21, "Redeeming code for tokens.");
var openIdMessage = new OpenIdConnectMessage()
{
ClientId = Options.ClientId,
ClientSecret = Options.ClientSecret,
Code = authorizationCode,
GrantType = "authorization_code",
RedirectUri = redirectUri
};
// TODO: Event that lets you customize the message. E.g. use certificates, specify resources.
var requestMessage = new HttpRequestMessage(HttpMethod.Post, _configuration.TokenEndpoint);
requestMessage.Content = new FormUrlEncodedContent(openIdMessage.Parameters);
requestMessage.Content = new FormUrlEncodedContent(tokenEndpointRequest.Parameters);
var responseMessage = await Backchannel.SendAsync(requestMessage);
responseMessage.EnsureSuccessStatusCode();
var tokenResonse = await responseMessage.Content.ReadAsStringAsync();
@ -874,19 +872,27 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
return authorizationResponseReceivedContext;
}
private async Task<AuthorizationCodeReceivedContext> RunAuthorizationCodeReceivedEventAsync(OpenIdConnectMessage message, AuthenticationProperties properties, AuthenticationTicket ticket, JwtSecurityToken jwt)
private async Task<AuthorizationCodeReceivedContext> RunAuthorizationCodeReceivedEventAsync(OpenIdConnectMessage authorizationResponse, AuthenticationProperties properties, AuthenticationTicket ticket, JwtSecurityToken jwt)
{
var redirectUri = properties.Items[OpenIdConnectDefaults.RedirectUriForCodePropertiesKey];
Logger.LogTrace(32, "AuthorizationCode received");
Logger.LogTrace(32, "AuthorizationCode received: '{0}'", message.Code);
var authorizationCodeReceivedContext = new AuthorizationCodeReceivedContext(Context, Options, properties)
var tokenEndpointRequest = new OpenIdConnectMessage()
{
Code = message.Code,
ProtocolMessage = message,
RedirectUri = redirectUri,
ClientId = Options.ClientId,
ClientSecret = Options.ClientSecret,
Code = authorizationResponse.Code,
GrantType = OpenIdConnectGrantTypes.AuthorizationCode,
RedirectUri = properties.Items[OpenIdConnectDefaults.RedirectUriForCodePropertiesKey]
};
var authorizationCodeReceivedContext = new AuthorizationCodeReceivedContext(Context, Options)
{
ProtocolMessage = authorizationResponse,
Properties = properties,
TokenEndpointRequest = tokenEndpointRequest,
Ticket = ticket,
JwtSecurityToken = jwt
JwtSecurityToken = jwt,
Backchannel = Backchannel,
};
await Options.Events.AuthorizationCodeReceived(authorizationCodeReceivedContext);