Skip to content

Conversation

@SolidProgramming
Copy link
Contributor

No description provided.

[+] Added `revokeRefreshToken` mutation to revoke tokens by setting `revoked_at`.
[+] Added `storeRefreshToken` mutation to insert new tokens into the database.
@SolidProgramming SolidProgramming self-assigned this Oct 30, 2025
SolidProgramming and others added 28 commits October 30, 2025 16:54
…air` classes to handle token-related data and operations.

[~] Updated `AuthQueries` static constructor to initialize refresh token queries.
[~] create table if not exists refresh token for upgrade script 9.0
[+] Token validation on api querys
[~] Updated `Authenticate` method to remove `sessionStorage` dependency.
[~] Switched from `string` JWT to `TokenPair` for session restoration.
[~] Improved error handling for null `AccessToken` during session restore.
    remove unused `sessionStorage` parameter.
…tion in `fworch-texts.sql`.

[+] Introduced `AccessTokenLifetimeHours` and `RefreshTokenLifetimeDays` properties in `ConfigData.cs`.
[+] Added UI elements in `SettingsDefaults.razor` for configuring token lifetimes with validation.
Introduced a new `RevokeToken` endpoint in `AuthenticationTokenController` to explicitly revoke refresh tokens. Updated `RefreshTokenInfo` to include `created_at` and `revoked_at` fields. Enhanced `TokenService` to revoke tokens during logout and renamed `ClearTokenPair` to `RevokeTokens`.

Refactored `UiUserHandler` to dynamically fetch expiration times based on configuration keys. Updated `JwtWriter` and `CreateTokenPair` to use configurable token lifetimes (`AccessTokenLifetimeHours` and `RefreshTokenLifetimeDays`).

Improved code readability by replacing `var` with explicit types, enhancing formatting, and adding better error handling. Removed unused imports and added necessary ones for compatibility. CactuseSecurity#1689
Removed `ITokenRefreshService` interface and refactored token management to consolidate operations within `TokenService`. Updated `TokenService` to use record-style constructor syntax, added XML documentation, and introduced new methods for token handling (`SetTokenPair`, `RevokeTokens`). Removed redundant methods and fields, modernized dependency injection, and improved token persistence with `ProtectedSessionStorage`.
Changed exception type in `UiUserHandler.cs` for better clarity and added a fallback mechanism for missing database values. Adjusted `Logout.razor` to ensure proper deauthentication before resource cleanup. Simplified `TokenService` by removing `RefreshAccessToken`, making `Initialize` private, and ensuring token initialization in `IsAccessTokenExpired` and `RevokeTokens`.
SolidProgramming and others added 24 commits December 11, 2025 16:36
Expanded and clarified API authentication documentation in HelpApiLogin.cshtml.
Added localized texts for token actions and error messages, and provided detailed examples for login, token refresh, and revoke endpoints, including error scenarios and expected responses.
Introduced .small-helpage CSS class for clearer response labeling.
Enhances user guidance and internationalization for API authentication.
Removed [Order] attribute from all test classes to allow default test execution order. Minor change in Program.cs regarding JwtBearer usage, but no functional impact.

fixed sonarqube  csharpsquid:S125 in Program.cs
Changed JWT token retrieval endpoint from /Get to /GetTokenPair/
in test-api.yml and test-auth.yml. Updates both valid and invalid
credentials test cases to use the new endpoint.
Removed commented-out and redundant ldapmodify tasks from modify_ldap_tree.yml. The playbook now only includes essential steps for adding or modifying the integration_testuser_jwt entry and for adding sample tenants, operators, and groups. Conditional logic and legacy code for roles and groups have been eliminated, resulting in a more streamlined and maintainable playbook.
@sonarqubecloud
Copy link

@SolidProgramming SolidProgramming marked this pull request as ready for review December 19, 2025 15:56
Copilot AI review requested due to automatic review settings December 19, 2025 15:56
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements JWT refresh token functionality to improve authentication security and user experience. The implementation adds a two-token system (access + refresh tokens) with automatic token rotation, secure hash-based storage in the database, and client-side token management.

Key changes:

  • New refresh token system with database storage using SHA256 hashing
  • Automatic token refresh in UI when access tokens expire
  • Token rotation on refresh for enhanced security

Reviewed changes

Copilot reviewed 52 out of 53 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
roles/ui/files/FWO.UI/Services/TokenService.cs New service managing token lifecycle (refresh, revoke, storage)
roles/middleware/files/FWO.Middleware.Server/Controllers/AuthenticationTokenController.cs New endpoints for token refresh and revocation
roles/ui/files/FWO.UI/Shared/MainLayout.razor Token refresh logic on navigation and initialization
roles/database/files/upgrade/9.0.sql New refresh_tokens table with expiration/revocation tracking
roles/lib/files/FWO.Data/Middleware/TokenPair.cs Data model for access+refresh token pairs
roles/tests-unit/files/FWO.Test/TokenServiceTest.cs Comprehensive unit tests for TokenService
roles/tests-unit/files/FWO.Test/AuthenticationTokenIntegrationTest.cs Integration tests for token workflows
roles/ui/files/FWO.UI/Auth/AuthStateProvider.cs Updated authentication flow to use TokenPair
roles/lib/files/FWO.Config.Api/Data/ConfigData.cs New config parameters for token lifetimes
Comments suppressed due to low confidence (2)

roles/ui/files/FWO.UI/Pages/Settings/SettingsDefaults.razor:310

                foreach (string group in groupsOfUser)
                {
                    string groupName = new DistName(group).Group;
                    if (!MatchesNamingConvention(groupName, namingConvention))
                    {
                        continue; // skip groups that do not match the naming convention
                    }
                    FwoOwner? owner = FindOwnerWithMatchingGroupName(groupName, allOwners);

                    if (owner != null)
                    {
                        user.Ownerships.Add(owner.Id);
                    }
                }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +140 to +148
private async void OnLocationChanged(object? sender, LocationChangedEventArgs e)
{
if (e == null || string.IsNullOrEmpty(e.UserDn))
if (LastLocation != e.Location)
{
return;
LastLocation = e.Location;

await CheckAndRefreshTokenAsync();
}
}
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The OnLocationChanged event handler is marked as async void, which is problematic. Exceptions thrown in async void methods cannot be caught by the caller and will crash the application. This should be changed to async Task and errors should be properly handled. Additionally, there's no unsubscription of this event handler in the Dispose method, which could lead to memory leaks.

Copilot uses AI. Check for mistakes.
top: calc(var(--bs-custom-modal-margin-top) + 1em - 1px);
}

.small-helpage {
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CSS class name contains a typo. "helpage" should be "helptext" or "help-text" to properly indicate this is styling for help text content.

Copilot uses AI. Check for mistakes.
Comment on lines +114 to +121
await refreshSemaphore.WaitAsync();

try
{
if (!await IsAccessTokenExpired())
{
return true;
}
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a potential race condition in the token refresh logic. The semaphore is acquired at line 114, but the token expiration is checked again at line 118. However, if multiple concurrent requests pass the initial check at line 154 (before acquiring the semaphore), they will all queue up. When the first request completes the refresh and releases the semaphore, subsequent requests will check expiration again at line 118 and may return true without refreshing (since the token was just refreshed), but this still results in multiple attempts hitting the middleware. Consider adding a check immediately after acquiring the semaphore to verify if another request has already refreshed the token.

Copilot uses AI. Check for mistakes.
{
// Spin up local test server using WebApplicationFactory
Log.WriteInfo("Test Setup", "Creating WebApplicationFactory for local testing");
factory = new WebApplicationFactory<Program>()
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disposable 'WebApplicationFactory' is created but not disposed.

Copilot uses AI. Check for mistakes.
}
catch (Exception) { }
Log.WriteError(( title != "" ? title : "Unspecified Error" ), ( message != "" ? message : "Unspecified error occured: " ), exception, userConfig.User.Name, role);
catch(Exception) { }
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Poor error handling: empty catch block.

Copilot uses AI. Check for mistakes.
Assert.That(ConfigFile.ApiServerUri, Is.Null);

Assert.DoesNotThrow(() => { var _ = ConfigFile.MiddlewareServerUri; });
Assert.DoesNotThrow(() => { var _ = ConfigFile.ApiServerUri; });
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to _ is useless, since its value is never read.

Copilot uses AI. Check for mistakes.
Assert.Catch(typeof(ApplicationException), () => { var _ = ConfigFile.JwtPublicKey; });

Assert.That(ConfigFile.JwtPublicKey, Is.Null);
Assert.DoesNotThrow(() => { var _ = ConfigFile.JwtPublicKey; });
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to _ is useless, since its value is never read.

Copilot uses AI. Check for mistakes.
Assert.Catch(typeof(ApplicationException), () => { var _ = ConfigFile.JwtPrivateKey; });

Assert.That(ConfigFile.JwtPrivateKey, Is.Null);
Assert.DoesNotThrow(() => { var _ = ConfigFile.JwtPrivateKey; });
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to _ is useless, since its value is never read.

Copilot uses AI. Check for mistakes.
<input type="password" class="form-control @InputClass" id="PasswordInput" placeholder="@(globalConfig.GetText("password"))" value="@Password" @oninput="OnPasswordInput">
</div>
@if (loginInProgress == false)
@if(loginInProgress == false)
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression 'A == false' can be simplified to '!A'.

Copilot uses AI. Check for mistakes.
<input type="password" class="form-control @InputClass" id="NewPasswordInput2" placeholder="@(userConfig.GetText("new_password"))" value="@newPassword2" @oninput="OnNewPassword2Input">
</div>
@if (passwordChangeInProgress == false)
@if(passwordChangeInProgress == false)
Copy link

Copilot AI Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression 'A == false' can be simplified to '!A'.

Copilot uses AI. Check for mistakes.
@SolidProgramming SolidProgramming marked this pull request as draft December 19, 2025 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant