validateOpaqueToken validates an opaque token by calling the introspection endpoint
(ctx context.Context, tokenStr string)
| 341 | |
| 342 | // validateOpaqueToken validates an opaque token by calling the introspection endpoint |
| 343 | func (a AuthService) validateOpaqueToken(ctx context.Context, tokenStr string) (map[string]any, error) { |
| 344 | logger, err := util.LoggerFromContext(ctx) |
| 345 | if err != nil { |
| 346 | return nil, fmt.Errorf("failed to get logger from context: %w", err) |
| 347 | } |
| 348 | |
| 349 | introspectionURL := a.introspectionURL |
| 350 | if introspectionURL == "" { |
| 351 | introspectionURL, err = url.JoinPath(a.AuthorizationServer, "introspect") |
| 352 | if err != nil { |
| 353 | return nil, fmt.Errorf("failed to construct introspection URL: %w", err) |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | paramName := a.IntrospectionParamName |
| 358 | if paramName == "" { |
| 359 | paramName = "token" |
| 360 | } |
| 361 | |
| 362 | var req *http.Request |
| 363 | if a.IntrospectionMethod == "GET" { |
| 364 | u, err := url.Parse(introspectionURL) |
| 365 | if err != nil { |
| 366 | return nil, fmt.Errorf("failed to parse introspection URL: %w", err) |
| 367 | } |
| 368 | q := u.Query() |
| 369 | q.Set(paramName, tokenStr) |
| 370 | u.RawQuery = q.Encode() |
| 371 | req, err = http.NewRequestWithContext(ctx, "GET", u.String(), nil) |
| 372 | if err != nil { |
| 373 | return nil, fmt.Errorf("failed to create introspection request: %w", err) |
| 374 | } |
| 375 | } else { |
| 376 | data := url.Values{} |
| 377 | data.Set(paramName, tokenStr) |
| 378 | req, err = http.NewRequestWithContext(ctx, "POST", introspectionURL, strings.NewReader(data.Encode())) |
| 379 | if err != nil { |
| 380 | return nil, fmt.Errorf("failed to create introspection request: %w", err) |
| 381 | } |
| 382 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| 383 | } |
| 384 | req.Header.Set("Accept", "application/json") |
| 385 | |
| 386 | // Send request to auth server's introspection endpoint |
| 387 | resp, err := a.client.Do(req) |
| 388 | if err != nil { |
| 389 | logger.ErrorContext(ctx, "failed to call introspection endpoint: %v", err) |
| 390 | return nil, &MCPAuthError{Code: http.StatusInternalServerError, Message: fmt.Sprintf("failed to call introspection endpoint: %v", err), ScopesRequired: a.ScopesRequired} |
| 391 | } |
| 392 | defer resp.Body.Close() |
| 393 | |
| 394 | if resp.StatusCode != http.StatusOK { |
| 395 | logger.WarnContext(ctx, "introspection failed with status: %d", resp.StatusCode) |
| 396 | return nil, &MCPAuthError{Code: http.StatusUnauthorized, Message: fmt.Sprintf("introspection failed with status: %d", resp.StatusCode), ScopesRequired: a.ScopesRequired} |
| 397 | } |
| 398 | |
| 399 | body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20)) |
| 400 | if err != nil { |