mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-12 02:54:20 +00:00
fix: improvements for WebAuthN (#1105)
* add missing translations * add missing passwordless funcs in api * remove u2f with verification from setup in login
This commit is contained in:
parent
7463bf4fe0
commit
71df1bcd0e
@ -2,6 +2,7 @@ package auth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/golang/protobuf/ptypes/empty"
|
"github.com/golang/protobuf/ptypes/empty"
|
||||||
|
|
||||||
"github.com/caos/zitadel/pkg/grpc/auth"
|
"github.com/caos/zitadel/pkg/grpc/auth"
|
||||||
@ -175,6 +176,14 @@ func (s *Server) RemoveMyMfaU2F(ctx context.Context, id *auth.WebAuthNTokenID) (
|
|||||||
return &empty.Empty{}, err
|
return &empty.Empty{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) GetMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNTokens, err error) {
|
||||||
|
tokens, err := s.repo.GetMyPasswordless(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return webAuthNTokensFromModel(tokens), err
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) AddMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNResponse, err error) {
|
func (s *Server) AddMyPasswordless(ctx context.Context, _ *empty.Empty) (_ *auth.WebAuthNResponse, err error) {
|
||||||
u2f, err := s.repo.AddMyPasswordless(ctx)
|
u2f, err := s.repo.AddMyPasswordless(ctx)
|
||||||
return verifyWebAuthNFromModel(u2f), err
|
return verifyWebAuthNFromModel(u2f), err
|
||||||
|
@ -436,3 +436,19 @@ func verifyWebAuthNFromModel(u2f *usr_model.WebAuthNToken) *auth.WebAuthNRespons
|
|||||||
State: mfaStateFromModel(u2f.State),
|
State: mfaStateFromModel(u2f.State),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func webAuthNTokensFromModel(tokens []*usr_model.WebAuthNToken) *auth.WebAuthNTokens {
|
||||||
|
result := make([]*auth.WebAuthNToken, len(tokens))
|
||||||
|
for i, token := range tokens {
|
||||||
|
result[i] = webAuthNTokenFromModel(token)
|
||||||
|
}
|
||||||
|
return &auth.WebAuthNTokens{Tokens: result}
|
||||||
|
}
|
||||||
|
|
||||||
|
func webAuthNTokenFromModel(token *usr_model.WebAuthNToken) *auth.WebAuthNToken {
|
||||||
|
return &auth.WebAuthNToken{
|
||||||
|
Id: token.WebAuthNTokenID,
|
||||||
|
Name: token.WebAuthNTokenName,
|
||||||
|
State: mfaStateFromModel(token.State),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,9 +2,11 @@ package management
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/ptypes/empty"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/api/authz"
|
"github.com/caos/zitadel/internal/api/authz"
|
||||||
"github.com/caos/zitadel/pkg/grpc/management"
|
"github.com/caos/zitadel/pkg/grpc/management"
|
||||||
"github.com/golang/protobuf/ptypes/empty"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) GetUserByID(ctx context.Context, id *management.UserID) (*management.UserView, error) {
|
func (s *Server) GetUserByID(ctx context.Context, id *management.UserID) (*management.UserView, error) {
|
||||||
@ -231,6 +233,19 @@ func (s *Server) RemoveMfaU2F(ctx context.Context, webAuthNTokenID *management.W
|
|||||||
return &empty.Empty{}, err
|
return &empty.Empty{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) GetPasswordless(ctx context.Context, userID *management.UserID) (_ *management.WebAuthNTokens, err error) {
|
||||||
|
tokens, err := s.user.GetPasswordless(ctx, userID.Id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return webAuthNTokensFromModel(tokens), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) RemovePasswordless(ctx context.Context, id *management.WebAuthNTokenID) (*empty.Empty, error) {
|
||||||
|
err := s.user.RemovePasswordless(ctx, id.UserId, id.Id)
|
||||||
|
return &empty.Empty{}, err
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) SearchUserMemberships(ctx context.Context, in *management.UserMembershipSearchRequest) (*management.UserMembershipSearchResponse, error) {
|
func (s *Server) SearchUserMemberships(ctx context.Context, in *management.UserMembershipSearchRequest) (*management.UserMembershipSearchResponse, error) {
|
||||||
request := userMembershipSearchRequestsToModel(in)
|
request := userMembershipSearchRequestsToModel(in)
|
||||||
request.AppendUserIDQuery(in.UserId)
|
request.AppendUserIDQuery(in.UserId)
|
||||||
|
@ -629,3 +629,19 @@ func userChangesToMgtAPI(changes *usr_model.UserChanges) (_ []*management.Change
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func webAuthNTokensFromModel(tokens []*usr_model.WebAuthNToken) *management.WebAuthNTokens {
|
||||||
|
result := make([]*management.WebAuthNToken, len(tokens))
|
||||||
|
for i, token := range tokens {
|
||||||
|
result[i] = webAuthNTokenFromModel(token)
|
||||||
|
}
|
||||||
|
return &management.WebAuthNTokens{Tokens: result}
|
||||||
|
}
|
||||||
|
|
||||||
|
func webAuthNTokenFromModel(token *usr_model.WebAuthNToken) *management.WebAuthNToken {
|
||||||
|
return &management.WebAuthNToken{
|
||||||
|
Id: token.WebAuthNTokenID,
|
||||||
|
Name: token.WebAuthNTokenName,
|
||||||
|
State: mfaStateFromModel(token.State),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -326,10 +326,18 @@ func (repo *UserRepo) RemoveMyMFAU2F(ctx context.Context, webAuthNTokenID string
|
|||||||
return repo.UserEvents.RemoveU2FToken(ctx, authz.GetCtxData(ctx).UserID, webAuthNTokenID)
|
return repo.UserEvents.RemoveU2FToken(ctx, authz.GetCtxData(ctx).UserID, webAuthNTokenID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) GetPasswordless(ctx context.Context, userID string) ([]*model.WebAuthNToken, error) {
|
||||||
|
return repo.UserEvents.GetPasswordless(ctx, userID)
|
||||||
|
}
|
||||||
|
|
||||||
func (repo *UserRepo) AddPasswordless(ctx context.Context, userID string) (*model.WebAuthNToken, error) {
|
func (repo *UserRepo) AddPasswordless(ctx context.Context, userID string) (*model.WebAuthNToken, error) {
|
||||||
return repo.UserEvents.AddPasswordless(ctx, userID, true)
|
return repo.UserEvents.AddPasswordless(ctx, userID, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) GetMyPasswordless(ctx context.Context) ([]*model.WebAuthNToken, error) {
|
||||||
|
return repo.UserEvents.GetPasswordless(ctx, authz.GetCtxData(ctx).UserID)
|
||||||
|
}
|
||||||
|
|
||||||
func (repo *UserRepo) AddMyPasswordless(ctx context.Context) (*model.WebAuthNToken, error) {
|
func (repo *UserRepo) AddMyPasswordless(ctx context.Context) (*model.WebAuthNToken, error) {
|
||||||
return repo.UserEvents.AddPasswordless(ctx, authz.GetCtxData(ctx).UserID, false)
|
return repo.UserEvents.AddPasswordless(ctx, authz.GetCtxData(ctx).UserID, false)
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ type UserRepository interface {
|
|||||||
VerifyMFAU2FSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error
|
VerifyMFAU2FSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error
|
||||||
RemoveMFAU2F(ctx context.Context, userID, webAuthNTokenID string) error
|
RemoveMFAU2F(ctx context.Context, userID, webAuthNTokenID string) error
|
||||||
|
|
||||||
|
GetPasswordless(ctx context.Context, id string) ([]*model.WebAuthNToken, error)
|
||||||
AddPasswordless(ctx context.Context, id string) (*model.WebAuthNToken, error)
|
AddPasswordless(ctx context.Context, id string) (*model.WebAuthNToken, error)
|
||||||
VerifyPasswordlessSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error
|
VerifyPasswordlessSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error
|
||||||
RemovePasswordless(ctx context.Context, userID, webAuthNTokenID string) error
|
RemovePasswordless(ctx context.Context, userID, webAuthNTokenID string) error
|
||||||
@ -80,6 +81,7 @@ type myUserRepo interface {
|
|||||||
VerifyMyMFAU2FSetup(ctx context.Context, tokenName string, data []byte) error
|
VerifyMyMFAU2FSetup(ctx context.Context, tokenName string, data []byte) error
|
||||||
RemoveMyMFAU2F(ctx context.Context, webAuthNTokenID string) error
|
RemoveMyMFAU2F(ctx context.Context, webAuthNTokenID string) error
|
||||||
|
|
||||||
|
GetMyPasswordless(ctx context.Context) ([]*model.WebAuthNToken, error)
|
||||||
AddMyPasswordless(ctx context.Context) (*model.WebAuthNToken, error)
|
AddMyPasswordless(ctx context.Context) (*model.WebAuthNToken, error)
|
||||||
VerifyMyPasswordlessSetup(ctx context.Context, tokenName string, data []byte) error
|
VerifyMyPasswordlessSetup(ctx context.Context, tokenName string, data []byte) error
|
||||||
RemoveMyPasswordless(ctx context.Context, webAuthNTokenID string) error
|
RemoveMyPasswordless(ctx context.Context, webAuthNTokenID string) error
|
||||||
|
@ -235,6 +235,14 @@ func (repo *UserRepo) RemoveU2F(ctx context.Context, userID, webAuthNTokenID str
|
|||||||
return repo.UserEvents.RemoveU2FToken(ctx, userID, webAuthNTokenID)
|
return repo.UserEvents.RemoveU2FToken(ctx, userID, webAuthNTokenID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) GetPasswordless(ctx context.Context, userID string) ([]*usr_model.WebAuthNToken, error) {
|
||||||
|
return repo.UserEvents.GetPasswordless(ctx, userID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (repo *UserRepo) RemovePasswordless(ctx context.Context, userID, webAuthNTokenID string) error {
|
||||||
|
return repo.UserEvents.RemovePasswordlessToken(ctx, userID, webAuthNTokenID)
|
||||||
|
}
|
||||||
|
|
||||||
func (repo *UserRepo) SetOneTimePassword(ctx context.Context, password *usr_model.Password) (*usr_model.Password, error) {
|
func (repo *UserRepo) SetOneTimePassword(ctx context.Context, password *usr_model.Password) (*usr_model.Password, error) {
|
||||||
policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
|
policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID)
|
||||||
if err != nil && caos_errs.IsNotFound(err) {
|
if err != nil && caos_errs.IsNotFound(err) {
|
||||||
|
@ -34,6 +34,9 @@ type UserRepository interface {
|
|||||||
RemoveOTP(ctx context.Context, userID string) error
|
RemoveOTP(ctx context.Context, userID string) error
|
||||||
RemoveU2F(ctx context.Context, userID, webAuthNTokenID string) error
|
RemoveU2F(ctx context.Context, userID, webAuthNTokenID string) error
|
||||||
|
|
||||||
|
GetPasswordless(ctx context.Context, userID string) ([]*model.WebAuthNToken, error)
|
||||||
|
RemovePasswordless(ctx context.Context, userID, webAuthNTokenID string) error
|
||||||
|
|
||||||
SearchExternalIDPs(ctx context.Context, request *model.ExternalIDPSearchRequest) (*model.ExternalIDPSearchResponse, error)
|
SearchExternalIDPs(ctx context.Context, request *model.ExternalIDPSearchRequest) (*model.ExternalIDPSearchResponse, error)
|
||||||
RemoveExternalIDP(ctx context.Context, externalIDP *model.ExternalIDP) error
|
RemoveExternalIDP(ctx context.Context, externalIDP *model.ExternalIDP) error
|
||||||
|
|
||||||
|
@ -446,9 +446,16 @@ EventTypes:
|
|||||||
login:
|
login:
|
||||||
added: Login Richtlinie hinzugefügt
|
added: Login Richtlinie hinzugefügt
|
||||||
changed: Login Richtlinie geändert
|
changed: Login Richtlinie geändert
|
||||||
|
removed: Login Richtline gelöscht
|
||||||
idpprovider:
|
idpprovider:
|
||||||
added: Idp Provider zu Login Richtlinie hinzugefügt
|
added: Idp Provider zu Login Richtlinie hinzugefügt
|
||||||
removed: Idp Provider aus Login Richtlinie gelöscht
|
removed: Idp Provider aus Login Richtlinie gelöscht
|
||||||
|
secondfactor:
|
||||||
|
added: Zweitfaktor zu Login Richtlinie hinzugefügt
|
||||||
|
removed: Zweitfaktor aus Login Richtlinie gelöscht
|
||||||
|
multifactor:
|
||||||
|
added: Multifaktor zu Login Richtlinie hinzugefügt
|
||||||
|
removed: Multifaktor aus Login Richtlinie gelöscht
|
||||||
password:
|
password:
|
||||||
complexity:
|
complexity:
|
||||||
added: Passwort Komplexitäts Richtlinie hinzugefügt
|
added: Passwort Komplexitäts Richtlinie hinzugefügt
|
||||||
|
@ -446,9 +446,16 @@ EventTypes:
|
|||||||
login:
|
login:
|
||||||
added: Login Policy added
|
added: Login Policy added
|
||||||
changed: Login Policy changed
|
changed: Login Policy changed
|
||||||
|
removed: Login Policy removed
|
||||||
idpprovider:
|
idpprovider:
|
||||||
added: Idp Provider added to Login Policy
|
added: Idp Provider added to Login Policy
|
||||||
removed: Idp Provider removed from Login Policy
|
removed: Idp Provider removed from Login Policy
|
||||||
|
secondfactor:
|
||||||
|
added: Second factor added to Login Policy
|
||||||
|
removed: Second factor removed from Login Policy
|
||||||
|
multifactor:
|
||||||
|
added: Multi factor added to Login Policy
|
||||||
|
removed: Multi factor removed from Login Policy
|
||||||
password:
|
password:
|
||||||
complexity:
|
complexity:
|
||||||
added: Password complexity policy added
|
added: Password complexity policy added
|
||||||
|
@ -136,16 +136,6 @@ func (u *UserView) MFATypesSetupPossible(level req_model.MFALevel, policy *iam_m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
//PLANNED: add sms
|
//PLANNED: add sms
|
||||||
fallthrough
|
|
||||||
case req_model.MFALevelMultiFactor:
|
|
||||||
if policy.HasMultiFactors() {
|
|
||||||
for _, mfaType := range policy.MultiFactors {
|
|
||||||
switch mfaType {
|
|
||||||
case iam_model.MultiFactorTypeU2FWithPIN:
|
|
||||||
types = append(types, req_model.MFATypeU2FUserVerification)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return types
|
return types
|
||||||
}
|
}
|
||||||
|
@ -1410,6 +1410,14 @@ func (es *UserEventstore) VerifyMFAU2F(ctx context.Context, userID string, crede
|
|||||||
return finishErr
|
return finishErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (es *UserEventstore) GetPasswordless(ctx context.Context, userID string) ([]*usr_model.WebAuthNToken, error) {
|
||||||
|
user, err := es.HumanByID(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return user.PasswordlessTokens, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (es *UserEventstore) AddPasswordless(ctx context.Context, userID string, isLoginUI bool) (*usr_model.WebAuthNToken, error) {
|
func (es *UserEventstore) AddPasswordless(ctx context.Context, userID string, isLoginUI bool) (*usr_model.WebAuthNToken, error) {
|
||||||
user, err := es.HumanByID(ctx, userID)
|
user, err := es.HumanByID(ctx, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -337,6 +337,15 @@ service AuthService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpc GetMyPasswordless(google.protobuf.Empty) returns (WebAuthNTokens) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
get: "/users/me/passwordless"
|
||||||
|
};
|
||||||
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
|
permission: "authenticated"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
rpc AddMyPasswordless(google.protobuf.Empty) returns (WebAuthNResponse) {
|
rpc AddMyPasswordless(google.protobuf.Empty) returns (WebAuthNResponse) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
post: "/users/me/passwordless"
|
post: "/users/me/passwordless"
|
||||||
@ -666,6 +675,16 @@ message MfaOtpResponse {
|
|||||||
MFAState state = 4;
|
MFAState state = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message WebAuthNTokens {
|
||||||
|
repeated WebAuthNToken tokens = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WebAuthNToken {
|
||||||
|
string id = 1;
|
||||||
|
string name = 2;
|
||||||
|
MFAState state = 3;
|
||||||
|
}
|
||||||
|
|
||||||
message WebAuthNResponse {
|
message WebAuthNResponse {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
bytes public_key = 2;
|
bytes public_key = 2;
|
||||||
|
@ -409,6 +409,26 @@ service ManagementService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpc GetPasswordless(UserID) returns (WebAuthNTokens) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
get: "/users/{id}/passwordless"
|
||||||
|
};
|
||||||
|
|
||||||
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
|
permission: "user.read"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc RemovePasswordless(WebAuthNTokenID) returns (google.protobuf.Empty) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
delete: "/users/{user_id}/passwordless"
|
||||||
|
};
|
||||||
|
|
||||||
|
option (caos.zitadel.utils.v1.auth_option) = {
|
||||||
|
permission: "user.write"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Sends an Notification (Email/SMS) with a password reset Link
|
// Sends an Notification (Email/SMS) with a password reset Link
|
||||||
rpc SendSetPasswordNotification(SetPasswordNotificationRequest) returns (google.protobuf.Empty) {
|
rpc SendSetPasswordNotification(SetPasswordNotificationRequest) returns (google.protobuf.Empty) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
@ -1656,6 +1676,16 @@ message UserID {
|
|||||||
string id = 1 [(validate.rules).string.min_len = 1];
|
string id = 1 [(validate.rules).string.min_len = 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message WebAuthNTokens {
|
||||||
|
repeated WebAuthNToken tokens = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WebAuthNToken {
|
||||||
|
string id = 1;
|
||||||
|
string name = 2;
|
||||||
|
MFAState state = 3;
|
||||||
|
}
|
||||||
|
|
||||||
message WebAuthNTokenID {
|
message WebAuthNTokenID {
|
||||||
string user_id = 1 [(validate.rules).string.min_len = 1];
|
string user_id = 1 [(validate.rules).string.min_len = 1];
|
||||||
string id = 2 [(validate.rules).string.min_len = 1];
|
string id = 2 [(validate.rules).string.min_len = 1];
|
||||||
@ -3097,6 +3127,7 @@ enum PasswordlessType {
|
|||||||
PASSWORDLESSTYPE_ALLOWED = 1;
|
PASSWORDLESSTYPE_ALLOWED = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
message IdpProviderID {
|
message IdpProviderID {
|
||||||
string idp_config_id = 1 [(validate.rules).string = {min_len: 1}];
|
string idp_config_id = 1 [(validate.rules).string = {min_len: 1}];
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user