MCPcopy Create free account
hub / github.com/bytebase/bytebase / handleRegister

Method handleRegister

backend/api/oauth2/register.go:44–139  ·  view source on GitHub ↗

handleRegister implements RFC 7591 Dynamic Client Registration. The endpoint is unauthenticated — clients are workspace-agnostic and get bound to a workspace when the user grants consent at /authorize. This matches the pattern used by Linear, Atlassian, Notion, and Cloudflare MCP servers.

(c *echo.Context)

Source from the content-addressed store, hash-verified

42// workspace when the user grants consent at /authorize. This matches the
43// pattern used by Linear, Atlassian, Notion, and Cloudflare MCP servers.
44func (s *Service) handleRegister(c *echo.Context) error {
45 ctx := c.Request().Context()
46
47 var req clientRegistrationRequest
48 if err := c.Bind(&req); err != nil {
49 return oauth2Error(c, http.StatusBadRequest, "invalid_client_metadata", "failed to parse request body")
50 }
51
52 if req.ClientName == "" {
53 return oauth2Error(c, http.StatusBadRequest, "invalid_client_metadata", "client_name is required")
54 }
55 if len(req.ClientName) > maxClientNameLen {
56 return oauth2Error(c, http.StatusBadRequest, "invalid_client_metadata", "client_name is too long")
57 }
58
59 if len(req.RedirectURIs) == 0 {
60 return oauth2Error(c, http.StatusBadRequest, "invalid_client_metadata", "redirect_uris is required")
61 }
62 if len(req.RedirectURIs) > maxRedirectURIs {
63 return oauth2Error(c, http.StatusBadRequest, "invalid_client_metadata", "too many redirect_uris")
64 }
65 for _, uri := range req.RedirectURIs {
66 if len(uri) > maxRedirectURILen {
67 return oauth2Error(c, http.StatusBadRequest, "invalid_redirect_uri", "redirect URI is too long")
68 }
69 if !isAllowedDynamicClientRedirectURI(uri) {
70 return oauth2Error(c, http.StatusBadRequest, "invalid_redirect_uri", "redirect URI must be a localhost URL, a recognized hosted MCP client callback (e.g. claude.ai, chatgpt.com), or a whitelisted app scheme (cursor://, vscode://, vscode-insiders://, jetbrains://gateway/...)")
71 }
72 }
73
74 if len(req.GrantTypes) == 0 {
75 req.GrantTypes = []string{"authorization_code"}
76 }
77 allowedGrantTypes := []string{"authorization_code", "refresh_token"}
78 for _, gt := range req.GrantTypes {
79 if !slices.Contains(allowedGrantTypes, gt) {
80 return oauth2Error(c, http.StatusBadRequest, "invalid_client_metadata", "unsupported grant_type: "+gt)
81 }
82 }
83
84 if req.TokenEndpointAuthMethod == "" {
85 req.TokenEndpointAuthMethod = "none"
86 }
87 allowedAuthMethods := []string{"none"}
88 if !slices.Contains(allowedAuthMethods, req.TokenEndpointAuthMethod) {
89 return oauth2Error(c, http.StatusBadRequest, "invalid_client_metadata", "unsupported token_endpoint_auth_method")
90 }
91
92 clientID, err := generateClientID()
93 if err != nil {
94 return oauth2Error(c, http.StatusInternalServerError, "server_error", "failed to generate client ID")
95 }
96
97 // Public clients (token_endpoint_auth_method=none) do not authenticate
98 // at the token endpoint and never receive a usable secret, so skip the
99 // (expensive) bcrypt round entirely. The token.go grant path already
100 // gates secret verification on Config.TokenEndpointAuthMethod != "none".
101 var clientSecret, secretHash string

Callers

nothing calls this directly

Calls 6

oauth2ErrorFunction · 0.85
generateClientIDFunction · 0.85
generateClientSecretFunction · 0.85
hashSecretFunction · 0.85
CreateOAuth2ClientMethod · 0.80

Tested by

no test coverage detected