Skip to content

OIDC callback at /rustfs/admin/v3/oidc/callback returns AccessDenied: Signature is required (browser redirect from IdP) #2924

@ilbarone87

Description

@ilbarone87

Describe the bug

The OIDC authorization code callback at /rustfs/admin/v3/oidc/callback is rejected by the s3s signature middleware with AccessDenied: Signature is required before reaching the OIDC handler.

The browser is redirected back from the IdP with valid code and state query parameters, but RustFS responds with HTTP 403 because the request (a browser-driven redirect from the IdP) cannot carry an AWS SigV4 signature.

This completely blocks OIDC login flows. Reproduced on both 1.0.0-beta.2 and 1.0.0-alpha.99.

To Reproduce

Steps to reproduce the behavior:

  1. Configure OIDC via environment variables:
    RUSTFS_IDENTITY_OPENID_ENABLE=true
    RUSTFS_IDENTITY_OPENID_CONFIG_URL=https://zitadel.example.com/.well-known/openid-configuration
    RUSTFS_IDENTITY_OPENID_CLIENT_ID=<client-id>
    RUSTFS_IDENTITY_OPENID_CLIENT_SECRET=<client-secret>
    RUSTFS_IDENTITY_OPENID_SCOPES=openid,profile,email
    RUSTFS_IDENTITY_OPENID_GROUPS_CLAIM=groups
    RUSTFS_IDENTITY_OPENID_ROLES_CLAIM=roles
    RUSTFS_IDENTITY_OPENID_DISPLAY_NAME=Zitadel
    
  2. Register the redirect URI in the IdP exactly as RustFS generates it: https://<console-host>/rustfs/admin/v3/oidc/callback.
  3. Open the console at https://<console-host>/ and click "Login with Zitadel."
  4. Authenticate at the IdP.
  5. The IdP redirects the browser back to https://<console-host>/rustfs/admin/v3/oidc/callback?code=...&state=....
  6. RustFS responds with HTTP 403 AccessDenied: Signature is required. Login fails.

Expected behavior

The OIDC callback should be processed by the OIDC handler, the authorization code exchanged for tokens with the IdP, STS credentials issued, and the user logged into the console.

The callback handler should not require AWS SigV4 signatures — browser redirects from OIDC providers fundamentally cannot sign requests.

Logs

Authorize endpoint works correctly:

DEBUG http response generated in 316.931µs
       status_code: 302 Found
       uri: /rustfs/admin/v3/oidc/authorize/zitadel
       location: https://zitadel.example.com/oauth/v2/authorize?response_type=code
                 &client_id=...&state=...&code_challenge=...&code_challenge_method=S256
                 &redirect_uri=https%3A%2F%2Fconsole.example.com%2Frustfs%2Fadmin%2Fv3%2Foidc%2Fcallback
                 &scope=openid+openid+profile+email&nonce=...

Callback fails:

DEBUG http started method: GET, url path: /rustfs/admin/v3/oidc/callback
DEBUG parsing path-style request, decoded_uri_path: "/rustfs/admin/v3/oidc/callback"
INFO  s3s::ops::signature v2_check
INFO  s3s::ops::signature v4_check
ERROR target: s3s::ops
      message: "custom route returns error"
      err: S3Error { code: AccessDenied, message: Some("Signature is required") }
DEBUG resp: Response { status: 403, headers: {"content-type": "application/xml"},
       body: "<?xml version=\"1.0\" encoding=\"UTF-8\"?><Error><Code>AccessDenied</Code><Message>Signature is required</Message></Error>" }

The signature middleware (s3s::ops::signature::v2_check, v4_check) runs before the custom OIDC route handler. The OIDC callback route is registered behind the SigV4 middleware, which rejects unsigned requests — but OIDC callbacks from a browser cannot include SigV4 signatures.

Request headers on the failing callback confirm a normal browser redirect (correct Host, no Authorization, no x-amz-* signature headers):

host: console.example.com
referer: https://console.example.com/
user-agent: Mozilla/5.0 ... Safari/605.1.15
accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
x-forwarded-proto: https

Workarounds attempted (none worked)

  • Verified registered redirect URI in IdP matches RustFS-generated URI exactly.
  • Toggled RUSTFS_IDENTITY_OPENID_REDIRECT_URI_DYNAMIC true/false.
  • Set / unset RUSTFS_BROWSER_REDIRECT_URL.
  • Tried separate hostnames for S3 (port 9000) and console (port 9001).
  • Tried single hostname with path-based split (/ → 9000, /rustfs/ → 9001).
  • Downgraded from 1.0.0-beta.2 to 1.0.0-alpha.99 — identical error.

Additional observations

In the authorize 302 Location header, the scope parameter contains a duplicated openid:

scope=openid+openid+profile+email

with RUSTFS_IDENTITY_OPENID_SCOPES=openid,profile,email configured. This is likely benign. RustFS appears to prepend openid automatically even when it's already in the user-configured scopes list.

Possibly related: rustfs/console#107 and rustfs/console#108, which describe related architectural assumptions about console / S3 API origin sharing.

Environment

  • RustFS version: 1.0.0-beta.2 (also reproduced on 1.0.0-alpha.99)
  • Deployment: Debian 13
  • IdP: Zitadel (self-hosted in k8s)
  • Client: Safari on macOS / iPadOS

Checklist

  • I have searched existing issues to ensure this is not a duplicate.
  • I have included steps to reproduce.
  • I have tested in the latest released version.
  • I have tested in an earlier version to determine whether this is a regression.

Metadata

Metadata

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions