From ffe10325b88063f285769dd93ed584db0b79fda5 Mon Sep 17 00:00:00 2001 From: sabith Date: Wed, 4 Mar 2026 17:29:00 -0800 Subject: [PATCH 1/2] feat: handle token exchange for local dex tokens --- .vscode/launch.json | 16 +++++++++ server/handlers.go | 80 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 79 insertions(+), 17 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000000..dcbc7b06a1 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,16 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Dex", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "./cmd/dex/", + "args": ["serve", "/Users/sabithks/src/github.com/dexidp/dex/config.dev.yaml"] + } + ] +} diff --git a/server/handlers.go b/server/handlers.go index 2d55b1af3a..92053ad886 100644 --- a/server/handlers.go +++ b/server/handlers.go @@ -1460,23 +1460,69 @@ func (s *Server) handleTokenExchange(w http.ResponseWriter, r *http.Request, cli return } - conn, err := s.getConnector(ctx, connID) - if err != nil { - s.logger.ErrorContext(r.Context(), "failed to get connector", "err", err) - s.tokenErrHelper(w, errInvalidRequest, "Requested connector does not exist.", http.StatusBadRequest) - return - } - teConn, ok := conn.Connector.(connector.TokenIdentityConnector) - if !ok { - s.logger.ErrorContext(r.Context(), "connector doesn't implement token exchange", "connector_id", connID) - s.tokenErrHelper(w, errInvalidRequest, "Requested connector does not exist.", http.StatusBadRequest) - return - } - identity, err := teConn.TokenIdentity(ctx, subjectTokenType, subjectToken) - if err != nil { - s.logger.ErrorContext(r.Context(), "failed to verify subject token", "err", err) - s.tokenErrHelper(w, errAccessDenied, "", http.StatusUnauthorized) - return + var err error + var identity connector.Identity + if connID == "local" || connID == "" { + // Verify local Dex token + verifier := oidc.NewVerifier(s.issuerURL.String(), &signerKeySet{s.signer}, &oidc.Config{SkipClientIDCheck: true}) + idToken, err := verifier.Verify(ctx, subjectToken) + if err != nil { + s.logger.ErrorContext(r.Context(), "failed to verify local subject token", "err", err) + s.tokenErrHelper(w, errAccessDenied, "Invalid subject token.", http.StatusUnauthorized) + return + } + + var claims idTokenClaims + if err := idToken.Claims(&claims); err != nil { + s.logger.ErrorContext(r.Context(), "failed to decode ID token claims", "err", err) + s.tokenErrHelper(w, errServerError, "Invalid subject token claims.", http.StatusInternalServerError) + return + } + + var sub internal.IDTokenSubject + if err := internal.Unmarshal(claims.Subject, &sub); err != nil { + s.logger.ErrorContext(r.Context(), "failed to unmarshal subject", "err", err) + s.tokenErrHelper(w, errServerError, "Invalid subject format.", http.StatusInternalServerError) + return + } + + if connID == "" { + connID = sub.ConnId + } + + emailVerified := false + if claims.EmailVerified != nil { + emailVerified = *claims.EmailVerified + } + + identity = connector.Identity{ + UserID: sub.UserId, + Username: claims.Name, + PreferredUsername: claims.PreferredUsername, + Email: claims.Email, + EmailVerified: emailVerified, + Groups: claims.Groups, + } + } else { + conn, err := s.getConnector(ctx, connID) + if err != nil { + s.logger.ErrorContext(r.Context(), "failed to get connector", "err", err) + s.tokenErrHelper(w, errInvalidRequest, "Requested connector does not exist.", http.StatusBadRequest) + return + } + teConn, ok := conn.Connector.(connector.TokenIdentityConnector) + if !ok { + s.logger.ErrorContext(r.Context(), "connector doesn't implement token exchange", "connector_id", connID) + s.tokenErrHelper(w, errInvalidRequest, "Requested connector does not exist.", http.StatusBadRequest) + return + } + + identity, err = teConn.TokenIdentity(ctx, subjectTokenType, subjectToken) + if err != nil { + s.logger.ErrorContext(r.Context(), "failed to verify subject token", "err", err) + s.tokenErrHelper(w, errAccessDenied, "", http.StatusUnauthorized) + return + } } claims := storage.Claims{ From 960c01344f50172cf85b02b2de3f6902f540a68b Mon Sep 17 00:00:00 2001 From: sks Date: Wed, 4 Mar 2026 17:58:58 -0800 Subject: [PATCH 2/2] Delete .vscode/launch.json Signed-off-by: sks --- .vscode/launch.json | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index dcbc7b06a1..0000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Dex", - "type": "go", - "request": "launch", - "mode": "auto", - "program": "./cmd/dex/", - "args": ["serve", "/Users/sabithks/src/github.com/dexidp/dex/config.dev.yaml"] - } - ] -}