mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-01 20:02:15 +00:00
fix: make recovery code active on add (#11734)
# Which Problems Are Solved - Recovery codes added via `GenerateRecoveryCodes` were never listed as active authentication methods by `ListAuthenticationMethodTypes`. The `user_auth_method` projection reducer (`reduceInitAuthMethod`) set the state to `MFAStateNotReady`, expecting a subsequent verification event to transition it to `MFAStateReady`. Unlike TOTP, U2F, and Passwordless — which all have distinct verification events — recovery codes have no such event, so they were permanently stuck in `NotReady`. # How the Problems Are Solved - Introduced a dedicated `reduceAddRecoveryCodes` reducer for `HumanRecoveryCodesAddedEvent` that sets `MFAStateReady` immediately, since recovery codes are usable as soon as they are generated. - Uses `NewUpsertStatement` (instead of `NewCreateStatement`) so that regenerating recovery codes for the same user updates the existing row rather than failing on a duplicate key conflict. - Removed the `HumanRecoveryCodesAddedEvent` case from `reduceInitAuthMethod`. # Additional Changes - Added `UserAuthMethodTypeRecoveryCode` mapping in `authMethodTypeToPb` unit test. - Added integration tests for `ListAuthenticationMethodTypes` covering both the "recovery codes present" and "recovery codes removed" cases. # Additional Context * https://github.com/zitadel/zitadel/issues/11317 --------- Co-authored-by: Marco A. <[email protected]> Co-authored-by: Cursor <[email protected]>
This commit is contained in:
co-authored by
Marco A.
Cursor
parent
f99142bc4a
commit
ec7a6d5526
@@ -131,7 +131,7 @@ func (p *userAuthMethodProjection) Reducers() []handler.AggregateReducer {
|
||||
},
|
||||
{
|
||||
Event: user.HumanRecoveryCodesAddedType,
|
||||
Reduce: p.reduceInitAuthMethod,
|
||||
Reduce: p.reduceAddRecoveryCodes,
|
||||
},
|
||||
{
|
||||
Event: user.HumanRecoveryCodesRemovedType,
|
||||
@@ -175,10 +175,8 @@ func (p *userAuthMethodProjection) reduceInitAuthMethod(event eventstore.Event)
|
||||
rpID = &e.RPID
|
||||
case *user.HumanOTPAddedEvent:
|
||||
methodType = domain.UserAuthMethodTypeTOTP
|
||||
case *user.HumanRecoveryCodesAddedEvent:
|
||||
methodType = domain.UserAuthMethodTypeRecoveryCode
|
||||
default:
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "PROJE-f92f", "reduce.wrong.event.type %v", []eventstore.EventType{user.HumanPasswordlessTokenAddedType, user.HumanU2FTokenAddedType, user.HumanMFAOTPAddedType, user.HumanRecoveryCodesAddedType})
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "PROJE-f92f", "reduce.wrong.event.type %v", []eventstore.EventType{user.HumanPasswordlessTokenAddedType, user.HumanU2FTokenAddedType, user.HumanMFAOTPAddedType})
|
||||
}
|
||||
cols := []handler.Column{
|
||||
handler.NewCol(UserAuthMethodTokenIDCol, tokenID),
|
||||
@@ -273,6 +271,33 @@ func (p *userAuthMethodProjection) reduceAddAuthMethod(event eventstore.Event) (
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *userAuthMethodProjection) reduceAddRecoveryCodes(event eventstore.Event) (*handler.Statement, error) {
|
||||
if _, ok := event.(*user.HumanRecoveryCodesAddedEvent); !ok {
|
||||
return nil, zerrors.ThrowInvalidArgumentf(nil, "PROJE-x93hq", "reduce.wrong.event.type %v", []eventstore.EventType{user.HumanRecoveryCodesAddedType})
|
||||
}
|
||||
return handler.NewUpsertStatement(
|
||||
event,
|
||||
[]handler.Column{
|
||||
handler.NewCol(UserAuthMethodInstanceIDCol, nil),
|
||||
handler.NewCol(UserAuthMethodUserIDCol, nil),
|
||||
handler.NewCol(UserAuthMethodTypeCol, nil),
|
||||
handler.NewCol(UserAuthMethodTokenIDCol, nil),
|
||||
},
|
||||
[]handler.Column{
|
||||
handler.NewCol(UserAuthMethodTokenIDCol, ""),
|
||||
handler.NewCol(UserAuthMethodCreationDateCol, handler.OnlySetValueOnInsert(UserAuthMethodTable, event.CreatedAt())),
|
||||
handler.NewCol(UserAuthMethodChangeDateCol, event.CreatedAt()),
|
||||
handler.NewCol(UserAuthMethodResourceOwnerCol, event.Aggregate().ResourceOwner),
|
||||
handler.NewCol(UserAuthMethodInstanceIDCol, event.Aggregate().InstanceID),
|
||||
handler.NewCol(UserAuthMethodUserIDCol, event.Aggregate().ID),
|
||||
handler.NewCol(UserAuthMethodSequenceCol, event.Sequence()),
|
||||
handler.NewCol(UserAuthMethodStateCol, domain.MFAStateReady),
|
||||
handler.NewCol(UserAuthMethodTypeCol, domain.UserAuthMethodTypeRecoveryCode),
|
||||
handler.NewCol(UserAuthMethodNameCol, ""),
|
||||
},
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *userAuthMethodProjection) reduceRemoveAuthMethod(event eventstore.Event) (*handler.Statement, error) {
|
||||
var tokenID string
|
||||
var methodType domain.UserAuthMethodType
|
||||
|
||||
Reference in New Issue
Block a user