mirror of
https://github.com/zitadel/zitadel.git
synced 2025-03-04 12:55:14 +00:00

* at least registration prompt works * in memory test for login * buttons to start webauthn process * begin eventstore impl * begin eventstore impl * serialize into bytes * fix: u2f, passwordless types * fix for localhost * fix script * fix: u2f, passwordless types * fix: add u2f * fix: verify u2f * fix: session data in event store * fix: u2f credentials in eventstore * fix: webauthn pkg handles business models * feat: tests * feat: append events * fix: test * fix: check only ready webauthn creds * fix: move u2f methods to authrepo * frontend improvements * fix return * feat: add passwordless * feat: add passwordless * improve ui / error handling * separate call for login * fix login * js * feat: u2f login methods * feat: remove unused session id * feat: error handling * feat: error handling * feat: refactor user eventstore * feat: finish webauthn * feat: u2f and passwordlss in auth.proto * u2f step * passwordless step * cleanup js * EndpointPasswordLessLogin * migration * update mfaChecked test * next step test * token name * cleanup * attribute * passwordless as tokens * remove sms as otp type * add "user" to amr for webauthn * error handling * fixes * fix tests * naming * naming * fixes * session handler * i18n * error handling in login * Update internal/ui/login/static/i18n/de.yaml Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * Update internal/ui/login/static/i18n/en.yaml Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> * improvements * merge fixes * fixes * fixes Co-authored-by: Fabiennne <fabienne.gerschwiler@gmail.com> Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
159 lines
5.3 KiB
Go
159 lines
5.3 KiB
Go
package webauthn
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
|
|
"github.com/duo-labs/webauthn/protocol"
|
|
"github.com/duo-labs/webauthn/webauthn"
|
|
|
|
caos_errs "github.com/caos/zitadel/internal/errors"
|
|
usr_model "github.com/caos/zitadel/internal/user/model"
|
|
)
|
|
|
|
type WebAuthN struct {
|
|
web *webauthn.WebAuthn
|
|
}
|
|
|
|
func StartServer(displayName, id, origin string) (*WebAuthN, error) {
|
|
web, err := webauthn.New(&webauthn.Config{
|
|
RPDisplayName: displayName,
|
|
RPID: id,
|
|
RPOrigin: origin,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &WebAuthN{
|
|
web: web,
|
|
}, err
|
|
}
|
|
|
|
type webUser struct {
|
|
*usr_model.User
|
|
credentials []webauthn.Credential
|
|
}
|
|
|
|
func (u *webUser) WebAuthnID() []byte {
|
|
return []byte(u.AggregateID)
|
|
}
|
|
|
|
func (u *webUser) WebAuthnName() string {
|
|
return u.UserName
|
|
}
|
|
|
|
func (u *webUser) WebAuthnDisplayName() string {
|
|
return u.DisplayName
|
|
}
|
|
|
|
func (u *webUser) WebAuthnIcon() string {
|
|
return ""
|
|
}
|
|
|
|
func (u *webUser) WebAuthnCredentials() []webauthn.Credential {
|
|
return u.credentials
|
|
}
|
|
|
|
func (w *WebAuthN) BeginRegistration(user *usr_model.User, authType usr_model.AuthenticatorAttachment, userVerification usr_model.UserVerificationRequirement, webAuthNs ...*usr_model.WebAuthNToken) (*usr_model.WebAuthNToken, error) {
|
|
creds := WebAuthNsToCredentials(webAuthNs)
|
|
existing := make([]protocol.CredentialDescriptor, len(creds))
|
|
for i, cred := range creds {
|
|
existing[i] = protocol.CredentialDescriptor{
|
|
Type: protocol.PublicKeyCredentialType,
|
|
CredentialID: cred.ID,
|
|
}
|
|
}
|
|
credentialOptions, sessionData, err := w.web.BeginRegistration(
|
|
&webUser{
|
|
User: user,
|
|
credentials: creds,
|
|
},
|
|
webauthn.WithAuthenticatorSelection(protocol.AuthenticatorSelection{
|
|
UserVerification: UserVerificationFromModel(userVerification),
|
|
AuthenticatorAttachment: AuthenticatorAttachmentFromModel(authType),
|
|
}),
|
|
webauthn.WithConveyancePreference(protocol.PreferNoAttestation),
|
|
webauthn.WithExclusions(existing),
|
|
)
|
|
if err != nil {
|
|
return nil, caos_errs.ThrowInternal(err, "WEBAU-bM8sd", "Errors.User.WebAuthN.BeginRegisterFailed")
|
|
}
|
|
cred, err := json.Marshal(credentialOptions)
|
|
if err != nil {
|
|
return nil, caos_errs.ThrowInternal(err, "WEBAU-D7cus", "Errors.User.WebAuthN.MarshalError")
|
|
}
|
|
return &usr_model.WebAuthNToken{
|
|
Challenge: sessionData.Challenge,
|
|
CredentialCreationData: cred,
|
|
AllowedCredentialIDs: sessionData.AllowedCredentialIDs,
|
|
UserVerification: UserVerificationToModel(sessionData.UserVerification),
|
|
}, nil
|
|
}
|
|
|
|
func (w *WebAuthN) FinishRegistration(user *usr_model.User, webAuthN *usr_model.WebAuthNToken, tokenName string, credData []byte) (*usr_model.WebAuthNToken, error) {
|
|
if webAuthN == nil {
|
|
return nil, caos_errs.ThrowInternal(nil, "WEBAU-5M9so", "Errors.User.WebAuthN.NotFound")
|
|
}
|
|
credentialData, err := protocol.ParseCredentialCreationResponseBody(bytes.NewReader(credData))
|
|
if err != nil {
|
|
return nil, caos_errs.ThrowInternal(err, "WEBAU-sEr8c", "Errors.User.WebAuthN.ErrorOnParseCredential")
|
|
}
|
|
sessionData := WebAuthNToSessionData(webAuthN)
|
|
credential, err := w.web.CreateCredential(
|
|
&webUser{
|
|
User: user,
|
|
},
|
|
sessionData, credentialData)
|
|
if err != nil {
|
|
return nil, caos_errs.ThrowInternal(err, "WEBAU-3Vb9s", "Errors.User.WebAuthN.CreateCredentialFailed")
|
|
}
|
|
|
|
webAuthN.KeyID = credential.ID
|
|
webAuthN.PublicKey = credential.PublicKey
|
|
webAuthN.AttestationType = credential.AttestationType
|
|
webAuthN.AAGUID = credential.Authenticator.AAGUID
|
|
webAuthN.SignCount = credential.Authenticator.SignCount
|
|
webAuthN.WebAuthNTokenName = tokenName
|
|
return webAuthN, nil
|
|
}
|
|
|
|
func (w *WebAuthN) BeginLogin(user *usr_model.User, userVerification usr_model.UserVerificationRequirement, webAuthNs ...*usr_model.WebAuthNToken) (*usr_model.WebAuthNLogin, error) {
|
|
assertion, sessionData, err := w.web.BeginLogin(&webUser{
|
|
User: user,
|
|
credentials: WebAuthNsToCredentials(webAuthNs),
|
|
}, webauthn.WithUserVerification(UserVerificationFromModel(userVerification)))
|
|
if err != nil {
|
|
return nil, caos_errs.ThrowInternal(err, "WEBAU-4G8sw", "Errors.User.WebAuthN.BeginLoginFailed")
|
|
}
|
|
cred, err := json.Marshal(assertion)
|
|
if err != nil {
|
|
return nil, caos_errs.ThrowInternal(err, "WEBAU-2M0s9", "Errors.User.WebAuthN.MarshalError")
|
|
}
|
|
return &usr_model.WebAuthNLogin{
|
|
Challenge: sessionData.Challenge,
|
|
CredentialAssertionData: cred,
|
|
AllowedCredentialIDs: sessionData.AllowedCredentialIDs,
|
|
UserVerification: userVerification,
|
|
}, nil
|
|
}
|
|
|
|
func (w *WebAuthN) FinishLogin(user *usr_model.User, webAuthN *usr_model.WebAuthNLogin, credData []byte, webAuthNs ...*usr_model.WebAuthNToken) ([]byte, uint32, error) {
|
|
assertionData, err := protocol.ParseCredentialRequestResponseBody(bytes.NewReader(credData))
|
|
if err != nil {
|
|
return nil, 0, caos_errs.ThrowInternal(err, "WEBAU-ADgv4", "Errors.User.WebAuthN.ValidateLoginFailed")
|
|
}
|
|
webUser := &webUser{
|
|
User: user,
|
|
credentials: WebAuthNsToCredentials(webAuthNs),
|
|
}
|
|
credential, err := w.web.ValidateLogin(webUser, WebAuthNLoginToSessionData(webAuthN), assertionData)
|
|
if err != nil {
|
|
return nil, 0, caos_errs.ThrowInternal(err, "WEBAU-3M9si", "Errors.User.WebAuthN.ValidateLoginFailed")
|
|
}
|
|
|
|
if credential.Authenticator.CloneWarning {
|
|
return credential.ID, credential.Authenticator.SignCount, caos_errs.ThrowInternal(err, "WEBAU-4M90s", "Errors.User.WebAuthN.CloneWarning")
|
|
}
|
|
return credential.ID, credential.Authenticator.SignCount, nil
|
|
}
|