feat(auth): authorise an allow-listed machine identity without a roles claim
ZITADEL does not put roles into a machine-user access token on this instance. The grant exists, the scope asks for urn:zitadel:iam:org:project:roles, and the claim still never arrives; a colon-free role key makes no difference either. A service account therefore cannot satisfy requiredCapability by any configuration. auth.allowListedMachineIdentities admits such a caller when its client_id appears in zitadelClientIds. The caller is still authenticated: it signed an assertion with a private key whose public half ZITADEL holds. The allow-list entry is a reviewed line of configuration, not a wildcard. The new path is off by default, and a token that does state its roles is judged by the role check as before, so the weaker rule never applies twice.
This commit was merged in pull request #71.
This commit is contained in:
@@ -66,6 +66,13 @@ type AuthConfig struct {
|
||||
JWKSURL string
|
||||
// RequiredCapability is the permission required for LLM inference (e.g., "llm:inference").
|
||||
RequiredCapability string
|
||||
// AllowListedMachineIdentities admits a caller whose token carries no roles
|
||||
// claim, when its client id is in ZitadelClientIDs. ZITADEL does not assert roles
|
||||
// in machine-user access tokens on this instance, so a service account can never
|
||||
// satisfy RequiredCapability. The allow-list is then the authorisation: the caller
|
||||
// still proves possession of a registered private key, and the entry is a reviewed
|
||||
// line of configuration, not a wildcard.
|
||||
AllowListedMachineIdentities bool
|
||||
// TokenURL is the OIDC token endpoint used for token exchange
|
||||
// (client_credentials / password grants).
|
||||
TokenURL string
|
||||
|
||||
+25
-23
@@ -20,16 +20,17 @@ type rawConfig struct {
|
||||
|
||||
// rawAuth represents auth configuration in YAML.
|
||||
type rawAuth struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Issuer string `yaml:"issuer"`
|
||||
Audience string `yaml:"audience"`
|
||||
JWKSURL string `yaml:"jwksUrl"`
|
||||
RequiredCapability string `yaml:"requiredCapability"`
|
||||
TokenURL string `yaml:"tokenUrl"`
|
||||
ClientID string `yaml:"clientId"`
|
||||
ZitadelDomain string `yaml:"zitadelDomain"`
|
||||
ZitadelInsecurePort string `yaml:"zitadelInsecurePort"`
|
||||
ZitadelClientIDs []string `yaml:"zitadelClientIds"`
|
||||
Enabled bool `yaml:"enabled"`
|
||||
Issuer string `yaml:"issuer"`
|
||||
Audience string `yaml:"audience"`
|
||||
JWKSURL string `yaml:"jwksUrl"`
|
||||
RequiredCapability string `yaml:"requiredCapability"`
|
||||
AllowListedMachineIdentities bool `yaml:"allowListedMachineIdentities"`
|
||||
TokenURL string `yaml:"tokenUrl"`
|
||||
ClientID string `yaml:"clientId"`
|
||||
ZitadelDomain string `yaml:"zitadelDomain"`
|
||||
ZitadelInsecurePort string `yaml:"zitadelInsecurePort"`
|
||||
ZitadelClientIDs []string `yaml:"zitadelClientIds"`
|
||||
}
|
||||
|
||||
// rawRoute represents a single route in the YAML configuration.
|
||||
@@ -44,8 +45,8 @@ type rawModel struct {
|
||||
Address string `yaml:"address"`
|
||||
Addresses []string `yaml:"addresses"`
|
||||
Path string `yaml:"path"`
|
||||
UpstreamModel string `yaml:"upstreamModel"`
|
||||
AuthRequired *bool `yaml:"authRequired"`
|
||||
UpstreamModel string `yaml:"upstreamModel"`
|
||||
AuthRequired *bool `yaml:"authRequired"`
|
||||
}
|
||||
|
||||
// rawAdapter represents a service adapter in the YAML configuration.
|
||||
@@ -187,17 +188,18 @@ func LoadRoutesAndModelsFromFile(path string) (map[string]*Route, map[string]*Mo
|
||||
|
||||
// Parse auth config
|
||||
authConfig := AuthConfig{
|
||||
Enabled: raw.Auth.Enabled,
|
||||
Issuer: raw.Auth.Issuer,
|
||||
Audience: raw.Auth.Audience,
|
||||
JWKSURL: raw.Auth.JWKSURL,
|
||||
RequiredCapability: raw.Auth.RequiredCapability,
|
||||
TokenURL: raw.Auth.TokenURL,
|
||||
ClientID: raw.Auth.ClientID,
|
||||
ClientSecret: os.Getenv("AUTH_CLIENT_SECRET"),
|
||||
ZitadelDomain: raw.Auth.ZitadelDomain,
|
||||
ZitadelInsecurePort: raw.Auth.ZitadelInsecurePort,
|
||||
ZitadelClientIDs: raw.Auth.ZitadelClientIDs,
|
||||
Enabled: raw.Auth.Enabled,
|
||||
Issuer: raw.Auth.Issuer,
|
||||
Audience: raw.Auth.Audience,
|
||||
JWKSURL: raw.Auth.JWKSURL,
|
||||
RequiredCapability: raw.Auth.RequiredCapability,
|
||||
AllowListedMachineIdentities: raw.Auth.AllowListedMachineIdentities,
|
||||
TokenURL: raw.Auth.TokenURL,
|
||||
ClientID: raw.Auth.ClientID,
|
||||
ClientSecret: os.Getenv("AUTH_CLIENT_SECRET"),
|
||||
ZitadelDomain: raw.Auth.ZitadelDomain,
|
||||
ZitadelInsecurePort: raw.Auth.ZitadelInsecurePort,
|
||||
ZitadelClientIDs: raw.Auth.ZitadelClientIDs,
|
||||
}
|
||||
|
||||
return routes, models, adapters, authConfig, nil
|
||||
|
||||
Reference in New Issue
Block a user