Skip to content

Commit

Permalink
(GH-270) Add static methods to instantiate OAuthConnectionInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
Jericho committed Feb 25, 2023
1 parent 0dbca72 commit 65e6e6f
Show file tree
Hide file tree
Showing 2 changed files with 155 additions and 12 deletions.
9 changes: 4 additions & 5 deletions Source/ZoomNet.IntegrationTests/TestsRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public async Task<int> RunAsync()
// Server-to-Server OAuth
if (!string.IsNullOrEmpty(accountId))
{
connectionInfo = new OAuthConnectionInfo(clientId, clientSecret, accountId,
connectionInfo = OAuthConnectionInfo.WithAccountId(clientId, clientSecret, accountId,
(_, newAccessToken) =>
{
Console.Out.WriteLine($"A new access token was issued: {newAccessToken}");
Expand All @@ -85,19 +85,18 @@ public async Task<int> RunAsync()
// Standard OAuth
else
{
connectionInfo = new OAuthConnectionInfo(clientId, clientSecret, refreshToken, null,
connectionInfo = OAuthConnectionInfo.WithRefreshToken(clientId, clientSecret, refreshToken, null,
(newRefreshToken, newAccessToken) =>
{
Environment.SetEnvironmentVariable("ZOOM_OAUTH_REFRESHTOKEN", newRefreshToken, EnvironmentVariableTarget.User);
});

//var authorizationCode = "<-- the code generated by Zoom when the app is authorized by the user -->";
//connectionInfo = new OAuthConnectionInfo(clientId, clientSecret, authorizationCode,
//connectionInfo = OAuthConnectionInfo.WithAuthorizationCode(clientId, clientSecret, authorizationCode,
// (newRefreshToken, newAccessToken) =>
// {
// Environment.SetEnvironmentVariable("ZOOM_OAUTH_REFRESHTOKEN", newRefreshToken, EnvironmentVariableTarget.User);
// },
// null);
// });
}

var resultCode = await RunApiTestsAsync(connectionInfo, proxy).ConfigureAwait(false);
Expand Down
158 changes: 151 additions & 7 deletions Source/ZoomNet/OAuthConnectionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,17 @@ public class OAuthConnectionInfo : IConnectionInfo
/// Gets the account id.
/// </summary>
/// <remarks>This is relevant only when using Server-to-Server authentication.</remarks>
public string AccountId { get; }
public string AccountId { get; private set; }

/// <summary>
/// Gets the client id.
/// </summary>
public string ClientId { get; }
public string ClientId { get; private set; }

/// <summary>
/// Gets the client secret.
/// </summary>
public string ClientSecret { get; }
public string ClientSecret { get; private set; }

/// <summary>
/// Gets the grant type.
Expand All @@ -40,7 +40,7 @@ public class OAuthConnectionInfo : IConnectionInfo
/// <summary>
/// Gets the authorization code.
/// </summary>
public string AuthorizationCode { get; }
public string AuthorizationCode { get; private set; }

/// <summary>
/// Gets the refresh token.
Expand All @@ -65,17 +65,17 @@ public class OAuthConnectionInfo : IConnectionInfo
/// <summary>
/// Gets the delegate invoked when the token is refreshed.
/// </summary>
public OnTokenRefreshedDelegate OnTokenRefreshed { get; }
public OnTokenRefreshedDelegate OnTokenRefreshed { get; private set; }

/// <summary>
/// Gets the redirectUri required for refresh of tokens.
/// </summary>
public string RedirectUri { get; }
public string RedirectUri { get; private set; }

/// <summary>
/// Gets the cryptographically random string used to correlate the authorization request to the token request.
/// </summary>
public string CodeVerifier { get; internal set; }
public string CodeVerifier { get; private set; }

/// <summary>
/// Initializes a new instance of the <see cref="OAuthConnectionInfo"/> class.
Expand All @@ -91,6 +91,7 @@ public class OAuthConnectionInfo : IConnectionInfo
/// </remarks>
/// <param name="clientId">Your Client Id.</param>
/// <param name="clientSecret">Your Client Secret.</param>
[Obsolete("This constructor has been replaced with OAuthConnectionInfo.WithClientCredentials")]
public OAuthConnectionInfo(string clientId, string clientSecret)
{
if (string.IsNullOrEmpty(clientId)) throw new ArgumentNullException(nameof(clientId));
Expand Down Expand Up @@ -124,6 +125,7 @@ public OAuthConnectionInfo(string clientId, string clientSecret)
/// <param name="onTokenRefreshed">The delegate invoked when the token is refreshed.</param>
/// <param name="redirectUri">The Redirect Uri.</param>
/// <param name="codeVerifier">The cryptographically random string used to correlate the authorization request to the token request.</param>
[Obsolete("This constructor has been replaced with OAuthConnectionInfo.WithAuthorizationCode")]
public OAuthConnectionInfo(string clientId, string clientSecret, string authorizationCode, OnTokenRefreshedDelegate onTokenRefreshed, string redirectUri = null, string codeVerifier = null)
{
if (string.IsNullOrEmpty(clientId)) throw new ArgumentNullException(nameof(clientId));
Expand Down Expand Up @@ -160,6 +162,7 @@ public OAuthConnectionInfo(string clientId, string clientSecret, string authoriz
/// <param name="refreshToken">The refresh token.</param>
/// <param name="accessToken">(Optional) The access token. We recommend you specify a null value. See remarks for more details.</param>
/// <param name="onTokenRefreshed">The delegate invoked when the token is refreshed.</param>
[Obsolete("This constructor has been replaced with OAuthConnectionInfo.WithAccessToken")]
public OAuthConnectionInfo(string clientId, string clientSecret, string refreshToken, string accessToken, OnTokenRefreshedDelegate onTokenRefreshed)
{
if (string.IsNullOrEmpty(clientId)) throw new ArgumentNullException(nameof(clientId));
Expand All @@ -185,6 +188,7 @@ public OAuthConnectionInfo(string clientId, string clientSecret, string refreshT
/// <param name="clientSecret">Your Client Secret.</param>
/// <param name="accountId">Your Account Id.</param>
/// <param name="onTokenRefreshed">The delegate invoked when the token is refreshed. In the Server-to-Server scenario, this delegate is optional.</param>
[Obsolete("This constructor has been replaced with OAuthConnectionInfo.WithAccountId")]
public OAuthConnectionInfo(string clientId, string clientSecret, string accountId, OnTokenRefreshedDelegate onTokenRefreshed)
{
if (string.IsNullOrEmpty(clientId)) throw new ArgumentNullException(nameof(clientId));
Expand All @@ -198,5 +202,145 @@ public OAuthConnectionInfo(string clientId, string clientSecret, string accountI
GrantType = OAuthGrantType.AccountCredentials;
OnTokenRefreshed = onTokenRefreshed;
}

private OAuthConnectionInfo() { }

/// <summary>
/// Initializes a new instance of the <see cref="OAuthConnectionInfo"/> class.
/// </summary>
/// <remarks>
/// This constructor is used to get access token for APIs that do not
/// need a user's permission, but rather a service's permission.
/// Within the realm of Zoom APIs, Client Credentials grant should be
/// used to get access token from the Chatbot Service in order to use
/// the "Send Chatbot Messages API". See the "Using OAuth 2.0 / Client
/// Credentials" section in the "Using Zoom APIs" document for more details
/// (https://marketplace.zoom.us/docs/api-reference/using-zoom-apis).
/// </remarks>
/// <param name="clientId">Your Client Id.</param>
/// <param name="clientSecret">Your Client Secret.</param>
/// <returns>The connection info.</returns>
public static OAuthConnectionInfo WithClientCredentials(string clientId, string clientSecret)
{
if (string.IsNullOrEmpty(clientId)) throw new ArgumentNullException(nameof(clientId));
if (string.IsNullOrEmpty(clientSecret)) throw new ArgumentNullException(nameof(clientSecret));

return new OAuthConnectionInfo
{
ClientId = clientId,
ClientSecret = clientSecret,
GrantType = OAuthGrantType.ClientCredentials,
};
}

/// <summary>
/// Initializes a new instance of the <see cref="OAuthConnectionInfo"/> class.
/// </summary>
/// <remarks>
/// The authorization code is generated by Zoom when the user authorizes the app.
/// This code can be used only one time to get the initial access token and refresh token.
/// Once the authorization code has been used, is is no longer valid and should be discarded.
///
/// Also, Zoom's documentation says that the redirect uri must be provided when validating an
/// authorization code and converting it into tokens. However I have observed that it's not
/// always necessary. It seems that some developers get a "REDIRECT URI MISMATCH" exception when
/// they omit this value but other developers don't. Therefore, the redirectUri parameter is
/// marked as optional in ZoomNet which allows you to specify it or omit it depending on your
/// situation. See this <a href="https://github.com/Jericho/ZoomNet/issues/104">Github issue</a>
/// and this <a href="https://devforum.zoom.us/t/trying-to-integrate-not-understanding-the-need-for-the-second-redirect-uri/43833">support thread</a>
/// for more details.
/// </remarks>
/// <param name="clientId">Your Client Id.</param>
/// <param name="clientSecret">Your Client Secret.</param>
/// <param name="authorizationCode">The authorization code.</param>
/// <param name="onTokenRefreshed">The delegate invoked when the token is refreshed.</param>
/// <param name="redirectUri">The Redirect Uri.</param>
/// <param name="codeVerifier">The cryptographically random string used to correlate the authorization request to the token request.</param>
/// <returns>The connection info.</returns>
public static OAuthConnectionInfo WithAuthorizationCode(string clientId, string clientSecret, string authorizationCode, OnTokenRefreshedDelegate onTokenRefreshed, string redirectUri = null, string codeVerifier = null)
{
if (string.IsNullOrEmpty(clientId)) throw new ArgumentNullException(nameof(clientId));
if (string.IsNullOrEmpty(clientSecret)) throw new ArgumentNullException(nameof(clientSecret));
if (string.IsNullOrEmpty(authorizationCode)) throw new ArgumentNullException(nameof(authorizationCode));

return new OAuthConnectionInfo
{
ClientId = clientId,
ClientSecret = clientSecret,
AuthorizationCode = authorizationCode,
RedirectUri = redirectUri,
TokenExpiration = DateTime.MinValue,
GrantType = OAuthGrantType.AuthorizationCode,
OnTokenRefreshed = onTokenRefreshed,
CodeVerifier = codeVerifier,
};
}

/// <summary>
/// Initializes a new instance of the <see cref="OAuthConnectionInfo"/> class.
/// </summary>
/// <remarks>
/// This is the most commonly used grant type for Zoom APIs.
///
/// Please note that the 'accessToken' parameter is optional.
/// In fact, we recommend that you specify a null value which
/// will cause ZoomNet to automatically obtain a new access
/// token from the Zoom API. The reason we recommend you omit
/// this parameter is that access tokens are ephemeral (they
/// expire in 60 minutes) and even if you specify a token that
/// was previously issued to you and that you preserved, this
/// token is very likely to be expired and therefore useless.
/// </remarks>
/// <param name="clientId">Your Client Id.</param>
/// <param name="clientSecret">Your Client Secret.</param>
/// <param name="refreshToken">The refresh token.</param>
/// <param name="accessToken">(Optional) The access token. We recommend you specify a null value. See remarks for more details.</param>
/// <param name="onTokenRefreshed">The delegate invoked when the token is refreshed.</param>
/// <returns>The connection info.</returns>
public static OAuthConnectionInfo WithRefreshToken(string clientId, string clientSecret, string refreshToken, string accessToken, OnTokenRefreshedDelegate onTokenRefreshed)
{
if (string.IsNullOrEmpty(clientId)) throw new ArgumentNullException(nameof(clientId));
if (string.IsNullOrEmpty(clientSecret)) throw new ArgumentNullException(nameof(clientSecret));
if (string.IsNullOrEmpty(refreshToken)) throw new ArgumentNullException(nameof(refreshToken));

return new OAuthConnectionInfo
{
ClientId = clientId,
ClientSecret = clientSecret,
RefreshToken = refreshToken,
AccessToken = accessToken,
TokenExpiration = string.IsNullOrEmpty(accessToken) ? DateTime.MinValue : DateTime.MaxValue, // Set expiration to DateTime.MaxValue when an access token is provided because we don't know when it will expire
GrantType = OAuthGrantType.RefreshToken,
OnTokenRefreshed = onTokenRefreshed,
};
}

/// <summary>
/// Initializes a new instance of the <see cref="OAuthConnectionInfo"/> class.
/// </summary>
/// <remarks>
/// Use this constructor when you want to use Server-to-Server OAuth authentication.
/// </remarks>
/// <param name="clientId">Your Client Id.</param>
/// <param name="clientSecret">Your Client Secret.</param>
/// <param name="accountId">Your Account Id.</param>
/// <param name="onTokenRefreshed">The delegate invoked when the token is refreshed. In the Server-to-Server scenario, this delegate is optional.</param>
/// <returns>The connection info.</returns>
public static OAuthConnectionInfo WithAccountId(string clientId, string clientSecret, string accountId, OnTokenRefreshedDelegate onTokenRefreshed = null)
{
if (string.IsNullOrEmpty(clientId)) throw new ArgumentNullException(nameof(clientId));
if (string.IsNullOrEmpty(clientSecret)) throw new ArgumentNullException(nameof(clientSecret));
if (string.IsNullOrEmpty(accountId)) throw new ArgumentNullException(nameof(accountId));

return new OAuthConnectionInfo
{
ClientId = clientId,
ClientSecret = clientSecret,
AccountId = accountId,
TokenExpiration = DateTime.MinValue,
GrantType = OAuthGrantType.AccountCredentials,
OnTokenRefreshed = onTokenRefreshed,
};
}
}
}

0 comments on commit 65e6e6f

Please sign in to comment.