Skip to content

Commit b3cba20

Browse files
committed
feat: use oauth2 for token structs
1 parent 3701b73 commit b3cba20

4 files changed

Lines changed: 56 additions & 66 deletions

File tree

oauthex/jwt_bearer.go

Lines changed: 23 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -26,42 +26,6 @@ import (
2626
// This is used in SEP-990 to exchange an ID-JAG for an access token at the MCP Server.
2727
const GrantTypeJWTBearer = "urn:ietf:params:oauth:grant-type:jwt-bearer"
2828

29-
// JWTBearerResponse represents the response from a JWT Bearer grant request
30-
// per RFC 7523. This uses the standard OAuth 2.0 token response format.
31-
type JWTBearerResponse struct {
32-
// AccessToken is the OAuth access token issued by the MCP Server's
33-
// authorization server.
34-
AccessToken string `json:"access_token"`
35-
// TokenType is the type of token issued. This is typically "Bearer".
36-
TokenType string `json:"token_type"`
37-
// ExpiresIn is the lifetime in seconds of the access token.
38-
ExpiresIn int `json:"expires_in,omitempty"`
39-
// RefreshToken is the refresh token, which can be used to obtain new
40-
// access tokens using the same authorization grant.
41-
RefreshToken string `json:"refresh_token,omitempty"`
42-
// Scope is the scope of the access token as described by RFC 6749 Section 3.3.
43-
Scope string `json:"scope,omitempty"`
44-
}
45-
46-
// JWTBearerError represents an error response from a JWT Bearer grant request.
47-
type JWTBearerError struct {
48-
// ErrorCode is the error code as defined in RFC 6749 Section 5.2.
49-
// The JSON field name is "error" per the RFC specification.
50-
ErrorCode string `json:"error"`
51-
// ErrorDescription is a human-readable description of the error.
52-
ErrorDescription string `json:"error_description,omitempty"`
53-
// ErrorURI is a URI identifying a human-readable web page with information
54-
// about the error.
55-
ErrorURI string `json:"error_uri,omitempty"`
56-
}
57-
58-
func (e *JWTBearerError) Error() string {
59-
if e.ErrorDescription != "" {
60-
return fmt.Sprintf("JWT bearer grant failed: %s (%s)", e.ErrorCode, e.ErrorDescription)
61-
}
62-
return fmt.Sprintf("JWT bearer grant failed: %s", e.ErrorCode)
63-
}
64-
6529
// ExchangeJWTBearer exchanges an Identity Assertion JWT Authorization Grant (ID-JAG)
6630
// for an access token using JWT Bearer Grant per RFC 7523. This is the second step
6731
// in Enterprise Managed Authorization (SEP-990) after obtaining the ID-JAG from the
@@ -151,7 +115,13 @@ func ExchangeJWTBearer(
151115
}
152116
// Handle success response (200 OK per OAuth 2.0)
153117
if httpResp.StatusCode == http.StatusOK {
154-
var resp JWTBearerResponse
118+
var resp struct {
119+
AccessToken string `json:"access_token"`
120+
TokenType string `json:"token_type"`
121+
ExpiresIn int `json:"expires_in,omitempty`
122+
RefreshToken string `json:"refresh_token,omitempty"`
123+
Scope string `json:"scope,omitempty"`
124+
}
155125
if err := json.Unmarshal(body, &resp); err != nil {
156126
return nil, fmt.Errorf("failed to parse JWT bearer grant response: %w (body: %s)", err, string(body))
157127
}
@@ -182,12 +152,25 @@ func ExchangeJWTBearer(
182152
}
183153
// Handle error response (400 Bad Request per RFC 6749)
184154
if httpResp.StatusCode == http.StatusBadRequest {
185-
var errResp JWTBearerError
155+
var errResp struct {
156+
Error string `json:"error"`
157+
ErrorDescription string `json:"error_description,omitempty"`
158+
ErrorURI string `json:"error_uri,omitempty"`
159+
}
186160
if err := json.Unmarshal(body, &errResp); err != nil {
187161
return nil, fmt.Errorf("failed to parse error response: %w (body: %s)", err, string(body))
188162
}
189-
return nil, &errResp
163+
return nil, &oauth2.RetrieveError{
164+
Response: httpResp,
165+
Body: body,
166+
ErrorCode: errResp.Error,
167+
ErrorDescription: errResp.ErrorDescription,
168+
ErrorURI: errResp.ErrorURI,
169+
}
190170
}
191171
// Handle unexpected status codes
192-
return nil, fmt.Errorf("unexpected status code %d: %s", httpResp.StatusCode, string(body))
172+
return nil, &oauth2.RetrieveError{
173+
Response: httpResp,
174+
Body: body,
175+
}
193176
}

oauthex/jwt_bearer_test.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,13 @@ func TestExchangeJWTBearer(t *testing.T) {
6464
return
6565
}
6666
// Return successful OAuth token response
67-
resp := JWTBearerResponse{
67+
resp := struct {
68+
AccessToken string `json:"access_token"`
69+
TokenType string `json:"token_type"`
70+
ExpiresIn int `json:"expires_in,omitempty"`
71+
Scope string `json:"scope,omitempty"`
72+
RefreshToken string `json:"refresh_token,omitempty"`
73+
}{
6874
AccessToken: "mcp-access-token-123",
6975
TokenType: "Bearer",
7076
ExpiresIn: 3600,
@@ -143,7 +149,10 @@ func TestExchangeJWTBearer(t *testing.T) {
143149

144150
// writeJWTBearerErrorResponse writes an OAuth 2.0 error response per RFC 6749 Section 5.2.
145151
func writeJWTBearerErrorResponse(w http.ResponseWriter, errorCode, errorDescription string) {
146-
errResp := JWTBearerError{
152+
errResp := struct {
153+
Error string `json:"error"`
154+
ErrorCode string `json:"error_description,omitempty"`
155+
}{
147156
ErrorCode: errorCode,
148157
ErrorDescription: errorDescription,
149158
}

oauthex/token_exchange.go

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import (
1717
"net/http"
1818
"net/url"
1919
"strings"
20+
21+
"golang.org/x/oauth2"
2022
)
2123

2224
// Token type identifiers defined by RFC 8693 and SEP-990.
@@ -92,26 +94,6 @@ type TokenExchangeResponse struct {
9294
ExpiresIn int `json:"expires_in,omitempty"`
9395
}
9496

95-
// TokenExchangeError represents an error response from a token exchange request.
96-
type TokenExchangeError struct {
97-
// Error is the error code as defined in RFC 6749 Section 5.2.
98-
ErrorCode string `json:"error"`
99-
100-
// ErrorDescription is a human-readable description of the error.
101-
ErrorDescription string `json:"error_description,omitempty"`
102-
103-
// ErrorURI is a URI identifying a human-readable web page with information
104-
// about the error.
105-
ErrorURI string `json:"error_uri,omitempty"`
106-
}
107-
108-
func (e *TokenExchangeError) Error() string {
109-
if e.ErrorDescription != "" {
110-
return fmt.Sprintf("token exchange failed: %s (%s)", e.ErrorCode, e.ErrorDescription)
111-
}
112-
return fmt.Sprintf("token exchange failed: %s", e.ErrorCode)
113-
}
114-
11597
// ExchangeToken performs a token exchange request per RFC 8693 for Enterprise
11698
// Managed Authorization (SEP-990). It exchanges an identity assertion (typically
11799
// an ID Token) for an Identity Assertion JWT Authorization Grant (ID-JAG) that
@@ -255,13 +237,26 @@ func ExchangeToken(
255237

256238
// Handle error response (400 Bad Request per RFC 6749)
257239
if httpResp.StatusCode == http.StatusBadRequest {
258-
var errResp TokenExchangeError
240+
var errResp struct {
241+
Error string `json:"error"`
242+
ErrorDescription string `json:"error_description,omitempty"`
243+
ErrorURI string `json:"error_uri,omitempty"`
244+
}
259245
if err := json.Unmarshal(body, &errResp); err != nil {
260246
return nil, fmt.Errorf("failed to parse error response: %w (body: %s)", err, string(body))
261247
}
262-
return nil, &errResp
248+
return nil, &oauth2.RetrieveError{
249+
Response: httpResp,
250+
Body: body,
251+
ErrorCode: errResp.Error,
252+
ErrorDescription: errResp.ErrorDescription,
253+
ErrorURI: errResp.ErrorURI,
254+
}
263255
}
264256

265257
// Handle unexpected status codes
266-
return nil, fmt.Errorf("unexpected status code %d: %s", httpResp.StatusCode, string(body))
258+
return nil, &oauth2.RetrieveError{
259+
Response: httpResp,
260+
Body: body,
261+
}
267262
}

oauthex/token_exchange_test.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,10 @@ func TestExchangeToken(t *testing.T) {
208208

209209
// writeErrorResponse writes an OAuth 2.0 error response per RFC 6749 Section 5.2.
210210
func writeErrorResponse(w http.ResponseWriter, errorCode, errorDescription string) {
211-
errResp := TokenExchangeError{
211+
errResp := struct {
212+
Error string `json:"error"`
213+
ErrorDescription string `json:"error_description,omitempty"`
214+
}{
212215
ErrorCode: errorCode,
213216
ErrorDescription: errorDescription,
214217
}

0 commit comments

Comments
 (0)