Skip to content

feature(middleware): JWT refresh token#4492

Merged
Y4nnikH merged 106 commits into
CactuseSecurity:developfrom
SolidProgramming:jwt_token_lifetime_internal
May 19, 2026
Merged

feature(middleware): JWT refresh token#4492
Y4nnikH merged 106 commits into
CactuseSecurity:developfrom
SolidProgramming:jwt_token_lifetime_internal

Conversation

@SolidProgramming
Copy link
Copy Markdown
Contributor

@SolidProgramming SolidProgramming commented Apr 7, 2026

Running tests with runsettings

The repository contains two .runsettings files for local dotnet test runs:

  • FWO.default.runsettings keeps FWO_RUN_INTEGRATION_TESTS=false. Use this for the normal unit test run so tests that require a local FWO environment stay skipped.

  • FWO.local.runsettings is intended for local integration scenarios. It sets FWO_RUN_INTEGRATION_TESTS=true and reads FWO_TEST_USERNAME plus FWO_TEST_PASSWORD from your local file. Copy FWO.local.runsettings.example to FWO.local.runsettings and replace the placeholders with credentials that are valid in your local test environment.

Typical commands:

dotnet test roles/tests-unit/files/FWO.Test/FWO.Test.csproj --settings FWO.default.runsettings
dotnet test roles/tests-unit/files/FWO.Test/FWO.Test.csproj --settings FWO.local.runsettings

This keeps the default test run safe for everyone while still making it easy to opt into the JWT integration tests locally when a prepared environment is available.

❕ Visual Studio: Select that file in Visual Studio via Test -> Configure Run Settings -> Select Solution Wide runsettings File. With FWO.local.runsettings active, the JWT integration tests are enabled in Test Explorer.

❕ VS Code: tasks(test_default, test_jwt_integration_local) were also added for running default tests and local JWT integration tests via "dotnet test --settings ..." or use Ctrl+Shift+P => Tasks: Run Task => then select "test_default" or "test_jwt_integration_local"

Task: test_jwt_integration_local (FWO.local.runsettings)

Test summary: total: 17; failed: 0; succeeded: 17; skipped: 0;

Task: test_default (FWO.default.runsettings)

Test summary: total: 916; failed: 0; succeeded: 897; skipped: 19;


❕ Please review: ReportJob and DailyCheckJob are affected due to internal jwt lifetime changes.


WDmEQcNVio

Under monitoring there will be a new page to view/refresh/revoke the current jwt tokens of the admin user. The page is only added in debug build because of security risks and only for users with admin role.


Closing #1689
Closing #4352

SolidProgramming and others added 30 commits March 6, 2026 11:35
Refactored AuthenticationTokenController to:
- Rename TokenPair endpoints for clarity ([HttpPost("Get")] → [HttpPost("GetTokenPair")], [HttpPost("GetForUser")] → [HttpPost("GetTokenPairForUser")).
- Add new endpoints to return only JWT strings: [HttpPost("Get")] and [HttpPost("GetForUser")].
- Provide XML documentation for new endpoints.
…essToken(), updating all usages accordingly. Improved TokenService initialization to robustly handle exceptions when reading from session storage, clearing invalid tokens and logging warnings as needed. Extracted token clearing logic to a new private method. Restructured Login.razor form markup for clarity and updated session restoration logic to use the new API and handle errors gracefully. Updated MainLayout.razor to use the new method names. These changes enhance code clarity, error handling, and maintainability.
Added [Authorize] to RefreshToken and RevokeToken endpoints, requiring a valid JWT access token. Changed RefreshToken to return BadRequest (400) for invalid/expired refresh tokens. Introduced a DEBUG-only TestAuth endpoint for testing JWT authentication via Swagger. Enhanced Swagger/OpenAPI docs to include JWT Bearer authentication and security requirements.
Strengthen refresh token rotation by ensuring each refresh token
can only be used once. The RevokeRefreshToken method now returns
the number of affected rows, and token refresh endpoints verify
that exactly one token is revoked before issuing new tokens.
Add logging for replay attempts and an integration test to
confirm that only one of multiple concurrent refresh requests
with the same token succeeds. This mitigates replay attacks
and improves overall authentication security.
Refactor user context handling for authentication and token refresh.

- Rename and generalize HandleUiUserAtLogin to SynchronizeUiUserContext
- Prevent login state updates during refresh token operations.
- Simplify JWT creation
Replaces RequiresGitHubActionsAttribute with RequiresIntegrationEnvironmentAttribute to allow integration tests to run locally or in CI when FWO_RUN_INTEGRATION_TESTS=true. AuthenticationTokenIntegrationTest now reads credentials from environment variables, skipping tests with a clear message if not configured. Updates GitHub Actions workflow to set integration test variables and report test mode. Adds runsettings files for integration test configuration and updates .gitignore. Tests now skip gracefully if credentials are invalid. Improves safety and flexibility for running JWT integration tests.
Copy link
Copy Markdown
Contributor

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

Implements JWT refresh-token support across the middleware, UI, and supporting automation, including configurable token lifetimes and additional tests/docs to validate and run the new flow.

Changes:

  • Introduces access+refresh token pair endpoints (issue/refresh/revoke), refresh-token persistence, and shorter internal-service JWT lifetimes with rotation.
  • Updates UI authentication/session restoration to store token pairs and periodically refresh expired access tokens.
  • Adds integration test opt-in via .runsettings, CI wiring, and updated help/docs/examples.

Reviewed changes

Copilot reviewed 90 out of 92 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
roles/ui/files/FWO.UI/wwwroot/css/site.css Formatting + new help text styles
roles/ui/files/FWO.UI/wwwroot/css/help-code.css New CSS for code blocks in help pages
roles/ui/files/FWO.UI/Shared/MonitoringLayout.razor Adds (Debug-only) nav link to JWT token page
roles/ui/files/FWO.UI/Shared/MainLayout.razor Adds periodic access-token refresh checks + event handling updates
roles/ui/files/FWO.UI/Services/TokenService.cs New session token-pair storage + refresh/revoke logic
roles/ui/files/FWO.UI/Services/SessionStorageWrapper.cs Wrapper to abstract protected session storage
roles/ui/files/FWO.UI/Services/PeriodicTaskRunner.cs Utility for periodic async callbacks
roles/ui/files/FWO.UI/Services/JwtEventService.cs Removes old static JWT timer/event service
roles/ui/files/FWO.UI/Services/ISessionStorage.cs New interface for session storage abstraction
roles/ui/files/FWO.UI/Program.cs Registers TokenService + storage wrapper; uses token pair bootstrap
roles/ui/files/FWO.UI/Pages/Settings/SettingsRoles.razor Publishes permission-change events via mediator
roles/ui/files/FWO.UI/Pages/Settings/SettingsDefaults.razor Adds configurable access/refresh token lifetimes with validation
roles/ui/files/FWO.UI/Pages/Monitoring/MonitorJWTToken.razor New (Debug-only) admin token view/refresh/revoke page
roles/ui/files/FWO.UI/Pages/Logout.razor Routes logout through async deauthentication cleanup
roles/ui/files/FWO.UI/Pages/Login.razor Restores auth via token-pair restore flow; uses token pair auth
roles/ui/files/FWO.UI/Pages/Help/HelpSettingsDefaults.cshtml Documents new token lifetime settings
roles/ui/files/FWO.UI/Pages/Help/HelpLayout.cshtml Includes new help-code stylesheet
roles/ui/files/FWO.UI/Pages/Help/HelpApiSubnetDataImport.cshtml Uses formatted code blocks
roles/ui/files/FWO.UI/Pages/Help/HelpApiReporting.cshtml Updates API examples to token-pair endpoint + formatted blocks
roles/ui/files/FWO.UI/Pages/Help/HelpApiLogin.cshtml Documents token-pair issuance/refresh/revoke examples
roles/ui/files/FWO.UI/Pages/Help/HelpApiFwoQuery.cshtml Updates sample queries + formatting
roles/ui/files/FWO.UI/Pages/Help/HelpApiFwoMutation.cshtml Updates sample mutation + formatting
roles/ui/files/FWO.UI/Pages/Help/HelpApiAppDataImport.cshtml Reformats JSON sample in help
roles/ui/files/FWO.UI/Auth/AuthStateProvider.cs Token-pair aware auth, restore, refresh, revoke-on-logout
roles/tests-unit/files/FWO.Test/UiUserHandlerTest.cs Unit tests for token lifetime lookup defaults
roles/tests-unit/files/FWO.Test/TokenLifetimeProviderTest.cs Unit tests for lifetime provider behaviors
roles/tests-unit/files/FWO.Test/SimulatedUserConfig.cs Makes GetText more resilient in tests
roles/tests-unit/files/FWO.Test/PeriodicTaskRunnerTest.cs Unit test for periodic task runner
roles/tests-unit/files/FWO.Test/Mocks/MockProtectedSessionStorage.cs Test storage mock implementing ISessionStorage
roles/tests-unit/files/FWO.Test/Mocks/MockMiddlewareClient.cs Middleware client mock for refresh/revoke calls
roles/tests-unit/files/FWO.Test/LockTest.cs Adjusts parallelization settings
roles/tests-unit/files/FWO.Test/JwtWriterClaimsTest.cs Adds test for jti claim
roles/tests-unit/files/FWO.Test/InternalApiTokenServiceTest.cs Tests internal API token rotation/refresh
roles/tests-unit/files/FWO.Test/Helpers/RequiresIntegrationEnvironmentAttribute.cs Opt-in attribute for integration-style tests
roles/tests-unit/files/FWO.Test/FWO.Test.csproj Adds MVC testing package for integration test harness
roles/tests-unit/files/FWO.Test/DataGenerators/TokenTestDataBuilder.cs Builder for auth test data
roles/tests-unit/files/FWO.Test/DataGenerators/AuthTestHelpers.cs Common assertions for token workflows
roles/tests-unit/files/FWO.Test/ConfigFileTest.cs Updates expected exception types
roles/tests-unit/files/FWO.Test/AuthenticationTokenIntegrationTest.cs Adds end-to-end integration tests for refresh/revoke flows
roles/tests-unit/files/FWO.Test/AuthenticationTokenControllerAuditTest.cs Tests audit-text content for issued tokens
roles/tests-unit/files/FWO.Test/AuthStateProviderTest.cs Unit tests for restore/refresh/auth-state behavior
roles/tests-integration/tasks/test-auth.yml Updates Ansible integration test to token-pair endpoint
roles/tests-integration/handlers/main.yml Adds LDAP cleanup handler for integration test user
roles/sample-auth-data/templates/tree_roles_for_integration_testuser.ldif.j2 Grants reporter role to integration test user
roles/sample-auth-data/templates/tree_integration_testuser_jwt.ldif.j2 Creates integration test user entry
roles/sample-auth-data/tasks/modify_ldap_tree.yml Ensures integration test user + role are provisioned
roles/middleware/files/FWO.Middleware.Server/UiUserHandler.cs Adds lifetime-key-based config lookup + refactors user sync
roles/middleware/files/FWO.Middleware.Server/Services/TokenLifetimeProvider.cs Central token lifetime provider
roles/middleware/files/FWO.Middleware.Server/Services/InternalApiTokenServiceOptions.cs Options for internal token refresh timing
roles/middleware/files/FWO.Middleware.Server/Services/InternalApiTokenService.cs Short-lived internal JWT rotation logic
roles/middleware/files/FWO.Middleware.Server/Services/InternalApiTokenRefreshService.cs Background hosted service for token rotation
roles/middleware/files/FWO.Middleware.Server/RecertCheck.cs Uses shorter reporter token lifetime
roles/middleware/files/FWO.Middleware.Server/Program.cs Configures JSON naming policy + registers token refresh services + swagger security
roles/middleware/files/FWO.Middleware.Server/JwtWriter.cs Adds jti, removes ultra-long token lifetimes, adds refresh-token generator
roles/middleware/files/FWO.Middleware.Server/Jobs/ReportJob.cs Uses capped delegated token lifetime
roles/middleware/files/FWO.Middleware.Server/Jobs/DailyCheckJob.cs Passes lifetime provider into recert check
roles/lib/files/FWO.Services/EventMediator/Events/PermissionChangedEventArgs.cs New event args type
roles/lib/files/FWO.Services/EventMediator/Events/PermissionChangedEvent.cs New event type
roles/lib/files/FWO.Services/EventMediator/Events/JwtExpiredEventArgs.cs New event args type
roles/lib/files/FWO.Services/EventMediator/Events/JwtExpiredEvent.cs New event type
roles/lib/files/FWO.Middleware.Client/MiddlewareClient.cs Switches auth to token-pair endpoint + adds refresh/revoke client calls
roles/lib/files/FWO.Data/Middleware/TokenPair.cs New DTO for access/refresh tokens
roles/lib/files/FWO.Data/Middleware/RefreshTokenRequest.cs DTO for refresh/revoke requests
roles/lib/files/FWO.Data/Middleware/RefreshTokenInfo.cs DTO for refresh-token DB records
roles/lib/files/FWO.Config.File/ConfigFile.cs Supports env-var overrides for config/key paths + exception changes
roles/lib/files/FWO.Config.Api/UserConfig.cs Improves API error-code parsing
roles/lib/files/FWO.Config.Api/Data/ConfigData.cs Adds access/refresh lifetime config fields
roles/lib/files/FWO.Api.Client/Queries/AuthQueries.cs Adds refresh-token GraphQL queries
roles/lib/files/FWO.Api.Client/GraphQlApiConnection.cs Removes placeholder expired-JWT special-case
roles/importer/files/importer/import_mgm.py Parses access token from token-pair response
roles/importer/files/importer/fwo_api.py Updates login endpoint to token-pair endpoint
roles/database/tasks/install-database.yml Adds refresh-token table creation to new installs
roles/database/files/upgrade/9.1.0.sql Upgrade script for refresh_token table
roles/database/files/sql/idempotent/fworch-texts.sql Adds new UI/help texts for token features
roles/database/files/sql/creation/fworch-create-tables-jwt-refresh.sql Refresh token table creation script
roles/common/files/fwo-api-calls/auth/storeRefreshToken.graphql Mutation to persist refresh token hash
roles/common/files/fwo-api-calls/auth/revokeRefreshToken.graphql Mutation to revoke refresh token
roles/common/files/fwo-api-calls/auth/getRefreshToken.graphql Query to validate refresh token
roles/api/files/replace_metadata.json Adds Hasura permissions for refresh_token table
inventory/group_vars/testservers.yml Adds integration test user config for test servers
inventory/group_vars/all.yml Bumps product version to 9.1.0
FWO.local.runsettings.example Example local runsettings enabling integration tests
FWO.default.runsettings Default runsettings disabling integration tests
CONTRIBUTING.md Documents runsettings-based test execution
.vscode/tasks.json Adds VS Code tasks for default vs local JWT integration tests
.gitignore Ignores local runsettings file
.github/workflows/test-install.yml Runs JWT integration tests + adjusts Ansible steps

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

Comment thread roles/ui/files/FWO.UI/Pages/Monitoring/MonitorJWTToken.razor Outdated
Comment thread roles/middleware/files/FWO.Middleware.Server/Program.cs
Comment thread roles/ui/files/FWO.UI/Auth/AuthStateProvider.cs
Comment thread roles/ui/files/FWO.UI/Pages/Settings/SettingsRoles.razor
Comment thread roles/ui/files/FWO.UI/Pages/Settings/SettingsRoles.razor
Comment thread roles/ui/files/FWO.UI/Pages/Monitoring/MonitorJWTToken.razor Outdated
Comment thread roles/ui/files/FWO.UI/Pages/Monitoring/MonitorJWTToken.razor Outdated
@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown
Contributor

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

Copilot reviewed 90 out of 92 changed files in this pull request and generated 4 comments.

Comment on lines +43 to +50
@if (IsDebugBuild)
{
<li class="nav-item px-2">
<NavLink class="nav-link" href="monitoring/jwttoken">
<span class="@Icons.Credential"></span> JWT Token
</NavLink>
</li>
}
Comment on lines 245 to 247
// There was an error trying to authenticate the user. Probably invalid credentials
errorMessage = ( authResponse.Data != null ? userConfig.GetApiText(authResponse.Data) : "Middleware Api Error: " + authResponse.Content );
errorMessage = (authResponse.Content != null ? userConfig.GetApiText(authResponse.Content) : "Middleware Api Error: " + authResponse.Content);
}
Comment on lines +33 to +38
{
"accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "a7f3c8b9e2d1...",
"accessTokenExpires": "2025-12-11T14:30:00Z",
"refreshTokenExpires": "2026-01-10T12:00:00Z"
}
Comment on lines +33 to +38
{
"accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "a7f3c8b9e2d1...",
"accessTokenExpires": "2025-12-11T14:30:00Z",
"refreshTokenExpires": "2026-01-10T12:00:00Z"
}
@sonarqubecloud
Copy link
Copy Markdown

@Y4nnikH Y4nnikH requested a review from tpurschke May 19, 2026 12:32
Copy link
Copy Markdown
Contributor

@tpurschke tpurschke left a comment

Choose a reason for hiding this comment

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

last medium finding will be moved to a separate issue

dotnet build
dotnet test --filter "Name=HtmlToPdfTest"

- name: Run JWT refresh integration tests
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i guess we need to have a small meeting regarding this point today 12:30 pm?

@Y4nnikH Y4nnikH merged commit 98a9ca1 into CactuseSecurity:develop May 19, 2026
4 checks passed
@github-project-automation github-project-automation Bot moved this from In progress to Done in FWO UI May 19, 2026
@github-project-automation github-project-automation Bot moved this from In progress to Done in FWO API May 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

in progress assignee is working on it

Projects

Status: Done
Status: Done

Development

Successfully merging this pull request may close these issues.

JWT Refresh Test Middleware JWT lifetime for API requests Auth - implement JWT auto-refresh

5 participants