diff --git a/internal/auth/oidc.go b/internal/auth/oidc.go index 2c6124b..e835419 100644 --- a/internal/auth/oidc.go +++ b/internal/auth/oidc.go @@ -204,23 +204,20 @@ func resolveLoginScopes(scopes []string) []string { } resolved := append([]string(nil), baseScopes...) + seen := make(map[string]struct{}, len(resolved)+len(scopes)) + for _, scope := range resolved { + seen[scope] = struct{}{} + } for _, scope := range scopes { - if !hasScope(resolved, scope) { - resolved = append(resolved, scope) + if _, ok := seen[scope]; ok { + continue } + seen[scope] = struct{}{} + resolved = append(resolved, scope) } return resolved } -func hasScope(scopes []string, scope string) bool { - for _, existing := range scopes { - if existing == scope { - return true - } - } - return false -} - func applyTokenExtraEmailFallback(session *Session, token *oauth2.Token) { if session.User.Email != "" { return diff --git a/internal/auth/oidc_test.go b/internal/auth/oidc_test.go index 2e1eee7..83244fd 100644 --- a/internal/auth/oidc_test.go +++ b/internal/auth/oidc_test.go @@ -67,6 +67,23 @@ func TestResolveLoginScopesDeduplicatesDefaultScopes(t *testing.T) { } } +func TestResolveLoginScopesDeduplicatesCustomScopesInOrder(t *testing.T) { + t.Parallel() + + input := []string{"gateway:access", "gateway:access", "openid", "custom:read", "custom:read"} + got := resolveLoginScopes(input) + want := []string{ + "openid", + "email", + "profile", + "gateway:access", + "custom:read", + } + if !reflect.DeepEqual(got, want) { + t.Fatalf("resolveLoginScopes(%#v) = %#v, want %#v", input, got, want) + } +} + func TestDecodeJWTClaims(t *testing.T) { t.Parallel()