Skip to content

Commit 3a77a64

Browse files
committed
Fixed the OAuth Flow
1 parent 167ede2 commit 3a77a64

5 files changed

Lines changed: 71 additions & 18 deletions

File tree

Contentstack.Management.Core/ContentstackClient.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,11 @@ internal ContentstackResponse InvokeSync<TRequest>(TRequest request, bool addAcc
219219
{
220220
ThrowIfDisposed();
221221

222+
if (contentstackOptions.IsOAuthToken && !string.IsNullOrEmpty(contentstackOptions.Authtoken))
223+
{
224+
EnsureOAuthTokenIsValid();
225+
}
226+
222227
ExecutionContext context = new ExecutionContext(
223228
new RequestContext()
224229
{
@@ -636,6 +641,14 @@ internal void ClearStoredOAuthTokens(string clientId)
636641

637642
_oauthTokens.Remove(clientId);
638643
}
644+
645+
/// <summary>
646+
/// Clears all OAuth tokens (useful for cleanup).
647+
/// </summary>
648+
internal void ClearAllOAuthTokens()
649+
{
650+
_oauthTokens.Clear();
651+
}
639652
#endregion
640653

641654
/// <summary>

Contentstack.Management.Core/Models/OAuthTokens.cs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,29 @@ public class OAuthTokens
2323

2424
public string AppId { get; set; }
2525

26-
public bool IsExpired => DateTime.UtcNow >= ExpiresAt;
27-
28-
public bool NeedsRefresh => DateTime.UtcNow >= ExpiresAt.AddMinutes(-5) || IsExpired;
26+
public bool IsExpired => ExpiresAt == DateTime.MinValue || DateTime.UtcNow >= ExpiresAt;
27+
28+
public bool NeedsRefresh
29+
{
30+
get
31+
{
32+
// If ExpiresAt is not set or is MinValue, consider it expired
33+
if (ExpiresAt == DateTime.MinValue)
34+
return true;
35+
36+
try
37+
{
38+
// Check if we need to refresh (5 minutes before expiration)
39+
var refreshTime = ExpiresAt.AddMinutes(-5);
40+
return DateTime.UtcNow >= refreshTime || IsExpired;
41+
}
42+
catch (ArgumentOutOfRangeException)
43+
{
44+
// If the calculation results in an unrepresentable DateTime, consider it expired
45+
return true;
46+
}
47+
}
48+
}
2949

3050
public bool IsValid => !string.IsNullOrEmpty(AccessToken) && !IsExpired;
3151
}

Contentstack.Management.Core/OAuthHandler.cs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -254,10 +254,13 @@ public async Task<string> AuthorizeAsync()
254254

255255
try
256256
{
257+
257258
// Build the base authorization URL using the correct OAuth hostname
258259
// Transform api.contentstack.io -> app.contentstack.com for OAuth authorization
259260
var oauthHost = GetOAuthHost(GetClient().contentstackOptions.Host);
261+
260262
var baseUrl = $"https://{oauthHost}/#!/apps/{_options.AppId}/authorize";
263+
261264
var authUrl = new UriBuilder(baseUrl);
262265

263266
// Add required OAuth parameters
@@ -310,10 +313,21 @@ public async Task<OAuthTokens> ExchangeCodeForTokenAsync(string authorizationCod
310313

311314
try
312315
{
316+
313317
// Create the OAuth token service for authorization code exchange
314318
OAuthTokenService tokenService;
315319

316-
if (_options.UsePkce && !string.IsNullOrEmpty(this.codeVerifier) )
320+
if (_options.UsePkce)
321+
{
322+
// PKCE code verifier should be available from the instance
323+
if (string.IsNullOrEmpty(this.codeVerifier))
324+
{
325+
throw new Exceptions.OAuthConfigurationException(
326+
"PKCE code verifier not found. Make sure to call AuthorizeAsync() before ExchangeCodeForTokenAsync().");
327+
}
328+
}
329+
330+
if (_options.UsePkce && !string.IsNullOrEmpty(this.codeVerifier))
317331
{
318332
tokenService = OAuthTokenService.CreateForAuthorizationCode(
319333
serializer: GetClient().serializer,
@@ -557,9 +571,18 @@ private static string GetOAuthHost(string baseHost)
557571
if (string.IsNullOrEmpty(baseHost))
558572
return baseHost;
559573

560-
// Transform api.contentstack.io -> app.contentstack.com
574+
// Extract hostname from URL if it contains protocol
561575
var oauthHost = baseHost;
562-
576+
if (oauthHost.StartsWith("https://"))
577+
{
578+
oauthHost = oauthHost.Substring(8); // Remove "https://"
579+
}
580+
else if (oauthHost.StartsWith("http://"))
581+
{
582+
oauthHost = oauthHost.Substring(7); // Remove "http://"
583+
}
584+
585+
// Transform api.contentstack.io -> app.contentstack.com
563586
// Replace .io with .com
564587
if (oauthHost.EndsWith(".io"))
565588
{
@@ -653,9 +676,9 @@ private async Task RevokeOauthAppAuthorizationAsync(string authorizationId)
653676
// Make the API call to revoke authorization
654677
var response = await GetClient().InvokeAsync<OAuthAppRevocationService, ContentstackResponse>(service);
655678
}
656-
catch
679+
catch (Exception ex)
657680
{
658-
throw;
681+
throw ex;
659682
}
660683
finally
661684
{

Contentstack.Management.Core/Services/ContentstackService.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -168,18 +168,17 @@ public virtual IHttpRequest CreateHttpRequest(HttpClient httpClient, Contentstac
168168
{
169169
Headers["authorization"] = this.ManagementToken;
170170
}
171-
else if (!string.IsNullOrEmpty(config.Authtoken))
171+
else if (config.IsOAuthToken)
172172
{
173-
if (config.IsOAuthToken)
173+
if (!string.IsNullOrEmpty(config.Authtoken))
174174
{
175-
// OAuth Bearer token format
175+
176176
Headers["authorization"] = $"Bearer {config.Authtoken}";
177177
}
178-
else
179-
{
180-
// Traditional authtoken format
181-
Headers["authtoken"] = config.Authtoken;
182-
}
178+
}
179+
else if (!string.IsNullOrEmpty(config.Authtoken))
180+
{
181+
Headers["authtoken"] = config.Authtoken;
183182
}
184183

185184
if (!string.IsNullOrEmpty(apiVersion))

Contentstack.Management.Core/Services/OAuth/OAuthTokenService.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,6 @@ public override IHttpRequest CreateHttpRequest(System.Net.Http.HttpClient httpCl
7878
Host = GetDeveloperHubHostname(config.Host),
7979
Port = config.Port,
8080
Version = "", // OAuth endpoints don't use versioning
81-
Authtoken = config.Authtoken,
82-
IsOAuthToken = config.IsOAuthToken
8381
};
8482

8583
var request = base.CreateHttpRequest(httpClient, devHubConfig, addAcceptMediaHeader, apiVersion);

0 commit comments

Comments
 (0)