discoverAndRegisterMCPOAuth2 performs the full MCP OAuth2 discovery and Dynamic Client Registration flow: 1. Discover the authorization server via Protected Resource Metadata (RFC 9728). 2. Fetch Authorization Server Metadata (RFC 8414). 3. Register a client via Dynamic Client Registration (RFC 759
(ctx context.Context, httpClient *http.Client, mcpServerURL, callbackURL string)
| 1625 | // serve metadata at path-specific URLs (e.g. |
| 1626 | // https://api.githubcopilot.com/mcp/). |
| 1627 | func discoverAndRegisterMCPOAuth2(ctx context.Context, httpClient *http.Client, mcpServerURL, callbackURL string) (*mcpOAuth2Discovery, error) { |
| 1628 | // Parse the MCP server URL into origin and path. |
| 1629 | parsed, err := url.Parse(mcpServerURL) |
| 1630 | if err != nil { |
| 1631 | return nil, xerrors.Errorf( |
| 1632 | "parse MCP server URL: %w", err, |
| 1633 | ) |
| 1634 | } |
| 1635 | origin := fmt.Sprintf("%s://%s", parsed.Scheme, parsed.Host) |
| 1636 | path := parsed.Path |
| 1637 | |
| 1638 | // Step 1: Discover the Protected Resource Metadata |
| 1639 | // (RFC 9728) to find the authorization server. |
| 1640 | prm, err := discoverProtectedResource(ctx, httpClient, origin, path) |
| 1641 | if err != nil { |
| 1642 | return nil, xerrors.Errorf( |
| 1643 | "protected resource discovery: %w", err, |
| 1644 | ) |
| 1645 | } |
| 1646 | |
| 1647 | // Step 2: Fetch Authorization Server Metadata (RFC 8414) |
| 1648 | // from the first advertised authorization server. |
| 1649 | asMeta, err := discoverAuthServerMetadata( |
| 1650 | ctx, httpClient, prm.AuthorizationServers[0], |
| 1651 | ) |
| 1652 | if err != nil { |
| 1653 | return nil, xerrors.Errorf( |
| 1654 | "auth server metadata discovery: %w", err, |
| 1655 | ) |
| 1656 | } |
| 1657 | |
| 1658 | // Only RegistrationEndpoint needs checking here; |
| 1659 | // discoverAuthServerMetadata already validates that |
| 1660 | // AuthorizationEndpoint and TokenEndpoint are present. |
| 1661 | if asMeta.RegistrationEndpoint == "" { |
| 1662 | return nil, xerrors.New( |
| 1663 | "authorization server does not advertise a " + |
| 1664 | "registration_endpoint (dynamic client " + |
| 1665 | "registration may not be supported)", |
| 1666 | ) |
| 1667 | } |
| 1668 | |
| 1669 | // Step 3: Register via Dynamic Client Registration |
| 1670 | // (RFC 7591). |
| 1671 | clientID, clientSecret, err := registerOAuth2Client( |
| 1672 | ctx, httpClient, asMeta.RegistrationEndpoint, callbackURL, "Coder", |
| 1673 | ) |
| 1674 | if err != nil { |
| 1675 | return nil, xerrors.Errorf( |
| 1676 | "dynamic client registration: %w", err, |
| 1677 | ) |
| 1678 | } |
| 1679 | |
| 1680 | scopes := strings.Join(asMeta.ScopesSupported, " ") |
| 1681 | |
| 1682 | return &mcpOAuth2Discovery{ |
| 1683 | clientID: clientID, |
| 1684 | clientSecret: clientSecret, |
no test coverage detected