Skip to content

Commit

Permalink
Merge branch 'release/0.74.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Jericho committed Feb 16, 2024
2 parents 977c5e6 + 141b48d commit 895d17c
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 62 deletions.
3 changes: 3 additions & 0 deletions Source/ZoomNet/JwtConnectionInfo.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using System;

namespace ZoomNet
{
/// <summary>
/// Connect using JWT.
/// </summary>
[Obsolete("As of September 8, 2023, the JWT app type has been deprecated. Use Server-to-Server OAuth or OAuth apps to replace the functionality of all JWT apps in your account. See the JWT deprecation FAQ and migration guide for details.")]
public class JwtConnectionInfo : IConnectionInfo
{
/// <summary>
Expand Down
33 changes: 33 additions & 0 deletions Source/ZoomNet/Models/ContinuousMeetingChatSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace ZoomNet.Models
{
/// <summary>
/// Information about the Enable continuous meeting chat feature.
/// </summary>
public class ContinuousMeetingChatSettings
{
/// <summary>
/// Gets or sets whether to enable the Enable continuous meeting chat setting.
/// </summary>
[JsonPropertyName("enable")]
public bool? Enable { get; set; }

/// <summary>
/// Gets or sets whether to enable the Automatically add invited external users setting.
/// </summary>
[JsonPropertyName("auto_add_invited_external_users")]
public bool? AutoAddInvitedExternalUsers { get; set; }

/// <summary>
/// Gets or sets the channel's ID.
/// </summary>
[JsonPropertyName("channel_id")]
public string ChannelId { get; set; }
}
}
6 changes: 6 additions & 0 deletions Source/ZoomNet/Models/MeetingSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,12 @@ public class MeetingSettings
[JsonPropertyName("watermark")]
public bool? Watermark { get; set; }

/// <summary>
/// Gets or sets information about the Enable continuous meeting chat feature.
/// </summary>
[JsonPropertyName("continuous_meeting_chat")]
public ContinuousMeetingChatSettings ContinuousMeetingChat { get; set; }

/// <summary>
/// Gets or sets the value indicating whether to allow attendees to join the meeting from multiple devices.
/// This setting only works for meetings that require registration.
Expand Down
15 changes: 9 additions & 6 deletions Source/ZoomNet/Utilities/JwtTokenHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,16 @@ public string RefreshTokenIfNecessary(bool forceRefresh)
{
_lock.EnterWriteLock();

_tokenExpiration = DateTime.UtcNow.Add(_tokenLifeSpan);
var jwtPayload = new Dictionary<string, object>()
if (forceRefresh || TokenIsExpired())
{
{ "iss", _connectionInfo.ApiKey },
{ "exp", _tokenExpiration.ToUnixTime() }
};
_jwtToken = JWT.Encode(jwtPayload, Encoding.ASCII.GetBytes(_connectionInfo.ApiSecret), JwsAlgorithm.HS256);
_tokenExpiration = DateTime.UtcNow.Add(_tokenLifeSpan);
var jwtPayload = new Dictionary<string, object>()
{
{ "iss", _connectionInfo.ApiKey },
{ "exp", _tokenExpiration.ToUnixTime() }
};
_jwtToken = JWT.Encode(jwtPayload, Encoding.ASCII.GetBytes(_connectionInfo.ApiSecret), JwsAlgorithm.HS256);
}
}
finally
{
Expand Down
109 changes: 56 additions & 53 deletions Source/ZoomNet/Utilities/OAuthTokenHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,61 +75,64 @@ public string RefreshTokenIfNecessary(bool forceRefresh)
{
_lock.EnterWriteLock();

var contentValues = new Dictionary<string, string>()
if (forceRefresh || TokenIsExpired())
{
{ "grant_type", _connectionInfo.GrantType.ToEnumString() },
};

switch (_connectionInfo.GrantType)
{
case OAuthGrantType.AccountCredentials:
contentValues.Add("account_id", _connectionInfo.AccountId);
break;
case OAuthGrantType.AuthorizationCode:
contentValues.Add("code", _connectionInfo.AuthorizationCode);
if (!string.IsNullOrEmpty(_connectionInfo.RedirectUri)) contentValues.Add("redirect_uri", _connectionInfo.RedirectUri);
if (!string.IsNullOrEmpty(_connectionInfo.CodeVerifier)) contentValues.Add("code_verifier", _connectionInfo.CodeVerifier);
break;
case OAuthGrantType.RefreshToken:
contentValues.Add("refresh_token", _connectionInfo.RefreshToken);
break;
var contentValues = new Dictionary<string, string>()
{
{ "grant_type", _connectionInfo.GrantType.ToEnumString() },
};

switch (_connectionInfo.GrantType)
{
case OAuthGrantType.AccountCredentials:
contentValues.Add("account_id", _connectionInfo.AccountId);
break;
case OAuthGrantType.AuthorizationCode:
contentValues.Add("code", _connectionInfo.AuthorizationCode);
if (!string.IsNullOrEmpty(_connectionInfo.RedirectUri)) contentValues.Add("redirect_uri", _connectionInfo.RedirectUri);
if (!string.IsNullOrEmpty(_connectionInfo.CodeVerifier)) contentValues.Add("code_verifier", _connectionInfo.CodeVerifier);
break;
case OAuthGrantType.RefreshToken:
contentValues.Add("refresh_token", _connectionInfo.RefreshToken);
break;
}

var requestTime = DateTime.UtcNow;
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.zoom.us/oauth/token");
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{_connectionInfo.ClientId}:{_connectionInfo.ClientSecret}")));
request.Content = new FormUrlEncodedContent(contentValues);
var response = _httpClient.SendAsync(request).ConfigureAwait(false).GetAwaiter().GetResult();
var responseContent = response.Content.ReadAsStringAsync(null).ConfigureAwait(false).GetAwaiter().GetResult();

if (string.IsNullOrEmpty(responseContent)) throw new Exception(response.ReasonPhrase);

var jsonResponse = JsonDocument.Parse(responseContent).RootElement;

if (!response.IsSuccessStatusCode)
{
var reason = jsonResponse.GetPropertyValue("reason", "The Zoom API did not provide a reason");
throw new ZoomException(reason, response, "No diagnostic available", null);
}

_connectionInfo.RefreshToken = jsonResponse.GetPropertyValue("refresh_token", string.Empty);
_connectionInfo.AccessToken = jsonResponse.GetPropertyValue("access_token", string.Empty);
_connectionInfo.TokenExpiration = requestTime.AddSeconds(jsonResponse.GetPropertyValue("expires_in", 60 * 60));
_connectionInfo.TokenScope = new ReadOnlyDictionary<string, string[]>(
jsonResponse.GetPropertyValue("scope", string.Empty)
.Split(' ')
.Select(x => x.Split(new[] { ':' }, 2))
.Select(x => new KeyValuePair<string, string[]>(x[0], x.Skip(1).ToArray()))
.GroupBy(x => x.Key)
.ToDictionary(
x => x.Key,
x => x.SelectMany(c => c.Value).ToArray()));

// Please note that Server-to-Server OAuth does not use the refresh token.
// Therefore change the grant type to 'RefreshToken' only when the response includes a refresh token.
if (!string.IsNullOrEmpty(_connectionInfo.RefreshToken)) _connectionInfo.GrantType = OAuthGrantType.RefreshToken;

_connectionInfo.OnTokenRefreshed?.Invoke(_connectionInfo.RefreshToken, _connectionInfo.AccessToken);
}

var requestTime = DateTime.UtcNow;
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.zoom.us/oauth/token");
request.Headers.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{_connectionInfo.ClientId}:{_connectionInfo.ClientSecret}")));
request.Content = new FormUrlEncodedContent(contentValues);
var response = _httpClient.SendAsync(request).ConfigureAwait(false).GetAwaiter().GetResult();
var responseContent = response.Content.ReadAsStringAsync(null).ConfigureAwait(false).GetAwaiter().GetResult();

if (string.IsNullOrEmpty(responseContent)) throw new Exception(response.ReasonPhrase);

var jsonResponse = JsonDocument.Parse(responseContent).RootElement;

if (!response.IsSuccessStatusCode)
{
var reason = jsonResponse.GetPropertyValue("reason", "The Zoom API did not provide a reason");
throw new ZoomException(reason, response, "No diagnostic available", null);
}

_connectionInfo.RefreshToken = jsonResponse.GetPropertyValue("refresh_token", string.Empty);
_connectionInfo.AccessToken = jsonResponse.GetPropertyValue("access_token", string.Empty);
_connectionInfo.TokenExpiration = requestTime.AddSeconds(jsonResponse.GetPropertyValue("expires_in", 60 * 60));
_connectionInfo.TokenScope = new ReadOnlyDictionary<string, string[]>(
jsonResponse.GetPropertyValue("scope", string.Empty)
.Split(' ')
.Select(x => x.Split(new[] { ':' }, 2))
.Select(x => new KeyValuePair<string, string[]>(x[0], x.Skip(1).ToArray()))
.GroupBy(x => x.Key)
.ToDictionary(
x => x.Key,
x => x.SelectMany(c => c.Value).ToArray()));

// Please note that Server-to-Server OAuth does not use the refresh token.
// Therefore change the grant type to 'RefreshToken' only when the response includes a refresh token.
if (!string.IsNullOrEmpty(_connectionInfo.RefreshToken)) _connectionInfo.GrantType = OAuthGrantType.RefreshToken;

_connectionInfo.OnTokenRefreshed?.Invoke(_connectionInfo.RefreshToken, _connectionInfo.AccessToken);
}
finally
{
Expand Down
4 changes: 2 additions & 2 deletions build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
#tool dotnet:?package=GitVersion.Tool&version=5.12.0
#tool dotnet:?package=coveralls.net&version=4.0.1
#tool nuget:https://f.feedz.io/jericho/jericho/nuget/?package=GitReleaseManager&version=0.17.0-collaborators0003
#tool nuget:?package=ReportGenerator&version=5.2.0
#tool nuget:?package=xunit.runner.console&version=2.6.6
#tool nuget:?package=ReportGenerator&version=5.2.1
#tool nuget:?package=xunit.runner.console&version=2.7.0
#tool nuget:?package=CodecovUploader&version=0.7.1

// Install addins.
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "8.0.101",
"version": "8.0.201",
"rollForward": "patch",
"allowPrerelease": false
}
Expand Down

0 comments on commit 895d17c

Please sign in to comment.