Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: implement register Passkey user API v2 (#5873)
* command/crypto: DRY the code - reuse the the algorithm switch to create a secret generator - add a verifyCryptoCode function * command: crypto code tests * migrate webauthn package * finish integration tests with webauthn mock client
- Loading branch information
Showing
44 changed files
with
2,528 additions
and
517 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package authz | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/zitadel/zitadel/internal/errors" | ||
) | ||
|
||
// UserIDInCTX checks if the userID | ||
// equals the authenticated user in the context. | ||
func UserIDInCTX(ctx context.Context, userID string) error { | ||
if GetCtxData(ctx).UserID != userID { | ||
return errors.ThrowUnauthenticated(nil, "AUTH-Bohd2", "Errors.User.UserIDWrong") | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package grpc | ||
|
||
import ( | ||
"testing" | ||
|
||
"google.golang.org/protobuf/reflect/protoreflect" | ||
) | ||
|
||
// AllFieldsSet recusively checks if all values in a message | ||
// have a non-zero value. | ||
func AllFieldsSet(t testing.TB, msg protoreflect.Message, ignoreTypes ...protoreflect.FullName) { | ||
ignore := make(map[protoreflect.FullName]bool, len(ignoreTypes)) | ||
for _, name := range ignoreTypes { | ||
ignore[name] = true | ||
} | ||
|
||
md := msg.Descriptor() | ||
name := md.FullName() | ||
if ignore[name] { | ||
return | ||
} | ||
|
||
fields := md.Fields() | ||
|
||
for i := 0; i < fields.Len(); i++ { | ||
fd := fields.Get(i) | ||
if !msg.Has(fd) { | ||
t.Errorf("not all fields set in %q, missing %q", name, fd.Name()) | ||
continue | ||
} | ||
|
||
if fd.Kind() == protoreflect.MessageKind { | ||
AllFieldsSet(t, msg.Get(fd).Message(), ignoreTypes...) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
package user | ||
|
||
import ( | ||
"context" | ||
|
||
"github.com/zitadel/zitadel/internal/api/authz" | ||
"github.com/zitadel/zitadel/internal/api/grpc/object/v2" | ||
"github.com/zitadel/zitadel/internal/domain" | ||
caos_errs "github.com/zitadel/zitadel/internal/errors" | ||
user "github.com/zitadel/zitadel/pkg/grpc/user/v2alpha" | ||
) | ||
|
||
func (s *Server) RegisterPasskey(ctx context.Context, req *user.RegisterPasskeyRequest) (resp *user.RegisterPasskeyResponse, err error) { | ||
var ( | ||
resourceOwner = authz.GetCtxData(ctx).ResourceOwner | ||
authenticator = passkeyAuthenticatorToDomain(req.GetAuthenticator()) | ||
) | ||
if code := req.GetCode(); code != nil { | ||
return passkeyRegistrationDetailsToPb( | ||
s.command.RegisterUserPasskeyWithCode(ctx, req.GetUserId(), resourceOwner, authenticator, code.Id, code.Code, s.userCodeAlg), | ||
) | ||
} | ||
return passkeyRegistrationDetailsToPb( | ||
s.command.RegisterUserPasskey(ctx, req.GetUserId(), resourceOwner, authenticator), | ||
) | ||
} | ||
|
||
func passkeyAuthenticatorToDomain(pa user.PasskeyAuthenticator) domain.AuthenticatorAttachment { | ||
switch pa { | ||
case user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_UNSPECIFIED: | ||
return domain.AuthenticatorAttachmentUnspecified | ||
case user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_PLATFORM: | ||
return domain.AuthenticatorAttachmentPlattform | ||
case user.PasskeyAuthenticator_PASSKEY_AUTHENTICATOR_CROSS_PLATFORM: | ||
return domain.AuthenticatorAttachmentCrossPlattform | ||
default: | ||
return domain.AuthenticatorAttachmentUnspecified | ||
} | ||
} | ||
|
||
func passkeyRegistrationDetailsToPb(details *domain.PasskeyRegistrationDetails, err error) (*user.RegisterPasskeyResponse, error) { | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &user.RegisterPasskeyResponse{ | ||
Details: object.DomainToDetailsPb(details.ObjectDetails), | ||
PasskeyId: details.PasskeyID, | ||
PublicKeyCredentialCreationOptions: details.PublicKeyCredentialCreationOptions, | ||
}, nil | ||
} | ||
|
||
func (s *Server) VerifyPasskeyRegistration(ctx context.Context, req *user.VerifyPasskeyRegistrationRequest) (*user.VerifyPasskeyRegistrationResponse, error) { | ||
resourceOwner := authz.GetCtxData(ctx).ResourceOwner | ||
objectDetails, err := s.command.HumanHumanPasswordlessSetup(ctx, req.GetUserId(), resourceOwner, req.GetPasskeyName(), "", req.GetPublicKeyCredential()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &user.VerifyPasskeyRegistrationResponse{ | ||
Details: object.DomainToDetailsPb(objectDetails), | ||
}, nil | ||
} | ||
|
||
func (s *Server) CreatePasskeyRegistrationLink(ctx context.Context, req *user.CreatePasskeyRegistrationLinkRequest) (resp *user.CreatePasskeyRegistrationLinkResponse, err error) { | ||
resourceOwner := authz.GetCtxData(ctx).ResourceOwner | ||
|
||
switch medium := req.Medium.(type) { | ||
case nil: | ||
return passkeyDetailsToPb( | ||
s.command.AddUserPasskeyCode(ctx, req.GetUserId(), resourceOwner, s.userCodeAlg), | ||
) | ||
case *user.CreatePasskeyRegistrationLinkRequest_SendLink: | ||
return passkeyDetailsToPb( | ||
s.command.AddUserPasskeyCodeURLTemplate(ctx, req.GetUserId(), resourceOwner, s.userCodeAlg, medium.SendLink.GetUrlTemplate()), | ||
) | ||
case *user.CreatePasskeyRegistrationLinkRequest_ReturnCode: | ||
return passkeyCodeDetailsToPb( | ||
s.command.AddUserPasskeyCodeReturn(ctx, req.GetUserId(), resourceOwner, s.userCodeAlg), | ||
) | ||
default: | ||
return nil, caos_errs.ThrowUnimplementedf(nil, "USERv2-gaD8y", "verification oneOf %T in method CreatePasskeyRegistrationLink not implemented", medium) | ||
} | ||
} | ||
|
||
func passkeyDetailsToPb(details *domain.ObjectDetails, err error) (*user.CreatePasskeyRegistrationLinkResponse, error) { | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &user.CreatePasskeyRegistrationLinkResponse{ | ||
Details: object.DomainToDetailsPb(details), | ||
}, nil | ||
} | ||
|
||
func passkeyCodeDetailsToPb(details *domain.PasskeyCodeDetails, err error) (*user.CreatePasskeyRegistrationLinkResponse, error) { | ||
if err != nil { | ||
return nil, err | ||
} | ||
return &user.CreatePasskeyRegistrationLinkResponse{ | ||
Details: object.DomainToDetailsPb(details.ObjectDetails), | ||
Code: &user.PasskeyRegistrationCode{ | ||
Id: details.CodeID, | ||
Code: details.Code, | ||
}, | ||
}, nil | ||
} |
Oops, something went wrong.
a301c40
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
docs – ./
docs-git-main-zitadel.vercel.app
zitadel-docs.vercel.app
docs-zitadel.vercel.app