8000 Add failing tests for URL encoding of path-style fragments during OAuth callback parsing by Copilot · Pull Request #243 · keycloak/keycloak-js · GitHub
[go: up one dir, main page]

Skip to content

Add failing tests for URL encoding of path-style fragments during OAuth callback parsing - #243

Closed
jonkoops with Copilot wants to merge 9 commits into
mainfrom
copilot/fix-url-encoding-issue
Closed

jonkoops with Copilot wants to merge 9 commits into
mainfrom
copilot/fix-url-encoding-issue

Conversation

Copilot AI commented Jan 12, 2026 •
Copy link
Copy Markdown

Fragment-based routes (e.g., #/admin/maintenance/scripts) are being URL-encoded during OAuth callback parsing, breaking hash-based routing. The route path is treated as a URLSearchParams key, causing #/admin/scripts to become #%2Fadmin%2Fscripts=.

Test Cases

This PR contains only failing tests that demonstrate the bug. Two test cases are included in test/tests/preserves-path-fragment.spec.ts:

  1. Path-style fragment: Tests that #/admin/maintenance/scripts is preserved without encoding after login
  2. Path-style fragment with query params: Tests that #/admin/users?tab=details is preserved without encoding after login

Expected Behavior (Currently Failing)

After OAuth redirect, the fragment should be preserved as-is:

  • #/admin/maintenance/scripts should remain #/admin/maintenance/scripts
  • NOT become #%2Fadmin%2Fmaintenance%2Fscripts%3D

Actual Behavior (Bug)

The fragment gets URL-encoded, breaking hash-based routing in single-page applications.


Note: This PR contains only the failing tests to demonstrate the bug. Implementation will be provided separately.

Original prompt

This section details on the original issue you should resolve

<issue_title>Update to 26.2.2: URL encoded redirect urls in Angular application</issue_title>
<issue_description>### Describe the bug

After Upgrade to keycloak-js 26.2.2 a page refresh in the browser leads to issues with the Angular router. It seems that the redirect urls arrive in url encoded form in the router like '/%2Fadmin%2Fmaintenance%2Fscripts%3D' whereas with keycloak-js 26.2.1 they were in the form '/admin/maintenance/scripts'

Version

26.2.2

Expected behavior

With keycloak-js 26.2.1, the Angular router gets the redirect urls without url encoding. This is working fine.

If I am on a page with the url http://localhost:4500/#/admin/maintenance/scripts and perform a page refresh in the browser, the Angular router gets the following events.

Router Event: NavigationStart router_module.mjs:1418:19 NavigationStart(id: 1, url: '/admin/maintenance/scripts') router_module.mjs:1419:19 Object { id: 1, url: "/admin/maintenance/scripts", type: 0, navigationTrigger: "imperative", restoredState: null } router_module.mjs:1420:19 Angular is running in development mode. debug_node.mjs:18311:17 .... Router Event: NavigationEnd router_module.mjs:1418:19 NavigationEnd(id: 1, url: '/admin/maintenance/scripts', urlAfterRedirects: '/admin/maintenance/scripts') router_module.mjs:1419:19 Object { id: 1, url: "/admin/maintenance/scripts", urlAfterRedirects: "/admin/maintenance/scripts", type: 1 }

Actual behavior

With keycloak-js 26.2.2, the Angular router gets the redirect urls in url encoded form, which it cannot handle.

If I am on a page with the url http://localhost:4500/#/admin/maintenance/scripts and perform a page refresh in the browser, the Angular router gets the following events.

Router Event: NavigationStart router_module.mjs:1418:19 NavigationStart(id: 1, url: '/%2Fadmin%2Fmaintenance%2Fscripts%3D') router_module.mjs:1419:19 Object { id: 1, url: "/%2Fadmin%2Fmaintenance%2Fscripts%3D", type: 0, navigationTrigger: "imperative", restoredState: null } router_module.mjs:1420:19 Angular is running in development mode. debug_node.mjs:18311:17 Router Event: NavigationError router_module.mjs:1418:19 NavigationError(id: 1, url: '/%2Fadmin%2Fmaintenance%2Fscripts%3D', error: Error: NG04002: Cannot match any routes. URL Segment: '%2Fadmin%2Fmaintenance%2Fscripts%3D') router_module.mjs:1419:19 Object { id: 1, url: "/%2Fadmin%2Fmaintenance%2Fscripts%3D", error: Error, target: undefined, type: 3 }

How to Reproduce?

On an initialized and logged in Angular application with an url like http://localhost:4500/#/admin/maintenance/scripts perform a page reload.

Anything else?

Is this a bug or did I miss to adapt to some behavioural change?

Kind regards</issue_description>

<agent_instructions>Write me a test to verify this bug, then fix the bug, likely by splitting off the original URLs fragment before parsing the callback parameters.</agent_instructions>

Comments on the Issue (you are @copilot in this section)

@jonkoops Please provide a reproducible example that does not involve a framework such as Angular or React, I have a [plain web application](https://github.com/jonkoops/kjs-playground) that can be used to reproduce it (fork if needed).

This issue is likely caused by some bug fixing that was done for 26.2.2 (see #230). Could you validate this issue was also present in 26.2.0?</comment_new>
<comment_new>@jonkoops
Very strange, I wonder what it could be. It is very likely related to the part of the code that parses the callback URL. Are you using responseMode with fragment, or query?</comment_new>
<comment_new>@jonkoops
Thanks for diving into this @djoho, that should be plenty of information to go on, appreciate the detailed debugging. It seems that the likely solution is to split the original route path off the returned fragment before parsing the arguments. I am currently bogged down on onboarding on a different project, so I likely won't be able to look at this until Friday (when I have some free time).

In the meantime, I am marking this as 'help wanted' in case anyone would like to provide a fix. Even a failing test that reproduces this behavior would be appreciated! If nobody gets to it, I will try to have a proper fix in at the end of the week.</comment_new>


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 3 commits January 12, 2026 11:01
Split application route paths from OAuth callback parameters before parsing to prevent URL encoding of the route portion. This fixes issues where fragments like #/admin/scripts become %2Fadmin%2Fscripts%3D after OAuth callbacks.

Co-authored-by: jonkoops <695720+jonkoops@users.noreply.github.com>
Simplify complex boolean expressions for better readability:
- Use Math.min with filtered indices to find parameter start
- Extract separator logic into clearer variables

Co-authored-by: jonkoops <695720+jonkoops@users.noreply.github.com>
Clarify that the second initializeAdapter call processes OAuth callback parameters after redirect.

Co-authored-by: jonkoops <695720+jonkoops@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix URL encoded redirect URLs in Angular application Fix URL encoding of path-style fragments during OAuth callback parsing Jan 12, 2026
Copilot AI requested a review from jonkoops January 12, 2026 11:08
@jonkoops
Copy link
Copy Markdown
< 8000 /details-menu>
Contributor

@copilot this is terrible and hacky, I want to use the original URL that is stored in the callback parameters and stored in local storage to find the cut-off point for the parameters for example, if .login() is called and the current URL is http://localhost:8080/#/foo/bar, it should use the fragment of this URL stored in the callback to determine where the callback parameters start. Also, did you even run the test? See the test documentation in the test directory.

Instead of heuristically detecting path-style fragments, use the original redirect URI stored during login to determine where OAuth callback parameters start. This eliminates the hacky approach and properly handles all fragment types by comparing the current callback URL with the stored redirect URI.

Co-authored-by: jonkoops <695720+jonkoops@users.noreply.github.com>
Copilot AI commented Jan 12, 2026
Copy link
Copy Markdown
Author

@copilot this is terrible and hacky, I want to use the original URL that is stored in the callback parameters and stored in local storage to find the cut-off point for the parameters for example, if `...

Completely rewrote the implementation to use the stored redirect URI from local storage. The solution now:

  1. Extracts the state parameter in first pass to retrieve stored callback data
  2. Uses the stored redirectUri to determine where OAuth params start by comparing fragments
  3. Correctly handles all fragment types without heuristics

Commit: cced3ed

Specify what happens in catch blocks when URL parsing fails.

Co-authored-by: jonkoops <695720+jonkoops@users.noreply.github.com>
@jonkoops
Copy link
Copy Markdown
Contributor

@copilot all the parsing logic should be isolated to parseCallbackUrl(), I don't want two passes of the same thing.

Move stored redirect URI retrieval into parseCallbackUrl itself, eliminating the two-pass approach. The method now extracts the state parameter internally to retrieve stored callback data and uses it for parsing in a single pass.

Co-authored-by: jonkoops <695720+jonkoops@users.noreply.github.com>
Copilot AI commented Jan 12, 2026
Copy link
Copy Markdown
Author

@copilot all the parsing logic should be isolated to parseCallbackUrl(), I don't want two passes of the same thing.

Refactored to isolate all parsing logic within parseCallbackUrl(). The method now internally retrieves the stored redirect URI by extracting the state parameter, eliminating the two-pass approach.

Commit: 68cc0b5

@jonkoops
Copy link
Copy Markdown
Contributor

Sigh, this is again terrible code quality. @copilot just make me a new PR with only the failing tests, we'll do the implementation ourselves.

Combine state extraction and parsing into single conditional blocks to eliminate redundant responseMode checks.

Co-authored-by: jonkoops <695720+jonkoops@users.noreply.github.com>
Reverted all implementation changes to lib/keycloak.js. PR now contains only the test cases that demonstrate the URL encoding bug with path-style fragments.

Co-authored-by: jonkoops <695720+jonkoops@users.noreply.github.com>
Copilot AI commented Jan 12, 2026
Copy link
Copy Markdown
Author

Sigh, this is again terrible code quality. @copilot just make me a new PR with only the failing tests, we'll do the implementation ourselves.

Reverted all implementation changes. PR now contains only the failing tests in test/tests/preserves-path-fragment.spec.ts that demonstrate the URL encoding bug.

Commit: 3685b58

Copilot AI changed the title Fix URL encoding of path-style fragments during OAuth callback parsing Add failing tests for URL encoding of path-style fragments during OAuth callback parsing Jan 12, 2026
@jonkoops
Copy link
Copy Markdown
Contributor

Going to work on the implementation for this one myself, closing.

@jonkoops jonkoops closed this Jan 27, 2026
@jonkoops
jonkoops deleted the copilot/fix-url-encoding-issue branch January 27, 2026 10:44
@jonkoops
jonkoops restored the copilot/fix-url-encoding-issue branch January 27, 2026 10:45
@jonkoops
jonkoops deleted the copilot/fix-url-encoding-issue branch January 27, 2026 10:46
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.

Update to 26.2.2: URL encoded redirect urls in Angular application

2 participants

0