diff --git a/Source/ZoomNet/JwtConnectionInfo.cs b/Source/ZoomNet/JwtConnectionInfo.cs
index eb498085..1f9ac2b2 100644
--- a/Source/ZoomNet/JwtConnectionInfo.cs
+++ b/Source/ZoomNet/JwtConnectionInfo.cs
@@ -1,8 +1,11 @@
+using System;
+
namespace ZoomNet
{
///
/// Connect using JWT.
///
+ [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
{
///
diff --git a/Source/ZoomNet/Models/ContinuousMeetingChatSettings.cs b/Source/ZoomNet/Models/ContinuousMeetingChatSettings.cs
new file mode 100644
index 00000000..02b9278d
--- /dev/null
+++ b/Source/ZoomNet/Models/ContinuousMeetingChatSettings.cs
@@ -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
+{
+ ///
+ /// Information about the Enable continuous meeting chat feature.
+ ///
+ public class ContinuousMeetingChatSettings
+ {
+ ///
+ /// Gets or sets whether to enable the Enable continuous meeting chat setting.
+ ///
+ [JsonPropertyName("enable")]
+ public bool? Enable { get; set; }
+
+ ///
+ /// Gets or sets whether to enable the Automatically add invited external users setting.
+ ///
+ [JsonPropertyName("auto_add_invited_external_users")]
+ public bool? AutoAddInvitedExternalUsers { get; set; }
+
+ ///
+ /// Gets or sets the channel's ID.
+ ///
+ [JsonPropertyName("channel_id")]
+ public string ChannelId { get; set; }
+ }
+}
diff --git a/Source/ZoomNet/Models/MeetingSettings.cs b/Source/ZoomNet/Models/MeetingSettings.cs
index 8a81cf63..557df330 100644
--- a/Source/ZoomNet/Models/MeetingSettings.cs
+++ b/Source/ZoomNet/Models/MeetingSettings.cs
@@ -159,6 +159,12 @@ public class MeetingSettings
[JsonPropertyName("watermark")]
public bool? Watermark { get; set; }
+ ///
+ /// Gets or sets information about the Enable continuous meeting chat feature.
+ ///
+ [JsonPropertyName("continuous_meeting_chat")]
+ public ContinuousMeetingChatSettings ContinuousMeetingChat { get; set; }
+
///
/// 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.
diff --git a/Source/ZoomNet/Utilities/JwtTokenHandler.cs b/Source/ZoomNet/Utilities/JwtTokenHandler.cs
index bfff4329..041a13f0 100644
--- a/Source/ZoomNet/Utilities/JwtTokenHandler.cs
+++ b/Source/ZoomNet/Utilities/JwtTokenHandler.cs
@@ -74,13 +74,16 @@ public string RefreshTokenIfNecessary(bool forceRefresh)
{
_lock.EnterWriteLock();
- _tokenExpiration = DateTime.UtcNow.Add(_tokenLifeSpan);
- var jwtPayload = new Dictionary()
+ 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()
+ {
+ { "iss", _connectionInfo.ApiKey },
+ { "exp", _tokenExpiration.ToUnixTime() }
+ };
+ _jwtToken = JWT.Encode(jwtPayload, Encoding.ASCII.GetBytes(_connectionInfo.ApiSecret), JwsAlgorithm.HS256);
+ }
}
finally
{
diff --git a/Source/ZoomNet/Utilities/OAuthTokenHandler.cs b/Source/ZoomNet/Utilities/OAuthTokenHandler.cs
index 5ebdd044..05f088d5 100644
--- a/Source/ZoomNet/Utilities/OAuthTokenHandler.cs
+++ b/Source/ZoomNet/Utilities/OAuthTokenHandler.cs
@@ -75,61 +75,64 @@ public string RefreshTokenIfNecessary(bool forceRefresh)
{
_lock.EnterWriteLock();
- var contentValues = new Dictionary()
+ 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()
+ {
+ { "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(
+ jsonResponse.GetPropertyValue("scope", string.Empty)
+ .Split(' ')
+ .Select(x => x.Split(new[] { ':' }, 2))
+ .Select(x => new KeyValuePair(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(
- jsonResponse.GetPropertyValue("scope", string.Empty)
- .Split(' ')
- .Select(x => x.Split(new[] { ':' }, 2))
- .Select(x => new KeyValuePair(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
{
diff --git a/build.cake b/build.cake
index 746a88a3..d0329230 100644
--- a/build.cake
+++ b/build.cake
@@ -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.
diff --git a/global.json b/global.json
index f43378f6..667094ad 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "8.0.101",
+ "version": "8.0.201",
"rollForward": "patch",
"allowPrerelease": false
}