@@ -149,44 +149,49 @@ func (c *TokenExchangeClient) generateClientAssertion() (string, error) {
149149 return string (signed ), nil
150150}
151151
152- // ExchangeToken exchanges a subject token for a new access token per RFC 8693.
153- func ( c * TokenExchangeClient ) ExchangeToken ( ctx context. Context , subjectToken string ) ( * TokenExchangeResponse , error ) {
154- // Build token exchange request per RFC 8693 Section 2.1.
155- data := url. Values {}
156- data . Set ( "grant_type" , "urn:ietf:params:oauth:grant-type:token-exchange" )
157- data . Set ( "client_id" , c . config . ClientID )
158- data . Set ( "subject_token" , subjectToken )
159- data . Set ( "subject_token_type" , c . config . SubjectTokenType )
160- data . Set ( "audience" , c . config . Audience )
152+ // isM2MToken reports whether token is a machine-to-machine (client credentials)
153+ // JWT, identified by Auth0's convention of a subject claim ending in "@clients".
154+ func isM2MToken ( token string ) bool {
155+ parsed , err := jwt . ParseInsecure ([] byte ( token ))
156+ if err != nil {
157+ return false
158+ }
159+ return strings . HasSuffix ( parsed . Subject (), "@clients" )
160+ }
161161
162- // Use client assertion if signing key is provided, otherwise use client secret.
162+ // addClientAuth adds client authentication fields (secret or JWT assertion) to data.
163+ func (c * TokenExchangeClient ) addClientAuth (data url.Values ) error {
163164 if c .config .ClientAssertionSigningKey != "" {
164165 assertion , err := c .generateClientAssertion ()
165166 if err != nil {
166- return nil , fmt .Errorf ("failed to generate client assertion: %w" , err )
167+ return fmt .Errorf ("failed to generate client assertion: %w" , err )
167168 }
168169 data .Set ("client_assertion" , assertion )
169170 data .Set ("client_assertion_type" , "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" )
170171 } else {
171172 data .Set ("client_secret" , c .config .ClientSecret )
172173 }
174+ return nil
175+ }
173176
177+ // postTokenRequest sends a token request and returns the parsed response.
178+ func (c * TokenExchangeClient ) postTokenRequest (ctx context.Context , data url.Values ) (* TokenExchangeResponse , error ) {
174179 req , err := http .NewRequestWithContext (ctx , http .MethodPost , c .config .TokenEndpoint , strings .NewReader (data .Encode ()))
175180 if err != nil {
176- return nil , fmt .Errorf ("failed to create token exchange request: %w" , err )
181+ return nil , fmt .Errorf ("failed to create token request: %w" , err )
177182 }
178183
179184 req .Header .Set ("Content-Type" , "application/x-www-form-urlencoded" )
180185
181186 resp , err := c .client .Do (req )
182187 if err != nil {
183- return nil , fmt .Errorf ("failed to exchange token: %w" , err )
188+ return nil , fmt .Errorf ("failed to execute token request : %w" , err )
184189 }
185190 defer resp .Body .Close ()
186191
187192 body , err := io .ReadAll (resp .Body )
188193 if err != nil {
189- return nil , fmt .Errorf ("failed to read token exchange response: %w" , err )
194+ return nil , fmt .Errorf ("failed to read token response: %w" , err )
190195 }
191196
192197 if resp .StatusCode != http .StatusOK {
@@ -195,8 +200,41 @@ func (c *TokenExchangeClient) ExchangeToken(ctx context.Context, subjectToken st
195200
196201 var tokenResp TokenExchangeResponse
197202 if err := json .Unmarshal (body , & tokenResp ); err != nil {
198- return nil , fmt .Errorf ("failed to parse token exchange response: %w" , err )
203+ return nil , fmt .Errorf ("failed to parse token response: %w" , err )
199204 }
200205
201206 return & tokenResp , nil
202207}
208+
209+ // ExchangeToken exchanges a subject token for a new access token per RFC 8693.
210+ func (c * TokenExchangeClient ) ExchangeToken (ctx context.Context , subjectToken string ) (* TokenExchangeResponse , error ) {
211+ data := url.Values {}
212+ data .Set ("grant_type" , "urn:ietf:params:oauth:grant-type:token-exchange" )
213+ data .Set ("client_id" , c .config .ClientID )
214+ data .Set ("subject_token" , subjectToken )
215+ data .Set ("subject_token_type" , c .config .SubjectTokenType )
216+ data .Set ("audience" , c .config .Audience )
217+
218+ if err := c .addClientAuth (data ); err != nil {
219+ return nil , err
220+ }
221+
222+ return c .postTokenRequest (ctx , data )
223+ }
224+
225+ // ClientCredentials obtains an LFX API token using the client_credentials grant.
226+ // This is used when the caller presents an M2M token, which Auth0 cannot exchange
227+ // via RFC 8693 token exchange (it requires a user subject). Instead, the MCP server
228+ // mints a fresh LFX token using its own client identity.
229+ func (c * TokenExchangeClient ) ClientCredentials (ctx context.Context ) (* TokenExchangeResponse , error ) {
230+ data := url.Values {}
231+ data .Set ("grant_type" , "client_credentials" )
232+ data .Set ("client_id" , c .config .ClientID )
233+ data .Set ("audience" , c .config .Audience )
234+
235+ if err := c .addClientAuth (data ); err != nil {
236+ return nil , err
237+ }
238+
239+ return c .postTokenRequest (ctx , data )
240+ }
0 commit comments