mirror of
https://github.com/zitadel/zitadel.git
synced 2026-08-19 01:14:48 -05:00
* 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 <cursoragent@cursor.com> * 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 <cursoragent@cursor.com> * 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 <cursoragent@cursor.com> * 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 <cursoragent@cursor.com> * 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 <cursoragent@cursor.com> * 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 <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
61 lines
1.6 KiB
Go
61 lines
1.6 KiB
Go
package command
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/zitadel/zitadel/internal/eventstore"
|
|
"github.com/zitadel/zitadel/internal/repository/org"
|
|
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
|
)
|
|
|
|
// checkOrgNotDeactivatedAfter returns an invalid refresh token error if the organization
|
|
// was deactivated after the given time. This keeps refresh grants dead after reactivation.
|
|
func (c *Commands) checkOrgNotDeactivatedAfter(ctx context.Context, orgID string, after time.Time) (err error) {
|
|
ctx, span := tracing.NewSpan(ctx)
|
|
defer func() { span.EndWithError(err) }()
|
|
|
|
if orgID == "" || after.IsZero() {
|
|
return nil
|
|
}
|
|
model := &orgDeactivatedAfterModel{
|
|
orgID: orgID,
|
|
after: after,
|
|
}
|
|
if err = c.eventstore.FilterToQueryReducer(ctx, model); err != nil {
|
|
return zerrors.ThrowPreconditionFailed(err, "OIDCS-oR9nD", "Errors.Internal")
|
|
}
|
|
if model.deactivated {
|
|
return zerrors.ThrowPreconditionFailed(nil, "OIDCS-oR9nR", "Errors.OIDCSession.RefreshTokenInvalid")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type orgDeactivatedAfterModel struct {
|
|
orgID string
|
|
after time.Time
|
|
|
|
events int
|
|
deactivated bool
|
|
}
|
|
|
|
func (m *orgDeactivatedAfterModel) Reduce() error {
|
|
m.deactivated = m.events > 0
|
|
return nil
|
|
}
|
|
|
|
func (m *orgDeactivatedAfterModel) AppendEvents(events ...eventstore.Event) {
|
|
m.events += len(events)
|
|
}
|
|
|
|
func (m *orgDeactivatedAfterModel) Query() *eventstore.SearchQueryBuilder {
|
|
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
|
CreationDateAfter(m.after).
|
|
AddQuery().
|
|
AggregateTypes(org.AggregateType).
|
|
AggregateIDs(m.orgID).
|
|
EventTypes(org.OrgDeactivatedEventType).
|
|
Builder()
|
|
}
|