forked from casdoor/casdoor
194 lines
7.0 KiB
Go
194 lines
7.0 KiB
Go
// Copyright 2026 The Casdoor Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package object
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/casdoor/casdoor/util"
|
|
)
|
|
|
|
// DynamicClientRegistrationRequest represents an RFC 7591 client registration request
|
|
type DynamicClientRegistrationRequest struct {
|
|
ClientName string `json:"client_name,omitempty"`
|
|
RedirectUris []string `json:"redirect_uris,omitempty"`
|
|
GrantTypes []string `json:"grant_types,omitempty"`
|
|
ResponseTypes []string `json:"response_types,omitempty"`
|
|
TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`
|
|
ApplicationType string `json:"application_type,omitempty"`
|
|
Contacts []string `json:"contacts,omitempty"`
|
|
LogoUri string `json:"logo_uri,omitempty"`
|
|
ClientUri string `json:"client_uri,omitempty"`
|
|
PolicyUri string `json:"policy_uri,omitempty"`
|
|
TosUri string `json:"tos_uri,omitempty"`
|
|
Scope string `json:"scope,omitempty"`
|
|
}
|
|
|
|
// DynamicClientRegistrationResponse represents an RFC 7591 client registration response
|
|
type DynamicClientRegistrationResponse struct {
|
|
ClientId string `json:"client_id"`
|
|
ClientSecret string `json:"client_secret,omitempty"`
|
|
ClientIdIssuedAt int64 `json:"client_id_issued_at,omitempty"`
|
|
ClientSecretExpiresAt int64 `json:"client_secret_expires_at,omitempty"`
|
|
ClientName string `json:"client_name,omitempty"`
|
|
RedirectUris []string `json:"redirect_uris,omitempty"`
|
|
GrantTypes []string `json:"grant_types,omitempty"`
|
|
ResponseTypes []string `json:"response_types,omitempty"`
|
|
TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`
|
|
ApplicationType string `json:"application_type,omitempty"`
|
|
Contacts []string `json:"contacts,omitempty"`
|
|
LogoUri string `json:"logo_uri,omitempty"`
|
|
ClientUri string `json:"client_uri,omitempty"`
|
|
PolicyUri string `json:"policy_uri,omitempty"`
|
|
TosUri string `json:"tos_uri,omitempty"`
|
|
Scope string `json:"scope,omitempty"`
|
|
RegistrationClientUri string `json:"registration_client_uri,omitempty"`
|
|
RegistrationAccessToken string `json:"registration_access_token,omitempty"`
|
|
}
|
|
|
|
// DcrError represents an RFC 7591 error response
|
|
type DcrError struct {
|
|
Error string `json:"error"`
|
|
ErrorDescription string `json:"error_description,omitempty"`
|
|
}
|
|
|
|
// RegisterDynamicClient creates a new application based on DCR request
|
|
func RegisterDynamicClient(req *DynamicClientRegistrationRequest, organization string) (*DynamicClientRegistrationResponse, *DcrError, error) {
|
|
// Validate organization exists and has DCR enabled
|
|
org, err := GetOrganization(util.GetId("admin", organization))
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if org == nil {
|
|
return nil, &DcrError{
|
|
Error: "invalid_client_metadata",
|
|
ErrorDescription: "organization not found",
|
|
}, nil
|
|
}
|
|
|
|
// Check if DCR is enabled for this organization
|
|
if org.DcrPolicy == "" || org.DcrPolicy == "disabled" {
|
|
return nil, &DcrError{
|
|
Error: "invalid_client_metadata",
|
|
ErrorDescription: "dynamic client registration is disabled for this organization",
|
|
}, nil
|
|
}
|
|
|
|
// Validate required fields
|
|
if len(req.RedirectUris) == 0 {
|
|
return nil, &DcrError{
|
|
Error: "invalid_redirect_uri",
|
|
ErrorDescription: "redirect_uris is required and must contain at least one URI",
|
|
}, nil
|
|
}
|
|
|
|
// Set defaults
|
|
if req.ClientName == "" {
|
|
clientIdPrefix := util.GenerateClientId()
|
|
if len(clientIdPrefix) > 8 {
|
|
clientIdPrefix = clientIdPrefix[:8]
|
|
}
|
|
req.ClientName = fmt.Sprintf("DCR Client %s", clientIdPrefix)
|
|
}
|
|
if len(req.GrantTypes) == 0 {
|
|
req.GrantTypes = []string{"authorization_code"}
|
|
}
|
|
if len(req.ResponseTypes) == 0 {
|
|
req.ResponseTypes = []string{"code"}
|
|
}
|
|
if req.TokenEndpointAuthMethod == "" {
|
|
req.TokenEndpointAuthMethod = "client_secret_basic"
|
|
}
|
|
if req.ApplicationType == "" {
|
|
req.ApplicationType = "web"
|
|
}
|
|
|
|
// Generate unique application name
|
|
randomName := util.GetRandomName()
|
|
appName := fmt.Sprintf("dcr_%s", randomName)
|
|
|
|
// Create Application object
|
|
// Note: DCR applications are created under "admin" owner by default
|
|
// This can be made configurable in future versions
|
|
clientId := util.GenerateClientId()
|
|
clientSecret := util.GenerateClientSecret()
|
|
createdTime := util.GetCurrentTime()
|
|
|
|
application := &Application{
|
|
Owner: "admin",
|
|
Name: appName,
|
|
Organization: organization,
|
|
CreatedTime: createdTime,
|
|
DisplayName: req.ClientName,
|
|
Category: "Agent",
|
|
Type: "MCP",
|
|
Scopes: []*ScopeItem{},
|
|
Logo: req.LogoUri,
|
|
HomepageUrl: req.ClientUri,
|
|
ClientId: clientId,
|
|
ClientSecret: clientSecret,
|
|
RedirectUris: req.RedirectUris,
|
|
GrantTypes: req.GrantTypes,
|
|
EnablePassword: false,
|
|
EnableSignUp: false,
|
|
DisableSignin: false,
|
|
EnableSigninSession: false,
|
|
EnableCodeSignin: true,
|
|
EnableAutoSignin: false,
|
|
TokenFormat: "JWT",
|
|
ExpireInHours: 168,
|
|
RefreshExpireInHours: 168,
|
|
CookieExpireInHours: 720,
|
|
FormOffset: 2,
|
|
Tags: []string{"dcr"},
|
|
TermsOfUse: req.TosUri,
|
|
}
|
|
|
|
// Add the application
|
|
affected, err := AddApplication(application)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if !affected {
|
|
return nil, &DcrError{
|
|
Error: "invalid_client_metadata",
|
|
ErrorDescription: "failed to create client application",
|
|
}, nil
|
|
}
|
|
|
|
// Build response
|
|
response := &DynamicClientRegistrationResponse{
|
|
ClientId: clientId,
|
|
ClientSecret: clientSecret,
|
|
ClientIdIssuedAt: time.Now().Unix(),
|
|
ClientSecretExpiresAt: 0, // Never expires
|
|
ClientName: req.ClientName,
|
|
RedirectUris: req.RedirectUris,
|
|
GrantTypes: req.GrantTypes,
|
|
ResponseTypes: req.ResponseTypes,
|
|
TokenEndpointAuthMethod: req.TokenEndpointAuthMethod,
|
|
ApplicationType: req.ApplicationType,
|
|
Contacts: req.Contacts,
|
|
LogoUri: req.LogoUri,
|
|
ClientUri: req.ClientUri,
|
|
PolicyUri: req.PolicyUri,
|
|
TosUri: req.TosUri,
|
|
Scope: req.Scope,
|
|
}
|
|
|
|
return response, nil, nil
|
|
}
|