Files
zitadel/internal/domain/request.go
masum-msphere 9514a626b8 feat(oidc): Added new claim in userinfo response to return all requested audience roles (#9861)
# Which Problems Are Solved

The /userinfo endpoint only returns roles for the current project, even
if the access token includes multiple project aud scopes.

This prevents clients from retrieving all user roles across multiple
projects, making multi-project access control ineffective.

# How the Problems Are Solved

Modified the /userinfo handler logic to resolve roles across all valid
project audience scopes provided in the token, not just the current
project.
Ensured that if **urn:zitadel:iam:org:projects:roles is in the scopes**,
roles from all declared project audiences are collected and included in
the response in **urn:zitadel:iam:org:projects:roles claim**.

# Additional Changes

# Additional Context

This change enables service-to-service authorization workflows and SPA
role resolution across multiple project contexts with a single token.
- Closes #9831

---------

Co-authored-by: Masum Patel <patelmasum98@gmail.com>
Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
(cherry picked from commit 295584648d)
2025-09-30 07:11:37 +02:00

82 lines
1.9 KiB
Go

package domain
const (
OrgDomainPrimaryScope = "urn:zitadel:iam:org:domain:primary:"
OrgIDScope = "urn:zitadel:iam:org:id:"
OrgRoleIDScope = "urn:zitadel:iam:org:roles:id:"
OrgDomainPrimaryClaim = "urn:zitadel:iam:org:domain:primary"
OrgIDClaim = "urn:zitadel:iam:org:id"
ProjectIDScope = "urn:zitadel:iam:org:project:id:"
ProjectsIDScope = "urn:zitadel:iam:org:projects:roles"
ProjectIDScopeZITADEL = "zitadel"
AudSuffix = ":aud"
ProjectScopeZITADEL = ProjectIDScope + ProjectIDScopeZITADEL + AudSuffix
SelectIDPScope = "urn:zitadel:iam:org:idp:id:"
)
// TODO: Change AuthRequest to interface and let oidcauthreqesut implement it
type Request interface {
Type() AuthRequestType
IsValid() bool
}
type AuthRequestType int32
const (
AuthRequestTypeOIDC AuthRequestType = iota
AuthRequestTypeSAML
AuthRequestTypeDevice
)
type AuthRequestOIDC struct {
Scopes []string
ResponseType OIDCResponseType
ResponseMode OIDCResponseMode
Nonce string
CodeChallenge *OIDCCodeChallenge
}
func (a *AuthRequestOIDC) Type() AuthRequestType {
return AuthRequestTypeOIDC
}
func (a *AuthRequestOIDC) IsValid() bool {
return len(a.Scopes) > 0 &&
a.CodeChallenge == nil || a.CodeChallenge != nil && a.CodeChallenge.IsValid()
}
type AuthRequestSAML struct {
ID string
BindingType string
Code string
Issuer string
IssuerName string
Destination string
}
func (a *AuthRequestSAML) Type() AuthRequestType {
return AuthRequestTypeSAML
}
func (a *AuthRequestSAML) IsValid() bool {
return true
}
type AuthRequestDevice struct {
ClientID string
DeviceCode string
UserCode string
Scopes []string
Audience []string
AppName string
ProjectName string
}
func (*AuthRequestDevice) Type() AuthRequestType {
return AuthRequestTypeDevice
}
func (a *AuthRequestDevice) IsValid() bool {
return a.DeviceCode != "" && a.UserCode != ""
}