Skip to content

Commit

Permalink
feat: Add optional cancellation token for oauth client
Browse files Browse the repository at this point in the history
  • Loading branch information
zoriya authored Jan 3, 2025
1 parent f9fb116 commit 4f892d6
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 31 deletions.
19 changes: 12 additions & 7 deletions Octokit.Reactive/Clients/IObservableOauthClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Threading;

namespace Octokit.Reactive
{
Expand All @@ -22,8 +23,9 @@ public interface IObservableOauthClient
/// an access token using this method.
/// </remarks>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
IObservable<OauthToken> CreateAccessToken(OauthTokenRequest request);
IObservable<OauthToken> CreateAccessToken(OauthTokenRequest request, CancellationToken cancellationToken = default);

/// <summary>
/// Makes a request to initiate the device flow authentication.
Expand All @@ -33,25 +35,28 @@ public interface IObservableOauthClient
/// This request also returns a device verification code that you must use to receive an access token to check the status of user authentication.
/// </remarks>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
IObservable<OauthDeviceFlowResponse> InitiateDeviceFlow(OauthDeviceFlowRequest request);
IObservable<OauthDeviceFlowResponse> InitiateDeviceFlow(OauthDeviceFlowRequest request, CancellationToken cancellationToken = default);

/// <summary>
/// Makes a request to get an access token using the response from <see cref="InitiateDeviceFlow(OauthDeviceFlowRequest)"/>.
/// Makes a request to get an access token using the response from <see cref="InitiateDeviceFlow(OauthDeviceFlowRequest, CancellationToken)"/>.
/// </summary>
/// <remarks>
/// Will poll the access token endpoint, until the device and user codes expire or the user has successfully authorized the app with a valid user code.
/// </remarks>
/// <param name="clientId">The client Id you received from GitHub when you registered the application.</param>
/// <param name="deviceFlowResponse">The response you received from <see cref="InitiateDeviceFlow(OauthDeviceFlowRequest)"/></param>
/// <param name="deviceFlowResponse">The response you received from <see cref="InitiateDeviceFlow(OauthDeviceFlowRequest, CancellationToken)"/></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
IObservable<OauthToken> CreateAccessTokenForDeviceFlow(string clientId, OauthDeviceFlowResponse deviceFlowResponse);
IObservable<OauthToken> CreateAccessTokenForDeviceFlow(string clientId, OauthDeviceFlowResponse deviceFlowResponse, CancellationToken cancellationToken = default);

/// <summary>
/// Makes a request to get an access token using the refresh token returned in <see cref="CreateAccessToken(OauthTokenRequest)"/>.
/// Makes a request to get an access token using the refresh token returned in <see cref="CreateAccessToken(OauthTokenRequest, CancellationToken)"/>.
/// </summary>
/// <param name="request">Token renewal request.</param>
/// <param name="cancellationToken"></param>
/// <returns><see cref="OauthToken"/> with the new token set.</returns>
IObservable<OauthToken> CreateAccessTokenFromRenewalToken(OauthTokenRenewalRequest request);
IObservable<OauthToken> CreateAccessTokenFromRenewalToken(OauthTokenRenewalRequest request, CancellationToken cancellationToken = default);
}
}
17 changes: 9 additions & 8 deletions Octokit.Reactive/Clients/ObservableOauthClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Reactive.Threading.Tasks;
using System.Threading;

namespace Octokit.Reactive
{
Expand All @@ -23,24 +24,24 @@ public Uri GetGitHubLoginUrl(OauthLoginRequest request)
return _client.Oauth.GetGitHubLoginUrl(request);
}

public IObservable<OauthToken> CreateAccessToken(OauthTokenRequest request)
public IObservable<OauthToken> CreateAccessToken(OauthTokenRequest request, CancellationToken cancellationToken = default)
{
return _client.Oauth.CreateAccessToken(request).ToObservable();
return _client.Oauth.CreateAccessToken(request, cancellationToken).ToObservable();
}

public IObservable<OauthDeviceFlowResponse> InitiateDeviceFlow(OauthDeviceFlowRequest request)
public IObservable<OauthDeviceFlowResponse> InitiateDeviceFlow(OauthDeviceFlowRequest request, CancellationToken cancellationToken = default)
{
return _client.Oauth.InitiateDeviceFlow(request).ToObservable();
return _client.Oauth.InitiateDeviceFlow(request, cancellationToken).ToObservable();
}

public IObservable<OauthToken> CreateAccessTokenForDeviceFlow(string clientId, OauthDeviceFlowResponse deviceFlowResponse)
public IObservable<OauthToken> CreateAccessTokenForDeviceFlow(string clientId, OauthDeviceFlowResponse deviceFlowResponse, CancellationToken cancellationToken = default)
{
return _client.Oauth.CreateAccessTokenForDeviceFlow(clientId, deviceFlowResponse).ToObservable();
return _client.Oauth.CreateAccessTokenForDeviceFlow(clientId, deviceFlowResponse, cancellationToken).ToObservable();
}

public IObservable<OauthToken> CreateAccessTokenFromRenewalToken(OauthTokenRenewalRequest request)
public IObservable<OauthToken> CreateAccessTokenFromRenewalToken(OauthTokenRenewalRequest request, CancellationToken cancellationToken = default)
{
return _client.Oauth.CreateAccessTokenFromRenewalToken(request)
return _client.Oauth.CreateAccessTokenFromRenewalToken(request, cancellationToken)
.ToObservable();
}
}
Expand Down
19 changes: 12 additions & 7 deletions Octokit/Clients/IOAuthClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Octokit
Expand Down Expand Up @@ -26,8 +27,9 @@ public interface IOauthClient
/// an access token using this method.
/// </remarks>
/// <param name="request"></param>
/// <param name="concellationToken"></param>
/// <returns></returns>
Task<OauthToken> CreateAccessToken(OauthTokenRequest request);
Task<OauthToken> CreateAccessToken(OauthTokenRequest request, CancellationToken concellationToken = default);

/// <summary>
/// Makes a request to initiate the device flow authentication.
Expand All @@ -37,25 +39,28 @@ public interface IOauthClient
/// This request also returns a device verification code that you must use to receive an access token to check the status of user authentication.
/// </remarks>
/// <param name="request"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
Task<OauthDeviceFlowResponse> InitiateDeviceFlow(OauthDeviceFlowRequest request);
Task<OauthDeviceFlowResponse> InitiateDeviceFlow(OauthDeviceFlowRequest request, CancellationToken cancellationToken = default);

/// <summary>
/// Makes a request to get an access token using the response from <see cref="InitiateDeviceFlow(OauthDeviceFlowRequest)"/>.
/// Makes a request to get an access token using the response from <see cref="InitiateDeviceFlow(OauthDeviceFlowRequest, CancellationToken)"/>.
/// </summary>
/// <remarks>
/// Will poll the access token endpoint, until the device and user codes expire or the user has successfully authorized the app with a valid user code.
/// </remarks>
/// <param name="clientId">The client Id you received from GitHub when you registered the application.</param>
/// <param name="deviceFlowResponse">The response you received from <see cref="InitiateDeviceFlow(OauthDeviceFlowRequest)"/></param>
/// <param name="deviceFlowResponse">The response you received from <see cref="InitiateDeviceFlow(OauthDeviceFlowRequest, CancellationToken)"/></param>
/// <param name="concellationToken"></param>
/// <returns></returns>
Task<OauthToken> CreateAccessTokenForDeviceFlow(string clientId, OauthDeviceFlowResponse deviceFlowResponse);
Task<OauthToken> CreateAccessTokenForDeviceFlow(string clientId, OauthDeviceFlowResponse deviceFlowResponse, CancellationToken concellationToken = default);

/// <summary>
/// Makes a request to get an access token using the refresh token returned in <see cref="CreateAccessToken(OauthTokenRequest)"/>.
/// Makes a request to get an access token using the refresh token returned in <see cref="CreateAccessToken(OauthTokenRequest, CancellationToken)"/>.
/// </summary>
/// <param name="request">Token renewal request.</param>
/// <param name="concellationToken"></param>
/// <returns><see cref="OauthToken"/> with the new token set.</returns>
Task<OauthToken> CreateAccessTokenFromRenewalToken(OauthTokenRenewalRequest request);
Task<OauthToken> CreateAccessTokenFromRenewalToken(OauthTokenRenewalRequest request, CancellationToken concellationToken = default);
}
}
21 changes: 12 additions & 9 deletions Octokit/Clients/OAuthClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Globalization;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace Octokit
Expand Down Expand Up @@ -48,33 +49,33 @@ public Uri GetGitHubLoginUrl(OauthLoginRequest request)
}

[ManualRoute("POST", "/login/oauth/access_token")]
public async Task<OauthToken> CreateAccessToken(OauthTokenRequest request)
public async Task<OauthToken> CreateAccessToken(OauthTokenRequest request, CancellationToken cancellationToken = default)
{
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);
var response = await connection.Post<OauthToken>(endPoint, body, "application/json", null, hostAddress, cancellationToken).ConfigureAwait(false);
return response.Body;
}

[ManualRoute("POST", "/login/device/code")]
public async Task<OauthDeviceFlowResponse> InitiateDeviceFlow(OauthDeviceFlowRequest request)
public async Task<OauthDeviceFlowResponse> InitiateDeviceFlow(OauthDeviceFlowRequest request, CancellationToken cancellationToken = default)
{
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);
var response = await connection.Post<OauthDeviceFlowResponse>(endPoint, body, "application/json", null, hostAddress, cancellationToken).ConfigureAwait(false);
return response.Body;
}

[ManualRoute("POST", "/login/oauth/access_token")]
public async Task<OauthToken> CreateAccessTokenForDeviceFlow(string clientId, OauthDeviceFlowResponse deviceFlowResponse)
public async Task<OauthToken> CreateAccessTokenForDeviceFlow(string clientId, OauthDeviceFlowResponse deviceFlowResponse, CancellationToken cancellationToken = default)
{
Ensure.ArgumentNotNullOrEmptyString(clientId, nameof(clientId));
Ensure.ArgumentNotNull(deviceFlowResponse, nameof(deviceFlowResponse));
Expand All @@ -85,9 +86,11 @@ public async Task<OauthToken> CreateAccessTokenForDeviceFlow(string clientId, Oa

while (true)
{
cancellationToken.ThrowIfCancellationRequested();

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);
var response = await connection.Post<OauthToken>(endPoint, body, "application/json", null, hostAddress, cancellationToken).ConfigureAwait(false);

if (response.Body.Error != null)
{
Expand All @@ -103,7 +106,7 @@ public async Task<OauthToken> CreateAccessTokenForDeviceFlow(string clientId, Oa
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));
await Task.Delay(TimeSpan.FromSeconds(pollingDelay), cancellationToken);
}
else
{
Expand All @@ -113,14 +116,14 @@ public async Task<OauthToken> CreateAccessTokenForDeviceFlow(string clientId, Oa
}

[ManualRoute("POST", "/login/oauth/access_token")]
public async Task<OauthToken> CreateAccessTokenFromRenewalToken(OauthTokenRenewalRequest request)
public async Task<OauthToken> CreateAccessTokenFromRenewalToken(OauthTokenRenewalRequest request, CancellationToken cancellationToken = default)
{
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);
var response = await connection.Post<OauthToken>(endPoint, body, "application/json", null, hostAddress, cancellationToken).ConfigureAwait(false);
return response.Body;
}
}
Expand Down

0 comments on commit 4f892d6

Please sign in to comment.