Skip to content
This repository was archived by the owner on Dec 13, 2018. It is now read-only.

Conversation

@speige
Copy link
Contributor

@speige speige commented Apr 6, 2018

#1695

Tested in my app & working

@dnfclas
Copy link

dnfclas commented Apr 6, 2018

CLA assistant check
All CLA requirements met.

Copy link
Member

@Tratcher Tratcher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before I review this too closely, please add a sample or test showing how you expect the new public APIs to be called from an application. Getting access to an instance of the handler is non-trivial.

namespace Microsoft.AspNetCore.Authentication.Twitter
{
public class TwitterHandler : RemoteAuthenticationHandler<TwitterOptions>
public class TwitterHandler : RemoteAuthenticationHandler<TwitterOptions>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spacing changed? Revert.


public async Task<HttpResponseMessage> ExecuteRequestAsync(string url, HttpMethod httpMethod, RequestToken accessToken = null, Dictionary<string, string> extraOAuthPairs = null, Dictionary<string, string> queryParameters = null, Dictionary<string, string> formData = null)
{
StringBuilder canonicalizedRequestBuilder = new StringBuilder();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also use var everywhere in the new code.

@speige
Copy link
Contributor Author

speige commented Apr 6, 2018

Accidental whitespace changes removed

@speige
Copy link
Contributor Author

speige commented Apr 7, 2018

updated based on request to use "var" instead of specific types

@speige
Copy link
Contributor Author

speige commented Apr 7, 2018

@Tratcher
The original intent of the PR wasn't to make the reusable method public (although that's an added bonus). It was because the code didn't match Twitter's API Spec (based on the issue I submitted). It seemed to work most of the time, but I was nervous there would be random hard to debug errors.

To get an instance of TwitterHandler via the constructor is painful. However, with DI it's not too bad.

I'm use .net core with SimpleInjector instead of the base DI. You have to use CrossWire to make classes registered with the built-in DI available to your custom classes. I assume it's trivial with the built-in DI, so I'm leaving out the DI code itself.

After DI of IHttpContextAccessor & AuthenticationHandlerProvider.

	        TwitterHandler twitterHandler = (TwitterHandler)await _authenticationHandlerProvider.GetHandlerAsync(_httpContextAccessor.HttpContext, "Twitter");

You could also DI TwitterHandler, but the Scheme won't be set, so you have to call InitializeAsync (which also requires DI of HttpContext).

@Tratcher
Copy link
Member

Tratcher commented Apr 7, 2018

Yeah, that's about what I'd expect. Can you add something to https://github.com/aspnet/Security/blob/dev/test/Microsoft.AspNetCore.Authentication.Test/TwitterTests.cs and/or https://github.com/aspnet/Security/blob/dev/samples/SocialSample/Startup.cs? We don't want to have any public APIs that we don't call, we'll never know if they break.

@speige
Copy link
Contributor Author

speige commented Apr 9, 2018

@Tratcher I'm confused. Pretty much every test in TwitterTests.cs already calls ExecuteRequestAsync. If you add a breakpoint to the method & debug unit tests, you'll see it.

I'm kindof braindead when it comes to unit testing, so I'm not confident I can write a test that will be satisfactory. Would you mind coding that portion if it's important for the pull request to be accepted? Sorry :(

@Tratcher
Copy link
Member

Sorry, I meant that shows what it's like to call the API directly like an end user would.

@speige
Copy link
Contributor Author

speige commented Apr 10, 2018

Makes sense. I'll throw something together.

@kevinchalet
Copy link
Contributor

Having public APIs in the handlers classes reminds me of a cool story about Pandora and a Box... :trollface:

IMHO, pointing folks to user-friendly Twitter clients like https://github.com/JoeMayo/LinqToTwitter or https://github.com/linvi/tweetinvi would be much better than exposing an obscure method in the handler itself (both will take care of generating the Authorization header with all the signing madness for you)

@speige
Copy link
Contributor Author

speige commented Apr 10, 2018

I'm not opposed to leaving the method private if it raises concerns. The initial intent of the pull request was because the current signing method did not match the spec. However, in that case, why have aspnet/Security implement the signing code itself instead of also referencing a 3rd party library?

@Eilon
Copy link
Contributor

Eilon commented Apr 12, 2018

Let's keep it private for now because it might otherwise be difficult code to maintain forever.

Copy link
Member

@Tratcher Tratcher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has a few tradeoffs that need to be worked out. I like how you've reduced the duplication, but some of the refactors have also reduced the readability and efficiency.

Logger.ObtainRequestToken();

var response = await Backchannel.SendAsync(request, Context.RequestAborted);
var response = await ExecuteRequestAsync(RequestTokenEndpoint, HttpMethod.Post, null, new Dictionary<string, string>() { { "oauth_callback", callBackUri } });
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can drop the null param, it has a default, and use a named parameter for the last param. The same goes for the other calls to ExecuteRequestAsync.

}

private async Task<RequestToken> ObtainRequestTokenAsync(string callBackUri, AuthenticationProperties properties)
public async Task<HttpResponseMessage> ExecuteRequestAsync(string url, HttpMethod httpMethod, RequestToken accessToken = null, Dictionary<string, string> extraOAuthPairs = null, Dictionary<string, string> queryParameters = null, Dictionary<string, string> formData = null)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's make this private for now.

Wrap long lines over ~160 chars

canonicalizedRequestBuilder.Append("&");
canonicalizedRequestBuilder.Append(UrlEncoder.Encode(RequestTokenEndpoint));
var signatureParts = new SortedDictionary<string, string>((queryParameters ?? new Dictionary<string, string>()).Union(authorizationParts).Union(formData ?? new Dictionary<string, string>()).ToDictionary(x => x.Key, x => x.Value));
string parameterString = signatureParts.Select(x => string.Format("{0}={1}", Uri.EscapeDataString(x.Key), Uri.EscapeDataString(x.Value))).Aggregate((x, y) => string.Format("{0}&{1}", x, y));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please revert the complex linq expressions. It reduces readability and code efficiency compared to the StringBuilder pattern that was used before.

canonicalizedRequestBuilder.Append(HttpMethod.Post.Method);
canonicalizedRequestBuilder.Append("&");
canonicalizedRequestBuilder.Append(UrlEncoder.Encode(RequestTokenEndpoint));
var signatureParts = new SortedDictionary<string, string>((queryParameters ?? new Dictionary<string, string>()).Union(authorizationParts).Union(formData ?? new Dictionary<string, string>()).ToDictionary(x => x.Key, x => x.Value));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the one linq expression that it may make sense to keep, but it needs a more readable layout rather than stringing it out all on one line.

@Tratcher Tratcher merged commit 2e3a107 into aspnet:dev Apr 17, 2018
@Tratcher
Copy link
Member

Thanks for the help

@Tratcher Tratcher added this to the 2.2.0-mq milestone Apr 17, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants