@@ -18,6 +18,8 @@ package auth
1818
1919import (
2020 "context"
21+ "crypto/rand"
22+ "crypto/sha256"
2123 "encoding/base64"
2224 "encoding/json"
2325 "errors"
@@ -85,6 +87,8 @@ func (ah *AuthHandler) HandleAuthorize(w http.ResponseWriter, r *http.Request) {
8587 }
8688 }
8789
90+ logger .V (2 ).Info ("AuthorizeRequest prepared" , "authReq" , authReq , "params" , params )
91+
8892 if authReq .RedirectURL == "" || authReq .SessionID == "" {
8993 logger .Error (errors .New ("missing required parameters" ), "failed to authorize" )
9094 ah .respondWithError (w , authReq .ClientType , "missing redirect_url or session_id" , http .StatusBadRequest )
@@ -94,20 +98,37 @@ func (ah *AuthHandler) HandleAuthorize(w http.ResponseWriter, r *http.Request) {
9498 scopes := []string {"openid" , "profile" , "email" , "offline_access" , "groups" }
9599 dataCode , err := json .Marshal (authReq )
96100 if err != nil {
97- logger .Info ( "failed to marshal auth code" , "error" , err )
101+ logger .Error ( err , "failed to marshal auth code" )
98102 ah .respondWithError (w , authReq .ClientType , err .Error (), http .StatusInternalServerError )
99103 return
100104 }
101105
102106 provider , err := ah .oidc .GetOIDCProvider (r .Context ())
103107 if err != nil {
104- logger .Info ( "failed to get OIDC provider" , "error" , err )
108+ logger .Error ( err , "failed to get OIDC provider" )
105109 ah .respondWithError (w , authReq .ClientType , err .Error (), http .StatusInternalServerError )
106110 return
107111 }
108112
109113 encoded := base64 .URLEncoding .EncodeToString (dataCode )
110- authURL := provider .OIDCProviderConfig (scopes ).AuthCodeURL (encoded )
114+
115+ verifier , challenge , err := generatePKCE ()
116+ if err != nil {
117+ logger .Error (err , "failed to generate PKCE" )
118+ ah .respondWithError (w , authReq .ClientType , "failed to generate PKCE" , http .StatusInternalServerError )
119+ return
120+ }
121+ if err := ah .sessionStore .SavePKCEVerifier (authReq .SessionID , verifier ); err != nil {
122+ logger .Error (err , "failed to store PKCE verifier" )
123+ ah .respondWithError (w , authReq .ClientType , "failed to store PKCE verifier" , http .StatusInternalServerError )
124+ return
125+ }
126+
127+ opts := []oauth2.AuthCodeOption {
128+ oauth2 .SetAuthURLParam ("code_challenge" , challenge ),
129+ oauth2 .SetAuthURLParam ("code_challenge_method" , "S256" ),
130+ }
131+ authURL := provider .OIDCProviderConfig (scopes ).AuthCodeURL (encoded , opts ... )
111132
112133 http .Redirect (w , r , authURL , http .StatusFound )
113134}
@@ -153,10 +174,11 @@ func (ah *AuthHandler) HandleCallback(w http.ResponseWriter, r *http.Request) {
153174 http .Error (w , err .Error (), http .StatusBadRequest )
154175 return
155176 }
177+ logger .V (2 ).Info ("HandleCallback state unmarshaled" , "authCode" , authCode )
156178
157179 provider , err := ah .oidc .GetOIDCProvider (r .Context ())
158180 if err != nil {
159- logger .Info ( "failed to get OIDC provider" , "error" , err )
181+ logger .Error ( err , "failed to get OIDC provider" )
160182 ah .respondWithError (w , authCode .ClientType , err .Error (), http .StatusInternalServerError )
161183 return
162184 }
@@ -172,7 +194,16 @@ func (ah *AuthHandler) HandleCallback(w http.ResponseWriter, r *http.Request) {
172194 ctx = context .WithValue (ctx , oauth2 .HTTPClient , client )
173195 }
174196
175- token , err := provider .OIDCProviderConfig (nil ).Exchange (ctx , code )
197+ verifier , err := ah .sessionStore .LoadAndDeletePKCEVerifier (authCode .SessionID )
198+ if err != nil || verifier == "" {
199+ logger .Error (err , "PKCE verifier not found for session; cannot exchange code" , "sessionID" , authCode .SessionID )
200+ msg := "PKCE verifier not found. If you run multiple backend instances, use a shared session store (e.g. Redis) so the instance handling the callback can read the verifier stored at authorize time."
201+ ah .respondWithError (w , authCode .ClientType , msg , http .StatusBadRequest )
202+ return
203+ }
204+
205+ exchangeOpts := []oauth2.AuthCodeOption {oauth2 .VerifierOption (verifier )}
206+ token , err := provider .OIDCProviderConfig (nil ).Exchange (ctx , code , exchangeOpts ... )
176207 if err != nil {
177208 logger .Error (err , "failed to exchange token" )
178209 http .Error (w , "internal error" , http .StatusInternalServerError )
@@ -204,6 +235,7 @@ func (ah *AuthHandler) HandleCallback(w http.ResponseWriter, r *http.Request) {
204235 sessionState .SessionID ,
205236 sessionState .ClusterID ,
206237 sessionState .RedirectURL ,
238+ sessionState .Token .Groups ,
207239 24 * time .Hour , // 24 hours expiration
208240 )
209241 if err != nil {
@@ -326,3 +358,14 @@ func (ah *AuthHandler) unwrapJWT(p string) ([]byte, error) {
326358 }
327359 return payload , nil
328360}
361+
362+ func generatePKCE () (string , string , error ) {
363+ data := make ([]byte , 32 )
364+ if _ , err := rand .Read (data ); err != nil {
365+ return "" , "" , err
366+ }
367+ verifier := base64 .RawURLEncoding .EncodeToString (data )
368+ hash := sha256 .Sum256 ([]byte (verifier ))
369+ challenge := base64 .RawURLEncoding .EncodeToString (hash [:])
370+ return verifier , challenge , nil
371+ }
0 commit comments