@@ -214,7 +214,7 @@ func (s *Server) handleAuthorization(w http.ResponseWriter, r *http.Request) {
214214 }
215215 }
216216
217- if err := s .templates .login (r , w , connectorInfos ); err != nil {
217+ if err := s .templates .login (r , w , connectorInfos , s . enableSignup ); err != nil {
218218 s .logger .ErrorContext (r .Context (), "server template error" , "err" , err )
219219 }
220220}
@@ -387,7 +387,7 @@ func (s *Server) handlePasswordLogin(w http.ResponseWriter, r *http.Request) {
387387
388388 switch r .Method {
389389 case http .MethodGet :
390- if err := s .templates .password (r , w , r .URL .String (), "" , usernamePrompt (pwConn ), false , backLink ); err != nil {
390+ if err := s .templates .password (r , w , r .URL .String (), "" , usernamePrompt (pwConn ), false , backLink , s . enableSignup ); err != nil {
391391 s .logger .ErrorContext (r .Context (), "server template error" , "err" , err )
392392 }
393393 case http .MethodPost :
@@ -402,7 +402,7 @@ func (s *Server) handlePasswordLogin(w http.ResponseWriter, r *http.Request) {
402402 return
403403 }
404404 if ! ok {
405- if err := s .templates .password (r , w , r .URL .String (), username , usernamePrompt (pwConn ), true , backLink ); err != nil {
405+ if err := s .templates .password (r , w , r .URL .String (), username , usernamePrompt (pwConn ), true , backLink , s . enableSignup ); err != nil {
406406 s .logger .ErrorContext (r .Context (), "server template error" , "err" , err )
407407 }
408408 s .logger .ErrorContext (r .Context (), "failed login attempt: Invalid credentials." , "user" , username )
@@ -1540,173 +1540,3 @@ func usernamePrompt(conn connector.PasswordConnector) string {
15401540 return "Username"
15411541}
15421542
1543- // clientRegistrationRequest represents an RFC 7591 client registration request
1544- type clientRegistrationRequest struct {
1545- RedirectURIs []string `json:"redirect_uris"`
1546- ClientName string `json:"client_name,omitempty"`
1547- TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`
1548- GrantTypes []string `json:"grant_types,omitempty"`
1549- ResponseTypes []string `json:"response_types,omitempty"`
1550- Scope string `json:"scope,omitempty"`
1551- LogoURI string `json:"logo_uri,omitempty"`
1552- }
1553-
1554- // clientRegistrationResponse represents an RFC 7591 client registration response
1555- type clientRegistrationResponse struct {
1556- ClientID string `json:"client_id"`
1557- ClientSecret string `json:"client_secret,omitempty"`
1558- ClientSecretExpiresAt int64 `json:"client_secret_expires_at"`
1559- ClientName string `json:"client_name,omitempty"`
1560- RedirectURIs []string `json:"redirect_uris"`
1561- TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`
1562- GrantTypes []string `json:"grant_types,omitempty"`
1563- ResponseTypes []string `json:"response_types,omitempty"`
1564- Scope string `json:"scope,omitempty"`
1565- LogoURI string `json:"logo_uri,omitempty"`
1566- }
1567-
1568- // handleClientRegistration implements RFC 7591 OAuth 2.0 Dynamic Client Registration Protocol
1569- func (s * Server ) handleClientRegistration (w http.ResponseWriter , r * http.Request ) {
1570- ctx := r .Context ()
1571-
1572- // Only POST method is allowed
1573- if r .Method != http .MethodPost {
1574- s .registrationErrHelper (w , errInvalidRequest , "Method not allowed" , http .StatusMethodNotAllowed )
1575- return
1576- }
1577-
1578- // Check Initial Access Token if configured (RFC 7591 Section 3.1)
1579- if s .registrationToken != "" {
1580- authHeader := r .Header .Get ("Authorization" )
1581- const bearerPrefix = "Bearer "
1582-
1583- if authHeader == "" || ! strings .HasPrefix (authHeader , bearerPrefix ) {
1584- w .Header ().Set ("WWW-Authenticate" , "Bearer" )
1585- s .registrationErrHelper (w , errInvalidRequest , "Initial access token required" , http .StatusUnauthorized )
1586- return
1587- }
1588-
1589- providedToken := strings .TrimPrefix (authHeader , bearerPrefix )
1590- if providedToken != s .registrationToken {
1591- w .Header ().Set ("WWW-Authenticate" , "Bearer error=\" invalid_token\" " )
1592- s .registrationErrHelper (w , errInvalidRequest , "Invalid initial access token" , http .StatusUnauthorized )
1593- return
1594- }
1595-
1596- s .logger .InfoContext (ctx , "client registration authenticated with initial access token" )
1597- } else {
1598- s .logger .WarnContext (ctx , "client registration endpoint is open - no authentication required. Set registrationToken in config for production use." )
1599- }
1600-
1601- // Parse the request body
1602- var req clientRegistrationRequest
1603- if err := json .NewDecoder (r .Body ).Decode (& req ); err != nil {
1604- s .logger .ErrorContext (ctx , "failed to parse registration request" , "err" , err )
1605- s .registrationErrHelper (w , errInvalidRequest , "Invalid JSON request body" , http .StatusBadRequest )
1606- return
1607- }
1608-
1609- // Validate required fields
1610- if len (req .RedirectURIs ) == 0 {
1611- s .registrationErrHelper (w , errInvalidRequest , "redirect_uris is required" , http .StatusBadRequest )
1612- return
1613- }
1614-
1615- // Apply default values
1616- if req .TokenEndpointAuthMethod == "" {
1617- req .TokenEndpointAuthMethod = "client_secret_basic"
1618- }
1619- if len (req .GrantTypes ) == 0 {
1620- req .GrantTypes = []string {grantTypeAuthorizationCode , grantTypeRefreshToken }
1621- }
1622- if len (req .ResponseTypes ) == 0 {
1623- req .ResponseTypes = []string {responseTypeCode }
1624- }
1625-
1626- // Validate token_endpoint_auth_method
1627- if req .TokenEndpointAuthMethod != "client_secret_basic" && req .TokenEndpointAuthMethod != "client_secret_post" && req .TokenEndpointAuthMethod != "none" {
1628- s .registrationErrHelper (w , errInvalidRequest , "Unsupported token_endpoint_auth_method" , http .StatusBadRequest )
1629- return
1630- }
1631-
1632- // Validate grant_types
1633- for _ , gt := range req .GrantTypes {
1634- if ! contains (s .supportedGrantTypes , gt ) {
1635- s .registrationErrHelper (w , errInvalidRequest , fmt .Sprintf ("Unsupported grant_type: %s" , gt ), http .StatusBadRequest )
1636- return
1637- }
1638- }
1639-
1640- // Validate response_types
1641- for _ , rt := range req .ResponseTypes {
1642- if ! s .supportedResponseTypes [rt ] {
1643- s .registrationErrHelper (w , errInvalidRequest , fmt .Sprintf ("Unsupported response_type: %s" , rt ), http .StatusBadRequest )
1644- return
1645- }
1646- }
1647-
1648- // Generate client_id and client_secret
1649- // Following the same pattern as the gRPC API (api.go:CreateClient)
1650- clientID := storage .NewID ()
1651-
1652- // Determine if this is a public client
1653- isPublic := req .TokenEndpointAuthMethod == "none"
1654-
1655- // Only generate secret for confidential clients
1656- var clientSecret string
1657- if ! isPublic {
1658- clientSecret = storage .NewID () + storage .NewID () // Double NewID for longer secret
1659- }
1660-
1661- // Create the client in storage
1662- client := storage.Client {
1663- ID : clientID ,
1664- Secret : clientSecret ,
1665- RedirectURIs : req .RedirectURIs ,
1666- Name : req .ClientName ,
1667- LogoURL : req .LogoURI ,
1668- Public : isPublic ,
1669- }
1670-
1671- if err := s .storage .CreateClient (ctx , client ); err != nil {
1672- s .logger .ErrorContext (ctx , "failed to create client" , "err" , err )
1673- if err == storage .ErrAlreadyExists {
1674- s .registrationErrHelper (w , errInvalidRequest , "Client ID already exists" , http .StatusBadRequest )
1675- } else {
1676- s .registrationErrHelper (w , errServerError , "Failed to register client" , http .StatusInternalServerError )
1677- }
1678- return
1679- }
1680-
1681- // Build the response
1682- resp := clientRegistrationResponse {
1683- ClientID : clientID ,
1684- ClientSecret : clientSecret ,
1685- ClientSecretExpiresAt : 0 , // 0 indicates the secret never expires
1686- ClientName : req .ClientName ,
1687- RedirectURIs : req .RedirectURIs ,
1688- TokenEndpointAuthMethod : req .TokenEndpointAuthMethod ,
1689- GrantTypes : req .GrantTypes ,
1690- ResponseTypes : req .ResponseTypes ,
1691- Scope : req .Scope ,
1692- LogoURI : req .LogoURI ,
1693- }
1694-
1695- // For public clients, don't return the secret
1696- if isPublic {
1697- resp .ClientSecret = ""
1698- }
1699-
1700- // Return HTTP 201 Created
1701- w .Header ().Set ("Content-Type" , "application/json" )
1702- w .WriteHeader (http .StatusCreated )
1703- if err := json .NewEncoder (w ).Encode (resp ); err != nil {
1704- s .logger .ErrorContext (ctx , "failed to encode registration response" , "err" , err )
1705- }
1706- }
1707-
1708- func (s * Server ) registrationErrHelper (w http.ResponseWriter , typ , description string , statusCode int ) {
1709- if err := tokenErr (w , typ , description , statusCode ); err != nil {
1710- s .logger .Error ("registration error response" , "err" , err )
1711- }
1712- }
0 commit comments