-
Notifications
You must be signed in to change notification settings - Fork 10.5k
OAuth - Creating event for ExchangeCode #9448
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
280f4a4
d3e3b81
1e60e82
fca8b60
0b57464
43e3c22
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| // 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.Net.Http; | ||
| using Microsoft.AspNetCore.Http; | ||
|
|
||
| namespace Microsoft.AspNetCore.Authentication.OAuth | ||
| { | ||
| /// <summary> | ||
| /// Contains information about the relevant to exchanging the code for the access token. | ||
| /// </summary> | ||
| public class OAuthExchangeCodeContext : PropertiesContext<OAuthOptions> | ||
| { | ||
| /// <summary> | ||
| /// Initializes a new <see cref="OAuthExchangeCodeContext"/>. | ||
| /// </summary> | ||
| /// <param name="properties">The <see cref="AuthenticationProperties"/>.</param> | ||
| /// <param name="context">The HTTP environment.</param> | ||
| /// <param name="scheme">The authentication scheme.</param> | ||
| /// <param name="options">The options used by the authentication middleware.</param> | ||
| /// <param name="backchannel">The HTTP client used by the authentication middleware</param> | ||
| /// <param name="redirectUri">The redirect URI</param> | ||
| /// <param name="code">The authorization code gained after user authentication</param> | ||
| public OAuthExchangeCodeContext( | ||
| AuthenticationProperties properties, | ||
| HttpContext context, | ||
| AuthenticationScheme scheme, | ||
| OAuthOptions options, | ||
| HttpClient backchannel, | ||
| string redirectUri, | ||
| string code) | ||
| : base(context, scheme, options, properties) | ||
| { | ||
| Backchannel = backchannel ?? throw new ArgumentNullException(nameof(backchannel)); | ||
| RedirectUri = redirectUri; | ||
| Code = code; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets the backchannel used to communicate with the provider. | ||
| /// </summary> | ||
| public HttpClient Backchannel { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the URI used for the redirect operation. | ||
| /// </summary> | ||
| public string RedirectUri { get; } | ||
|
|
||
| /// <summary> | ||
| /// Gets the code returned by the authentication provider after user authenticates | ||
|
||
| /// </summary> | ||
| public string Code { get; } | ||
|
|
||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -99,7 +99,7 @@ protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync | |
| return HandleRequestResult.Fail("Code was not found.", properties); | ||
| } | ||
|
|
||
| using (var tokens = await ExchangeCodeAsync(code, BuildRedirectUri(Options.CallbackPath))) | ||
| using (var tokens = await ExchangeCodeAsync(code, BuildRedirectUri(Options.CallbackPath), properties)) | ||
| { | ||
| if (tokens.Error != null) | ||
| { | ||
|
|
@@ -159,42 +159,10 @@ protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync | |
| } | ||
| } | ||
|
|
||
| protected virtual async Task<OAuthTokenResponse> ExchangeCodeAsync(string code, string redirectUri) | ||
| protected virtual async Task<OAuthTokenResponse> ExchangeCodeAsync(string code, string redirectUri, AuthenticationProperties properties) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a non-breaking way to add this new parameter? E.g. with a new overload?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Tratcher hi, I was trying to make this non-breaking but I really don't see how... An overload won't work because now it is called from
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers/search?q=ExchangeCodeAsync&unscoped_q=ExchangeCodeAsync
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, the first one about simply augmenting the Dictionary, I totally agree with you and I already changed it.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Tratcher is it possible to add a new API ExchangeCodeAsnyc(ExchangeCodeContext context)? This would future proof us a bit. We have used this in other places.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @brentschmaltz yes, that's plausible. Either way it's a breaking change for many providers. If we added PKCE support directly we might be able to avoid this break. #9448 (comment) |
||
| { | ||
| var tokenRequestParameters = new Dictionary<string, string>() | ||
| { | ||
| { "client_id", Options.ClientId }, | ||
| { "redirect_uri", redirectUri }, | ||
| { "client_secret", Options.ClientSecret }, | ||
| { "code", code }, | ||
| { "grant_type", "authorization_code" }, | ||
| }; | ||
|
|
||
| var requestContent = new FormUrlEncodedContent(tokenRequestParameters); | ||
|
|
||
| var requestMessage = new HttpRequestMessage(HttpMethod.Post, Options.TokenEndpoint); | ||
| requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); | ||
| requestMessage.Content = requestContent; | ||
| var response = await Backchannel.SendAsync(requestMessage, Context.RequestAborted); | ||
| if (response.IsSuccessStatusCode) | ||
| { | ||
| var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync()); | ||
| return OAuthTokenResponse.Success(payload); | ||
| } | ||
| else | ||
| { | ||
| var error = "OAuth token endpoint failure: " + await Display(response); | ||
| return OAuthTokenResponse.Failed(new Exception(error)); | ||
| } | ||
| } | ||
|
|
||
| private static async Task<string> Display(HttpResponseMessage response) | ||
| { | ||
| var output = new StringBuilder(); | ||
| output.Append("Status: " + response.StatusCode + ";"); | ||
| output.Append("Headers: " + response.Headers.ToString() + ";"); | ||
| output.Append("Body: " + await response.Content.ReadAsStringAsync() + ";"); | ||
| return output.ToString(); | ||
| var exchangeCodeContext = new OAuthExchangeCodeContext(properties, Context, Scheme, Options, Backchannel, redirectUri, code); | ||
| return await Events.OnExchangeCode(exchangeCodeContext); | ||
| } | ||
|
|
||
| protected virtual async Task<AuthenticationTicket> CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is more code than is appropriate for events. Deriving from OAuthHandler is more appropriate once you need to replace this much.
Is there a more specific event that would still be useful? Like adding an event on line 45 that passed the parameters dictionary so it could be augmented? The default implementation would no-op. Would that be enough for your PKCE example?