generated from coulomb/repo-seed
fix(authelia): use adapter's own client_id/redirect_uri in AuthorizeURL
Some checks failed
Build and Publish Container Image / build-and-push (push) Has been cancelled
Some checks failed
Build and Publish Container Image / build-and-push (push) Has been cancelled
The adapter was forwarding the downstream client's client_id and redirect_uri to Authelia, which would always be rejected — Authelia only recognises client_id=keycape and its registered callback URI. Also removed downstream PKCE forwarding: KeyCape is a confidential OIDC client to Authelia and authenticates via client_secret instead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -37,26 +37,20 @@ func New(cfg Config, httpClient HTTPClient) *AutheliaAdapter {
|
||||
|
||||
// AuthorizeURL builds the Authelia OIDC authorization URL to which the user
|
||||
// should be redirected.
|
||||
//
|
||||
// KeyCape is a confidential OIDC client to Authelia. The adapter always uses
|
||||
// its own registered client_id and redirect_uri — NOT the downstream client's
|
||||
// values — and requests the full fixed scope set. PKCE is omitted because
|
||||
// the confidential client_secret authenticates the token exchange instead.
|
||||
func (a *AutheliaAdapter) AuthorizeURL(_ context.Context, req domain.AuthRequest) (string, error) {
|
||||
base := strings.TrimRight(a.cfg.BaseURL, "/") + "/api/oidc/authorization"
|
||||
|
||||
q := url.Values{}
|
||||
q.Set("client_id", req.ClientID)
|
||||
q.Set("redirect_uri", req.RedirectURI)
|
||||
q.Set("client_id", a.cfg.ClientID)
|
||||
q.Set("redirect_uri", a.cfg.RedirectURI)
|
||||
q.Set("response_type", "code")
|
||||
q.Set("state", req.State)
|
||||
if req.Nonce != "" {
|
||||
q.Set("nonce", req.Nonce)
|
||||
}
|
||||
if len(req.Scopes) > 0 {
|
||||
q.Set("scope", strings.Join(req.Scopes, " "))
|
||||
} else {
|
||||
q.Set("scope", "openid profile")
|
||||
}
|
||||
if req.PKCEChallenge != "" {
|
||||
q.Set("code_challenge", req.PKCEChallenge)
|
||||
q.Set("code_challenge_method", req.PKCEChallengeMethod)
|
||||
}
|
||||
q.Set("scope", "openid profile email groups")
|
||||
|
||||
return base + "?" + q.Encode(), nil
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ func jsonResponse(body string) *http.Response {
|
||||
|
||||
func TestAuthorizeURL_ContainsRequiredParams(t *testing.T) {
|
||||
adapter := authelia.New(testConfig(), &mockHTTPClient{})
|
||||
// Downstream client values — must NOT appear in the Authelia URL.
|
||||
req := domain.AuthRequest{
|
||||
ClientID: "myapp",
|
||||
RedirectURI: "https://myapp.local/cb",
|
||||
@@ -90,22 +91,29 @@ func TestAuthorizeURL_ContainsRequiredParams(t *testing.T) {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
checks := []string{
|
||||
"client_id=myapp",
|
||||
// Must use adapter's own client_id and redirect_uri, not the downstream client's.
|
||||
required := []string{
|
||||
"client_id=keycape",
|
||||
"redirect_uri=",
|
||||
"response_type=code",
|
||||
"state=state-abc",
|
||||
"nonce=nonce-xyz",
|
||||
"code_challenge=challenge123",
|
||||
"code_challenge_method=S256",
|
||||
"scope=",
|
||||
"openid",
|
||||
}
|
||||
for _, want := range checks {
|
||||
for _, want := range required {
|
||||
if !strings.Contains(u, want) {
|
||||
t.Errorf("AuthorizeURL missing %q in: %s", want, u)
|
||||
}
|
||||
}
|
||||
|
||||
// Downstream client_id must NOT be forwarded to Authelia.
|
||||
if strings.Contains(u, "client_id=myapp") {
|
||||
t.Errorf("AuthorizeURL must not forward downstream client_id to Authelia, got: %s", u)
|
||||
}
|
||||
// PKCE must NOT be forwarded — confidential client uses client_secret instead.
|
||||
if strings.Contains(u, "code_challenge") {
|
||||
t.Errorf("AuthorizeURL must not include PKCE params for confidential client, got: %s", u)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthorizeURL_UsesBaseURL(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user