Skip to content

Commit

Permalink
Merge branch 'release/0.54.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Jericho committed Dec 14, 2022
2 parents 7998fdf + cae7208 commit 4606a16
Show file tree
Hide file tree
Showing 10 changed files with 54 additions and 38 deletions.
27 changes: 14 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ PM> Install-Package ZoomNet
ZoomNet currently supports:
- .NET framework 4.8
- any framework supporting `.NET Standard 2.1` (which includes `.NET Core 3.x` and `ASP.NET Core 3.x`)
- `.NET 5.0`
- `.NET 6.0`
- `.NET 7.0`

The last version of ZoomNet that supported `.NET 4.6.1`, `.NET 4.7.2` and `.NET Standard 2.0` was 0.35.0

Expand Down Expand Up @@ -66,15 +66,14 @@ Using OAuth is much more complicated than using JWT but at the same time, it is
The Zoom documentation has a document about [how to create an OAuth app](https://marketplace.zoom.us/docs/guides/build/oauth-app) and another document about the [OAuth autorization flow](https://marketplace.zoom.us/docs/guides/auth/oauth) but I personnality was very confused by the later document so here is a brief step-by-step summary:
- you create an OAuth app, define which permissions your app requires and publish the app in the Zoom marketplace.
- user installs your app. During installation, user is presentd with a screen listing the permissons your app requires. User must click `accept`.
- Zoom generates a "authorization code". This code can be used only once to generate the first access token and refresh token. I CAN'T STRESS THIS ENOUGH: the authorization code can be used only one time. This was the confusing part to me: somehow I didn't understand that this code could be used only one time and I was attempting to use it repeatedly. Zoom would accept the code the first time and would reject it subsequently, which lead to many hours of frustration while trying to figure out why the code was sometimes rejected.
- Zoom generates an "authorization code". This code can be used only once to generate the first access token and refresh token. I CAN'T STRESS THIS ENOUGH: the authorization code can be used only one time. This was the confusing part to me: somehow I didn't understand that this code could be used only one time and I was attempting to use it repeatedly. Zoom would accept the code the first time and would reject it subsequently, which lead to many hours of frustration while trying to figure out why the code was sometimes rejected.
- The access token is valid for 60 minutes and must therefore be "refreshed" periodically.

When you initially add an OAuth application to your Zoom account, you will be issued an "authorization code".
You can provide this autorization code to ZoomNet like so:
```csharp
var clientId = "... your client ID ...";
var clientSecret = "... your client secret ...";
var refreshToken = "... the refresh token previously issued by Zoom ...";
var authorizationCode = "... the code that Zoom issued when you added the OAuth app to your account ...";
var redirectUri = "... the URI you have configured when setting up your OAuth app ..."; // Please note that Zoom sometimes accepts a null value and sometimes rejects it with a 'Redirect URI mismatch' error
var connectionInfo = new OAuthConnectionInfo(clientId, clientSecret, authorizationCode,
Expand All @@ -85,33 +84,36 @@ var connectionInfo = new OAuthConnectionInfo(clientId, clientSecret, authorizati
is converted into an access token and also when the
access token is subsequently refreshed.
You should use this callback to save the two new tokens
to a safe place so you can provide them the next time you
You should use this callback to save the refresh token
to a safe place so you can provide it the next time you
need to instantiate an OAuthConnectionInfo.
The access token on the other hand does not need to be
preserved because it is ephemeral (meaning it expires
after 60 minutes). Even if you preserve it, it is very
likely to be expired (and therefore useless) before the
next time you need to instantiate an OAuthConnectionInfo.
For demonstration purposes, here's how you could use your
operating system's environment variables to store the tokens:
operating system's environment variables to store the token:
*/
Environment.SetEnvironmentVariable("ZOOM_OAUTH_REFRESHTOKEN", newRefreshToken, EnvironmentVariableTarget.User);
Environment.SetEnvironmentVariable("ZOOM_OAUTH_ACCESSTOKEN", newAccessToken, EnvironmentVariableTarget.User);
},
redirectUri);
var zoomClient = new ZoomClient(connectionInfo);
```

> **Warning:** This sample I just provided can be used only when Zoom issues a new the autorization code. ZoomNet will take care of converting this code into an access token at which point the autorization code is no longer valid.
Once the autorization code is converted into an access token, you can instantiate a 'connection info' object like so:
Once the autorization code is converted into an access token and a refresh token, you can instantiate a 'connection info' object like so:
```csharp
var clientId = "... your client ID ...";
var clientSecret = "... your client secret ...";
var refreshToken = Environment.GetEnvironmentVariable("ZOOM_OAUTH_REFRESHTOKEN", EnvironmentVariableTarget.User);
var accessToken = Environment.GetEnvironmentVariable("ZOOM_OAUTH_ACCESSTOKEN", EnvironmentVariableTarget.User);
var connectionInfo = new OAuthConnectionInfo(clientId, clientSecret, refreshToken, accessToken,
var connectionInfo = new OAuthConnectionInfo(clientId, clientSecret, refreshToken, null,
(newRefreshToken, newAccessToken) =>
{
Environment.SetEnvironmentVariable("ZOOM_OAUTH_REFRESHTOKEN", newRefreshToken, EnvironmentVariableTarget.User);
Environment.SetEnvironmentVariable("ZOOM_OAUTH_ACCESSTOKEN", newAccessToken, EnvironmentVariableTarget.User);
});
var zoomClient = new ZoomClient(connectionInfo);
```
Expand All @@ -135,8 +137,7 @@ var connectionInfo = new OAuthConnectionInfo(clientId, clientSecret, accountId,
/*
Server-to-Server OAuth does not use a refresh token. That's why I used '_' as the first parameter
in this delegate declaration. Furthermore, ZoomNet will take care of getting a new access token
and to refresh it whenever it expires therefore there is no need for you to preserve the token
like you must do for the 'standard' OAuth authentication.
and to refresh it whenever it expires therefore there is no need for you to preserve the token.
In fact, this delegate is completely optional when using Server-to-Server OAuth. Feel free to pass
a null value in lieu of a delegate.
Expand Down
8 changes: 8 additions & 0 deletions Source/ZoomNet.IntegrationTests/Tests/Users.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -13,6 +14,13 @@ public async Task RunAsync(User myUser, string[] myPermissions, IZoomClient clie

await log.WriteLineAsync("\n***** USERS *****\n").ConfigureAwait(false);

// UPDATE CUSTOM ATTRIBUTES
await client.Users.UpdateAsync(myUser.Id,
customAttributes: new List<CustomAttribute> { new CustomAttribute { Key = "TestKey1", Name = "TestName1", Value = "TestValue1" } },
cancellationToken: cancellationToken).ConfigureAwait(false);
await log.WriteLineAsync("My user custom attributes were updated").ConfigureAwait(false);
await Task.Delay(500, cancellationToken).ConfigureAwait(false);

// UPDATE MY USER
await client.Users.UpdateAsync(myUser.Id,
firstName: "Hello",
Expand Down
5 changes: 1 addition & 4 deletions Source/ZoomNet.IntegrationTests/TestsRunner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ public async Task<int> RunAsync()
var clientSecret = Environment.GetEnvironmentVariable("ZOOM_OAUTH_CLIENTSECRET", EnvironmentVariableTarget.User);
var accountId = Environment.GetEnvironmentVariable("ZOOM_OAUTH_ACCOUNTID", EnvironmentVariableTarget.User);
var refreshToken = Environment.GetEnvironmentVariable("ZOOM_OAUTH_REFRESHTOKEN", EnvironmentVariableTarget.User);
var accessToken = Environment.GetEnvironmentVariable("ZOOM_OAUTH_ACCESSTOKEN", EnvironmentVariableTarget.User);

// Server-to-Server OAuth
if (!string.IsNullOrEmpty(accountId))
Expand All @@ -75,19 +74,17 @@ public async Task<int> RunAsync()
// Standard OAuth
else
{
connectionInfo = new OAuthConnectionInfo(clientId, clientSecret, refreshToken, accessToken,
connectionInfo = new OAuthConnectionInfo(clientId, clientSecret, refreshToken, null,
(newRefreshToken, newAccessToken) =>
{
Environment.SetEnvironmentVariable("ZOOM_OAUTH_REFRESHTOKEN", newRefreshToken, EnvironmentVariableTarget.User);
Environment.SetEnvironmentVariable("ZOOM_OAUTH_ACCESSTOKEN", newAccessToken, EnvironmentVariableTarget.User);
});

//var authorizationCode = "<-- the code generated by Zoom when the app is authorized by the user -->";
//connectionInfo = new OAuthConnectionInfo(clientId, clientSecret, authorizationCode,
// (newRefreshToken, newAccessToken) =>
// {
// Environment.SetEnvironmentVariable("ZOOM_OAUTH_REFRESHTOKEN", newRefreshToken, EnvironmentVariableTarget.User);
// Environment.SetEnvironmentVariable("ZOOM_OAUTH_ACCESSTOKEN", newAccessToken, EnvironmentVariableTarget.User);
// },
// null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net7.0</TargetFramework>
<AssemblyName>ZoomNet.IntegrationTests</AssemblyName>
<RootNamespace>ZoomNet.IntegrationTests</RootNamespace>
</PropertyGroup>
Expand All @@ -13,8 +13,8 @@

<ItemGroup>
<PackageReference Include="Logzio.DotNet.NLog" Version="1.0.13" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="NLog.Extensions.Logging" Version="1.7.5" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
<PackageReference Include="NLog.Extensions.Logging" Version="5.2.0" />
</ItemGroup>

<ItemGroup>
Expand Down
12 changes: 6 additions & 6 deletions Source/ZoomNet.UnitTests/ZoomNet.UnitTests.csproj
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net48;netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
<TargetFrameworks>net48;net6.0;net7.0</TargetFrameworks>
<AssemblyName>ZoomNet.UnitTests</AssemblyName>
<RootNamespace>ZoomNet.UnitTests</RootNamespace>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.msbuild" Version="3.1.2">
<PackageReference Include="coverlet.msbuild" Version="3.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Include="Moq" Version="4.18.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.0" />
<PackageReference Include="Moq" Version="4.18.3" />
<PackageReference Include="RichardSzalay.MockHttp" Version="6.0.0" />
<PackageReference Include="Shouldly" Version="4.0.3" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="Shouldly" Version="4.1.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
Expand Down
12 changes: 10 additions & 2 deletions Source/ZoomNet/OAuthConnectionInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,18 +145,26 @@ public OAuthConnectionInfo(string clientId, string clientSecret, string authoriz
/// </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">The access token. Access tokens expire after 1 hour. ZoomNet will automatically refresh this token when it expires.</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>
public OAuthConnectionInfo(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));
if (string.IsNullOrEmpty(accessToken)) throw new ArgumentNullException(nameof(accessToken));

ClientId = clientId;
ClientSecret = clientSecret;
Expand Down
3 changes: 2 additions & 1 deletion Source/ZoomNet/Resources/IUsers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,10 @@ public interface IUsers
/// <param name="type">The type of user.</param>
/// <param name="usePmi">Use Personal Meeting ID for instant meetings.</param>
/// <param name="personalMeetingRoomName">Personal meeting room name.</param>
/// <param name="customAttributes">Custom Attributes.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>The async task.</returns>
Task UpdateAsync(string userId, string firstName = null, string lastName = null, string company = null, string department = null, string groupId = null, string hostKey = null, string jobTitle = null, string language = null, string location = null, string manager = null, IEnumerable<PhoneNumber> phoneNumbers = null, string pmi = null, string pronouns = null, PronounDisplayType? pronounsDisplay = null, TimeZones? timezone = null, UserType? type = null, bool? usePmi = null, string personalMeetingRoomName = null, CancellationToken cancellationToken = default);
Task UpdateAsync(string userId, string firstName = null, string lastName = null, string company = null, string department = null, string groupId = null, string hostKey = null, string jobTitle = null, string language = null, string location = null, string manager = null, IEnumerable<PhoneNumber> phoneNumbers = null, string pmi = null, string pronouns = null, PronounDisplayType? pronounsDisplay = null, TimeZones? timezone = null, UserType? type = null, bool? usePmi = null, string personalMeetingRoomName = null, IEnumerable<CustomAttribute> customAttributes = null, CancellationToken cancellationToken = default);

/// <summary>
/// Retrieve the information of a specific user on a Zoom account.
Expand Down
3 changes: 2 additions & 1 deletion Source/ZoomNet/Resources/Users.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ public Task<User> CreateAsync(string email, string firstName = null, string last
}

/// <inheritdoc/>
public Task UpdateAsync(string userId, string firstName = null, string lastName = null, string company = null, string department = null, string groupId = null, string hostKey = null, string jobTitle = null, string language = null, string location = null, string manager = null, IEnumerable<PhoneNumber> phoneNumbers = null, string pmi = null, string pronouns = null, PronounDisplayType? pronounsDisplay = null, TimeZones? timezone = null, UserType? type = null, bool? usePmi = null, string personalMeetingRoomName = null, CancellationToken cancellationToken = default)
public Task UpdateAsync(string userId, string firstName = null, string lastName = null, string company = null, string department = null, string groupId = null, string hostKey = null, string jobTitle = null, string language = null, string location = null, string manager = null, IEnumerable<PhoneNumber> phoneNumbers = null, string pmi = null, string pronouns = null, PronounDisplayType? pronounsDisplay = null, TimeZones? timezone = null, UserType? type = null, bool? usePmi = null, string personalMeetingRoomName = null, IEnumerable<CustomAttribute> customAttributes = null, CancellationToken cancellationToken = default)
{
var data = new JsonObject
{
Expand All @@ -148,6 +148,7 @@ public Task UpdateAsync(string userId, string firstName = null, string lastName
{ "type", type?.ToEnumString() },
{ "use_pmi", usePmi },
{ "vanity_name", personalMeetingRoomName },
{ "custom_attributes", customAttributes?.ToArray() }
};

return _client
Expand Down
10 changes: 5 additions & 5 deletions Source/ZoomNet/ZoomNet.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net48;netstandard2.1;net5.0;net6.0</TargetFrameworks>
<TargetFrameworks>net48;netstandard2.1;net6.0;net7.0</TargetFrameworks>
<LangVersion>preview</LangVersion>
<PlatformTarget>anycpu</PlatformTarget>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
Expand Down Expand Up @@ -36,13 +36,13 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="HttpMultipartParser" Version="7.0.0" />
<PackageReference Include="jose-jwt" Version="4.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageReference Include="HttpMultipartParser" Version="7.1.0" />
<PackageReference Include="jose-jwt" Version="4.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
<PackageReference Include="Pathoschild.Http.FluentClient" Version="4.2.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" PrivateAssets="All" />
<PackageReference Include="System.Text.Json" Version="6.0.6" />
<PackageReference Include="System.Text.Json" Version="7.0.1" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
</ItemGroup>

Expand Down
6 changes: 3 additions & 3 deletions build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#tool dotnet:?package=GitVersion.Tool&version=5.11.1
#tool dotnet:?package=coveralls.net&version=4.0.1
#tool nuget:?package=GitReleaseManager&version=0.13.0
#tool nuget:?package=ReportGenerator&version=5.1.11
#tool nuget:?package=ReportGenerator&version=5.1.12
#tool nuget:?package=xunit.runner.console&version=2.4.2
#tool nuget:?package=Codecov&version=1.13.0

Expand Down Expand Up @@ -297,7 +297,7 @@ Task("Run-Code-Coverage")

Task("Upload-Coverage-Result-Coveralls")
.IsDependentOn("Run-Code-Coverage")
.OnError(exception => Information($"ONERROR: Failed to upload coverage result to Coveralls: {exception.Message}"))
.OnError(exception => Information($"ONERROR: Failed to upload coverage result to Coveralls: {exception.Message}"))
.Does(() =>
{
//CoverallsNet(new FilePath($"{codeCoverageDir}coverage.{DefaultFramework}.xml"), CoverallsNetReportType.OpenCover, new CoverallsNetSettings()
Expand All @@ -308,7 +308,7 @@ Task("Upload-Coverage-Result-Coveralls")

Task("Upload-Coverage-Result-Codecov")
.IsDependentOn("Run-Code-Coverage")
.OnError(exception => Information($"ONERROR: Failed to upload coverage result to Codecov: {exception.Message}"))
.OnError(exception => Information($"ONERROR: Failed to upload coverage result to Codecov: {exception.Message}"))
.Does(() =>
{
//Codecov($"{codeCoverageDir}coverage.{DefaultFramework}.xml", codecovToken);
Expand Down

0 comments on commit 4606a16

Please sign in to comment.