square / square-dotnet-sdk

.NET client library for the Square API
https://developer.squareup.com
Other
55 stars 30 forks source link

RevokeToken is not intuitive #22

Open jsgoupil opened 4 years ago

jsgoupil commented 4 years ago

I am aware that you have to match the API for other languages, but this RevokeToken API is everything but intuitive in C#. I had to go check your API explorer and even try it like this:

https://developer.squareup.com/explorer/square/o-auth-api/revoke-token

Because I was getting

{
  "message": "bad authorization header",
  "type": "bad_request.invalid_parameter"
}

All other API takes a SquareClient.Builder().AccessToken() but not this one. It has to go in the RevokeTokenRequest. Then, I need to pass in the ApplicationSecret which is called authorization on the method, rather than using the clientSecret like we can find on ObtainTokenRequest.

Then to put the cherry on top, I have to put the word Client, before my ApplicationSecret. Which makes even less sense as for the public API.

Here is a snippet of my code. The RevokeTokenAsync is so different than this ObtainTokenAsync that I had to spend extra time trying to figure out in your code what your API is really doing here.

public async Task<bool> DisconnectAsync(int contractorId, CancellationToken cancellationToken = default(CancellationToken))
{
    var accessToken = await GetAccessTokenAsync(contractorId);
    var request = new RevokeTokenRequest.Builder()
        .ClientId(paymentSquareOptions.ApplicationId)
        .AccessToken(accessToken)
        .RevokeOnlyAccessToken(false)
        .Build();

    var response = await GetSquareClient(null).OAuthApi.RevokeTokenAsync(request, $"Client {paymentSquareOptions.ApplicationSecret}", cancellationToken);
    return response.Success ?? false;
}

private async Task<(string AccessToken, string ExpiresAt, string RefreshToken)> GetAccessTokenFromAuthorizationCodeAsync(string authorizedCode, CancellationToken cancellationToken = default(CancellationToken))
{
    var request = new ObtainTokenRequest.Builder(
        paymentSquareOptions.ApplicationId,
        paymentSquareOptions.ApplicationSecret,
        AUTHORIZATION_CODE
    )
    .Code(authorizedCode)
    .Build();

    var response = await GetSquareClient(null).OAuthApi.ObtainTokenAsync(request, cancellationToken);
    return (response.AccessToken, response.ExpiresAt, response.RefreshToken);
}

protected virtual internal ISquareClient GetSquareClient(string accessToken)
{
    var builder = new SquareClient.Builder()
        .Environment(GetEnvironment());

    if (!string.IsNullOrEmpty(accessToken))
    {
        builder = builder
            .AccessToken(accessToken);
    }

    return builder
        .Build();
}

I would have expected the following:

var request = new RevokeTokenRequest.Builder(
    paymentSquareOptions.ApplicationId,
    paymentSquareOptions.ApplicationSecret
)
.AccessToken(accessToken)
.Build();

var response = await GetSquareClient(null).OAuthApi.RevokeTokenAsync(request, cancellationToken);

OR

var request = new RevokeTokenRequest.Builder(
    paymentSquareOptions.ApplicationId,
    paymentSquareOptions.ApplicationSecret
)
.Build();

var response = await GetSquareClient(accessToken).OAuthApi.RevokeTokenAsync(request, cancellationToken);
StephenJosey commented 4 years ago

Thanks for the feedback! I'll share this with the team and we'll look into it. I'll follow-up here once I have more information.

jsgoupil commented 4 years ago

Sorry revisiting this bug. I have pocket closed it! wow... Sorry