Skip to content

feat(oidc): support idp initialized auth#3678

Open
fiftin wants to merge 1 commit intodevelopfrom
oidc_idp_init
Open

feat(oidc): support idp initialized auth#3678
fiftin wants to merge 1 commit intodevelopfrom
oidc_idp_init

Conversation

@fiftin
Copy link
Copy Markdown
Collaborator

@fiftin fiftin commented Mar 3, 2026

No description provided.

Comment thread api/login.go
redirectPath = stateData.Return
} else {
redirectPath = mux.Vars(r)["redirect_path"]
}

Check warning

Code scanning / CodeQL

Bad redirect check Medium

This is a check that
this value
, which flows into a
redirect
, has a leading slash, but not that it does not have '/' or '' in its second position.
This is a check that
this value
, which flows into a
redirect
, has a leading slash, but not that it does not have '/' or '' in its second position.

Copilot Autofix

AI about 2 months ago

In general: When accepting a redirect path derived from untrusted input, ensure it is a safe, purely local path. At a minimum, require that it starts with a single / and that the second character is neither / nor \, to avoid scheme-relative (//...) and backslash-based (/\...) paths that browsers and URL parsers can misinterpret.

Best concrete fix here: Before using redirectPath in url.JoinPath, replace the current HasPrefix check with a stricter normalization/sanitization:

  1. If redirectPath is empty, default to /.
  2. If redirectPath does not start with /, prepend /.
  3. After that, if the second character exists and is / or \, treat the whole thing as unsafe and fall back to /.

This preserves existing behavior for all normal, safe paths (like /dashboard or dashboard/dashboard), while rejecting problematic ones like //evil and /\evil. The minimal code change is in api/login.go around lines 806–818, where redirectPath is post-processed. No new imports are needed; we can rely on len and direct byte indexing of the string.

Concretely:

  • In api/login.go, in the oidcRedirect function:
    • Replace the existing block:

      if !strings.HasPrefix(redirectPath, "/") {
          redirectPath = "/" + redirectPath
      }
    • With a new block that:

      • Ensures leading /.
      • Rejects paths whose second character is / or \ by resetting redirectPath to /.

This single change fixes both alert variants (for stateData.Return and mux.Vars(r)["redirect_path"]) because both flow into the same redirectPath variable and go through the same sanitization.

Suggested changeset 1
api/login.go

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/api/login.go b/api/login.go
--- a/api/login.go
+++ b/api/login.go
@@ -812,9 +812,16 @@
 		}
 	}
 
+	// Normalize and validate redirectPath to avoid open redirects.
+	if redirectPath == "" {
+		redirectPath = "/"
+	}
 	if !strings.HasPrefix(redirectPath, "/") {
 		redirectPath = "/" + redirectPath
 	}
+	if len(redirectPath) > 1 && (redirectPath[1] == '/' || redirectPath[1] == '\\') {
+		redirectPath = "/"
+	}
 
 	redirectURL, err := url.JoinPath(util.Config.WebHost, redirectPath)
 	if err != nil {
EOF
@@ -812,9 +812,16 @@
}
}

// Normalize and validate redirectPath to avoid open redirects.
if redirectPath == "" {
redirectPath = "/"
}
if !strings.HasPrefix(redirectPath, "/") {
redirectPath = "/" + redirectPath
}
if len(redirectPath) > 1 && (redirectPath[1] == '/' || redirectPath[1] == '\\') {
redirectPath = "/"
}

redirectURL, err := url.JoinPath(util.Config.WebHost, redirectPath)
if err != nil {
Copilot is powered by AI and may make mistakes. Always verify output.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0a0a7545d0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread api/login.go
Comment on lines +688 to +692
idpInitiated := stateParam == ""

var stateData oAuthState
err = json.Unmarshal(b, &stateData)

if err != nil {
log.Error(err.Error())
http.Redirect(w, r, loginURL, http.StatusTemporaryRedirect)
return
}
if idpInitiated {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Require request binding for IdP-initiated callbacks

When allow_idp_initiated is enabled, oidcRedirect accepts any callback that omits state and skips all CSRF/session correlation, then immediately exchanges code and creates a session; this permits login CSRF/account confusion (an attacker can obtain an auth code for their own IdP account and send /redirect?code=... to a victim, who will be logged into the attacker's Semaphore account). This branch needs an additional binding signal (separate endpoint, signed IdP hint, or other anti-replay correlation) instead of treating every missing-state callback as trusted.

Useful? React with 👍 / 👎.

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.

2 participants