Files
octokit.net/Octokit/Clients/OAuthClient.cs
Ken Christensen bbcd33d96e Support refreshtokens in OAuth flow (#2749)
* Support refreshtokens in OAuth flow
Fixes #2731

* Added summary to OauthToken.cs constructors

* Mark deprecation of non-refreshToken constructor for OauthToken

* Remove unnecessary comment

---------

Co-authored-by: Keegan Campbell <me@kfcampbell.com>
2023-07-27 15:46:03 -07:00

128 lines
5.1 KiB
C#

using System;
using System.Globalization;
using System.Net.Http;
using System.Threading.Tasks;
namespace Octokit
{
/// <summary>
/// Provides methods used in the OAuth web flow.
/// </summary>
/// <inheritdoc />
public class OauthClient : IOauthClient
{
readonly IConnection connection;
readonly Uri hostAddress;
/// <summary>
/// Create an instance of the OauthClient
/// </summary>
/// <param name="connection">The underlying connection to use</param>
public OauthClient(IConnection connection)
{
Ensure.ArgumentNotNull(connection, nameof(connection));
this.connection = connection;
var baseAddress = connection.BaseAddress ?? GitHubClient.GitHubDotComUrl;
// The Oauth login stuff uses the main website and not the API URLs
// For https://api.github.com we use https://github.com
// For any other address (presumably a GitHub Enterprise address) we need to strip any relative Uri such as /api/v3
hostAddress = baseAddress.Host.Equals("api.github.com")
? new Uri("https://github.com")
: baseAddress.StripRelativeUri();
}
/// <summary>
/// Gets the URL used in the first step of the web flow. The Web application should redirect to this URL.
/// </summary>
/// <param name="request">Parameters to the Oauth web flow login url</param>
/// <returns></returns>
[DotNetSpecificRoute]
public Uri GetGitHubLoginUrl(OauthLoginRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
return new Uri(hostAddress, ApiUrls.OauthAuthorize())
.ApplyParameters(request.ToParametersDictionary());
}
[ManualRoute("POST", "/login/oauth/access_token")]
public async Task<OauthToken> CreateAccessToken(OauthTokenRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
var endPoint = ApiUrls.OauthAccessToken();
var body = new FormUrlEncodedContent(request.ToParametersDictionary());
var response = await connection.Post<OauthToken>(endPoint, body, "application/json", null, hostAddress).ConfigureAwait(false);
return response.Body;
}
[ManualRoute("POST", "/login/device/code")]
public async Task<OauthDeviceFlowResponse> InitiateDeviceFlow(OauthDeviceFlowRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
var endPoint = ApiUrls.OauthDeviceCode();
var body = new FormUrlEncodedContent(request.ToParametersDictionary());
var response = await connection.Post<OauthDeviceFlowResponse>(endPoint, body, "application/json", null, hostAddress).ConfigureAwait(false);
return response.Body;
}
[ManualRoute("POST", "/login/oauth/access_token")]
public async Task<OauthToken> CreateAccessTokenForDeviceFlow(string clientId, OauthDeviceFlowResponse deviceFlowResponse)
{
Ensure.ArgumentNotNullOrEmptyString(clientId, nameof(clientId));
Ensure.ArgumentNotNull(deviceFlowResponse, nameof(deviceFlowResponse));
var endPoint = ApiUrls.OauthAccessToken();
int pollingDelay = deviceFlowResponse.Interval;
while (true)
{
var request = new OauthTokenRequestForDeviceFlow(clientId, deviceFlowResponse.DeviceCode);
var body = new FormUrlEncodedContent(request.ToParametersDictionary());
var response = await connection.Post<OauthToken>(endPoint, body, "application/json", null, hostAddress).ConfigureAwait(false);
if (response.Body.Error != null)
{
switch (response.Body.Error)
{
case "authorization_pending":
break;
case "slow_down":
pollingDelay += 5;
break;
case "expired_token":
default:
throw new ApiException(string.Format(CultureInfo.InvariantCulture, "{0}: {1}\n{2}", response.Body.Error, response.Body.ErrorDescription, response.Body.ErrorUri), null);
}
await Task.Delay(TimeSpan.FromSeconds(pollingDelay));
}
else
{
return response.Body;
}
}
}
[ManualRoute("POST", "/login/oauth/access_token")]
public async Task<OauthToken> CreateAccessTokenFromRenewalToken(OauthTokenRenewalRequest request)
{
Ensure.ArgumentNotNull(request, nameof(request));
var endPoint = ApiUrls.OauthAccessToken();
var body = new FormUrlEncodedContent(request.ToParametersDictionary());
var response = await connection.Post<OauthToken>(endPoint, body, "application/json", null, hostAddress).ConfigureAwait(false);
return response.Body;
}
}
}