forked from baron/baron-sso
offline 스코프 제거, rp_claims 값 표준화
This commit is contained in:
@@ -15,6 +15,7 @@ import (
|
||||
"io"
|
||||
"log/slog"
|
||||
"maps"
|
||||
"math"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -2099,7 +2100,7 @@ func (h *DevHandler) CreateClient(c *fiber.Ctx) error {
|
||||
return errorJSON(c, fiber.StatusBadRequest, "redirectUris is required")
|
||||
}
|
||||
|
||||
scopes := derefSlice(req.Scopes, defaultClientScopes())
|
||||
scopes := normalizeClientScopes(derefSlice(req.Scopes, defaultClientScopes()))
|
||||
grantTypes := derefSlice(req.GrantTypes, defaultGrantTypes())
|
||||
responseTypes := derefSlice(req.ResponseTypes, defaultResponseTypes())
|
||||
|
||||
@@ -2186,7 +2187,7 @@ func (h *DevHandler) CreateClient(c *fiber.Ctx) error {
|
||||
RedirectURIs: redirectURIs,
|
||||
GrantTypes: grantTypes,
|
||||
ResponseTypes: responseTypes,
|
||||
Scope: strings.Join(scopes, " "),
|
||||
Scope: buildScope(scopes),
|
||||
TokenEndpointAuthMethod: tokenAuthMethod,
|
||||
SkipConsent: new(valueOrBool(req.SkipConsent, true)),
|
||||
JWKSUri: jwksURI,
|
||||
@@ -3593,7 +3594,7 @@ func normalizeIDTokenClaimsWithOptions(rawClaims any, allowTopLevel bool) ([]nor
|
||||
return nil, fmt.Errorf("metadata.id_token_claims valueType is invalid: %s", valueType)
|
||||
}
|
||||
|
||||
value := strings.TrimSpace(readInterfaceString(record["value"], ""))
|
||||
value := strings.TrimSpace(readClaimValueString(record["value"], ""))
|
||||
nullable, _ := record["nullable"].(bool)
|
||||
if !(nullable && value == "") {
|
||||
if _, err := parseConfiguredClaimValue(value, valueType); err != nil {
|
||||
@@ -3631,6 +3632,35 @@ func readInterfaceString(value any, fallback string) string {
|
||||
return fallback
|
||||
}
|
||||
|
||||
func readClaimValueString(value any, fallback string) string {
|
||||
if value == nil {
|
||||
return fallback
|
||||
}
|
||||
switch typed := value.(type) {
|
||||
case string:
|
||||
return typed
|
||||
case float64:
|
||||
if typed == math.Trunc(typed) {
|
||||
return strconv.FormatInt(int64(typed), 10)
|
||||
}
|
||||
return strconv.FormatFloat(typed, 'f', -1, 64)
|
||||
case float32:
|
||||
floatValue := float64(typed)
|
||||
if floatValue == math.Trunc(floatValue) {
|
||||
return strconv.FormatInt(int64(floatValue), 10)
|
||||
}
|
||||
return strconv.FormatFloat(floatValue, 'f', -1, 64)
|
||||
case int:
|
||||
return strconv.Itoa(typed)
|
||||
case int64:
|
||||
return strconv.FormatInt(typed, 10)
|
||||
case json.Number:
|
||||
return typed.String()
|
||||
default:
|
||||
return fallback
|
||||
}
|
||||
}
|
||||
|
||||
func parseConfiguredClaimValue(rawValue string, valueType string) (any, error) {
|
||||
trimmed := strings.TrimSpace(rawValue)
|
||||
|
||||
@@ -3703,21 +3733,36 @@ func parseConfiguredClaimValue(rawValue string, valueType string) (any, error) {
|
||||
if trimmed == "" {
|
||||
return nil, errors.New("date value is required")
|
||||
}
|
||||
if _, err := time.Parse("2006-01-02", trimmed); err != nil {
|
||||
return nil, errors.New("date value must use YYYY-MM-DD")
|
||||
if isIntegerClaimLiteral(trimmed) {
|
||||
parsed, err := strconv.ParseInt(trimmed, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.New("date value must use unix seconds or YYYY-MM-DD")
|
||||
}
|
||||
return parsed, nil
|
||||
}
|
||||
return trimmed, nil
|
||||
parsed, err := time.Parse("2006-01-02", trimmed)
|
||||
if err != nil {
|
||||
return nil, errors.New("date value must use unix seconds or YYYY-MM-DD")
|
||||
}
|
||||
return parsed.Unix(), nil
|
||||
case "datetime":
|
||||
if trimmed == "" {
|
||||
return nil, errors.New("datetime value is required")
|
||||
}
|
||||
if _, err := time.Parse(time.RFC3339, trimmed); err == nil {
|
||||
return trimmed, nil
|
||||
if isIntegerClaimLiteral(trimmed) {
|
||||
parsed, err := strconv.ParseInt(trimmed, 10, 64)
|
||||
if err != nil {
|
||||
return nil, errors.New("datetime value must use unix seconds, RFC3339, or YYYY-MM-DDTHH:mm")
|
||||
}
|
||||
return parsed, nil
|
||||
}
|
||||
if _, err := time.Parse("2006-01-02T15:04", trimmed); err == nil {
|
||||
return trimmed, nil
|
||||
if parsed, err := time.Parse(time.RFC3339, trimmed); err == nil {
|
||||
return parsed.Unix(), nil
|
||||
}
|
||||
return nil, errors.New("datetime value must use RFC3339 or YYYY-MM-DDTHH:mm")
|
||||
if parsed, err := time.ParseInLocation("2006-01-02T15:04", trimmed, time.UTC); err == nil {
|
||||
return parsed.Unix(), nil
|
||||
}
|
||||
return nil, errors.New("datetime value must use unix seconds, RFC3339, or YYYY-MM-DDTHH:mm")
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported claim value type: %s", valueType)
|
||||
}
|
||||
@@ -3795,7 +3840,33 @@ func defaultResponseTypes() []string {
|
||||
}
|
||||
|
||||
func buildScope(scopes []string) string {
|
||||
return strings.Join(scopes, " ")
|
||||
return strings.Join(normalizeClientScopes(scopes), " ")
|
||||
}
|
||||
|
||||
func normalizeClientScopes(scopes []string) []string {
|
||||
normalized := make([]string, 0, len(scopes))
|
||||
seen := make(map[string]struct{}, len(scopes))
|
||||
for _, scope := range scopes {
|
||||
scope = strings.TrimSpace(scope)
|
||||
if scope == "" || isRefreshTokenScopeAlias(scope) {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[scope]; ok {
|
||||
continue
|
||||
}
|
||||
seen[scope] = struct{}{}
|
||||
normalized = append(normalized, scope)
|
||||
}
|
||||
return normalized
|
||||
}
|
||||
|
||||
func isRefreshTokenScopeAlias(scope string) bool {
|
||||
switch strings.ToLower(strings.TrimSpace(scope)) {
|
||||
case "offline", "offline_access":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func valueOr(ptr *string, fallback string) string {
|
||||
|
||||
Reference in New Issue
Block a user