diff --git a/backend/internal/dto/oidc_dto.go b/backend/internal/dto/oidc_dto.go index 1e534dce..afa649d8 100644 --- a/backend/internal/dto/oidc_dto.go +++ b/backend/internal/dto/oidc_dto.go @@ -16,7 +16,7 @@ type OidcClientDto struct { type OidcClientCreateDto struct { Name string `json:"name" binding:"required,max=50"` - CallbackURLs []string `json:"callbackURLs" binding:"required,urlList"` + CallbackURLs []string `json:"callbackURLs" binding:"required"` IsPublic bool `json:"isPublic"` PkceEnabled bool `json:"pkceEnabled"` } diff --git a/backend/internal/dto/validations.go b/backend/internal/dto/validations.go index bec57db6..8f083cc3 100644 --- a/backend/internal/dto/validations.go +++ b/backend/internal/dto/validations.go @@ -4,21 +4,9 @@ import ( "github.com/gin-gonic/gin/binding" "github.com/go-playground/validator/v10" "log" - "net/url" "regexp" ) -var validateUrlList validator.Func = func(fl validator.FieldLevel) bool { - urls := fl.Field().Interface().([]string) - for _, u := range urls { - _, err := url.ParseRequestURI(u) - if err != nil { - return false - } - } - return true -} - var validateUsername validator.Func = func(fl validator.FieldLevel) bool { // [a-zA-Z0-9] : The username must start with an alphanumeric character // [a-zA-Z0-9_.@-]* : The rest of the username can contain alphanumeric characters, dots, underscores, hyphens, and "@" symbols @@ -36,11 +24,6 @@ var validateClaimKey validator.Func = func(fl validator.FieldLevel) bool { } func init() { - if v, ok := binding.Validator.Engine().(*validator.Validate); ok { - if err := v.RegisterValidation("urlList", validateUrlList); err != nil { - log.Fatalf("Failed to register custom validation: %v", err) - } - } if v, ok := binding.Validator.Engine().(*validator.Validate); ok { if err := v.RegisterValidation("username", validateUsername); err != nil { log.Fatalf("Failed to register custom validation: %v", err) diff --git a/backend/internal/middleware/error_handler.go b/backend/internal/middleware/error_handler.go index 487fda4f..e79747e9 100644 --- a/backend/internal/middleware/error_handler.go +++ b/backend/internal/middleware/error_handler.go @@ -83,8 +83,6 @@ func handleValidationError(validationErrors validator.ValidationErrors) string { errorMessage = fmt.Sprintf("%s must be at least %s characters long", fieldName, ve.Param()) case "max": errorMessage = fmt.Sprintf("%s must be at most %s characters long", fieldName, ve.Param()) - case "urlList": - errorMessage = fmt.Sprintf("%s must be a list of valid URLs", fieldName) default: errorMessage = fmt.Sprintf("%s is invalid", fieldName) } diff --git a/backend/internal/service/oidc_service.go b/backend/internal/service/oidc_service.go index 9a3b9ebf..22d00f93 100644 --- a/backend/internal/service/oidc_service.go +++ b/backend/internal/service/oidc_service.go @@ -14,7 +14,7 @@ import ( "gorm.io/gorm" "mime/multipart" "os" - "slices" + "regexp" "strings" "time" ) @@ -432,8 +432,16 @@ func (s *OidcService) getCallbackURL(client model.OidcClient, inputCallbackURL s if inputCallbackURL == "" { return client.CallbackURLs[0], nil } - if slices.Contains(client.CallbackURLs, inputCallbackURL) { - return inputCallbackURL, nil + + for _, callbackPattern := range client.CallbackURLs { + regexPattern := strings.ReplaceAll(regexp.QuoteMeta(callbackPattern), `\*`, ".*") + "$" + matched, err := regexp.MatchString(regexPattern, inputCallbackURL) + if err != nil { + return "", err + } + if matched { + return inputCallbackURL, nil + } } return "", &common.OidcInvalidCallbackURLError{} diff --git a/frontend/src/routes/settings/admin/oidc-clients/oidc-client-form.svelte b/frontend/src/routes/settings/admin/oidc-clients/oidc-client-form.svelte index be1ac72c..d1729721 100644 --- a/frontend/src/routes/settings/admin/oidc-clients/oidc-client-form.svelte +++ b/frontend/src/routes/settings/admin/oidc-clients/oidc-client-form.svelte @@ -36,7 +36,7 @@ const formSchema = z.object({ name: z.string().min(2).max(50), - callbackURLs: z.array(z.string().url()).nonempty(), + callbackURLs: z.array(z.string()).nonempty(), isPublic: z.boolean(), pkceEnabled: z.boolean() });