-
-
Notifications
You must be signed in to change notification settings - Fork 4
fix: Improved Portainer authentication jwt expiry handling #113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| using System.Text.Json; | ||
|
|
||
| namespace PatchPanda.Web.Helpers; | ||
|
|
||
| public class JwtHelper | ||
| { | ||
| public static DateTime? GetJwtExpiry(string jwt) | ||
| { | ||
| try | ||
| { | ||
| var parts = jwt.Split('.'); | ||
| if (parts.Length < 2) return null; | ||
|
|
||
| var payload = parts[1]; | ||
| var base64 = payload.Replace('-', '+').Replace('_', '/'); | ||
| switch (base64.Length % 4) | ||
| { | ||
| case 2: base64 += "=="; break; | ||
| case 3: base64 += "="; break; | ||
| } | ||
|
|
||
| var bytes = Convert.FromBase64String(base64); | ||
| using var doc = JsonDocument.Parse(bytes); | ||
| if (doc.RootElement.TryGetProperty("exp", out var expElement) && expElement.TryGetInt64(out var exp)) | ||
| { | ||
| return DateTimeOffset.FromUnixTimeSeconds(exp).UtcDateTime; | ||
| } | ||
| } | ||
| catch | ||
| { | ||
| return null; | ||
| } | ||
|
|
||
| return null; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -67,6 +67,8 @@ private async Task EnsureAuthenticatedAsync() | |||||||||||
| return; | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| _logger.LogInformation("Using username and password to authenticate with Portainer..."); | ||||||||||||
|
|
||||||||||||
| var payload = JsonSerializer.Serialize(new { username = _username, password = _password }); | ||||||||||||
| var resp = await _httpClient.PostAsync( | ||||||||||||
| "/api/auth", | ||||||||||||
|
|
@@ -94,10 +96,11 @@ private async Task EnsureAuthenticatedAsync() | |||||||||||
| "Bearer", | ||||||||||||
| _jwt | ||||||||||||
| ); | ||||||||||||
| _jwtExpiry = DateTime.UtcNow.AddHours(8); | ||||||||||||
| _jwtExpiry = JwtHelper.GetJwtExpiry(_jwt) ?? DateTime.UtcNow.AddHours(8); | ||||||||||||
| _logger.LogInformation("Portainer authentication successful."); | ||||||||||||
|
Comment on lines
+99
to
+100
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Consider a small clock-skew buffer when storing JWT expiry. Using the exact 🧩 Suggested tweak- _jwtExpiry = JwtHelper.GetJwtExpiry(_jwt) ?? DateTime.UtcNow.AddHours(8);
+ var exp = JwtHelper.GetJwtExpiry(_jwt);
+ _jwtExpiry = exp?.AddSeconds(-60) ?? DateTime.UtcNow.AddHours(8);📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||
| } | ||||||||||||
| else | ||||||||||||
| _logger.LogWarning("Failed parsing Portainer auth response"); | ||||||||||||
| _logger.LogWarning("Failed parsing Portainer auth response."); | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| private async Task<PortainerStackDto?> GetStack(string stackName) | ||||||||||||
|
|
||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard against null/whitespace and malformed JWTs to avoid invalid caching.
jwtcan still be null/empty at runtime, and accepting 2-part tokens can treat malformed JWTs as valid. Add an early guard and require exactly 3 parts.🛠️ Proposed fix
public static DateTime? GetJwtExpiry(string jwt) { + if (string.IsNullOrWhiteSpace(jwt)) return null; try { var parts = jwt.Split('.'); - if (parts.Length < 2) return null; + if (parts.Length != 3) return null;📝 Committable suggestion
🤖 Prompt for AI Agents