Dex: Token-exchange endpoint is missing AllowedConnectors enforcement
Date: 2026-06
Affected: go/github.com/dexidp/dex
Attack vector: auth-bypass
Category: vulnerability-disclosure
Severity: High
CWE: CWE-285
CVSS: 8.7 (CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:N)

## Summary `server/handlers.go::handleTokenExchange` (lines 1804-1893) does not call `isConnectorAllowed(client.AllowedConnectors, connID)` before issuing tokens, while sibling handlers do. This is a per-client connector ACL gap on the token-exchange endpoint; the redirect-flow paths enforce the same field correctly. ## Affected code path `handleTokenExchange` reads `connector_id` from the request body at `server/handlers.go:1822`. Validators called between read and token issuance: - `s.getConnector(ctx, connID)` at line 1836 - confirms connector exists - `GrantTypeAllowed(conn.GrantTypes, grantTypeTokenExchange)` at line 1842 - confirms connector permits this grant - **(missing)** `isConnectorAllowed(client.AllowedConnectors, connID)` - never called Tokens are issued at lines 1887 / 1889, bound to `client.ID` carrying claims derived from `connID`. Sibling handlers DO enforce the check: - `server/handlers.go::handleConnectorLogin:377` - calls `isConnectorAllowed`, returns HTTP 403 "Connector not allowed for this client." (line 380). - `server/oauth2.go::parseAuthorizationRequest:535` - same enforcement for the authorization-code flow. The doc-string at `storage/storage.go:192-194` reads: > *AllowedConnectors is a list of connector IDs that the client is allowed to use for authentication. If empty, all connectors are allowed.* The phrasing is unconditional - a permission ACL, not a UX filter. ## Impact (concrete scenario) - Connector `corp-okta` - high-trust, gates


Mitigations:

Architectural layers (MAESTRO):