Skip to content

Commit 5486525

Browse files
committed
fix(auth): force fresh upstream auth on every SSO click
Append prompt=login and max_age=0 to the OIDC authorize URL wrapped in the source-login next param. Authentik no longer reuses the existing session cookie on returning external users, so the source-enrollment flow stops landing on /if/user/ — which only internal users may access.
1 parent 770aa63 commit 5486525

2 files changed

Lines changed: 19 additions & 3 deletions

File tree

internal/user/port/http/oidc_handlers.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,14 +95,22 @@ func (h *OIDCHandler) SSORedirect(c *gin.Context) {
9595
}
9696

9797
authURL := h.oidc.AuthCodeURL("goshop-state")
98-
if _, err := url.Parse(authURL); err != nil {
98+
u, err := url.Parse(authURL)
99+
if err != nil {
99100
apperror.Wrap(apperror.ErrInternal, err).HTTPError(c)
100101
return
101102
}
103+
// Force a fresh authentication round-trip so Authentik never reuses an
104+
// existing session cookie. Without this, the source-enrollment flow on a
105+
// returning external user lands on /if/user/ (Internal-only) and 403s.
106+
q := u.Query()
107+
q.Set("prompt", "login")
108+
q.Set("max_age", "0")
109+
u.RawQuery = q.Encode()
102110

103111
base := strings.TrimRight(h.cfg.AuthentikAPIBase, "/")
104112
target := fmt.Sprintf("%s/source/oauth/login/%s/?next=%s",
105-
base, slug, url.QueryEscape(authURL))
113+
base, slug, url.QueryEscape(u.String()))
106114
c.Redirect(http.StatusFound, target)
107115
}
108116

internal/user/port/http/oidc_handlers_test.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,15 @@ func (s *OIDCHandlerTestSuite) TestSSO_ValidProvider_RedirectsToSourceLoginWithN
9595
loc, err := url.Parse(w.Header().Get("Location"))
9696
s.Require().NoError(err)
9797
s.Equal("/source/oauth/login/"+p+"/", loc.Path, "path must target the source login endpoint")
98-
s.Equal(authURL, loc.Query().Get("next"), "next must hold the OIDC authorize URL verbatim")
98+
99+
// next holds the authorize URL with the prompt/max_age params we add
100+
// to force a fresh upstream auth round-trip.
101+
next, err := url.Parse(loc.Query().Get("next"))
102+
s.Require().NoError(err)
103+
s.Equal("login", next.Query().Get("prompt"))
104+
s.Equal("0", next.Query().Get("max_age"))
105+
s.Equal("x", next.Query().Get("client_id"))
106+
s.Equal("goshop-state", next.Query().Get("state"))
99107
}
100108
}
101109

0 commit comments

Comments
 (0)