Files
zitadel/internal/api/authz/api_token_verifier.go
T
260446f91f Merge commit from fork
* fix: block Login V2 auth for users in deactivated organizations

Enforce organization state on session creation, OIDC token issuance/refresh,
and SAML session creation so deactivated org users cannot authenticate.

Co-authored-by: Cursor <[email protected]>

* fix: reject claim serving for users in deactivated organizations

Require an active resource-owner org in OIDC userinfo and SAML attribute
paths, and use distinct error IDs for inactive user vs inactive org at
token issuance.

Co-authored-by: Cursor <[email protected]>

* fix: drop sessions and tokens when an organization is deactivated

Mirror OrgRemoved cleanup for OrgDeactivated in the V2 session projection
and V1 auth user_session, token, and refresh_token handlers. Also delete
V2 sessions on OrgRemoved, which does not emit per-user removal events.

Co-authored-by: Cursor <[email protected]>

* fix: reject API tokens from deactivated organizations

Check the caller's resource-owner org state in authz middleware via a
cached OrgByID lookup, so already-issued tokens lose ZITADEL API access
when their organization is deactivated. Return unauthenticated (401).
Target org remains unrestricted so instance admins can still manage
deactivated orgs.

Co-authored-by: Cursor <[email protected]>

* fix: invalidate OIDC refresh tokens after org deactivation

Reject refresh exchange if the user's organization was deactivated
after the refresh token was issued, so grants stay dead after reactivate.

Co-authored-by: Cursor <[email protected]>

* fix: invalidate V2 access tokens after org deactivation

Treat OrgDeactivated after the token position as session termination in
ActiveAccessTokenByToken, so issued ATs stay dead after reactivation.

Co-authored-by: Cursor <[email protected]>

---------

Co-authored-by: Cursor <[email protected]>
2026-08-14 06:12:45 +02:00

77 lines
2.5 KiB
Go

package authz
import (
"context"
"sync"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
)
// TODO: Define interfaces where they are accepted
type APITokenVerifier interface {
AccessTokenVerifier
SystemTokenVerifier
RegisterServer(appName, methodPrefix string, mappings MethodMapping)
CheckAuthMethod(method string) (Option, bool)
ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (_ string, _ []string, err error)
ExistsOrg(ctx context.Context, id, domain string) (orgID string, err error)
CheckOrgActive(ctx context.Context, orgID string) error
SearchMyMemberships(ctx context.Context, orgID string, shouldTriggerBulk bool) (_ []*Membership, err error)
}
type ApiTokenVerifier struct {
AccessTokenVerifier
SystemTokenVerifier
authZRepo authZRepo
clients sync.Map
authMethods MethodMapping
}
func StartAPITokenVerifier(authZRepo authZRepo, accessTokenVerifier AccessTokenVerifier, systemTokenVerifier SystemTokenVerifier) *ApiTokenVerifier {
return &ApiTokenVerifier{
authZRepo: authZRepo,
SystemTokenVerifier: systemTokenVerifier,
AccessTokenVerifier: accessTokenVerifier,
}
}
func (v *ApiTokenVerifier) RegisterServer(appName, methodPrefix string, mappings MethodMapping) {
v.clients.Store(methodPrefix, &client{name: appName})
if v.authMethods == nil {
v.authMethods = make(map[string]Option)
}
for method, option := range mappings {
v.authMethods[method] = option
}
}
func (v *ApiTokenVerifier) SearchMyMemberships(ctx context.Context, orgID string, shouldTriggerBulk bool) (_ []*Membership, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
return v.authZRepo.SearchMyMemberships(ctx, orgID, shouldTriggerBulk)
}
func (v *ApiTokenVerifier) ProjectIDAndOriginsByClientID(ctx context.Context, clientID string) (_ string, _ []string, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
return v.authZRepo.ProjectIDAndOriginsByClientID(ctx, clientID)
}
func (v *ApiTokenVerifier) ExistsOrg(ctx context.Context, id, domain string) (orgID string, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
return v.authZRepo.ExistsOrg(ctx, id, domain)
}
func (v *ApiTokenVerifier) CheckOrgActive(ctx context.Context, orgID string) (err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
return v.authZRepo.CheckOrgActive(ctx, orgID)
}
func (v *ApiTokenVerifier) CheckAuthMethod(method string) (Option, bool) {
authOpt, ok := v.authMethods[method]
return authOpt, ok
}