mirror of
https://github.com/zitadel/zitadel.git
synced 2026-07-25 18:28:00 +00:00
# Which Problems Are Solved As part of #11035 , this PR implements the Passkey check logic for session validation # How the Problems Are Solved - Refactor webauth FinishLogin to support new domain model - Add webauth config to defaults - Implement passkey check logic and tests - Manual transaction management to avoid stalling the DB while FinishLogin callback is executed - Update passkey Type condition to allow passing a text operation (equal, contains, etc..) # Additional Context This is a cherry-picked PR + minor changes, coming from https://github.com/zitadel/zitadel/pull/11164 - Relates to #11035 --------- Co-authored-by: Fabienne Bühler <fabienne@zitadel.com> Co-authored-by: Gayathri Vijayan <66356931+grvijayan@users.noreply.github.com>
91 lines
3.5 KiB
Go
91 lines
3.5 KiB
Go
package command
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
|
|
"github.com/zitadel/zitadel/internal/domain"
|
|
"github.com/zitadel/zitadel/internal/eventstore"
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
|
)
|
|
|
|
type humanWebAuthNTokens struct {
|
|
human *domain.Human
|
|
tokens []*domain.WebAuthNToken
|
|
}
|
|
|
|
func (s *SessionCommands) getHumanWebAuthNTokens(ctx context.Context, userVerification domain.UserVerificationRequirement) (*humanWebAuthNTokens, error) {
|
|
humanWritemodel, err := s.gethumanWriteModel(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
tokenReadModel, err := s.getHumanWebAuthNTokenReadModel(ctx, userVerification)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &humanWebAuthNTokens{
|
|
human: writeModelToHuman(humanWritemodel),
|
|
tokens: readModelToWebAuthNTokens(tokenReadModel),
|
|
}, nil
|
|
}
|
|
|
|
func (s *SessionCommands) getHumanWebAuthNTokenReadModel(ctx context.Context, userVerification domain.UserVerificationRequirement) (readModel HumanWebAuthNTokensReadModel, err error) {
|
|
readModel = NewHumanU2FTokensReadModel(s.sessionWriteModel.UserID, s.sessionWriteModel.UserResourceOwner)
|
|
if userVerification == domain.UserVerificationRequirementRequired {
|
|
readModel = NewHumanPasswordlessTokensReadModel(s.sessionWriteModel.UserID, s.sessionWriteModel.UserResourceOwner)
|
|
}
|
|
err = s.eventstore.FilterToQueryReducer(ctx, readModel)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return readModel, nil
|
|
}
|
|
|
|
func (c *Commands) CreateWebAuthNChallenge(userVerification domain.UserVerificationRequirement, rpid string, dst json.Unmarshaler) SessionCommand {
|
|
return func(ctx context.Context, cmd *SessionCommands) ([]eventstore.Command, error) {
|
|
humanPasskeys, err := cmd.getHumanWebAuthNTokens(ctx, userVerification)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
webAuthNLogin, err := c.webauthnConfig.BeginLogin(ctx, humanPasskeys.human, userVerification, rpid, humanPasskeys.tokens...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err = json.Unmarshal(webAuthNLogin.CredentialAssertionData, dst); err != nil {
|
|
return nil, zerrors.ThrowInternal(err, "COMMAND-Yah6A", "Errors.Internal")
|
|
}
|
|
|
|
cmd.WebAuthNChallenged(ctx, webAuthNLogin.Challenge, webAuthNLogin.AllowedCredentialIDs, webAuthNLogin.UserVerification, rpid)
|
|
return nil, nil
|
|
}
|
|
}
|
|
|
|
func (c *Commands) CheckWebAuthN(credentialAssertionData json.Marshaler) SessionCommand {
|
|
return func(ctx context.Context, cmd *SessionCommands) ([]eventstore.Command, error) {
|
|
credentialAssertionData, err := json.Marshal(credentialAssertionData)
|
|
if err != nil {
|
|
return nil, zerrors.ThrowInternal(err, "COMMAND-ohG2o", "Errors.Internal")
|
|
}
|
|
challenge := cmd.sessionWriteModel.WebAuthNChallenge
|
|
if challenge == nil {
|
|
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Ioqu5", "Errors.Session.WebAuthN.NoChallenge")
|
|
}
|
|
webAuthNTokens, err := cmd.getHumanWebAuthNTokens(ctx, challenge.UserVerification)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
webAuthN := challenge.WebAuthNLogin(webAuthNTokens.human, credentialAssertionData)
|
|
|
|
credential, err := c.webauthnConfig.FinishLogin(ctx, webAuthNTokens.human, webAuthN, credentialAssertionData, webAuthNTokens.tokens...)
|
|
if err != nil && (credential == nil || credential.ID == nil) {
|
|
return nil, err
|
|
}
|
|
token := domain.GetTokenByKeyID(webAuthNTokens.tokens, credential.ID)
|
|
if token == nil {
|
|
return nil, zerrors.ThrowPreconditionFailed(nil, "COMMAND-Aej7i", "Errors.User.WebAuthN.NotFound")
|
|
}
|
|
cmd.WebAuthNChecked(ctx, cmd.now(), token.WebAuthNTokenID, credential.Authenticator.SignCount, credential.Flags.UserVerified)
|
|
return nil, nil
|
|
}
|
|
}
|