DependencyTrack / DependencyTrack/dependency-track

Infinite redirect loop after successful OIDC authentication with Okta (Frontend 4.13.1 / Chart 0.39.0-0.40.0-0.41.0)

Open
#5,750 0 comments 0 reactions 0 assignees View on GitHub
defect in triage
Dominant language
Java
Stars
4.2k
Forks
811
Avg merge
8h 39m
Merged PRs (30d)
237

Description

### Current Behavior

After successful Okta authentication, the frontend enters an infinite redirect loop instead of completing the login process. The browser console shows:
`RangeError: Maximum call stack size exceeded
at router.replace (chunk-vendors.c39ed462.js:63)
at Vue router navigation (app.72a50f68.js:1)`

The authentication flow completes successfully up to token exchange with Okta, but then the Vue router enters a recursive loop.

### Steps to Reproduce

Environment
• Dependency-Track Helm Chart: 0.39.0, 0.40.0 and 0.41.0 (tested all - same issue)
• Frontend Image: dependencytrack/frontend:4.13.1 and 4.12.0 (tested both - same issue)
• API Server Image: dependencytrack/apiserver:4.13.1
• IdP: Okta with default authorization server
• Deployment: Kubernetes (AWS EKS)
• Browser: Chrome 143.0.0.0

Okta Configuration
• App Type: Single-Page Application (SPA, public client)
• Client ID: 0xxxxxxxxxxx
• Issuer: https://my.okta.com/oauth2/default
• Sign-in redirect URI: https://dependency-track.my.syte/static/oidc-callback.html
• Sign-out redirect URI: https://dependency-track.my.syte
• Trusted Origin: https://dependency-track.my.syte (CORS + Redirect enabled)
• Grant types: Authorization Code only (no Implicit flow)

API Server Configuration (Backend OIDC)
`env:
- name: ALPINE_OIDC_ENABLED
value: "true"
- name: ALPINE_OIDC_ISSUER
value: "https://my.okta.com/oauth2/default"
- name: ALPINE_OIDC_CLIENT_ID
value: "0xxxxxxxxxxx"
- name: ALPINE_OIDC_USERNAME_CLAIM
value: "email"
- name: ALPINE_OIDC_TEAMS_CLAIM
value: "groups"
- name: ALPINE_OIDC_USER_PROVISIONING
value: "true"
- name: ALPINE_OIDC_TEAM_SYNCHRONIZATION
value: "true"
- name: ALPINE_SECRET_KEY_PASSWORD
valueFrom:
secretKeyRef:
name: dependency-track-oidc-env
key: ALPINE_SECRET_KEY_PASSWORD`

Backend OIDC is working: curl https://dependency-track.my.syte/api/v1/oidc/available returns true.
Frontend Configuration (via environment variables)
`kubectl set env deployment/dependency-track-frontend -n dependency-track \
API_BASE_URL="" \
OIDC_ISSUER="https://my.okta.com/oauth2/default" \
OIDC_CLIENT_ID="0xxxxxxxxxxx" \
OIDC_SCOPE="openid profile email"`

Resulting config.json:
`{
"API_BASE_URL": "",
"API_WITH_CREDENTIALS": null,
"OIDC_CLIENT_ID": "0xxxxxxxxxxx",
"OIDC_FLOW": null,
"OIDC_ISSUER": "https://my.okta.com/oauth2/default",
"OIDC_LOGIN_BUTTON_TEXT": null,
"OIDC_SCOPE": "openid profile email"
}`

Reproduction Steps
1. Navigate to https://dependency-track.my.syte
2. Click "OpenID" button on login page
3. Authenticate successfully with Okta
4. Browser redirects to /static/oidc-callback.html?code=...&state=...
5. BUG OCCURS: Infinite redirect loop starts

Observed Network Activity
Frontend nginx access log shows successful callback:

`GET /static/oidc-callback.html?code=mjyyyyyyyyyyyyyyyyyyyy=cdzzzzzzzzzzzzzzzzzz HTTP/1.1" 200
GET /static/js/oidc-client.min.js HTTP/1.1" 200
GET /static/config.json HTTP/1.1" 200`

Browser Network tab shows:
• ✓ GET /.well-known/openid-configuration (200)
• ✓ POST /oauth2/default/v1/token (200, returns tokens)
• ✓ GET /static/config.json (200)
• ✗ Missing: POST /api/v1/user/oidc/login (never called)
Browser localStorage shows tokens were successfully stored:

`{
"oidc.user:https://my.okta.com/oauth2/default:0xxxxxxxxxxx": {
"id_token": "eyJraWQiOi...",
"access_token": "eyJraWQiOi...",
"token_type": "Bearer",
"scope": "email profile openid",
"profile": {
"sub": "00uthyuo38k1OeZoR417",
"name": "Oleg Pchelintsev",
"email": "Pchelintsev.Oleg@my.syte",
"groups": ["Everyone"]
},
"expires_at": 1769098211
}
}`

The frontend successfully obtains tokens but never calls the API server to complete authentication.
Browser Console Error

`chunk-vendors.c39ed462.js:63 RangeError: Maximum call stack size exceeded
o @ chunk-vendors.c39ed462.js:63
h @ chunk-vendors.c39ed462.js:63
r @ chunk-vendors.c39ed462.js:63
Gt @ chunk-vendors.c39ed462.js:63
te.confirmTransition @ chunk-vendors.c39ed462.js:63
te.transitionTo @ chunk-vendors.c39ed462.js:63
e.replace @ chunk-vendors.c39ed462.js:63
(anonymous) @ chunk-vendors.c39ed462.js:63
(anonymous) @ app.72a50f68.js:1
... (repeats until stack overflow)`

The Vue router's router.replace() method calls itself recursively until the stack overflows.

Attempted Workarounds
1. Tried Implicit Flow
Set OIDC_FLOW="implicit" in frontend config.
Result: White screen with error:

`ErrorResponse: The response type is not supported by the authorization server. Configured response types: [code].`

(Okta app only supports Authorization Code flow)

2 .Tested Multiple Versions
• Chart 0.39.0 with frontend 4.12.1: Same bug
• Chart 0.39.0 with frontend 4.13.1: Same bug
• Chart 0.40.0 with frontend 4.12.0: Same bug
• Chart 0.40.0 with frontend 4.13.0: Same bug
• Chart 0.41.0 with frontend 4.13.0: Same bug
• Chart 0.41.0 with frontend snapshot: Same bug
3 .Verified API Endpoint
Tested /api/v1/user/oidc/login endpoint manually:

`kubectl run curl-test --image=curlimages/curl:latest -n dependency-track --rm -it --restart=Never -- \
curl -v -X POST http://dependency-track-api-server:8080/api/v1/user/oidc/login \
-H "Content-Type: application/json" \
-d '{"idToken": "eyJ..."}'`

Result: 415 Unsupported Media Type (unclear what format API expects)
Tried with Authorization header:

`curl -v http://dependency-track-api-server:8080/api/v1/user/oidc/login \
-H "Authorization: Bearer eyJ..."`

Result: 405 Method Not Allowed with Allow: POST,OPTIONS
The API endpoint exists and accepts POST, but the frontend never reaches it due to the redirect loop.

Additional Context

• API server logs show no OIDC-related errors
• Clearing browser cache/cookies does not help
• Issue is consistent across multiple developer machines
• Bug appears to be in frontend JavaScript between token exchange and API login call

Proposed Solution

The infinite redirect loop suggests an issue in the OIDC callback handling logic in the frontend, likely in:
• /static/oidc-callback.html
• Vue router navigation guards
• OIDC state management after token receipt
The frontend should call /api/v1/user/oidc/login with the id_token after successful token exchange, but this call never happens.

Environment Details

`Helm Chart: 0.39.0 / 0.40.0 / 0.41.0
Frontend: dependencytrack/frontend:4.13.1 / 4.12.0 / snapshot
API Server: dependencytrack/apiserver:4.13.1
Kubernetes: AWS EKS
Browser: Chrome 143.0.0.0 on macOS
IdP: Okta (default authorization server)`

### Expected Behavior

After successful OIDC callback, the frontend should:
1. Receive the authorization code
2. Exchange code for tokens with Okta (✓ working)
3. Call /api/v1/user/oidc/login with the id_token
4. Redirect to /dashboard

### Dependency-Track Version

4.13.1

### Dependency-Track Distribution

Container Image

### Database Server

PostgreSQL

### Database Server Version

17.5

### Browser

Google Chrome

### Checklist

- [x] I have read and understand the [contributing guidelines](https://github.com/DependencyTrack/dependency-track/blob/master/CONTRIBUTING.md#filing-issues)
- [x] I have checked the [existing issues](https://github.com/DependencyTrack/dependency-track/issues) for whether this defect was already reported

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.