diff --git a/api/proto/teleport/legacy/types/webauthn/webauthn.proto b/api/proto/teleport/legacy/types/webauthn/webauthn.proto index b14ae49764993..8b3f0c4a8dfe0 100644 --- a/api/proto/teleport/legacy/types/webauthn/webauthn.proto +++ b/api/proto/teleport/legacy/types/webauthn/webauthn.proto @@ -33,7 +33,7 @@ package webauthn; import "gogoproto/gogo.proto"; -option go_package = "github.com/gravitational/teleport/api/types/webauthn"; +option go_package = "github.com/gravitational/teleport/api/types/webauthn;webauthnpb"; option (gogoproto.marshaler_all) = true; option (gogoproto.unmarshaler_all) = true; @@ -256,8 +256,9 @@ message CredentialParameter { message RelyingPartyEntity { string id = 1; string name = 2; - // URL to the icon of the Relying Party. - string icon = 3; + + reserved 3; // string icon + reserved "icon"; } // User information. @@ -273,6 +274,7 @@ message UserEntity { // Human-palatable name for the user account, intended only for display. // The Relying Party _should_ let the user choose this value. string display_name = 3; - // URL to a resource which can be the avatar image for the user. - string icon = 4; + + reserved 4; // string icon + reserved "icon"; } diff --git a/api/types/webauthn/webauthn.pb.go b/api/types/webauthn/webauthn.pb.go index a976ad09805b5..35fdb48a7605a 100644 --- a/api/types/webauthn/webauthn.pb.go +++ b/api/types/webauthn/webauthn.pb.go @@ -17,7 +17,7 @@ // case for _any_ of the fields mapped here, all bytes fields are transmitted // raw/unencoded. -package webauthn +package webauthnpb import ( fmt "fmt" @@ -1093,10 +1093,8 @@ func (m *CredentialParameter) GetAlg() int32 { // See https://www.w3.org/TR/webauthn-2/#dictdef-publickeycredentialrpentity and // https://developer.mozilla.org/en-US/docs/Web/API/PublicKeyCredentialCreationOptions/rp. type RelyingPartyEntity struct { - Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - // URL to the icon of the Relying Party. - Icon string `protobuf:"bytes,3,opt,name=icon,proto3" json:"icon,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1149,13 +1147,6 @@ func (m *RelyingPartyEntity) GetName() string { return "" } -func (m *RelyingPartyEntity) GetIcon() string { - if m != nil { - return m.Icon - } - return "" -} - // User information. // See https://www.w3.org/TR/webauthn-2/#dictdef-publickeycredentialuserentity // and @@ -1168,9 +1159,7 @@ type UserEntity struct { Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // Human-palatable name for the user account, intended only for display. // The Relying Party _should_ let the user choose this value. - DisplayName string `protobuf:"bytes,3,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` - // URL to a resource which can be the avatar image for the user. - Icon string `protobuf:"bytes,4,opt,name=icon,proto3" json:"icon,omitempty"` + DisplayName string `protobuf:"bytes,3,opt,name=display_name,json=displayName,proto3" json:"display_name,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -1230,13 +1219,6 @@ func (m *UserEntity) GetDisplayName() string { return "" } -func (m *UserEntity) GetIcon() string { - if m != nil { - return m.Icon - } - return "" -} - func init() { proto.RegisterType((*SessionData)(nil), "webauthn.SessionData") proto.RegisterType((*User)(nil), "webauthn.User") @@ -1262,74 +1244,75 @@ func init() { } var fileDescriptor_0d490a6db28e8798 = []byte{ - // 1062 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0x4b, 0x6f, 0x1c, 0x45, - 0x10, 0xd6, 0xec, 0xc3, 0xf1, 0xd6, 0x2e, 0x91, 0xd3, 0x5e, 0x27, 0x8b, 0x71, 0xd6, 0x9b, 0x01, - 0xa4, 0x15, 0x7e, 0x2c, 0x32, 0x70, 0xe0, 0x29, 0xf9, 0x11, 0x84, 0x63, 0x88, 0xad, 0x89, 0x40, - 0x82, 0xcb, 0xa8, 0x3d, 0x53, 0xec, 0x76, 0x98, 0x9d, 0x99, 0x74, 0xf7, 0xc4, 0x59, 0xf1, 0x93, - 0xb8, 0x71, 0xe2, 0xc6, 0x15, 0x89, 0x0b, 0xbf, 0xc0, 0x44, 0x3e, 0xfa, 0x57, 0xa0, 0xee, 0x79, - 0xee, 0xee, 0x38, 0x36, 0x20, 0xe5, 0xd6, 0x53, 0x55, 0x5f, 0x75, 0x77, 0x7d, 0xf5, 0xd5, 0x34, - 0x6c, 0x49, 0xf4, 0x30, 0x0c, 0xb8, 0x1c, 0x78, 0x38, 0xa4, 0xce, 0x64, 0x20, 0x27, 0x21, 0x8a, - 0xc1, 0x19, 0x9e, 0xd2, 0x48, 0x8e, 0xfc, 0x6c, 0xb1, 0x1d, 0xf2, 0x40, 0x06, 0x64, 0x31, 0xfd, - 0x5e, 0x6d, 0x0f, 0x83, 0x61, 0xa0, 0x8d, 0x03, 0xb5, 0x8a, 0xfd, 0xe6, 0x9f, 0x15, 0x68, 0x3e, - 0x41, 0x21, 0x58, 0xe0, 0x1f, 0x50, 0x49, 0xc9, 0x47, 0xd0, 0x70, 0x46, 0xd4, 0xf3, 0xd0, 0x1f, - 0x62, 0xc7, 0xe8, 0x19, 0xfd, 0xd6, 0xde, 0xbd, 0xcb, 0xf3, 0xf5, 0xe5, 0xcc, 0xb8, 0x19, 0x8c, - 0x99, 0xc4, 0x71, 0x28, 0x27, 0x56, 0x1e, 0x49, 0xb6, 0xe0, 0x56, 0x24, 0x90, 0xdb, 0xcc, 0xed, - 0x54, 0x34, 0xa8, 0x7d, 0x79, 0xbe, 0xbe, 0xa4, 0x4c, 0x87, 0x6e, 0x01, 0xb1, 0x10, 0x5b, 0xc8, - 0x11, 0xdc, 0xa1, 0x9e, 0x17, 0x9c, 0xd9, 0x0e, 0x47, 0x17, 0x7d, 0xc9, 0xa8, 0x27, 0x3a, 0xd5, - 0x5e, 0xb5, 0xdf, 0xda, 0xeb, 0x5e, 0x9e, 0xaf, 0xaf, 0x6a, 0xe7, 0x7e, 0xee, 0x2b, 0xa4, 0x58, - 0x9a, 0xf5, 0x91, 0xcf, 0xa0, 0xc5, 0x51, 0x30, 0xf5, 0x6d, 0xff, 0x84, 0x93, 0x4e, 0xad, 0x67, - 0xf4, 0x17, 0xf7, 0xde, 0xbc, 0x3c, 0x5f, 0x5f, 0x49, 0xed, 0x47, 0x38, 0x29, 0xa4, 0x68, 0x16, - 0xcc, 0xea, 0x28, 0xfa, 0xe4, 0xcf, 0x91, 0xb3, 0x1f, 0x99, 0x43, 0x25, 0x0b, 0xfc, 0x4e, 0xbd, - 0x67, 0xf4, 0x1b, 0xf1, 0x51, 0x94, 0xf3, 0xbb, 0x82, 0xaf, 0x78, 0x94, 0x59, 0x9f, 0xb9, 0x01, - 0xb5, 0x6f, 0x05, 0x72, 0xf2, 0x36, 0xbc, 0x91, 0xd2, 0x64, 0xab, 0x20, 0x5d, 0xc9, 0x86, 0xd5, - 0x4a, 0x8d, 0x2a, 0xc8, 0xa4, 0xb0, 0x9c, 0x5f, 0x63, 0x57, 0x08, 0xe4, 0x2a, 0x07, 0x79, 0x04, - 0x10, 0x46, 0xa7, 0x1e, 0x73, 0xf4, 0x65, 0x14, 0xb0, 0xb9, 0xb3, 0xb1, 0x9d, 0xd1, 0x7a, 0xa2, - 0x7d, 0x47, 0x38, 0xc9, 0xb1, 0x16, 0x3e, 0x8b, 0x50, 0xc8, 0xe3, 0x50, 0xe1, 0x85, 0xd5, 0x08, - 0xd3, 0x10, 0xf3, 0xf7, 0x0a, 0x3c, 0xb8, 0x16, 0x40, 0xd6, 0xe6, 0x38, 0x2f, 0x52, 0x7b, 0x1f, - 0x40, 0xb2, 0x31, 0x06, 0x91, 0xb4, 0xc7, 0x42, 0xb3, 0x5b, 0xb5, 0x1a, 0x89, 0xe5, 0x1b, 0x41, - 0x96, 0xa1, 0xce, 0x43, 0xc5, 0x7b, 0x55, 0x5f, 0xb1, 0xc6, 0xc3, 0xab, 0xf8, 0xad, 0xf5, 0xaa, - 0xfd, 0xe6, 0x4e, 0x37, 0xbf, 0x4a, 0x7e, 0xa0, 0x03, 0x14, 0x0e, 0x67, 0xa1, 0x0c, 0x78, 0x09, - 0xbf, 0x8f, 0x01, 0xf0, 0x85, 0x44, 0x5f, 0xf5, 0xa8, 0xd0, 0xd4, 0x34, 0x77, 0xb6, 0xf3, 0x2c, - 0xbb, 0x91, 0x1c, 0xa9, 0xd0, 0x98, 0x82, 0x87, 0x59, 0xe4, 0xbe, 0xc7, 0xd0, 0x97, 0x87, 0x7e, - 0x18, 0x49, 0x61, 0x15, 0x32, 0x90, 0x8d, 0x32, 0xc6, 0x17, 0xf4, 0xe9, 0xe7, 0x19, 0xfd, 0xdb, - 0x80, 0xb7, 0x4a, 0x58, 0xb2, 0x50, 0x84, 0x81, 0x2f, 0x90, 0x10, 0xa8, 0x29, 0x01, 0x26, 0x04, - 0xeb, 0x35, 0x59, 0x81, 0x05, 0x4e, 0xcf, 0x32, 0x2d, 0x58, 0x75, 0x4e, 0xcf, 0x0e, 0x5d, 0x72, - 0x00, 0x8b, 0x3c, 0x81, 0xe9, 0x62, 0x35, 0x77, 0xfa, 0xa5, 0xb7, 0x08, 0xf8, 0xdc, 0x36, 0x56, - 0x86, 0x24, 0xc7, 0x53, 0xd5, 0xa8, 0xe9, 0x3c, 0x83, 0x9b, 0x56, 0xe3, 0x38, 0x92, 0xb3, 0xe5, - 0x30, 0x7f, 0x33, 0xa0, 0xfb, 0xea, 0xdd, 0x49, 0x1f, 0x96, 0x1c, 0x8d, 0xb7, 0x5d, 0x2a, 0xa9, - 0xfd, 0x54, 0x04, 0x7e, 0xd2, 0x27, 0xb7, 0x63, 0xbb, 0x1a, 0x1d, 0x8f, 0x44, 0xe0, 0x93, 0x2d, - 0x20, 0xb4, 0x98, 0x4b, 0x03, 0x92, 0x32, 0xdc, 0x99, 0xf2, 0xe8, 0x69, 0xb3, 0x06, 0x0d, 0xc1, - 0x86, 0x3e, 0x95, 0x11, 0x8f, 0x6b, 0xd2, 0xb2, 0x72, 0x03, 0x59, 0x87, 0xa6, 0x26, 0x6a, 0x44, - 0x7d, 0xd7, 0x43, 0x7d, 0xd7, 0x96, 0x05, 0xca, 0xf4, 0x95, 0xb6, 0x98, 0x14, 0x48, 0xce, 0xcd, - 0x3e, 0x47, 0x7d, 0x67, 0x72, 0x54, 0x22, 0xa0, 0xcd, 0x57, 0x0a, 0x28, 0x85, 0x96, 0x28, 0xe8, - 0x97, 0x1a, 0x98, 0xd7, 0x23, 0xae, 0x91, 0xd0, 0x26, 0x54, 0x78, 0xa8, 0xab, 0xd0, 0xdc, 0x59, - 0xcb, 0x4f, 0x62, 0xa1, 0x37, 0x61, 0xfe, 0xf0, 0x84, 0x72, 0x39, 0x79, 0xe8, 0x4b, 0x26, 0x27, - 0x56, 0x85, 0x87, 0xa4, 0x0f, 0x35, 0x3d, 0x33, 0xe2, 0x1e, 0x69, 0xe7, 0xf1, 0x6a, 0x6a, 0x24, - 0x71, 0x3a, 0x82, 0x58, 0xb0, 0x92, 0x0b, 0xcc, 0x0e, 0x29, 0xa7, 0x63, 0x94, 0xc8, 0x53, 0xa9, - 0xdd, 0x2f, 0x93, 0xda, 0x49, 0x1a, 0x65, 0xb5, 0x9d, 0x79, 0xa3, 0x98, 0x91, 0x7b, 0x7d, 0x56, - 0xee, 0xc7, 0xb0, 0x8c, 0x2f, 0x1c, 0x2f, 0x72, 0x71, 0x4a, 0xdb, 0x0b, 0x37, 0xd2, 0x36, 0x49, - 0xa0, 0x45, 0x75, 0xf7, 0xa0, 0x49, 0xa5, 0x44, 0x21, 0x63, 0x1d, 0xde, 0xd2, 0x3a, 0x2a, 0x9a, - 0x66, 0xf4, 0xbf, 0xf8, 0xbf, 0xf5, 0xff, 0x3d, 0xdc, 0x9b, 0xee, 0x51, 0x81, 0x1e, 0x3a, 0x7a, - 0xf7, 0x86, 0x4e, 0xde, 0xbb, 0x42, 0x96, 0x4f, 0xd2, 0x38, 0xeb, 0x2e, 0x2d, 0xb5, 0x9b, 0x2f, - 0x0d, 0x58, 0x9d, 0x6f, 0x92, 0xff, 0x32, 0x2c, 0xbe, 0x9c, 0x1b, 0x16, 0xef, 0x5d, 0x35, 0x2c, - 0xf2, 0x52, 0xbd, 0x8e, 0x71, 0xf1, 0x33, 0xf4, 0xae, 0xdb, 0xfe, 0x5f, 0xce, 0x8b, 0x3c, 0x81, - 0x1d, 0x9c, 0x3e, 0x45, 0x47, 0x66, 0xf3, 0x22, 0xf7, 0x1c, 0x6b, 0x87, 0xf9, 0x39, 0xbc, 0x73, - 0x13, 0xba, 0x55, 0x51, 0x69, 0xa8, 0xff, 0x4a, 0x71, 0xa9, 0xeb, 0x34, 0x0c, 0x0f, 0x5d, 0xf3, - 0x0b, 0x78, 0xf7, 0x46, 0x17, 0x9e, 0xc1, 0x2f, 0xa6, 0xf8, 0x5f, 0x0d, 0xb8, 0x5b, 0xde, 0x11, - 0xe4, 0x63, 0xe8, 0x4c, 0x37, 0x15, 0x95, 0x92, 0x3a, 0xa3, 0x31, 0xfa, 0x32, 0x39, 0xc3, 0x74, - 0xd3, 0xed, 0x66, 0x6e, 0xf2, 0x3e, 0xb4, 0x39, 0x3e, 0x8b, 0x18, 0x47, 0x7b, 0xea, 0x1d, 0x53, - 0xd1, 0x5b, 0x93, 0xc4, 0x67, 0x15, 0xde, 0x2c, 0xa5, 0x7f, 0xb0, 0xea, 0x15, 0x7f, 0xb0, 0x4f, - 0xa0, 0x5d, 0x26, 0xc6, 0xd2, 0x66, 0xbc, 0x0d, 0x95, 0xac, 0x11, 0x2b, 0xcc, 0x35, 0x3f, 0x2d, - 0x3e, 0x51, 0xb2, 0x21, 0x51, 0x0a, 0x5d, 0x82, 0x2a, 0xf5, 0x86, 0x1a, 0x5b, 0xb7, 0xd4, 0xd2, - 0xfc, 0x1a, 0xc8, 0xfc, 0x84, 0x4b, 0xb6, 0x88, 0x91, 0x15, 0xe6, 0xaa, 0x5c, 0x3e, 0x1d, 0xa3, - 0x06, 0x36, 0x2c, 0xbd, 0x56, 0x36, 0xe6, 0x64, 0x57, 0xd2, 0x6b, 0x73, 0x08, 0x90, 0xcf, 0xbf, - 0x42, 0x96, 0xd6, 0x95, 0x59, 0x1e, 0x40, 0xcb, 0x65, 0x22, 0xf4, 0xe8, 0xc4, 0xd6, 0xbe, 0x38, - 0x5b, 0x33, 0xb1, 0x3d, 0x2e, 0x6e, 0x54, 0xcb, 0x37, 0xda, 0xdb, 0xfb, 0xe3, 0xa2, 0x6b, 0xfc, - 0x75, 0xd1, 0x35, 0x5e, 0x5e, 0x74, 0x8d, 0x1f, 0x3e, 0x1c, 0x32, 0x39, 0x8a, 0x4e, 0xb7, 0x9d, - 0x60, 0x3c, 0x18, 0x72, 0xfa, 0x9c, 0xc5, 0xfd, 0x48, 0xbd, 0x41, 0xf6, 0x0e, 0xa7, 0x21, 0x9b, - 0x79, 0x84, 0x9f, 0x2e, 0xe8, 0xc7, 0xf5, 0x07, 0xff, 0x04, 0x00, 0x00, 0xff, 0xff, 0x8f, 0xff, - 0x0d, 0xa1, 0xad, 0x0b, 0x00, 0x00, + // 1073 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x56, 0x5b, 0x6f, 0x1b, 0x45, + 0x14, 0xd6, 0xda, 0xeb, 0xd4, 0x3e, 0x36, 0x95, 0x3b, 0x71, 0x5b, 0x53, 0x5a, 0xc7, 0x5d, 0x40, + 0xb2, 0xc8, 0xc5, 0x28, 0x88, 0x07, 0x28, 0x17, 0xe5, 0x52, 0x44, 0x12, 0xb5, 0x89, 0xb6, 0x02, + 0x09, 0x5e, 0x56, 0xe3, 0xdd, 0x83, 0x3d, 0x65, 0xbd, 0xbb, 0x9d, 0x99, 0x6d, 0x6a, 0xf1, 0x93, + 0x78, 0xe3, 0x89, 0x37, 0x5e, 0x91, 0x78, 0xe1, 0x17, 0x84, 0x2a, 0x8f, 0xf9, 0x15, 0x68, 0x67, + 0xaf, 0xb6, 0x37, 0x4d, 0x00, 0x89, 0xb7, 0xd9, 0x73, 0xce, 0x77, 0x66, 0xe6, 0x7c, 0xe7, 0x3b, + 0x3b, 0xb0, 0x29, 0xd1, 0xc5, 0xc0, 0xe7, 0x72, 0xe8, 0xe2, 0x98, 0xda, 0xb3, 0xa1, 0x9c, 0x05, + 0x28, 0x86, 0xa7, 0x38, 0xa2, 0xa1, 0x9c, 0x78, 0xd9, 0x62, 0x2b, 0xe0, 0xbe, 0xf4, 0x49, 0x3d, + 0xfd, 0xbe, 0xd7, 0x19, 0xfb, 0x63, 0x5f, 0x19, 0x87, 0xd1, 0x2a, 0xf6, 0x1b, 0x7f, 0x54, 0xa0, + 0xf9, 0x0c, 0x85, 0x60, 0xbe, 0xb7, 0x4f, 0x25, 0x25, 0x1f, 0x43, 0xc3, 0x9e, 0x50, 0xd7, 0x45, + 0x6f, 0x8c, 0x5d, 0xad, 0xaf, 0x0d, 0x5a, 0xbb, 0x77, 0x2f, 0xce, 0xd6, 0x56, 0x33, 0xe3, 0x86, + 0x3f, 0x65, 0x12, 0xa7, 0x81, 0x9c, 0x99, 0x79, 0x24, 0xd9, 0x84, 0x1b, 0xa1, 0x40, 0x6e, 0x31, + 0xa7, 0x5b, 0x51, 0xa0, 0xce, 0xc5, 0xd9, 0x5a, 0x3b, 0x32, 0x1d, 0x38, 0x05, 0xc4, 0x4a, 0x6c, + 0x21, 0x47, 0x70, 0x8b, 0xba, 0xae, 0x7f, 0x6a, 0xd9, 0x1c, 0x1d, 0xf4, 0x24, 0xa3, 0xae, 0xe8, + 0x56, 0xfb, 0xd5, 0x41, 0x6b, 0xb7, 0x77, 0x71, 0xb6, 0x76, 0x4f, 0x39, 0xf7, 0x72, 0x5f, 0x21, + 0x45, 0x7b, 0xd1, 0x47, 0x3e, 0x83, 0x16, 0x47, 0xc1, 0xa2, 0x6f, 0xeb, 0x47, 0x9c, 0x75, 0xf5, + 0xbe, 0x36, 0xa8, 0xef, 0xbe, 0x7d, 0x71, 0xb6, 0x76, 0x3b, 0xb5, 0x1f, 0xe1, 0xac, 0x90, 0xa2, + 0x59, 0x30, 0x47, 0x47, 0x51, 0x27, 0x7f, 0x89, 0x9c, 0xfd, 0xc0, 0x6c, 0x2a, 0x99, 0xef, 0x75, + 0x6b, 0x7d, 0x6d, 0xd0, 0x88, 0x8f, 0x12, 0x39, 0xbf, 0x2d, 0xf8, 0x8a, 0x47, 0x59, 0xf4, 0x19, + 0xeb, 0xa0, 0x7f, 0x23, 0x90, 0x93, 0x77, 0xe1, 0xad, 0x94, 0x26, 0x2b, 0x0a, 0x52, 0x95, 0x6c, + 0x98, 0xad, 0xd4, 0x18, 0x05, 0x19, 0x14, 0x56, 0xf3, 0x6b, 0xec, 0x08, 0x81, 0x3c, 0xca, 0x41, + 0x0e, 0x01, 0x82, 0x70, 0xe4, 0x32, 0x5b, 0x5d, 0x26, 0x02, 0x36, 0xb7, 0xd7, 0xb7, 0x32, 0x5a, + 0x4f, 0x94, 0xef, 0x08, 0x67, 0x39, 0xd6, 0xc4, 0x17, 0x21, 0x0a, 0x79, 0x1c, 0x44, 0x78, 0x61, + 0x36, 0x82, 0x34, 0xc4, 0xf8, 0xad, 0x02, 0x0f, 0xaf, 0x04, 0x90, 0xfb, 0x4b, 0x9c, 0x17, 0xa9, + 0x7d, 0x00, 0x20, 0xd9, 0x14, 0xfd, 0x50, 0x5a, 0x53, 0xa1, 0xd8, 0xad, 0x9a, 0x8d, 0xc4, 0xf2, + 0x44, 0x90, 0x55, 0xa8, 0xf1, 0x20, 0xe2, 0xbd, 0xaa, 0xae, 0xa8, 0xf3, 0xe0, 0x32, 0x7e, 0xf5, + 0x7e, 0x75, 0xd0, 0xdc, 0xee, 0xe5, 0x57, 0xc9, 0x0f, 0xb4, 0x8f, 0xc2, 0xe6, 0x2c, 0x90, 0x3e, + 0x2f, 0xe1, 0xf7, 0x29, 0x00, 0xbe, 0x92, 0xe8, 0x45, 0x3d, 0x2a, 0x14, 0x35, 0xcd, 0xed, 0xad, + 0x3c, 0xcb, 0x4e, 0x28, 0x27, 0x51, 0x68, 0x4c, 0xc1, 0xe3, 0x2c, 0x72, 0xcf, 0x65, 0xe8, 0xc9, + 0x03, 0x2f, 0x08, 0xa5, 0x30, 0x0b, 0x19, 0xc8, 0x7a, 0x19, 0xe3, 0x2b, 0xea, 0xf4, 0xcb, 0x8c, + 0xfe, 0xa5, 0xc1, 0x3b, 0x25, 0x2c, 0x99, 0x28, 0x02, 0xdf, 0x13, 0x48, 0x08, 0xe8, 0x91, 0x00, + 0x13, 0x82, 0xd5, 0x9a, 0xdc, 0x86, 0x15, 0x4e, 0x4f, 0x33, 0x2d, 0x98, 0x35, 0x4e, 0x4f, 0x0f, + 0x1c, 0xb2, 0x0f, 0x75, 0x9e, 0xc0, 0x54, 0xb1, 0x9a, 0xdb, 0x83, 0xd2, 0x5b, 0xf8, 0x7c, 0x69, + 0x1b, 0x33, 0x43, 0x92, 0xe3, 0xb9, 0x6a, 0xe8, 0x2a, 0xcf, 0xf0, 0xba, 0xd5, 0x38, 0x0e, 0xe5, + 0x62, 0x39, 0x8c, 0x5f, 0x35, 0xe8, 0xbd, 0x79, 0x77, 0x32, 0x80, 0xb6, 0xad, 0xf0, 0x96, 0x43, + 0x25, 0xb5, 0x9e, 0x0b, 0xdf, 0x4b, 0xfa, 0xe4, 0x66, 0x6c, 0x8f, 0x46, 0xc7, 0xa1, 0xf0, 0x3d, + 0xb2, 0x09, 0x84, 0x16, 0x73, 0x29, 0x40, 0x52, 0x86, 0x5b, 0x73, 0x1e, 0x35, 0x6d, 0xee, 0x43, + 0x43, 0xb0, 0xb1, 0x47, 0x65, 0xc8, 0xe3, 0x9a, 0xb4, 0xcc, 0xdc, 0x40, 0xd6, 0xa0, 0xa9, 0x88, + 0x9a, 0x50, 0xcf, 0x71, 0x51, 0xdd, 0xb5, 0x65, 0x42, 0x64, 0xfa, 0x5a, 0x59, 0x0c, 0x0a, 0x24, + 0xe7, 0x66, 0x8f, 0xa3, 0xba, 0x33, 0x39, 0x2a, 0x11, 0xd0, 0xc6, 0x1b, 0x05, 0x94, 0x42, 0x4b, + 0x14, 0xf4, 0xb3, 0x0e, 0xc6, 0xd5, 0x88, 0x2b, 0x24, 0xb4, 0x01, 0x15, 0x1e, 0xa8, 0x2a, 0x34, + 0xb7, 0xef, 0xe7, 0x27, 0x31, 0xd1, 0x9d, 0x31, 0x6f, 0x7c, 0x42, 0xb9, 0x9c, 0x3d, 0xf6, 0x24, + 0x93, 0x33, 0xb3, 0xc2, 0x03, 0x32, 0x00, 0x5d, 0xcd, 0x8c, 0xb8, 0x47, 0x3a, 0x79, 0x7c, 0x34, + 0x35, 0x92, 0x38, 0x15, 0x41, 0x4c, 0xb8, 0x9d, 0x0b, 0xcc, 0x0a, 0x28, 0xa7, 0x53, 0x94, 0xc8, + 0x53, 0xa9, 0x3d, 0x28, 0x93, 0xda, 0x49, 0x1a, 0x65, 0x76, 0xec, 0x65, 0xa3, 0x58, 0x90, 0x7b, + 0x6d, 0x51, 0xee, 0xc7, 0xb0, 0x8a, 0xaf, 0x6c, 0x37, 0x74, 0x70, 0x4e, 0xdb, 0x2b, 0xd7, 0xd2, + 0x36, 0x49, 0xa0, 0x45, 0x75, 0xf7, 0xa1, 0x49, 0xa5, 0x44, 0x21, 0x63, 0x1d, 0xde, 0x50, 0x3a, + 0x2a, 0x9a, 0x16, 0xf4, 0x5f, 0xff, 0xcf, 0xfa, 0xff, 0x0e, 0xee, 0xce, 0xf7, 0xa8, 0x40, 0x17, + 0x6d, 0xb5, 0x7b, 0x43, 0x25, 0xef, 0x5f, 0x22, 0xcb, 0x67, 0x69, 0x9c, 0x79, 0x87, 0x96, 0xda, + 0x8d, 0xd7, 0x1a, 0xdc, 0x5b, 0x6e, 0x92, 0x7f, 0x33, 0x2c, 0xbe, 0x5a, 0x1a, 0x16, 0x1f, 0x5c, + 0x36, 0x2c, 0xf2, 0x52, 0xfd, 0x1f, 0xe3, 0xe2, 0x27, 0xe8, 0x5f, 0xb5, 0xfd, 0x3f, 0x9c, 0x17, + 0x79, 0x02, 0xcb, 0x1f, 0x3d, 0x47, 0x5b, 0x66, 0xf3, 0x22, 0xf7, 0x1c, 0x2b, 0x87, 0xf1, 0x39, + 0xbc, 0x77, 0x1d, 0xba, 0xa3, 0xa2, 0xd2, 0x40, 0xfd, 0x95, 0xe2, 0x52, 0xd7, 0x68, 0x10, 0x1c, + 0x38, 0xc6, 0x17, 0xf0, 0xfe, 0xb5, 0x2e, 0xbc, 0x80, 0xaf, 0xa7, 0xf8, 0x5f, 0x34, 0xb8, 0x53, + 0xde, 0x11, 0xe4, 0x13, 0xe8, 0xce, 0x37, 0x15, 0x95, 0x92, 0xda, 0x93, 0x29, 0x7a, 0x32, 0x39, + 0xc3, 0x7c, 0xd3, 0xed, 0x64, 0x6e, 0xf2, 0x21, 0x74, 0x38, 0xbe, 0x08, 0x19, 0x47, 0x6b, 0xee, + 0x1d, 0x53, 0x51, 0x5b, 0x93, 0xc4, 0x67, 0x16, 0xde, 0x2c, 0xa5, 0x7f, 0xb0, 0xea, 0x25, 0x7f, + 0xb0, 0x4f, 0xa1, 0x53, 0x26, 0xc6, 0xd2, 0x66, 0xbc, 0x09, 0x95, 0xac, 0x11, 0x2b, 0xcc, 0x31, + 0x1e, 0x15, 0x9f, 0x28, 0xd9, 0x90, 0x28, 0x85, 0xb6, 0xa1, 0x4a, 0xdd, 0xb1, 0xc2, 0xd6, 0xcc, + 0x68, 0x69, 0xec, 0x03, 0x59, 0x9e, 0x70, 0xc9, 0x16, 0x31, 0xb2, 0xc2, 0x9c, 0x28, 0x97, 0x47, + 0xa7, 0xa8, 0x80, 0x0d, 0x53, 0xad, 0x0f, 0xf5, 0x7a, 0xb5, 0xad, 0x9b, 0x3a, 0xb3, 0x7d, 0xcf, + 0xb0, 0x00, 0xf2, 0xb9, 0x57, 0x40, 0xb7, 0x2e, 0x43, 0x93, 0x87, 0xd0, 0x72, 0x98, 0x08, 0x5c, + 0x3a, 0xb3, 0x94, 0x2f, 0x2e, 0x4c, 0x33, 0xb1, 0x3d, 0x8d, 0x37, 0xd0, 0xdb, 0xb5, 0x78, 0x83, + 0xdd, 0x27, 0xbf, 0x9f, 0xf7, 0xb4, 0x3f, 0xcf, 0x7b, 0xda, 0xeb, 0xf3, 0x9e, 0xf6, 0xfd, 0x97, + 0x63, 0x26, 0x27, 0xe1, 0x68, 0xcb, 0xf6, 0xa7, 0xc3, 0x31, 0xa7, 0x2f, 0x59, 0xdc, 0x7f, 0xd4, + 0x1d, 0x66, 0xef, 0x6e, 0x1a, 0xb0, 0x85, 0x47, 0xf7, 0xa3, 0x74, 0x11, 0x8c, 0x46, 0x2b, 0xea, + 0x5d, 0xfd, 0xd1, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x89, 0xb7, 0x8c, 0x49, 0xa8, 0x0b, 0x00, + 0x00, } func (m *SessionData) Marshal() (dAtA []byte, err error) { @@ -2164,13 +2147,6 @@ func (m *RelyingPartyEntity) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Icon) > 0 { - i -= len(m.Icon) - copy(dAtA[i:], m.Icon) - i = encodeVarintWebauthn(dAtA, i, uint64(len(m.Icon))) - i-- - dAtA[i] = 0x1a - } if len(m.Name) > 0 { i -= len(m.Name) copy(dAtA[i:], m.Name) @@ -2212,13 +2188,6 @@ func (m *UserEntity) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.Icon) > 0 { - i -= len(m.Icon) - copy(dAtA[i:], m.Icon) - i = encodeVarintWebauthn(dAtA, i, uint64(len(m.Icon))) - i-- - dAtA[i] = 0x22 - } if len(m.DisplayName) > 0 { i -= len(m.DisplayName) copy(dAtA[i:], m.DisplayName) @@ -2634,10 +2603,6 @@ func (m *RelyingPartyEntity) Size() (n int) { if l > 0 { n += 1 + l + sovWebauthn(uint64(l)) } - l = len(m.Icon) - if l > 0 { - n += 1 + l + sovWebauthn(uint64(l)) - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -2662,10 +2627,6 @@ func (m *UserEntity) Size() (n int) { if l > 0 { n += 1 + l + sovWebauthn(uint64(l)) } - l = len(m.Icon) - if l > 0 { - n += 1 + l + sovWebauthn(uint64(l)) - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5009,38 +4970,6 @@ func (m *RelyingPartyEntity) Unmarshal(dAtA []byte) error { } m.Name = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 3: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Icon", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowWebauthn - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthWebauthn - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthWebauthn - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Icon = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipWebauthn(dAtA[iNdEx:]) @@ -5190,38 +5119,6 @@ func (m *UserEntity) Unmarshal(dAtA []byte) error { } m.DisplayName = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 4: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Icon", wireType) - } - var stringLen uint64 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowWebauthn - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - stringLen |= uint64(b&0x7F) << shift - if b < 0x80 { - break - } - } - intStringLen := int(stringLen) - if intStringLen < 0 { - return ErrInvalidLengthWebauthn - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthWebauthn - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.Icon = string(dAtA[iNdEx:postIndex]) - iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipWebauthn(dAtA[iNdEx:]) diff --git a/lib/auth/accountrecovery_test.go b/lib/auth/accountrecovery_test.go index 4bd5a1e651958..3b3092abfaecf 100644 --- a/lib/auth/accountrecovery_test.go +++ b/lib/auth/accountrecovery_test.go @@ -34,7 +34,7 @@ import ( "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/api/types" apievents "github.com/gravitational/teleport/api/types/events" - wantypes "github.com/gravitational/teleport/api/types/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/events/eventstest" @@ -358,7 +358,7 @@ func TestVerifyAccountRecovery_WithAuthnErrors(t *testing.T) { AuthnCred: &proto.VerifyAccountRecoveryRequest_MFAAuthenticateResponse{ MFAAuthenticateResponse: &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: &wantypes.CredentialAssertionResponse{}, // invalid response + Webauthn: &wanpb.CredentialAssertionResponse{}, // invalid response }, }, }, @@ -873,7 +873,7 @@ func TestCompleteAccountRecovery_WithErrors(t *testing.T) { NewAuthnCred: &proto.CompleteAccountRecoveryRequest_NewMFAResponse{ NewMFAResponse: &proto.MFARegisterResponse{ Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: &wantypes.CredentialCreationResponse{}, + Webauthn: &wanpb.CredentialCreationResponse{}, }, }, }, diff --git a/lib/auth/auth.go b/lib/auth/auth.go index 7cd27cc271d41..0c1b99bcf9061 100644 --- a/lib/auth/auth.go +++ b/lib/auth/auth.go @@ -68,6 +68,7 @@ import ( "github.com/gravitational/teleport/lib/auth/keystore" "github.com/gravitational/teleport/lib/auth/native" wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/authz" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/circleci" @@ -2485,7 +2486,7 @@ func (a *Server) createRegisterChallenge(ctx context.Context, req *newRegisterCh } return &proto.MFARegisterChallenge{Request: &proto.MFARegisterChallenge_Webauthn{ - Webauthn: wanlib.CredentialCreationToProto(credentialCreation), + Webauthn: wantypes.CredentialCreationToProto(credentialCreation), }}, nil default: @@ -2794,7 +2795,7 @@ func (a *Server) registerWebauthnDevice(ctx context.Context, regResp *proto.MFAR dev, err := webRegistration.Finish(ctx, wanlib.RegisterResponse{ User: req.username, DeviceName: req.newDeviceName, - CreationResponse: wanlib.CredentialCreationResponseFromProto(regResp.GetWebauthn()), + CreationResponse: wantypes.CredentialCreationResponseFromProto(regResp.GetWebauthn()), Passwordless: req.deviceUsage == proto.DeviceUsage_DEVICE_USAGE_PASSWORDLESS, }) return dev, trace.Wrap(err) @@ -4579,7 +4580,7 @@ func (a *Server) mfaAuthChallenge(ctx context.Context, user string, passwordless return nil, trace.Wrap(err) } return &proto.MFAAuthenticateChallenge{ - WebauthnChallenge: wanlib.CredentialAssertionToProto(assertion), + WebauthnChallenge: wantypes.CredentialAssertionToProto(assertion), }, nil } @@ -4611,7 +4612,7 @@ func (a *Server) mfaAuthChallenge(ctx context.Context, user string, passwordless if err != nil { return nil, trace.Wrap(err) } - challenge.WebauthnChallenge = wanlib.CredentialAssertionToProto(assertion) + challenge.WebauthnChallenge = wantypes.CredentialAssertionToProto(assertion) } return challenge, nil @@ -4674,7 +4675,7 @@ func (a *Server) validateMFAAuthResponse( return nil, "", trace.Wrap(err) } - assertionResp := wanlib.CredentialAssertionResponseFromProto(res.Webauthn) + assertionResp := wantypes.CredentialAssertionResponseFromProto(res.Webauthn) var dev *types.MFADevice if passwordless { webLogin := &wanlib.PasswordlessFlow{ @@ -4688,7 +4689,7 @@ func (a *Server) validateMFAAuthResponse( Webauthn: webConfig, Identity: a.Services, } - dev, err = webLogin.Finish(ctx, user, wanlib.CredentialAssertionResponseFromProto(res.Webauthn)) + dev, err = webLogin.Finish(ctx, user, wantypes.CredentialAssertionResponseFromProto(res.Webauthn)) } if err != nil { return nil, "", trace.AccessDenied("MFA response validation failed: %v", err) diff --git a/lib/auth/auth_login_test.go b/lib/auth/auth_login_test.go index 5eb4bf7fa97ab..a5a67bd0d4698 100644 --- a/lib/auth/auth_login_test.go +++ b/lib/auth/auth_login_test.go @@ -28,7 +28,7 @@ import ( "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/auth/mocku2f" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/services" ) @@ -453,7 +453,7 @@ func TestServer_AuthenticateUser_mfaDevices(t *testing.T) { switch { case resp.GetWebauthn() != nil: - authReq.Webauthn = wanlib.CredentialAssertionResponseFromProto(resp.GetWebauthn()) + authReq.Webauthn = wantypes.CredentialAssertionResponseFromProto(resp.GetWebauthn()) case resp.GetTOTP() != nil: authReq.OTP = &OTPCreds{ Password: []byte(password), @@ -537,14 +537,14 @@ func TestServer_Authenticate_passwordless(t *testing.T) { require.NoError(t, err) pwdKey.SetPasswordless() const origin = "https://localhost" - ccr, err := pwdKey.SignCredentialCreation(origin, wanlib.CredentialCreationFromProto(registerChallenge.GetWebauthn())) + ccr, err := pwdKey.SignCredentialCreation(origin, wantypes.CredentialCreationFromProto(registerChallenge.GetWebauthn())) require.NoError(t, err) _, err = userClient.AddMFADeviceSync(ctx, &proto.AddMFADeviceSyncRequest{ TokenID: token.GetName(), NewDeviceName: "pwdless1", NewMFAResponse: &proto.MFARegisterResponse{ Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: wanlib.CredentialCreationResponseToProto(ccr), + Webauthn: wantypes.CredentialCreationResponseToProto(ccr), }, }, }) @@ -567,11 +567,11 @@ func TestServer_Authenticate_passwordless(t *testing.T) { tests := []struct { name string loginHooks []LoginHook - authenticate func(t *testing.T, resp *wanlib.CredentialAssertionResponse) + authenticate func(t *testing.T, resp *wantypes.CredentialAssertionResponse) }{ { name: "ssh", - authenticate: func(t *testing.T, resp *wanlib.CredentialAssertionResponse) { + authenticate: func(t *testing.T, resp *wantypes.CredentialAssertionResponse) { loginResp, err := proxyClient.AuthenticateSSHUser(ctx, AuthenticateSSHRequest{ AuthenticateUserRequest: AuthenticateUserRequest{ Webauthn: resp, @@ -591,7 +591,7 @@ func TestServer_Authenticate_passwordless(t *testing.T) { loginHook, loginHook, }, - authenticate: func(t *testing.T, resp *wanlib.CredentialAssertionResponse) { + authenticate: func(t *testing.T, resp *wantypes.CredentialAssertionResponse) { loginResp, err := proxyClient.AuthenticateSSHUser(ctx, AuthenticateSSHRequest{ AuthenticateUserRequest: AuthenticateUserRequest{ Webauthn: resp, @@ -607,7 +607,7 @@ func TestServer_Authenticate_passwordless(t *testing.T) { }, { name: "web", - authenticate: func(t *testing.T, resp *wanlib.CredentialAssertionResponse) { + authenticate: func(t *testing.T, resp *wantypes.CredentialAssertionResponse) { session, err := proxyClient.AuthenticateWebUser(ctx, AuthenticateUserRequest{ Webauthn: resp, }) @@ -620,7 +620,7 @@ func TestServer_Authenticate_passwordless(t *testing.T) { loginHooks: []LoginHook{ loginHook, }, - authenticate: func(t *testing.T, resp *wanlib.CredentialAssertionResponse) { + authenticate: func(t *testing.T, resp *wantypes.CredentialAssertionResponse) { session, err := proxyClient.AuthenticateWebUser(ctx, AuthenticateUserRequest{ Webauthn: resp, }) @@ -641,7 +641,7 @@ func TestServer_Authenticate_passwordless(t *testing.T) { _, err := proxyClient.AuthenticateSSHUser(ctx, AuthenticateSSHRequest{ AuthenticateUserRequest: AuthenticateUserRequest{ Username: user, - Webauthn: &wanlib.CredentialAssertionResponse{}, // bad response + Webauthn: &wantypes.CredentialAssertionResponse{}, // bad response PublicKey: []byte(sshPubKey), }, TTL: 24 * time.Hour, @@ -660,7 +660,7 @@ func TestServer_Authenticate_passwordless(t *testing.T) { require.NoError(t, err, "Failed to create passwordless challenge") // Sign challenge (mocks user interaction). - assertionResp, err := pwdKey.SignAssertion(origin, wanlib.CredentialAssertionFromProto(mfaChallenge.GetWebauthnChallenge())) + assertionResp, err := pwdKey.SignAssertion(origin, wantypes.CredentialAssertionFromProto(mfaChallenge.GetWebauthnChallenge())) require.NoError(t, err) assertionResp.AssertionResponse.UserHandle = userWebID // identify user, a real device would set this @@ -727,7 +727,7 @@ func TestServer_Authenticate_nonPasswordlessRequiresUsername(t *testing.T) { } switch { case mfaResp.GetWebauthn() != nil: - req.Webauthn = wanlib.CredentialAssertionResponseFromProto(mfaResp.GetWebauthn()) + req.Webauthn = wantypes.CredentialAssertionResponseFromProto(mfaResp.GetWebauthn()) case mfaResp.GetTOTP() != nil: req.OTP = &OTPCreds{ Password: []byte(password), @@ -813,7 +813,7 @@ func TestServer_Authenticate_headless(t *testing.T) { _, err = proxyClient.AuthenticateSSHUser(ctx, AuthenticateSSHRequest{ AuthenticateUserRequest: AuthenticateUserRequest{ Username: username, - Webauthn: &wanlib.CredentialAssertionResponse{}, // bad response + Webauthn: &wantypes.CredentialAssertionResponse{}, // bad response PublicKey: []byte(sshPubKey), }, TTL: 24 * time.Hour, @@ -857,7 +857,7 @@ func TestServer_Authenticate_headless(t *testing.T) { AuthenticateUserRequest: AuthenticateUserRequest{ // HeadlessAuthenticationID should take precedence over WebAuthn and OTP fields. HeadlessAuthenticationID: headlessID, - Webauthn: &wanlib.CredentialAssertionResponse{}, + Webauthn: &wantypes.CredentialAssertionResponse{}, OTP: &OTPCreds{}, Username: username, PublicKey: []byte(sshPubKey), diff --git a/lib/auth/auth_with_roles_test.go b/lib/auth/auth_with_roles_test.go index fc4ded1b8d385..1de6cedae5f61 100644 --- a/lib/auth/auth_with_roles_test.go +++ b/lib/auth/auth_with_roles_test.go @@ -44,7 +44,7 @@ import ( "github.com/gravitational/teleport/api/types" apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/api/types/installers" - "github.com/gravitational/teleport/api/types/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" "github.com/gravitational/teleport/api/types/wrappers" apiutils "github.com/gravitational/teleport/api/utils" "github.com/gravitational/teleport/api/utils/sshutils" @@ -4881,7 +4881,7 @@ func TestUpdateHeadlessAuthenticationState(t *testing.T) { // default to failed mfa challenge response resp := &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: &webauthn.CredentialAssertionResponse{ + Webauthn: &wanpb.CredentialAssertionResponse{ Type: "bad response", }, }, diff --git a/lib/auth/grpcserver_test.go b/lib/auth/grpcserver_test.go index b8d6ed6555dcb..cc5f432e59064 100644 --- a/lib/auth/grpcserver_test.go +++ b/lib/auth/grpcserver_test.go @@ -61,7 +61,7 @@ import ( "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/lib/auth/mocku2f" "github.com/gravitational/teleport/lib/auth/testauthority" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/authz" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/modules" @@ -177,12 +177,12 @@ func TestMFADeviceManagement(t *testing.T) { authHandler: devs.webAuthHandler, checkAuthErr: require.NoError, registerHandler: func(t *testing.T, challenge *proto.MFARegisterChallenge) *proto.MFARegisterResponse { - ccr, err := webKey2.SignCredentialCreation(webOrigin, wanlib.CredentialCreationFromProto(challenge.GetWebauthn())) + ccr, err := webKey2.SignCredentialCreation(webOrigin, wantypes.CredentialCreationFromProto(challenge.GetWebauthn())) require.NoError(t, err) return &proto.MFARegisterResponse{ Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: wanlib.CredentialCreationResponseToProto(ccr), + Webauthn: wantypes.CredentialCreationResponseToProto(ccr), }, } }, @@ -204,11 +204,11 @@ func TestMFADeviceManagement(t *testing.T) { require.NoError(t, err) key.PreferRPID = true key.IgnoreAllowedCredentials = true - resp, err := key.SignAssertion(webOrigin, wanlib.CredentialAssertionFromProto(challenge.WebauthnChallenge)) + resp, err := key.SignAssertion(webOrigin, wantypes.CredentialAssertionFromProto(challenge.WebauthnChallenge)) require.NoError(t, err) return &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(resp), + Webauthn: wantypes.CredentialAssertionResponseToProto(resp), }, } }, @@ -235,11 +235,11 @@ func TestMFADeviceManagement(t *testing.T) { key.PreferRPID = true ccr, err := key.SignCredentialCreation( - "http://badorigin.com" /* origin */, wanlib.CredentialCreationFromProto(challenge.GetWebauthn())) + "http://badorigin.com" /* origin */, wantypes.CredentialCreationFromProto(challenge.GetWebauthn())) require.NoError(t, err) return &proto.MFARegisterResponse{ Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: wanlib.CredentialCreationResponseToProto(ccr), + Webauthn: wantypes.CredentialCreationResponseToProto(ccr), }, } }, @@ -267,12 +267,12 @@ func TestMFADeviceManagement(t *testing.T) { key.PreferRPID = true key.SetPasswordless() - ccr, err := key.SignCredentialCreation(webOrigin, wanlib.CredentialCreationFromProto(challenge.GetWebauthn())) + ccr, err := key.SignCredentialCreation(webOrigin, wantypes.CredentialCreationFromProto(challenge.GetWebauthn())) require.NoError(t, err) return &proto.MFARegisterResponse{ Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: wanlib.CredentialCreationResponseToProto(ccr), + Webauthn: wantypes.CredentialCreationResponseToProto(ccr), }, } }, @@ -355,11 +355,11 @@ func TestMFADeviceManagement(t *testing.T) { require.NoError(t, err) key.PreferRPID = true key.IgnoreAllowedCredentials = true - resp, err := key.SignAssertion(webOrigin, wanlib.CredentialAssertionFromProto(challenge.WebauthnChallenge)) + resp, err := key.SignAssertion(webOrigin, wantypes.CredentialAssertionFromProto(challenge.WebauthnChallenge)) require.NoError(t, err) return &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(resp), + Webauthn: wantypes.CredentialAssertionResponseToProto(resp), }, } }, @@ -404,11 +404,11 @@ func TestMFADeviceManagement(t *testing.T) { }, authHandler: func(t *testing.T, challenge *proto.MFAAuthenticateChallenge) *proto.MFAAuthenticateResponse { resp, err := webKey2.SignAssertion( - webOrigin, wanlib.CredentialAssertionFromProto(challenge.WebauthnChallenge)) + webOrigin, wantypes.CredentialAssertionFromProto(challenge.WebauthnChallenge)) require.NoError(t, err) return &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(resp), + Webauthn: wantypes.CredentialAssertionResponseToProto(resp), }, } }, @@ -458,11 +458,11 @@ func (d *mfaDevices) webAuthHandler(t *testing.T, challenge *proto.MFAAuthentica require.NotNil(t, challenge.WebauthnChallenge) resp, err := d.WebKey.SignAssertion( - d.webOrigin, wanlib.CredentialAssertionFromProto(challenge.WebauthnChallenge)) + d.webOrigin, wantypes.CredentialAssertionFromProto(challenge.WebauthnChallenge)) require.NoError(t, err) return &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(resp), + Webauthn: wantypes.CredentialAssertionResponseToProto(resp), }, } } @@ -541,11 +541,11 @@ func addOneOfEachMFADevice(t *testing.T, cl *Client, clock clockwork.Clock, orig registerHandler: func(t *testing.T, challenge *proto.MFARegisterChallenge) *proto.MFARegisterResponse { require.NotNil(t, challenge.GetWebauthn()) - ccr, err := mfaDevs.WebKey.SignCredentialCreation(origin, wanlib.CredentialCreationFromProto(challenge.GetWebauthn())) + ccr, err := mfaDevs.WebKey.SignCredentialCreation(origin, wantypes.CredentialCreationFromProto(challenge.GetWebauthn())) require.NoError(t, err) return &proto.MFARegisterResponse{ Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: wanlib.CredentialCreationResponseToProto(ccr), + Webauthn: wantypes.CredentialCreationResponseToProto(ccr), }, } }, diff --git a/lib/auth/helpers_mfa.go b/lib/auth/helpers_mfa.go index e1e2ae09ff45e..c021bb399679a 100644 --- a/lib/auth/helpers_mfa.go +++ b/lib/auth/helpers_mfa.go @@ -28,7 +28,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/auth/mocku2f" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) // TestDevice is a test MFA device. @@ -178,13 +178,13 @@ func (d *TestDevice) solveAuthnKey(c *proto.MFAAuthenticateChallenge) (*proto.MF if c.WebauthnChallenge == nil { return nil, trace.BadParameter("key-based challenge not present") } - resp, err := d.Key.SignAssertion(d.Origin(), wanlib.CredentialAssertionFromProto(c.WebauthnChallenge)) + resp, err := d.Key.SignAssertion(d.Origin(), wantypes.CredentialAssertionFromProto(c.WebauthnChallenge)) if err != nil { return nil, trace.Wrap(err) } return &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(resp), + Webauthn: wantypes.CredentialAssertionResponseToProto(resp), }, }, nil } @@ -238,13 +238,13 @@ func (d *TestDevice) solveRegisterWebauthn(c *proto.MFARegisterChallenge) (*prot d.Key.SetUV = true } - resp, err := d.Key.SignCredentialCreation(d.Origin(), wanlib.CredentialCreationFromProto(c.GetWebauthn())) + resp, err := d.Key.SignCredentialCreation(d.Origin(), wantypes.CredentialCreationFromProto(c.GetWebauthn())) if err != nil { return nil, trace.Wrap(err) } return &proto.MFARegisterResponse{ Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: wanlib.CredentialCreationResponseToProto(resp), + Webauthn: wantypes.CredentialCreationResponseToProto(resp), }, }, nil } diff --git a/lib/auth/httpfallback.go b/lib/auth/httpfallback.go index d969bf02fbce5..69b34773ab93d 100644 --- a/lib/auth/httpfallback.go +++ b/lib/auth/httpfallback.go @@ -22,7 +22,7 @@ import ( "github.com/gravitational/trace" "github.com/gravitational/teleport/api/client/proto" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) // httpfallback.go holds endpoints that have been converted to gRPC @@ -39,7 +39,7 @@ type legacyChangePasswordReq struct { // SecondFactorToken is user 2nd factor token SecondFactorToken string `json:"second_factor_token"` // WebauthnResponse is Webauthn sign response - WebauthnResponse *wanlib.CredentialAssertionResponse `json:"webauthn_response"` + WebauthnResponse *wantypes.CredentialAssertionResponse `json:"webauthn_response"` } // ChangePassword updates users password based on the old password. @@ -57,7 +57,7 @@ func (c *Client) ChangePassword(ctx context.Context, req *proto.ChangePasswordRe OldPassword: req.OldPassword, NewPassword: req.NewPassword, SecondFactorToken: req.SecondFactorToken, - WebauthnResponse: wanlib.CredentialAssertionResponseFromProto(req.Webauthn), + WebauthnResponse: wantypes.CredentialAssertionResponseFromProto(req.Webauthn), }) return trace.Wrap(err) } diff --git a/lib/auth/methods.go b/lib/auth/methods.go index 5302a06bd464d..8fc43112d7c4e 100644 --- a/lib/auth/methods.go +++ b/lib/auth/methods.go @@ -30,7 +30,7 @@ import ( "github.com/gravitational/teleport/api/types" apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/api/utils/keys" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/services" @@ -57,7 +57,7 @@ type AuthenticateUserRequest struct { // Pass is a password used in local authentication schemes Pass *PassCreds `json:"pass,omitempty"` // Webauthn is a signed credential assertion, used in MFA authentication - Webauthn *wanlib.CredentialAssertionResponse `json:"webauthn,omitempty"` + Webauthn *wantypes.CredentialAssertionResponse `json:"webauthn,omitempty"` // OTP is a password and second factor, used for MFA authentication OTP *OTPCreds `json:"otp,omitempty"` // Session is a web session credential used to authenticate web sessions @@ -233,7 +233,7 @@ func (s *Server) authenticateUser(ctx context.Context, req AuthenticateUserReque authenticateFn = func() (*types.MFADevice, error) { mfaResponse := &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(req.Webauthn), + Webauthn: wantypes.CredentialAssertionResponseToProto(req.Webauthn), }, } dev, _, err := s.validateMFAAuthResponse(ctx, mfaResponse, user, passwordless) @@ -327,7 +327,7 @@ func (s *Server) authenticateUser(ctx context.Context, req AuthenticateUserReque func (s *Server) authenticatePasswordless(ctx context.Context, req AuthenticateUserRequest) (*types.MFADevice, string, error) { mfaResponse := &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(req.Webauthn), + Webauthn: wantypes.CredentialAssertionResponseToProto(req.Webauthn), }, } dev, user, err := s.validateMFAAuthResponse(ctx, mfaResponse, "", true /* passwordless */) diff --git a/lib/auth/mocku2f/webauthn.go b/lib/auth/mocku2f/webauthn.go index 833e1c82a3087..03faeb31c0d51 100644 --- a/lib/auth/mocku2f/webauthn.go +++ b/lib/auth/mocku2f/webauthn.go @@ -30,11 +30,12 @@ import ( wanlib "github.com/gravitational/teleport/lib/auth/webauthn" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) // SignAssertion signs a WebAuthn assertion following the // U2F-compat-getAssertion algorithm. -func (muk *Key) SignAssertion(origin string, assertion *wanlib.CredentialAssertion) (*wanlib.CredentialAssertionResponse, error) { +func (muk *Key) SignAssertion(origin string, assertion *wantypes.CredentialAssertion) (*wantypes.CredentialAssertionResponse, error) { // Reference: // https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#u2f-authenticatorGetAssertion-interoperability @@ -52,7 +53,7 @@ func (muk *Key) SignAssertion(origin string, assertion *wanlib.CredentialAsserti // Use RPID or App ID? appID := assertion.Response.RelyingPartyID - if value, ok := assertion.Response.Extensions[wanlib.AppIDExtension]; !muk.PreferRPID && ok { + if value, ok := assertion.Response.Extensions[wantypes.AppIDExtension]; !muk.PreferRPID && ok { if appID, ok = value.(string); !ok { return nil, trace.BadParameter("u2f app ID has unexpected type: %T", value) } @@ -76,9 +77,9 @@ func (muk *Key) SignAssertion(origin string, assertion *wanlib.CredentialAsserti return nil, trace.Wrap(err) } - return &wanlib.CredentialAssertionResponse{ - PublicKeyCredential: wanlib.PublicKeyCredential{ - Credential: wanlib.Credential{ + return &wantypes.CredentialAssertionResponse{ + PublicKeyCredential: wantypes.PublicKeyCredential{ + Credential: wantypes.Credential{ ID: base64.RawURLEncoding.EncodeToString(muk.KeyHandle), Type: string(protocol.PublicKeyCredentialType), }, @@ -86,8 +87,8 @@ func (muk *Key) SignAssertion(origin string, assertion *wanlib.CredentialAsserti // Mimic browsers and don't set the output AppID extension, even if we // used it. }, - AssertionResponse: wanlib.AuthenticatorAssertionResponse{ - AuthenticatorResponse: wanlib.AuthenticatorResponse{ + AssertionResponse: wantypes.AuthenticatorAssertionResponse{ + AuthenticatorResponse: wantypes.AuthenticatorResponse{ ClientDataJSON: ccd, }, AuthenticatorData: res.AuthData, @@ -99,7 +100,7 @@ func (muk *Key) SignAssertion(origin string, assertion *wanlib.CredentialAsserti // SignCredentialCreation signs a WebAuthn credential creation request following // the U2F-compat-makeCredential algorithm. -func (muk *Key) SignCredentialCreation(origin string, cc *wanlib.CredentialCreation) (*wanlib.CredentialCreationResponse, error) { +func (muk *Key) SignCredentialCreation(origin string, cc *wantypes.CredentialCreation) (*wantypes.CredentialCreationResponse, error) { // Reference: // https: // fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#fig-u2f-compat-makeCredential @@ -181,16 +182,16 @@ func (muk *Key) SignCredentialCreation(origin string, cc *wanlib.CredentialCreat return nil, trace.Wrap(err) } - return &wanlib.CredentialCreationResponse{ - PublicKeyCredential: wanlib.PublicKeyCredential{ - Credential: wanlib.Credential{ + return &wantypes.CredentialCreationResponse{ + PublicKeyCredential: wantypes.PublicKeyCredential{ + Credential: wantypes.Credential{ ID: base64.RawURLEncoding.EncodeToString(muk.KeyHandle), Type: string(protocol.PublicKeyCredentialType), }, RawID: muk.KeyHandle, }, - AttestationResponse: wanlib.AuthenticatorAttestationResponse{ - AuthenticatorResponse: wanlib.AuthenticatorResponse{ + AttestationResponse: wantypes.AuthenticatorAttestationResponse{ + AuthenticatorResponse: wantypes.AuthenticatorResponse{ ClientDataJSON: ccd, }, AttestationObject: attObj, diff --git a/lib/auth/password.go b/lib/auth/password.go index 02bacd190fbdc..bb02160d39e09 100644 --- a/lib/auth/password.go +++ b/lib/auth/password.go @@ -30,7 +30,7 @@ import ( "github.com/gravitational/teleport/api/types" apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/api/utils/keys" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/authz" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/events" @@ -123,7 +123,7 @@ func (s *Server) ChangePassword(ctx context.Context, req *proto.ChangePasswordRe user := req.User authReq := AuthenticateUserRequest{ Username: user, - Webauthn: wanlib.CredentialAssertionResponseFromProto(req.Webauthn), + Webauthn: wantypes.CredentialAssertionResponseFromProto(req.Webauthn), } if len(req.OldPassword) > 0 { authReq.Pass = &PassCreds{ diff --git a/lib/auth/password_test.go b/lib/auth/password_test.go index 1211bef28a6e6..f05003dbb42d5 100644 --- a/lib/auth/password_test.go +++ b/lib/auth/password_test.go @@ -33,7 +33,7 @@ import ( "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/api/types" apievents "github.com/gravitational/teleport/api/types/events" - wantypes "github.com/gravitational/teleport/api/types/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" "github.com/gravitational/teleport/lib/auth/keystore" authority "github.com/gravitational/teleport/lib/auth/testauthority" "github.com/gravitational/teleport/lib/backend" @@ -312,7 +312,7 @@ func TestChangeUserAuthentication(t *testing.T) { TokenID: resetTokenID, NewPassword: []byte("password2"), NewMFARegisterResponse: &proto.MFARegisterResponse{Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: &wantypes.CredentialCreationResponse{}, + Webauthn: &wanpb.CredentialCreationResponse{}, }}, } }, diff --git a/lib/auth/touchid/api.go b/lib/auth/touchid/api.go index 5745593b6c370..5e00e8da914b4 100644 --- a/lib/auth/touchid/api.go +++ b/lib/auth/touchid/api.go @@ -36,7 +36,7 @@ import ( "github.com/gravitational/trace" log "github.com/sirupsen/logrus" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/darwin" ) @@ -185,7 +185,7 @@ func Diag() (*DiagResult, error) { // Confirm may replace equivalent keys with the new key, at the implementation's // discretion. type Registration struct { - CCR *wanlib.CredentialCreationResponse + CCR *wantypes.CredentialCreationResponse credentialID string @@ -217,7 +217,7 @@ func (r *Registration) Rollback() error { // Callers are encouraged to either explicitly Confirm or Rollback the returned // registration. // See Registration. -func Register(origin string, cc *wanlib.CredentialCreation) (*Registration, error) { +func Register(origin string, cc *wantypes.CredentialCreation) (*Registration, error) { if !IsAvailable() { return nil, ErrNotAvailable } @@ -319,16 +319,16 @@ func Register(origin string, cc *wanlib.CredentialCreation) (*Registration, erro return nil, trace.Wrap(err) } - ccr := &wanlib.CredentialCreationResponse{ - PublicKeyCredential: wanlib.PublicKeyCredential{ - Credential: wanlib.Credential{ + ccr := &wantypes.CredentialCreationResponse{ + PublicKeyCredential: wantypes.PublicKeyCredential{ + Credential: wantypes.Credential{ ID: credentialID, Type: string(protocol.PublicKeyCredentialType), }, RawID: []byte(credentialID), }, - AttestationResponse: wanlib.AuthenticatorAttestationResponse{ - AuthenticatorResponse: wanlib.AuthenticatorResponse{ + AttestationResponse: wantypes.AuthenticatorAttestationResponse{ + AuthenticatorResponse: wantypes.AuthenticatorResponse{ ClientDataJSON: attData.ccdJSON, }, AttestationObject: attObj, @@ -433,7 +433,7 @@ type CredentialPicker interface { // Login authenticates using a Secure Enclave-backed biometric credential. // It returns the assertion response and the user that owns the credential to // sign it. -func Login(origin, user string, assertion *wanlib.CredentialAssertion, picker CredentialPicker) (*wanlib.CredentialAssertionResponse, string, error) { +func Login(origin, user string, assertion *wantypes.CredentialAssertion, picker CredentialPicker) (*wantypes.CredentialAssertionResponse, string, error) { if !IsAvailable() { return nil, "", ErrNotAvailable } @@ -503,16 +503,16 @@ func Login(origin, user string, assertion *wanlib.CredentialAssertion, picker Cr return nil, "", trace.Wrap(err) } - return &wanlib.CredentialAssertionResponse{ - PublicKeyCredential: wanlib.PublicKeyCredential{ - Credential: wanlib.Credential{ + return &wantypes.CredentialAssertionResponse{ + PublicKeyCredential: wantypes.PublicKeyCredential{ + Credential: wantypes.Credential{ ID: cred.CredentialID, Type: string(protocol.PublicKeyCredentialType), }, RawID: []byte(cred.CredentialID), }, - AssertionResponse: wanlib.AuthenticatorAssertionResponse{ - AuthenticatorResponse: wanlib.AuthenticatorResponse{ + AssertionResponse: wantypes.AuthenticatorAssertionResponse{ + AuthenticatorResponse: wantypes.AuthenticatorResponse{ ClientDataJSON: attData.ccdJSON, }, AuthenticatorData: attData.rawAuthData, @@ -524,7 +524,7 @@ func Login(origin, user string, assertion *wanlib.CredentialAssertion, picker Cr func pickCredential( actx AuthContext, - infos []CredentialInfo, allowedCredentials []protocol.CredentialDescriptor, + infos []CredentialInfo, allowedCredentials []wantypes.CredentialDescriptor, picker CredentialPicker, promptOnce func(), userRequested bool, ) (*CredentialInfo, error) { // Handle early exits. diff --git a/lib/auth/touchid/api_test.go b/lib/auth/touchid/api_test.go index 5e73000e48f8c..459121b382ba6 100644 --- a/lib/auth/touchid/api_test.go +++ b/lib/auth/touchid/api_test.go @@ -35,7 +35,7 @@ import ( "github.com/stretchr/testify/require" "github.com/gravitational/teleport/lib/auth/touchid" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) func init() { @@ -68,14 +68,14 @@ func TestRegisterAndLogin(t *testing.T) { name string webUser *fakeUser origin, user string - modifyAssertion func(a *wanlib.CredentialAssertion) + modifyAssertion func(a *wantypes.CredentialAssertion) wantUser string }{ { name: "passwordless", webUser: &fakeUser{id: []byte{1, 2, 3, 4, 5}, name: llamaUser}, origin: web.Config.RPOrigin, - modifyAssertion: func(a *wanlib.CredentialAssertion) { + modifyAssertion: func(a *wantypes.CredentialAssertion) { a.Response.AllowedCredentials = nil // aka passwordless }, wantUser: llamaUser, @@ -94,7 +94,7 @@ func TestRegisterAndLogin(t *testing.T) { cc, sessionData, err := web.BeginRegistration(webUser) require.NoError(t, err) - reg, err := touchid.Register(origin, (*wanlib.CredentialCreation)(cc)) + reg, err := touchid.Register(origin, wantypes.CredentialCreationFromProtocol(cc)) require.NoError(t, err, "Register failed") assert.Equal(t, 1, fake.userPrompts, "unexpected number of Registration prompts") @@ -116,7 +116,7 @@ func TestRegisterAndLogin(t *testing.T) { // Login section. a, sessionData, err := web.BeginLogin(webUser) require.NoError(t, err, "BeginLogin failed") - assertion := (*wanlib.CredentialAssertion)(a) + assertion := wantypes.CredentialAssertionFromProtocol(a) test.modifyAssertion(assertion) assertionResp, actualUser, err := touchid.Login(origin, user, assertion, simplePicker{}) @@ -162,7 +162,7 @@ func TestRegister_rollback(t *testing.T) { require.NoError(t, err) // Register and then Rollback a credential. - reg, err := touchid.Register(origin, (*wanlib.CredentialCreation)(cc)) + reg, err := touchid.Register(origin, wantypes.CredentialCreationFromProtocol(cc)) require.NoError(t, err, "Register failed") require.NoError(t, reg.Rollback(), "Rollback failed") @@ -170,8 +170,8 @@ func TestRegister_rollback(t *testing.T) { require.Contains(t, fake.nonInteractiveDelete, reg.CCR.ID, "Credential ID not found in (fake) nonInteractiveDeletes") // Attempt to authenticate. - _, _, err = touchid.Login(origin, llamaUser, &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + _, _, err = touchid.Login(origin, llamaUser, &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: []byte{1, 2, 3, 4, 5}, // doesn't matter as long as it's not empty RelyingPartyID: web.Config.RPID, UserVerification: "required", @@ -225,7 +225,7 @@ func TestLogin_findsCorrectCredential(t *testing.T) { cc, _, err := web.BeginRegistration(u) require.NoError(t, err, "BeginRegistration #%v failed, user %v", i+1, u.name) - reg, err := touchid.Register(origin, (*wanlib.CredentialCreation)(cc)) + reg, err := touchid.Register(origin, wantypes.CredentialCreationFromProtocol(cc)) require.NoError(t, err, "Register #%v failed, user %v", i+1, u.name) require.NoError(t, reg.Confirm(), "Confirm failed") } @@ -243,7 +243,7 @@ func TestLogin_findsCorrectCredential(t *testing.T) { cc, _, err := web2.BeginRegistration(u) require.NoError(t, err, "web2 BeginRegistration failed") - reg, err := touchid.Register(web2.Config.RPOrigin, (*wanlib.CredentialCreation)(cc)) + reg, err := touchid.Register(web2.Config.RPOrigin, wantypes.CredentialCreationFromProtocol(cc)) require.NoError(t, err, "web2 Register failed") require.NoError(t, reg.Confirm(), "Confirm failed") } @@ -324,16 +324,16 @@ func TestLogin_findsCorrectCredential(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - var allowedCreds []protocol.CredentialDescriptor + var allowedCreds []wantypes.CredentialDescriptor for _, cred := range test.allowedCreds { - allowedCreds = append(allowedCreds, protocol.CredentialDescriptor{ + allowedCreds = append(allowedCreds, wantypes.CredentialDescriptor{ Type: protocol.PublicKeyCredentialType, CredentialID: []byte(cred.id), }) } - _, gotUser, err := touchid.Login(origin, test.user, &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + _, gotUser, err := touchid.Login(origin, test.user, &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: []byte{1, 2, 3, 4, 5}, // arbitrary RelyingPartyID: web.Config.RPID, AllowedCredentials: allowedCreds, @@ -361,8 +361,8 @@ func TestLogin_noCredentials_failsWithoutUserInteraction(t *testing.T) { *touchid.Native = fake const origin = "https://goteleport.com" - baseAssertion := &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + baseAssertion := &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: []byte{1, 2, 3, 4, 5}, // arbitrary RelyingPartyID: "goteleport.com", UserVerification: protocol.VerificationRequired, @@ -370,7 +370,7 @@ func TestLogin_noCredentials_failsWithoutUserInteraction(t *testing.T) { } mfaAssertion := *baseAssertion mfaAssertion.Response.UserVerification = protocol.VerificationDiscouraged - mfaAssertion.Response.AllowedCredentials = []protocol.CredentialDescriptor{ + mfaAssertion.Response.AllowedCredentials = []wantypes.CredentialDescriptor{ { Type: protocol.PublicKeyCredentialType, CredentialID: []byte{1, 2, 3, 4, 5}, // arbitrary @@ -381,7 +381,7 @@ func TestLogin_noCredentials_failsWithoutUserInteraction(t *testing.T) { for _, test := range []struct { name string user string - assertion *wanlib.CredentialAssertion + assertion *wantypes.CredentialAssertion }{ { name: "passwordless empty user", @@ -415,29 +415,29 @@ func TestLogin_noCredentials_failsWithoutUserInteraction(t *testing.T) { const userLlama = "llama" const userAlpaca = "alpaca" rrk := true - cc1 := &wanlib.CredentialCreation{ - Response: protocol.PublicKeyCredentialCreationOptions{ + cc1 := &wantypes.CredentialCreation{ + Response: wantypes.PublicKeyCredentialCreationOptions{ Challenge: []byte{1, 2, 3, 4, 5}, // arbitrary, not important here - RelyingParty: protocol.RelyingPartyEntity{ - CredentialEntity: protocol.CredentialEntity{ + RelyingParty: wantypes.RelyingPartyEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "Teleport", }, ID: baseAssertion.Response.RelyingPartyID, }, - User: protocol.UserEntity{ - CredentialEntity: protocol.CredentialEntity{ + User: wantypes.UserEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: userLlama, }, DisplayName: "Llama", ID: []byte{1, 1, 1, 1, 1}, }, - Parameters: []protocol.CredentialParameter{ + Parameters: []wantypes.CredentialParameter{ { Type: protocol.PublicKeyCredentialType, Algorithm: webauthncose.AlgES256, }, }, - AuthenticatorSelection: protocol.AuthenticatorSelection{ + AuthenticatorSelection: wantypes.AuthenticatorSelection{ RequireResidentKey: &rrk, UserVerification: protocol.VerificationRequired, }, @@ -445,14 +445,14 @@ func TestLogin_noCredentials_failsWithoutUserInteraction(t *testing.T) { }, } cc2 := *cc1 - cc2.Response.User = protocol.UserEntity{ - CredentialEntity: protocol.CredentialEntity{ + cc2.Response.User = wantypes.UserEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: userAlpaca, }, DisplayName: "Alpaca", ID: []byte{1, 1, 1, 1, 2}, } - for _, cc := range []*wanlib.CredentialCreation{cc1, &cc2} { + for _, cc := range []*wantypes.CredentialCreation{cc1, &cc2} { reg, err := touchid.Register(origin, cc) require.NoError(t, err, "Register failed") require.NoError(t, reg.Confirm(), "Confirm failed") @@ -461,7 +461,7 @@ func TestLogin_noCredentials_failsWithoutUserInteraction(t *testing.T) { mfaAllCreds := mfaAssertion mfaAllCreds.Response.AllowedCredentials = nil for _, c := range fake.creds { - mfaAllCreds.Response.AllowedCredentials = append(mfaAllCreds.Response.AllowedCredentials, protocol.CredentialDescriptor{ + mfaAllCreds.Response.AllowedCredentials = append(mfaAllCreds.Response.AllowedCredentials, wantypes.CredentialDescriptor{ Type: protocol.PublicKeyCredentialType, CredentialID: []byte(c.id), }) @@ -471,7 +471,7 @@ func TestLogin_noCredentials_failsWithoutUserInteraction(t *testing.T) { for _, test := range []struct { name string user string - assertion *wanlib.CredentialAssertion + assertion *wantypes.CredentialAssertion }{ { name: "passwordless existing credentials", @@ -539,17 +539,17 @@ func TestLogin_credentialPicker(t *testing.T) { const rpID = "goteleport.com" const origin = "https://goteleport.com" - baseAssertion := &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + baseAssertion := &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: []byte{1, 2, 3, 4, 5}, // arbitrary RelyingPartyID: rpID, UserVerification: protocol.VerificationRequired, }, } - newAssertion := func(allowedCreds [][]byte) *wanlib.CredentialAssertion { + newAssertion := func(allowedCreds [][]byte) *wantypes.CredentialAssertion { cp := *baseAssertion for _, id := range allowedCreds { - cp.Response.AllowedCredentials = append(cp.Response.AllowedCredentials, protocol.CredentialDescriptor{ + cp.Response.AllowedCredentials = append(cp.Response.AllowedCredentials, wantypes.CredentialDescriptor{ Type: protocol.PublicKeyCredentialType, CredentialID: id, }) diff --git a/lib/auth/touchid/attempt.go b/lib/auth/touchid/attempt.go index ed1bd1fcc7a11..549752ddf7025 100644 --- a/lib/auth/touchid/attempt.go +++ b/lib/auth/touchid/attempt.go @@ -19,7 +19,7 @@ import ( "github.com/gravitational/trace" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) // ErrAttemptFailed is returned by AttemptLogin and AttemptDeleteNonInteractive @@ -54,7 +54,7 @@ func (e *ErrAttemptFailed) As(target interface{}) bool { // AttemptLogin attempts a touch ID login. // It returns ErrAttemptFailed if the attempt failed before user interaction. // See Login. -func AttemptLogin(origin, user string, assertion *wanlib.CredentialAssertion, picker CredentialPicker) (*wanlib.CredentialAssertionResponse, string, error) { +func AttemptLogin(origin, user string, assertion *wantypes.CredentialAssertion, picker CredentialPicker) (*wantypes.CredentialAssertionResponse, string, error) { resp, actualUser, err := Login(origin, user, assertion, picker) switch { case errors.Is(err, ErrNotAvailable), errors.Is(err, ErrCredentialNotFound): diff --git a/lib/auth/usertoken_test.go b/lib/auth/usertoken_test.go index 1f0467f4d0f59..ba6379caf7a07 100644 --- a/lib/auth/usertoken_test.go +++ b/lib/auth/usertoken_test.go @@ -34,7 +34,7 @@ import ( "github.com/gravitational/teleport/api/constants" "github.com/gravitational/teleport/api/types" apievents "github.com/gravitational/teleport/api/types/events" - "github.com/gravitational/teleport/api/types/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/events" "github.com/gravitational/teleport/lib/events/eventstest" @@ -399,7 +399,7 @@ func TestCreatePrivilegeToken_WithLock(t *testing.T) { getReq: func() *proto.CreatePrivilegeTokenRequest { return &proto.CreatePrivilegeTokenRequest{ ExistingMFAResponse: &proto.MFAAuthenticateResponse{Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: &webauthn.CredentialAssertionResponse{}, + Webauthn: &wanpb.CredentialAssertionResponse{}, }}, } }, diff --git a/lib/auth/webauthn/compat.go b/lib/auth/webauthn/compat.go new file mode 100644 index 0000000000000..8ffbb4b75c2a1 --- /dev/null +++ b/lib/auth/webauthn/compat.go @@ -0,0 +1,38 @@ +// Copyright 2023 Gravitational, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package webauthn + +import wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" + +// TODO(codingllama): Delete aliases and vars below once e/ is updated. + +type CredentialAssertion = wantypes.CredentialAssertion +type CredentialAssertionResponse = wantypes.CredentialAssertionResponse +type CredentialCreation = wantypes.CredentialCreation +type CredentialCreationResponse = wantypes.CredentialCreationResponse + +var ( + CredentialAssertionFromProto = wantypes.CredentialAssertionFromProto + CredentialAssertionToProto = wantypes.CredentialAssertionToProto + CredentialAssertionResponseFromProto = wantypes.CredentialAssertionResponseFromProto + CredentialAssertionResponseToProto = wantypes.CredentialAssertionResponseToProto +) + +var ( + CredentialCreationFromProto = wantypes.CredentialCreationFromProto + CredentialCreationToProto = wantypes.CredentialCreationToProto + CredentialCreationResponseFromProto = wantypes.CredentialCreationResponseFromProto + CredentialCreationResponseToProto = wantypes.CredentialCreationResponseToProto +) diff --git a/lib/auth/webauthn/extensions.go b/lib/auth/webauthn/extensions.go deleted file mode 100644 index cc81a53dddc11..0000000000000 --- a/lib/auth/webauthn/extensions.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright 2021 Gravitational, Inc. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package webauthn - -// AppIDExtension is the key for the appid extension. -// https://www.w3.org/TR/webauthn-2/#sctn-appid-extension. -const AppIDExtension = "appid" diff --git a/lib/auth/webauthn/httpserver/README.md b/lib/auth/webauthn/httpserver/README.md deleted file mode 100644 index bd7dc6a975389..0000000000000 --- a/lib/auth/webauthn/httpserver/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# Webauthn httpserver - -The httpserver package contains a toy Webauthn HTTPS server used to test web -integration and the interplay between web-registered and tsh-registered devices. - -Devices registered via either interface should be able to be used -interchangeably (with the exception of Touch ID, which is always tied to a -specific app). - -This is meant for local testing and debugging only. Keep it far away from any -production uses. - -## Why this exists? - -* It is a simple way to test web integration -* It is a simple way to interact with Touch ID -* Device registration (currently) relies on a streaming RPC, which is difficult - to interact with using pure Js - -Note that browser Webauthn APIs need a secure context (or you have to disable a -bunch of checks in your browser, YMMV). - -## Usage - -1. Start the Teleport service -2. Start the httpserver - -```shell -# Generate TLS certificates for the server -# (Eg, `cd; mkcert localhost 127.0.0.1 ::1`) - -# Start the test server -# cd /path/to/teleport/repo -go run ./lib/auth/webauthn/httpserver/ \ - -cert_file ~/localhost+2.pem \ - -key_file ~/localhost+3-key.pem -``` - -3. Navigate to https://localhost:8080/index.html -4. ? -5. Profit diff --git a/lib/auth/webauthn/httpserver/index.html b/lib/auth/webauthn/httpserver/index.html deleted file mode 100644 index a31d818cbad60..0000000000000 --- a/lib/auth/webauthn/httpserver/index.html +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - WebAuthn Playground - - - -
-

Login and Registration

- - - -
- - - -
-
-
-

Registration

- - - -
- - - -
-
- - - - - - diff --git a/lib/auth/webauthn/httpserver/main.go b/lib/auth/webauthn/httpserver/main.go deleted file mode 100644 index 4a009d704a40c..0000000000000 --- a/lib/auth/webauthn/httpserver/main.go +++ /dev/null @@ -1,319 +0,0 @@ -// Copyright 2021 Gravitational, Inc -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "bytes" - "context" - _ "embed" // enable embed - "encoding/json" - "flag" - "fmt" - "io" - "log" - "net/http" - "sync" - - "github.com/gravitational/trace" - - apiclient "github.com/gravitational/teleport/api/client" - "github.com/gravitational/teleport/api/client/proto" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" - libclient "github.com/gravitational/teleport/lib/client" -) - -//go:embed index.html -var indexPage []byte - -var ( - addr = flag.String("addr", "localhost:8080", "Server bind address") - certFile = flag.String("cert_file", "cert.pem", "Cert PEM file") - keyFile = flag.String("key_file", "cert-key.pem", "Key PEM file") - - authAddr = flag.String("auth_addr", "localhost:3025", "Teleport Auth address") - webAddr = flag.String("web_addr", "localhost:3080", "Teleport Web API address") -) - -func main() { - flag.Parse() - if err := run(); err != nil { - log.Fatal(err) - } -} - -func run() error { - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - - fmt.Println("Starting Teleport client") - profile := apiclient.LoadProfile("", "") - teleport, err := apiclient.New(ctx, apiclient.Config{ - Addrs: []string{*authAddr}, - Credentials: []apiclient.Credentials{profile}, - }) - if err != nil { - fmt.Println("Teleport client startup failed, did you run tsh login?") - return trace.Wrap(err) - } - - http.Handle("/", http.RedirectHandler("/index.html", http.StatusSeeOther)) - http.HandleFunc("/index.html", func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "text/html; charset=UTF-8") - w.WriteHeader(http.StatusOK) - w.Write(indexPage) - }) - - s := &server{ - ctx: ctx, - teleport: teleport, - } - http.HandleFunc("/login/1", s.login1) - http.HandleFunc("/login/2", s.login2) - http.HandleFunc("/register/1", s.register1) - http.HandleFunc("/register/2", s.register2) - - fmt.Printf("Listening at %v\n", *addr) - return http.ListenAndServeTLS(*addr, *certFile, *keyFile, nil /* handler */) -} - -type server struct { - ctx context.Context - teleport *apiclient.Client - - mu sync.Mutex - inFlightAddStream *proto.AuthService_AddMFADeviceClient -} - -type login1Request struct { - User string `json:"user"` - Pass string `json:"pass"` -} - -func (s *server) login1(w http.ResponseWriter, r *http.Request) { - var req login1Request - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - // Get login challenge. - body, err := json.Marshal(&libclient.MFAChallengeRequest{ - User: req.User, - Pass: req.Pass, - }) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - resp, err := http.Post("https://"+*webAddr+"/webapi/mfa/login/begin", "application/json", bytes.NewReader(body)) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - log.Printf("INFO /mfa/login/begin: %#v", resp) - http.Error(w, "Unexpected status from /mfa/login/begin", http.StatusBadRequest) - } - var challenge libclient.MFAAuthenticateChallenge - if err := json.NewDecoder(resp.Body).Decode(&challenge); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if challenge.WebauthnChallenge == nil { - http.Error(w, "nil credential assertion", http.StatusBadRequest) - return - } - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - if err := json.NewEncoder(w).Encode(challenge.WebauthnChallenge); err != nil { - log.Println(err) - } -} - -type login2Request struct { - wanlib.CredentialAssertionResponse - User string `json:"user"` -} - -func (s *server) login2(w http.ResponseWriter, r *http.Request) { - // Request body is a wanlib.CredentialAssertionResponse. - // User passed as a query param to make things simpler. - - var req login2Request - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - body, err := json.Marshal(&libclient.AuthenticateWebUserRequest{ - User: req.User, - WebauthnAssertionResponse: &req.CredentialAssertionResponse, - }) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - // Solve login challenge - resp, err := http.Post("https://"+*webAddr+"/webapi/mfa/login/finishsession", "application/json", bytes.NewReader(body)) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - io.Copy(io.Discard, resp.Body) - resp.Body.Close() - if resp.StatusCode != http.StatusOK { - log.Printf("INFO /mfa/login/finishsession: %#v", resp) - http.Error(w, "Unexpected status from /mfa/login/finishsession", http.StatusBadRequest) - } - - // Login OK. - w.WriteHeader(http.StatusOK) -} - -type register1Request struct { - User string `json:"user"` - Pass string `json:"pass"` - DevName string `json:"dev_name"` - TOTPCode string `json:"totp_code"` -} - -func (s *server) register1(w http.ResponseWriter, r *http.Request) { - s.mu.Lock() // Hold lock for the entire time, we don't care. - defer s.mu.Unlock() - - var req register1Request - if err := json.NewDecoder(r.Body).Decode(&req); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - // Init stream with device name and type. - ctx := s.ctx - stream, err := s.teleport.AddMFADevice(ctx) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if err := stream.Send(&proto.AddMFADeviceRequest{ - Request: &proto.AddMFADeviceRequest_Init{ - Init: &proto.AddMFADeviceRequestInit{ - DeviceName: req.DevName, - DeviceType: proto.DeviceType_DEVICE_TYPE_WEBAUTHN, - }, - }, - }); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - // Solve authn challenge. - resp, err := stream.Recv() - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - var authResp *proto.MFAAuthenticateResponse - challenge := resp.GetExistingMFAChallenge() - switch { - case challenge.GetTOTP() == nil && challenge.GetWebauthnChallenge() == nil: // aka empty challenge - authResp = &proto.MFAAuthenticateResponse{} - case challenge.GetTOTP() != nil: - authResp = &proto.MFAAuthenticateResponse{ - Response: &proto.MFAAuthenticateResponse_TOTP{ - TOTP: &proto.TOTPResponse{ - Code: req.TOTPCode, - }, - }, - } - default: - http.Error(w, "TOTP challenge not present", http.StatusBadRequest) - return - } - if err := stream.Send(&proto.AddMFADeviceRequest{ - Request: &proto.AddMFADeviceRequest_ExistingMFAResponse{ - ExistingMFAResponse: authResp, - }, - }); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - resp, err = stream.Recv() - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - ccProto := resp.GetNewMFARegisterChallenge().GetWebauthn() - if ccProto == nil { - http.Error(w, "nil credential creation", http.StatusBadRequest) - return - } - - cc := wanlib.CredentialCreationFromProto(ccProto) - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - if err := json.NewEncoder(w).Encode(cc); err != nil { - log.Println(err) - } - - s.inFlightAddStream = &stream // Save stream for 2nd step -} - -func (s *server) register2(w http.ResponseWriter, r *http.Request) { - // Request body is a wanlib.CredentialCreationResponse. - - s.mu.Lock() // Hold lock for the entire time, we don't care. - defer s.mu.Unlock() - - if s.inFlightAddStream == nil { - http.Error(w, "In-flight add stream is nil", http.StatusBadRequest) - return - } - stream := *s.inFlightAddStream - - var ccr wanlib.CredentialCreationResponse - if err := json.NewDecoder(r.Body).Decode(&ccr); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - // Send register response. - if err := stream.Send(&proto.AddMFADeviceRequest{ - Request: &proto.AddMFADeviceRequest_NewMFARegisterResponse{ - NewMFARegisterResponse: &proto.MFARegisterResponse{ - Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: wanlib.CredentialCreationResponseToProto(&ccr), - }, - }, - }, - }); err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - resp, err := stream.Recv() - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if resp.GetAck() == nil { - log.Printf("WARN Expected Ack, got %#v", resp) - } - - s.inFlightAddStream = nil - w.WriteHeader(http.StatusOK) -} diff --git a/lib/auth/webauthn/login.go b/lib/auth/webauthn/login.go index e18a1d6f4065f..b550529351a36 100644 --- a/lib/auth/webauthn/login.go +++ b/lib/auth/webauthn/login.go @@ -31,7 +31,8 @@ import ( "golang.org/x/exp/slices" "github.com/gravitational/teleport/api/types" - wantypes "github.com/gravitational/teleport/api/types/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) // loginIdentity contains the subset of services.Identity methods used by @@ -50,8 +51,8 @@ type loginIdentity interface { // * Passwordless uses global variants // (services.Identity.Update/Get/DeleteGlobalWebauthnSessionData methods). type sessionIdentity interface { - Upsert(ctx context.Context, user string, sd *wantypes.SessionData) error - Get(ctx context.Context, user string, challenge string) (*wantypes.SessionData, error) + Upsert(ctx context.Context, user string, sd *wanpb.SessionData) error + Get(ctx context.Context, user string, challenge string) (*wanpb.SessionData, error) Delete(ctx context.Context, user string, challenge string) error } @@ -64,7 +65,7 @@ type loginFlow struct { sessionData sessionIdentity } -func (f *loginFlow) begin(ctx context.Context, user string, passwordless bool) (*CredentialAssertion, error) { +func (f *loginFlow) begin(ctx context.Context, user string, passwordless bool) (*wantypes.CredentialAssertion, error) { if user == "" && !passwordless { return nil, trace.BadParameter("user required") } @@ -134,7 +135,7 @@ func (f *loginFlow) begin(ctx context.Context, user string, passwordless bool) ( if f.U2F != nil && f.U2F.AppID != "" { // See https://www.w3.org/TR/webauthn-2/#sctn-appid-extension. opts = append(opts, wan.WithAssertionExtensions(protocol.AuthenticationExtensions{ - AppIDExtension: f.U2F.AppID, + wantypes.AppIDExtension: f.U2F.AppID, })) } @@ -168,7 +169,7 @@ func (f *loginFlow) begin(ctx context.Context, user string, passwordless bool) ( return nil, trace.Wrap(err) } - return (*CredentialAssertion)(assertion), nil + return wantypes.CredentialAssertionFromProtocol(assertion), nil } func (f *loginFlow) getWebID(ctx context.Context, user string) ([]byte, error) { @@ -182,7 +183,7 @@ func (f *loginFlow) getWebID(ctx context.Context, user string) ([]byte, error) { return wla.UserID, nil } -func (f *loginFlow) finish(ctx context.Context, user string, resp *CredentialAssertionResponse, passwordless bool) (*types.MFADevice, string, error) { +func (f *loginFlow) finish(ctx context.Context, user string, resp *wantypes.CredentialAssertionResponse, passwordless bool) (*types.MFADevice, string, error) { switch { case user == "" && !passwordless: return nil, "", trace.BadParameter("user required") @@ -318,7 +319,7 @@ func (f *loginFlow) finish(ctx context.Context, user string, resp *CredentialAss return dev, user, nil } -func parseCredentialResponse(resp *CredentialAssertionResponse) (*protocol.ParsedCredentialAssertionData, error) { +func parseCredentialResponse(resp *wantypes.CredentialAssertionResponse) (*protocol.ParsedCredentialAssertionData, error) { // Do not pass extensions on to duo-labs/webauthn, they won't go past JSON // unmarshal. exts := resp.Extensions diff --git a/lib/auth/webauthn/login_mfa.go b/lib/auth/webauthn/login_mfa.go index 33f6cc4c60a41..6f595759dcbf3 100644 --- a/lib/auth/webauthn/login_mfa.go +++ b/lib/auth/webauthn/login_mfa.go @@ -21,7 +21,8 @@ import ( "github.com/gravitational/trace" "github.com/gravitational/teleport/api/types" - wantypes "github.com/gravitational/teleport/api/types/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) // ErrInvalidCredentials is a special kind of credential "NotFound" error, where @@ -39,8 +40,8 @@ type LoginIdentity interface { GetMFADevices(ctx context.Context, user string, withSecrets bool) ([]*types.MFADevice, error) UpsertMFADevice(ctx context.Context, user string, d *types.MFADevice) error - UpsertWebauthnSessionData(ctx context.Context, user, sessionID string, sd *wantypes.SessionData) error - GetWebauthnSessionData(ctx context.Context, user, sessionID string) (*wantypes.SessionData, error) + UpsertWebauthnSessionData(ctx context.Context, user, sessionID string, sd *wanpb.SessionData) error + GetWebauthnSessionData(ctx context.Context, user, sessionID string) (*wanpb.SessionData, error) DeleteWebauthnSessionData(ctx context.Context, user, sessionID string) error } @@ -89,7 +90,7 @@ type LoginFlow struct { // assertion. // As a side effect Begin may assign (and record in storage) a WebAuthn ID for // the user. -func (f *LoginFlow) Begin(ctx context.Context, user string) (*CredentialAssertion, error) { +func (f *LoginFlow) Begin(ctx context.Context, user string) (*wantypes.CredentialAssertion, error) { lf := &loginFlow{ U2F: f.U2F, Webauthn: f.Webauthn, @@ -103,7 +104,7 @@ func (f *LoginFlow) Begin(ctx context.Context, user string) (*CredentialAssertio // It returns the MFADevice used to solve the challenge. If login is successful, // Finish has the side effect of updating the counter and last used timestamp of // the returned device. -func (f *LoginFlow) Finish(ctx context.Context, user string, resp *CredentialAssertionResponse) (*types.MFADevice, error) { +func (f *LoginFlow) Finish(ctx context.Context, user string, resp *wantypes.CredentialAssertionResponse) (*types.MFADevice, error) { lf := &loginFlow{ U2F: f.U2F, Webauthn: f.Webauthn, @@ -125,11 +126,11 @@ func (m mfaIdentity) GetTeleportUserByWebauthnID(_ context.Context, _ []byte) (s // userSessionStorage implements sessionIdentity using LoginFlow. type userSessionStorage LoginFlow -func (s *userSessionStorage) Upsert(ctx context.Context, user string, sd *wantypes.SessionData) error { +func (s *userSessionStorage) Upsert(ctx context.Context, user string, sd *wanpb.SessionData) error { return s.Identity.UpsertWebauthnSessionData(ctx, user, scopeLogin, sd) } -func (s *userSessionStorage) Get(ctx context.Context, user string, _ string) (*wantypes.SessionData, error) { +func (s *userSessionStorage) Get(ctx context.Context, user string, _ string) (*wanpb.SessionData, error) { return s.Identity.GetWebauthnSessionData(ctx, user, scopeLogin) } diff --git a/lib/auth/webauthn/login_passwordless.go b/lib/auth/webauthn/login_passwordless.go index 71584305ecf25..a659d6fc7e8aa 100644 --- a/lib/auth/webauthn/login_passwordless.go +++ b/lib/auth/webauthn/login_passwordless.go @@ -20,7 +20,8 @@ import ( "errors" "github.com/gravitational/teleport/api/types" - wantypes "github.com/gravitational/teleport/api/types/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) // PasswordlessIdentity represents the subset of Identity methods used by @@ -29,8 +30,8 @@ type PasswordlessIdentity interface { GetMFADevices(ctx context.Context, user string, withSecrets bool) ([]*types.MFADevice, error) UpsertMFADevice(ctx context.Context, user string, d *types.MFADevice) error - UpsertGlobalWebauthnSessionData(ctx context.Context, scope, id string, sd *wantypes.SessionData) error - GetGlobalWebauthnSessionData(ctx context.Context, scope, id string) (*wantypes.SessionData, error) + UpsertGlobalWebauthnSessionData(ctx context.Context, scope, id string, sd *wanpb.SessionData) error + GetGlobalWebauthnSessionData(ctx context.Context, scope, id string) (*wanpb.SessionData, error) DeleteGlobalWebauthnSessionData(ctx context.Context, scope, id string) error GetTeleportUserByWebauthnID(ctx context.Context, webID []byte) (string, error) } @@ -44,7 +45,7 @@ type PasswordlessFlow struct { // Begin is the first step of the passwordless login flow. // It works similarly to LoginFlow.Begin, but it doesn't require a Teleport // username nor implies a previous password-validation step. -func (f *PasswordlessFlow) Begin(ctx context.Context) (*CredentialAssertion, error) { +func (f *PasswordlessFlow) Begin(ctx context.Context) (*wantypes.CredentialAssertion, error) { lf := &loginFlow{ Webauthn: f.Webauthn, identity: passwordlessIdentity{f.Identity}, @@ -56,7 +57,7 @@ func (f *PasswordlessFlow) Begin(ctx context.Context) (*CredentialAssertion, err // Finish is the last step of the passwordless login flow. // It works similarly to LoginFlow.Finish, but the user identity is established // via the response UserHandle, instead of an explicit Teleport username. -func (f *PasswordlessFlow) Finish(ctx context.Context, resp *CredentialAssertionResponse) (*types.MFADevice, string, error) { +func (f *PasswordlessFlow) Finish(ctx context.Context, resp *wantypes.CredentialAssertionResponse) (*types.MFADevice, string, error) { lf := &loginFlow{ Webauthn: f.Webauthn, identity: passwordlessIdentity{f.Identity}, @@ -79,12 +80,12 @@ func (p passwordlessIdentity) GetWebauthnLocalAuth(ctx context.Context, user str type globalSessionStorage PasswordlessFlow -func (g *globalSessionStorage) Upsert(ctx context.Context, user string, sd *wantypes.SessionData) error { +func (g *globalSessionStorage) Upsert(ctx context.Context, user string, sd *wanpb.SessionData) error { id := base64.RawURLEncoding.EncodeToString(sd.Challenge) return g.Identity.UpsertGlobalWebauthnSessionData(ctx, scopeLogin, id, sd) } -func (g *globalSessionStorage) Get(ctx context.Context, user string, challenge string) (*wantypes.SessionData, error) { +func (g *globalSessionStorage) Get(ctx context.Context, user string, challenge string) (*wanpb.SessionData, error) { return g.Identity.GetGlobalWebauthnSessionData(ctx, scopeLogin, challenge) } diff --git a/lib/auth/webauthn/login_test.go b/lib/auth/webauthn/login_test.go index bc42edb2d97b1..27306b6e14f63 100644 --- a/lib/auth/webauthn/login_test.go +++ b/lib/auth/webauthn/login_test.go @@ -31,9 +31,10 @@ import ( "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/types" - wantypes "github.com/gravitational/teleport/api/types/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" "github.com/gravitational/teleport/lib/auth/mocku2f" wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) func TestLoginFlow_BeginFinish(t *testing.T) { @@ -127,7 +128,7 @@ func TestLoginFlow_BeginFinish(t *testing.T) { // Did we record the SessionData in storage? require.Len(t, identity.SessionData, 1) // Did we record the web ID in the SessionData? - var sd *wantypes.SessionData + var sd *wanpb.SessionData for _, v := range identity.SessionData { sd = v // Retrieve without guessing the key break @@ -265,27 +266,27 @@ func TestLoginFlow_Finish_errors(t *testing.T) { tests := []struct { name string user string - createResp func() *wanlib.CredentialAssertionResponse + createResp func() *wantypes.CredentialAssertionResponse }{ { name: "NOK empty user", user: "", - createResp: func() *wanlib.CredentialAssertionResponse { return okResp }, + createResp: func() *wantypes.CredentialAssertionResponse { return okResp }, }, { name: "NOK nil resp", user: user, - createResp: func() *wanlib.CredentialAssertionResponse { return nil }, + createResp: func() *wantypes.CredentialAssertionResponse { return nil }, }, { name: "NOK empty resp", user: user, - createResp: func() *wanlib.CredentialAssertionResponse { return &wanlib.CredentialAssertionResponse{} }, + createResp: func() *wantypes.CredentialAssertionResponse { return &wantypes.CredentialAssertionResponse{} }, }, { name: "NOK assertion with bad origin", user: user, - createResp: func() *wanlib.CredentialAssertionResponse { + createResp: func() *wantypes.CredentialAssertionResponse { assertion, err := webLogin.Begin(ctx, user) require.NoError(t, err) resp, err := key.SignAssertion("https://badorigin.com", assertion) @@ -296,7 +297,7 @@ func TestLoginFlow_Finish_errors(t *testing.T) { { name: "NOK assertion with bad RPID", user: user, - createResp: func() *wanlib.CredentialAssertionResponse { + createResp: func() *wantypes.CredentialAssertionResponse { assertion, err := webLogin.Begin(ctx, user) require.NoError(t, err) assertion.Response.RelyingPartyID = "badrpid.com" @@ -309,7 +310,7 @@ func TestLoginFlow_Finish_errors(t *testing.T) { { name: "NOK assertion signed by unknown device", user: user, - createResp: func() *wanlib.CredentialAssertionResponse { + createResp: func() *wantypes.CredentialAssertionResponse { assertion, err := webLogin.Begin(ctx, user) require.NoError(t, err) @@ -326,7 +327,7 @@ func TestLoginFlow_Finish_errors(t *testing.T) { { name: "NOK assertion with invalid signature", user: user, - createResp: func() *wanlib.CredentialAssertionResponse { + createResp: func() *wantypes.CredentialAssertionResponse { assertion, err := webLogin.Begin(ctx, user) require.NoError(t, err) // Flip a challenge bit, this should be enough to consistently fail @@ -409,12 +410,12 @@ func TestPasswordlessFlow_BeginAndFinish(t *testing.T) { // Verify that we recorded user verification requirements in storage. require.Len(t, identity.SessionData, 1) - var sd *wantypes.SessionData + var sd *wanpb.SessionData for _, v := range identity.SessionData { sd = v // Get SessionData without guessing the key. break } - wantSD := &wantypes.SessionData{ + wantSD := &wanpb.SessionData{ Challenge: sd.Challenge, UserId: nil, // aka unset AllowCredentials: nil, // aka unset @@ -471,13 +472,13 @@ func TestPasswordlessFlow_Finish_errors(t *testing.T) { tests := []struct { name string - createResp func() *wanlib.CredentialAssertionResponse + createResp func() *wantypes.CredentialAssertionResponse assertErrType func(error) bool wantErrMsg string }{ { name: "NOK response without UserID", - createResp: func() *wanlib.CredentialAssertionResponse { + createResp: func() *wantypes.CredentialAssertionResponse { // UserHandle is already nil on assertionResp return assertionResp }, @@ -486,7 +487,7 @@ func TestPasswordlessFlow_Finish_errors(t *testing.T) { }, { name: "NOK unknown user handle", - createResp: func() *wanlib.CredentialAssertionResponse { + createResp: func() *wantypes.CredentialAssertionResponse { unknownHandle := make([]byte, 10 /* arbitrary */) cp := *assertionResp cp.AssertionResponse.UserHandle = unknownHandle @@ -627,7 +628,7 @@ type fakeIdentity struct { // It's automatically assigned when UpsertWebauthnLocalAuth is called. MappedUser string UpdatedDevices []*types.MFADevice - SessionData map[string]*wantypes.SessionData + SessionData map[string]*wanpb.SessionData } func newFakeIdentity(user string, devices ...*types.MFADevice) *fakeIdentity { @@ -642,7 +643,7 @@ func newFakeIdentity(user string, devices ...*types.MFADevice) *fakeIdentity { }, }, }, - SessionData: make(map[string]*wantypes.SessionData), + SessionData: make(map[string]*wanpb.SessionData), } } @@ -687,12 +688,12 @@ func (f *fakeIdentity) GetTeleportUserByWebauthnID(ctx context.Context, webID [] return f.MappedUser, nil } -func (f *fakeIdentity) UpsertWebauthnSessionData(ctx context.Context, user, sessionID string, sd *wantypes.SessionData) error { +func (f *fakeIdentity) UpsertWebauthnSessionData(ctx context.Context, user, sessionID string, sd *wanpb.SessionData) error { f.SessionData[sessionDataKey(user, sessionID)] = sd return nil } -func (f *fakeIdentity) GetWebauthnSessionData(ctx context.Context, user, sessionID string) (*wantypes.SessionData, error) { +func (f *fakeIdentity) GetWebauthnSessionData(ctx context.Context, user, sessionID string) (*wanpb.SessionData, error) { sd, ok := f.SessionData[sessionDataKey(user, sessionID)] if !ok { return nil, trace.NotFound("not found") @@ -709,12 +710,12 @@ func sessionDataKey(user string, sessionID string) string { return fmt.Sprintf("user/%v/%v", user, sessionID) } -func (f *fakeIdentity) UpsertGlobalWebauthnSessionData(ctx context.Context, scope, id string, sd *wantypes.SessionData) error { +func (f *fakeIdentity) UpsertGlobalWebauthnSessionData(ctx context.Context, scope, id string, sd *wanpb.SessionData) error { f.SessionData[globalSessionDataKey(scope, id)] = sd return nil } -func (f *fakeIdentity) GetGlobalWebauthnSessionData(ctx context.Context, scope, id string) (*wantypes.SessionData, error) { +func (f *fakeIdentity) GetGlobalWebauthnSessionData(ctx context.Context, scope, id string) (*wanpb.SessionData, error) { sd, ok := f.SessionData[globalSessionDataKey(scope, id)] if !ok { return nil, trace.NotFound("not found") diff --git a/lib/auth/webauthn/messages.go b/lib/auth/webauthn/messages.go deleted file mode 100644 index 94218687e2a9d..0000000000000 --- a/lib/auth/webauthn/messages.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2021 Gravitational, Inc -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package webauthn - -import ( - "github.com/go-webauthn/webauthn/protocol" - "github.com/gravitational/trace" -) - -// CredentialAssertion is the payload sent to authenticators to initiate login. -type CredentialAssertion protocol.CredentialAssertion - -// Validate performs client-side validation of CredentialAssertion. -// It makes sure that data are valid and can be sent to authenticator. -// This is general purpose validation and authenticator should add its own -// on top of it, if necessary. -func (ca *CredentialAssertion) Validate() error { - switch { - case ca == nil: - return trace.BadParameter("credential assertion required") - case len(ca.Response.Challenge) == 0: - return trace.BadParameter("credential assertion challenge required") - case ca.Response.RelyingPartyID == "": - return trace.BadParameter("credential assertion relying party ID required") - } - return nil -} - -// CredentialAssertionResponse is the reply from authenticators to complete -// login. -type CredentialAssertionResponse struct { - // CredentialAssertionResponse is redefined because, unlike - // CredentialAssertion, it is likely to be manually created by package users. - // Redefining allows us to 1) make sure it can be properly JSON-marshaled - // (protocol.CredentialAssertionResponse.Extensions can't) and 2) we avoid - // leaking the duo-labs/webauthn dependency. - // The nesting of types is identical to protocol.CredentialAssertionResponse. - - PublicKeyCredential - AssertionResponse AuthenticatorAssertionResponse `json:"response"` -} - -// CredentialCreation is the payload sent to authenticators to initiate -// registration. -type CredentialCreation protocol.CredentialCreation - -// RequireResidentKey returns information whether resident key is required or -// not. It checks ResidentKey and fallbacks to RequireResidentKey. -func (cc *CredentialCreation) RequireResidentKey() (bool, error) { - as := cc.Response.AuthenticatorSelection - switch as.ResidentKey { - case protocol.ResidentKeyRequirementRequired: - if as.RequireResidentKey != nil && !*as.RequireResidentKey { - return false, trace.BadParameter("invalid combination of ResidentKey: %v and RequireResidentKey: %v", as.ResidentKey, *as.RequireResidentKey) - } - return true, nil - case protocol.ResidentKeyRequirementDiscouraged: - if as.RequireResidentKey != nil && *as.RequireResidentKey { - return false, trace.BadParameter("invalid combination of ResidentKey: %v and RequireResidentKey: %v", as.ResidentKey, *as.RequireResidentKey) - } - return false, nil - case protocol.ResidentKeyRequirementPreferred: - return false, nil - } - // If ResidentKey is not set, then fallback to the legacy RequireResidentKey - // field. - return as.RequireResidentKey != nil && *as.RequireResidentKey, nil -} - -// Validate performs client-side validation of CredentialCreation. -// It makes sure that data are valid and can be sent to authenticator. -// This is general purpose validation and authenticator should add its own -// on top of it, if necessary. -func (cc *CredentialCreation) Validate() error { - switch { - case cc == nil: - return trace.BadParameter("credential creation required") - case len(cc.Response.Challenge) == 0: - return trace.BadParameter("credential creation challenge required") - case cc.Response.RelyingParty.ID == "": - return trace.BadParameter("credential creation relying party ID required") - case len(cc.Response.RelyingParty.Name) == 0: - return trace.BadParameter("relying party name required") - case len(cc.Response.User.Name) == 0: - return trace.BadParameter("user name required") - case len(cc.Response.User.DisplayName) == 0: - return trace.BadParameter("user display name required") - case len(cc.Response.User.ID) == 0: - return trace.BadParameter("user ID required") - default: - return nil - } -} - -// CredentialCreationResponse is the reply from authenticators to complete -// registration. -type CredentialCreationResponse struct { - // CredentialCreationResponse is manually redefined, instead of directly based - // in protocol.CredentialCreationResponse, for the same reasoning that - // CredentialAssertionResponse is - in short, we want a clean package. - // The nesting of types is identical to protocol.CredentialCreationResponse. - - PublicKeyCredential - AttestationResponse AuthenticatorAttestationResponse `json:"response"` -} - -type PublicKeyCredential struct { - Credential - RawID protocol.URLEncodedBase64 `json:"rawId"` - Extensions *AuthenticationExtensionsClientOutputs `json:"extensions,omitempty"` -} - -type Credential protocol.Credential - -type AuthenticationExtensionsClientOutputs struct { - AppID bool `json:"appid,omitempty"` -} - -type AuthenticatorAssertionResponse struct { - AuthenticatorResponse - AuthenticatorData protocol.URLEncodedBase64 `json:"authenticatorData"` - Signature protocol.URLEncodedBase64 `json:"signature"` - UserHandle protocol.URLEncodedBase64 `json:"userHandle,omitempty"` -} - -type AuthenticatorResponse protocol.AuthenticatorResponse - -type AuthenticatorAttestationResponse struct { - AuthenticatorResponse - AttestationObject protocol.URLEncodedBase64 `json:"attestationObject"` -} diff --git a/lib/auth/webauthn/register.go b/lib/auth/webauthn/register.go index fef63d9c5c304..2f16c26569221 100644 --- a/lib/auth/webauthn/register.go +++ b/lib/auth/webauthn/register.go @@ -30,7 +30,8 @@ import ( log "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/types" - wantypes "github.com/gravitational/teleport/api/types/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) // RegistrationIdentity represents the subset of Identity methods used by @@ -42,8 +43,8 @@ type RegistrationIdentity interface { GetMFADevices(ctx context.Context, user string, withSecrets bool) ([]*types.MFADevice, error) UpsertMFADevice(ctx context.Context, user string, d *types.MFADevice) error - UpsertWebauthnSessionData(ctx context.Context, user, sessionID string, sd *wantypes.SessionData) error - GetWebauthnSessionData(ctx context.Context, user, sessionID string) (*wantypes.SessionData, error) + UpsertWebauthnSessionData(ctx context.Context, user, sessionID string, sd *wanpb.SessionData) error + GetWebauthnSessionData(ctx context.Context, user, sessionID string) (*wanpb.SessionData, error) DeleteWebauthnSessionData(ctx context.Context, user, sessionID string) error } @@ -52,7 +53,7 @@ type RegistrationIdentity interface { func WithInMemorySessionData(identity RegistrationIdentity) RegistrationIdentity { return &inMemoryIdentity{ RegistrationIdentity: identity, - sessionData: make(map[string]*wantypes.SessionData), + sessionData: make(map[string]*wanpb.SessionData), } } @@ -63,17 +64,17 @@ type inMemoryIdentity struct { // We don't foresee concurrent use for inMemoryIdentity, but it's easy enough // to play it safe. mu sync.RWMutex - sessionData map[string]*wantypes.SessionData + sessionData map[string]*wanpb.SessionData } -func (identity *inMemoryIdentity) UpsertWebauthnSessionData(ctx context.Context, user, sessionID string, sd *wantypes.SessionData) error { +func (identity *inMemoryIdentity) UpsertWebauthnSessionData(ctx context.Context, user, sessionID string, sd *wanpb.SessionData) error { identity.mu.Lock() defer identity.mu.Unlock() identity.sessionData[sessionDataKey(user, sessionID)] = sd return nil } -func (identity *inMemoryIdentity) GetWebauthnSessionData(ctx context.Context, user, sessionID string) (*wantypes.SessionData, error) { +func (identity *inMemoryIdentity) GetWebauthnSessionData(ctx context.Context, user, sessionID string) (*wanpb.SessionData, error) { identity.mu.RLock() defer identity.mu.RUnlock() sd, ok := identity.sessionData[sessionDataKey(user, sessionID)] @@ -126,7 +127,7 @@ type RegistrationFlow struct { // resident key. // As a side effect Begin may assign (and record in storage) a WebAuthn ID for // the user. -func (f *RegistrationFlow) Begin(ctx context.Context, user string, passwordless bool) (*CredentialCreation, error) { +func (f *RegistrationFlow) Begin(ctx context.Context, user string, passwordless bool) (*wantypes.CredentialCreation, error) { if user == "" { return nil, trace.BadParameter("user required") } @@ -192,7 +193,7 @@ func (f *RegistrationFlow) Begin(ctx context.Context, user string, passwordless return nil, trace.Wrap(err) } - return (*CredentialCreation)(cc), nil + return wantypes.CredentialCreationFromProtocol(cc), nil } func upsertOrGetWebID(ctx context.Context, user string, identity RegistrationIdentity) ([]byte, error) { @@ -233,7 +234,7 @@ type RegisterResponse struct { // DeviceName is the name for the new device. DeviceName string // CreationResponse is the response from the new device. - CreationResponse *CredentialCreationResponse + CreationResponse *wantypes.CredentialCreationResponse // Passwordless is true if this is expected to be a passwordless registration. // Callers may make certain concessions when processing passwordless // registration (such as skipping password validation), this flag reflects that. @@ -349,7 +350,7 @@ func (f *RegistrationFlow) Finish(ctx context.Context, req RegisterResponse) (*t return newDevice, nil } -func parseCredentialCreationResponse(resp *CredentialCreationResponse) (*protocol.ParsedCredentialCreationData, error) { +func parseCredentialCreationResponse(resp *wantypes.CredentialCreationResponse) (*protocol.ParsedCredentialCreationData, error) { // Remove extensions before marshaling, duo-labs/webauthn isn't expecting it. exts := resp.Extensions resp.Extensions = nil diff --git a/lib/auth/webauthn/register_test.go b/lib/auth/webauthn/register_test.go index f78fbb3fa1481..fd4292aab18a4 100644 --- a/lib/auth/webauthn/register_test.go +++ b/lib/auth/webauthn/register_test.go @@ -29,6 +29,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/auth/mocku2f" wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) func TestRegistrationFlow_BeginFinish(t *testing.T) { @@ -197,9 +198,9 @@ func TestRegistrationFlow_Begin_excludeList(t *testing.T) { return bytes.Compare(got[i].CredentialID, got[j].CredentialID) == -1 }) - want := make([]protocol.CredentialDescriptor, len(test.wantExcludeList)) + want := make([]wantypes.CredentialDescriptor, len(test.wantExcludeList)) for i, id := range test.wantExcludeList { - want[i] = protocol.CredentialDescriptor{ + want[i] = wantypes.CredentialDescriptor{ Type: protocol.PublicKeyCredentialType, CredentialID: id, } @@ -306,7 +307,7 @@ func TestRegistrationFlow_Finish_errors(t *testing.T) { tests := []struct { name string user, deviceName string - createResp func() *wanlib.CredentialCreationResponse + createResp func() *wantypes.CredentialCreationResponse wantErr string passwordless bool }{ @@ -314,28 +315,28 @@ func TestRegistrationFlow_Finish_errors(t *testing.T) { name: "NOK user empty", user: "", deviceName: "webauthn2", - createResp: func() *wanlib.CredentialCreationResponse { return okCCR }, + createResp: func() *wantypes.CredentialCreationResponse { return okCCR }, wantErr: "user required", }, { name: "NOK device name empty", user: user, deviceName: "", - createResp: func() *wanlib.CredentialCreationResponse { return okCCR }, + createResp: func() *wantypes.CredentialCreationResponse { return okCCR }, wantErr: "device name required", }, { name: "NOK credential response nil", user: user, deviceName: "webauthn2", - createResp: func() *wanlib.CredentialCreationResponse { return nil }, + createResp: func() *wantypes.CredentialCreationResponse { return nil }, wantErr: "response required", }, { name: "NOK credential with bad origin", user: user, deviceName: "webauthn2", - createResp: func() *wanlib.CredentialCreationResponse { + createResp: func() *wantypes.CredentialCreationResponse { resp, err := key.SignCredentialCreation("https://alpacasarerad.com" /* origin */, cc) require.NoError(t, err) return resp @@ -346,7 +347,7 @@ func TestRegistrationFlow_Finish_errors(t *testing.T) { name: "NOK credential with bad RPID", user: user, deviceName: "webauthn2", - createResp: func() *wanlib.CredentialCreationResponse { + createResp: func() *wantypes.CredentialCreationResponse { cc, err := webRegistration.Begin(ctx, user, false /* passwordless */) require.NoError(t, err) cc.Response.RelyingParty.ID = "badrpid.com" @@ -361,7 +362,7 @@ func TestRegistrationFlow_Finish_errors(t *testing.T) { name: "NOK credential with invalid signature", user: user, deviceName: "webauthn2", - createResp: func() *wanlib.CredentialCreationResponse { + createResp: func() *wantypes.CredentialCreationResponse { cc, err := webRegistration.Begin(ctx, user, false /* passwordless */) require.NoError(t, err) // Flip a challenge bit, this should be enough to consistently fail @@ -379,7 +380,7 @@ func TestRegistrationFlow_Finish_errors(t *testing.T) { user: user, deviceName: "webauthn2", passwordless: true, - createResp: func() *wanlib.CredentialCreationResponse { + createResp: func() *wantypes.CredentialCreationResponse { cc, err := webRegistration.Begin(ctx, user, false /* passwordless */) require.NoError(t, err) resp, err := key.SignCredentialCreation(webOrigin, cc) @@ -393,7 +394,7 @@ func TestRegistrationFlow_Finish_errors(t *testing.T) { user: user, deviceName: "webauthn2", passwordless: true, - createResp: func() *wanlib.CredentialCreationResponse { + createResp: func() *wantypes.CredentialCreationResponse { cc, err := webRegistration.Begin(ctx, user, true /* passwordless */) require.NoError(t, err) diff --git a/lib/auth/webauthn/session.go b/lib/auth/webauthn/session.go index fc9dd129964b6..2928038e8087a 100644 --- a/lib/auth/webauthn/session.go +++ b/lib/auth/webauthn/session.go @@ -23,7 +23,7 @@ import ( wan "github.com/go-webauthn/webauthn/webauthn" "github.com/gravitational/trace" - wantypes "github.com/gravitational/teleport/api/types/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" ) // scopeLogin identifies session data stored for login. @@ -37,13 +37,13 @@ const scopeLogin = "login" // that use in-memory storage. const scopeSession = "registration" -func sessionToPB(sd *wan.SessionData) (*wantypes.SessionData, error) { +func sessionToPB(sd *wan.SessionData) (*wanpb.SessionData, error) { rawChallenge, err := base64.RawURLEncoding.DecodeString(sd.Challenge) if err != nil { return nil, trace.Wrap(err) } // TODO(codingllama): Record extensions in stored session data. - return &wantypes.SessionData{ + return &wanpb.SessionData{ Challenge: rawChallenge, UserId: sd.UserID, AllowCredentials: sd.AllowedCredentialIDs, @@ -51,7 +51,7 @@ func sessionToPB(sd *wan.SessionData) (*wantypes.SessionData, error) { }, nil } -func sessionFromPB(sd *wantypes.SessionData) *wan.SessionData { +func sessionFromPB(sd *wanpb.SessionData) *wan.SessionData { // TODO(codingllama): Record extensions in stored session data. return &wan.SessionData{ Challenge: base64.RawURLEncoding.EncodeToString(sd.Challenge), diff --git a/lib/auth/webauthncli/api.go b/lib/auth/webauthncli/api.go index 08feab8305303..5ee83859dccce 100644 --- a/lib/auth/webauthncli/api.go +++ b/lib/auth/webauthncli/api.go @@ -24,8 +24,8 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/lib/auth/touchid" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" - "github.com/gravitational/teleport/lib/auth/webauthnwin" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" + wanwin "github.com/gravitational/teleport/lib/auth/webauthnwin" ) // ErrUsingNonRegisteredDevice is returned from Login when the user attempts to @@ -117,7 +117,7 @@ type LoginOpts struct { // authentication and connected devices. func Login( ctx context.Context, - origin string, assertion *wanlib.CredentialAssertion, prompt LoginPrompt, opts *LoginOpts, + origin string, assertion *wantypes.CredentialAssertion, prompt LoginPrompt, opts *LoginOpts, ) (*proto.MFAAuthenticateResponse, string, error) { // origin vs RPID sanity check. // Doesn't necessarily means a failure, but it's likely to be one. @@ -137,10 +137,10 @@ func Login( user = opts.User } - if webauthnwin.IsAvailable() { + if wanwin.IsAvailable() { log.Debug("WebAuthnWin: Using windows webauthn for credential assertion") - return webauthnwin.Login(ctx, origin, assertion, &webauthnwin.LoginOpts{ - AuthenticatorAttachment: webauthnwin.AuthenticatorAttachment(attachment), + return wanwin.Login(ctx, origin, assertion, &wanwin.LoginOpts{ + AuthenticatorAttachment: wanwin.AuthenticatorAttachment(attachment), }) } @@ -165,7 +165,7 @@ func Login( func crossPlatformLogin( ctx context.Context, - origin string, assertion *wanlib.CredentialAssertion, prompt LoginPrompt, opts *LoginOpts, + origin string, assertion *wantypes.CredentialAssertion, prompt LoginPrompt, opts *LoginOpts, ) (*proto.MFAAuthenticateResponse, string, error) { if isLibfido2Enabled() { log.Debug("FIDO2: Using libfido2 for assertion") @@ -189,14 +189,14 @@ func crossPlatformLogin( return resp, "" /* credentialUser */, err } -func platformLogin(origin, user string, assertion *wanlib.CredentialAssertion, prompt LoginPrompt) (*proto.MFAAuthenticateResponse, string, error) { +func platformLogin(origin, user string, assertion *wantypes.CredentialAssertion, prompt LoginPrompt) (*proto.MFAAuthenticateResponse, string, error) { resp, credentialUser, err := touchid.AttemptLogin(origin, user, assertion, ToTouchIDCredentialPicker(prompt)) if err != nil { return nil, "", err } return &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(resp), + Webauthn: wantypes.CredentialAssertionResponseToProto(resp), }, }, credentialUser, nil } @@ -225,10 +225,10 @@ type RegisterPrompt interface { // type of authentication and connected devices. func Register( ctx context.Context, - origin string, cc *wanlib.CredentialCreation, prompt RegisterPrompt) (*proto.MFARegisterResponse, error) { - if webauthnwin.IsAvailable() { + origin string, cc *wantypes.CredentialCreation, prompt RegisterPrompt) (*proto.MFARegisterResponse, error) { + if wanwin.IsAvailable() { log.Debug("WebAuthnWin: Using windows webauthn for credential creation") - return webauthnwin.Register(ctx, origin, cc) + return wanwin.Register(ctx, origin, cc) } if isLibfido2Enabled() { @@ -258,5 +258,5 @@ func HasPlatformSupport() bool { // IsFIDO2Available returns true if FIDO2 is implemented either via native // libfido2 library or Windows WebAuthn API. func IsFIDO2Available() bool { - return isLibfido2Enabled() || webauthnwin.IsAvailable() + return isLibfido2Enabled() || wanwin.IsAvailable() } diff --git a/lib/auth/webauthncli/fido2.go b/lib/auth/webauthncli/fido2.go index c2aa25d76d328..3e98416808260 100644 --- a/lib/auth/webauthncli/fido2.go +++ b/lib/auth/webauthncli/fido2.go @@ -37,7 +37,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" wanpb "github.com/gravitational/teleport/api/types/webauthn" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) // User-friendly device filter errors. @@ -95,7 +95,7 @@ func isLibfido2Enabled() bool { // fido2Login implements FIDO2Login. func fido2Login( ctx context.Context, - origin string, assertion *wanlib.CredentialAssertion, prompt LoginPrompt, opts *LoginOpts, + origin string, assertion *wantypes.CredentialAssertion, prompt LoginPrompt, opts *LoginOpts, ) (*proto.MFAAuthenticateResponse, string, error) { switch { case origin == "": @@ -131,7 +131,7 @@ func fido2Login( rpID := assertion.Response.RelyingPartyID var appID string - if val, ok := assertion.Response.Extensions[wanlib.AppIDExtension]; ok { + if val, ok := assertion.Response.Extensions[wantypes.AppIDExtension]; ok { appID = fmt.Sprint(val) } @@ -330,7 +330,7 @@ func pickAssertion( // fido2Register implements FIDO2Register. func fido2Register( ctx context.Context, - origin string, cc *wanlib.CredentialCreation, prompt RegisterPrompt, + origin string, cc *wantypes.CredentialCreation, prompt RegisterPrompt, ) (*proto.MFARegisterResponse, error) { switch { case origin == "": @@ -380,7 +380,6 @@ func fido2Register( ID: cc.Response.User.ID, Name: cc.Response.User.Name, DisplayName: cc.Response.User.DisplayName, - Icon: cc.Response.User.Icon, } plat := cc.Response.AuthenticatorSelection.AuthenticatorAttachment == protocol.Platform uv := cc.Response.AuthenticatorSelection.UserVerification == protocol.VerificationRequired diff --git a/lib/auth/webauthncli/fido2_common.go b/lib/auth/webauthncli/fido2_common.go index f77e42e4c26cc..e9ed61eda9a6f 100644 --- a/lib/auth/webauthncli/fido2_common.go +++ b/lib/auth/webauthncli/fido2_common.go @@ -24,7 +24,7 @@ import ( "github.com/gravitational/trace" "github.com/gravitational/teleport/api/client/proto" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) // FIDO2PollInterval is the poll interval used to check for new FIDO2 devices. @@ -41,7 +41,7 @@ var FIDO2PollInterval = 200 * time.Millisecond // IsFIDO2Available. func FIDO2Login( ctx context.Context, - origin string, assertion *wanlib.CredentialAssertion, prompt LoginPrompt, opts *LoginOpts, + origin string, assertion *wantypes.CredentialAssertion, prompt LoginPrompt, opts *LoginOpts, ) (*proto.MFAAuthenticateResponse, string, error) { return fido2Login(ctx, origin, assertion, prompt, opts) } @@ -53,7 +53,7 @@ func FIDO2Login( // IsFIDO2Available. func FIDO2Register( ctx context.Context, - origin string, cc *wanlib.CredentialCreation, prompt RegisterPrompt, + origin string, cc *wantypes.CredentialCreation, prompt RegisterPrompt, ) (*proto.MFARegisterResponse, error) { return fido2Register(ctx, origin, cc, prompt) } @@ -74,23 +74,23 @@ func FIDO2Diag(ctx context.Context, promptOut io.Writer) (*FIDO2DiagResult, erro // Attempt registration. const origin = "localhost" - cc := &wanlib.CredentialCreation{ - Response: protocol.PublicKeyCredentialCreationOptions{ + cc := &wantypes.CredentialCreation{ + Response: wantypes.PublicKeyCredentialCreationOptions{ Challenge: make([]byte, 32), - RelyingParty: protocol.RelyingPartyEntity{ - CredentialEntity: protocol.CredentialEntity{ + RelyingParty: wantypes.RelyingPartyEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "localhost", }, ID: "localhost", }, - User: protocol.UserEntity{ - CredentialEntity: protocol.CredentialEntity{ + User: wantypes.UserEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "test", }, DisplayName: "test", ID: []byte("test"), }, - Parameters: []protocol.CredentialParameter{ + Parameters: []wantypes.CredentialParameter{ { Type: protocol.PublicKeyCredentialType, Algorithm: webauthncose.AlgES256, @@ -107,11 +107,11 @@ func FIDO2Diag(ctx context.Context, promptOut io.Writer) (*FIDO2DiagResult, erro res.RegisterSuccessful = true // Attempt login. - assertion := &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + assertion := &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: make([]byte, 32), RelyingPartyID: cc.Response.RelyingParty.ID, - AllowedCredentials: []protocol.CredentialDescriptor{ + AllowedCredentials: []wantypes.CredentialDescriptor{ { Type: protocol.PublicKeyCredentialType, CredentialID: ccr.GetWebauthn().GetRawId(), diff --git a/lib/auth/webauthncli/fido2_other.go b/lib/auth/webauthncli/fido2_other.go index 4b2505f4a67d6..be014196559c6 100644 --- a/lib/auth/webauthncli/fido2_other.go +++ b/lib/auth/webauthncli/fido2_other.go @@ -22,7 +22,7 @@ import ( "errors" "github.com/gravitational/teleport/api/client/proto" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) var errFIDO2Unavailable = errors.New("FIDO2 unavailable in current build") @@ -34,14 +34,14 @@ func isLibfido2Enabled() bool { func fido2Login( ctx context.Context, - origin string, assertion *wanlib.CredentialAssertion, prompt LoginPrompt, opts *LoginOpts, + origin string, assertion *wantypes.CredentialAssertion, prompt LoginPrompt, opts *LoginOpts, ) (*proto.MFAAuthenticateResponse, string, error) { return nil, "", errFIDO2Unavailable } func fido2Register( ctx context.Context, - origin string, cc *wanlib.CredentialCreation, prompt RegisterPrompt, + origin string, cc *wantypes.CredentialCreation, prompt RegisterPrompt, ) (*proto.MFARegisterResponse, error) { return nil, errFIDO2Unavailable } diff --git a/lib/auth/webauthncli/fido2_test.go b/lib/auth/webauthncli/fido2_test.go index 7b6ec46acd8a0..558b9b409093a 100644 --- a/lib/auth/webauthncli/fido2_test.go +++ b/lib/auth/webauthncli/fido2_test.go @@ -40,6 +40,7 @@ import ( "github.com/gravitational/teleport/lib/auth/mocku2f" wanlib "github.com/gravitational/teleport/lib/auth/webauthn" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) var ( @@ -243,14 +244,14 @@ func TestFIDO2Login(t *testing.T) { legacy1 := mustNewFIDO2Device("/legacy1", "" /* pin */, &libfido2.DeviceInfo{Options: authOpts}) legacy1.wantRPID = appID - challenge, err := protocol.CreateChallenge() + challenge, err := wantypes.CreateChallenge() require.NoError(t, err, "CreateChallenge failed") - baseAssertion := &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + baseAssertion := &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: challenge, RelyingPartyID: rpID, - AllowedCredentials: []protocol.CredentialDescriptor{}, + AllowedCredentials: []wantypes.CredentialDescriptor{}, UserVerification: protocol.VerificationDiscouraged, Extensions: map[string]interface{}{}, }, @@ -261,7 +262,7 @@ func TestFIDO2Login(t *testing.T) { timeout time.Duration fido2 *fakeFIDO2 setUP func() - createAssertion func() *wanlib.CredentialAssertion + createAssertion func() *wantypes.CredentialAssertion prompt wancli.LoginPrompt opts *wancli.LoginOpts // assertResponse and wantErr are mutually exclusive. @@ -279,9 +280,9 @@ func TestFIDO2Login(t *testing.T) { auth1.setUP() }() }, - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion - cp.Response.AllowedCredentials = []protocol.CredentialDescriptor{ + cp.Response.AllowedCredentials = []wantypes.CredentialDescriptor{ {CredentialID: auth1.credentialID()}, } return &cp @@ -294,9 +295,9 @@ func TestFIDO2Login(t *testing.T) { name: "pin protected device", fido2: newFakeFIDO2(pin1), setUP: pin1.setUP, - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion - cp.Response.AllowedCredentials = []protocol.CredentialDescriptor{ + cp.Response.AllowedCredentials = []wantypes.CredentialDescriptor{ {CredentialID: pin1.credentialID()}, } return &cp @@ -306,9 +307,9 @@ func TestFIDO2Login(t *testing.T) { name: "biometric device", fido2: newFakeFIDO2(bio1), setUP: bio1.setUP, - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion - cp.Response.AllowedCredentials = []protocol.CredentialDescriptor{ + cp.Response.AllowedCredentials = []wantypes.CredentialDescriptor{ {CredentialID: bio1.credentialID()}, } return &cp @@ -318,13 +319,13 @@ func TestFIDO2Login(t *testing.T) { name: "legacy device (AppID)", fido2: newFakeFIDO2(legacy1), setUP: legacy1.setUP, - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion - cp.Response.AllowedCredentials = []protocol.CredentialDescriptor{ + cp.Response.AllowedCredentials = []wantypes.CredentialDescriptor{ {CredentialID: legacy1.credentialID()}, } - cp.Response.Extensions = protocol.AuthenticationExtensions{ - wanlib.AppIDExtension: appID, + cp.Response.Extensions = wantypes.AuthenticationExtensions{ + wantypes.AppIDExtension: appID, } return &cp }, @@ -341,16 +342,16 @@ func TestFIDO2Login(t *testing.T) { legacy1, ), setUP: bio1.setUP, - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion - cp.Response.AllowedCredentials = []protocol.CredentialDescriptor{ + cp.Response.AllowedCredentials = []wantypes.CredentialDescriptor{ {CredentialID: auth1.credentialID()}, {CredentialID: pin1.credentialID()}, {CredentialID: bio1.credentialID()}, {CredentialID: legacy1.credentialID()}, } - cp.Response.Extensions = protocol.AuthenticationExtensions{ - wanlib.AppIDExtension: appID, + cp.Response.Extensions = wantypes.AuthenticationExtensions{ + wantypes.AppIDExtension: appID, } return &cp }, @@ -367,15 +368,15 @@ func TestFIDO2Login(t *testing.T) { legacy1, // doesn't match RPID or AppID ), setUP: auth1.setUP, - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion - cp.Response.AllowedCredentials = []protocol.CredentialDescriptor{ + cp.Response.AllowedCredentials = []wantypes.CredentialDescriptor{ {CredentialID: auth1.credentialID()}, {CredentialID: bio1.credentialID()}, {CredentialID: legacy1.credentialID()}, } - cp.Response.Extensions = protocol.AuthenticationExtensions{ - wanlib.AppIDExtension: "https://badexample.com", + cp.Response.Extensions = wantypes.AuthenticationExtensions{ + wantypes.AppIDExtension: "https://badexample.com", } return &cp }, @@ -391,9 +392,9 @@ func TestFIDO2Login(t *testing.T) { bio1, ), setUP: pin2.setUP, - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion - cp.Response.AllowedCredentials = []protocol.CredentialDescriptor{ + cp.Response.AllowedCredentials = []wantypes.CredentialDescriptor{ {CredentialID: auth1.credentialID()}, {CredentialID: pin1.credentialID()}, {CredentialID: pin2.credentialID()}, @@ -410,9 +411,9 @@ func TestFIDO2Login(t *testing.T) { timeout: 10 * time.Millisecond, fido2: newFakeFIDO2(), setUP: func() {}, - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion - cp.Response.AllowedCredentials = []protocol.CredentialDescriptor{ + cp.Response.AllowedCredentials = []wantypes.CredentialDescriptor{ {CredentialID: auth1.credentialID()}, } return &cp @@ -424,9 +425,9 @@ func TestFIDO2Login(t *testing.T) { timeout: 10 * time.Millisecond, fido2: newFakeFIDO2(auth1, pin1, bio1, legacy1), setUP: func() {}, // no interaction - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion - cp.Response.AllowedCredentials = []protocol.CredentialDescriptor{ + cp.Response.AllowedCredentials = []wantypes.CredentialDescriptor{ {CredentialID: auth1.credentialID()}, {CredentialID: pin1.credentialID()}, {CredentialID: bio1.credentialID()}, @@ -440,9 +441,9 @@ func TestFIDO2Login(t *testing.T) { timeout: 10 * time.Millisecond, fido2: newFakeFIDO2(auth1, pin1), setUP: func() {}, // no interaction - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion - cp.Response.AllowedCredentials = []protocol.CredentialDescriptor{ + cp.Response.AllowedCredentials = []wantypes.CredentialDescriptor{ {CredentialID: auth1.credentialID()}, } return &cp @@ -454,7 +455,7 @@ func TestFIDO2Login(t *testing.T) { fido2: newFakeFIDO2(pin3, bio2), // pin3 and bio2 have resident credentials setUP: pin3.setUP, // user chooses pin3, but cancels before further touches prompt: &pinCancelPrompt{pin: pin3.pin}, // cancel set on test body - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion cp.Response.AllowedCredentials = nil // passwordless forces PIN cp.Response.UserVerification = protocol.VerificationRequired @@ -466,7 +467,7 @@ func TestFIDO2Login(t *testing.T) { name: "passwordless pin", fido2: newFakeFIDO2(pin3), setUP: pin3.setUP, - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion cp.Response.AllowedCredentials = nil cp.Response.UserVerification = protocol.VerificationRequired @@ -483,7 +484,7 @@ func TestFIDO2Login(t *testing.T) { name: "passwordless biometric (llama)", fido2: newFakeFIDO2(bio2), setUP: bio2.setUP, - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion cp.Response.AllowedCredentials = nil cp.Response.UserVerification = protocol.VerificationRequired @@ -503,7 +504,7 @@ func TestFIDO2Login(t *testing.T) { name: "passwordless biometric (alpaca)", fido2: newFakeFIDO2(bio2), setUP: bio2.setUP, - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion cp.Response.AllowedCredentials = nil cp.Response.UserVerification = protocol.VerificationRequired @@ -523,7 +524,7 @@ func TestFIDO2Login(t *testing.T) { name: "passwordless single-choice credential picker", fido2: newFakeFIDO2(pin3), setUP: pin3.setUP, - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion cp.Response.AllowedCredentials = nil cp.Response.UserVerification = protocol.VerificationRequired @@ -540,7 +541,7 @@ func TestFIDO2Login(t *testing.T) { name: "passwordless multi-choice credential picker", fido2: newFakeFIDO2(bio2), setUP: bio2.setUP, - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion cp.Response.AllowedCredentials = nil cp.Response.UserVerification = protocol.VerificationRequired @@ -557,7 +558,7 @@ func TestFIDO2Login(t *testing.T) { name: "NOK passwordless no credentials", fido2: newFakeFIDO2(bio1), setUP: bio1.setUP, - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion cp.Response.AllowedCredentials = nil cp.Response.UserVerification = protocol.VerificationRequired @@ -570,7 +571,7 @@ func TestFIDO2Login(t *testing.T) { name: "NOK passwordless unknown user", fido2: newFakeFIDO2(bio2), setUP: bio2.setUP, - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { cp := *baseAssertion cp.Response.AllowedCredentials = nil cp.Response.UserVerification = protocol.VerificationRequired @@ -691,8 +692,8 @@ func TestFIDO2Login_retryUVFailures(t *testing.T) { const rpID = "example.com" const origin = "https://example.com" ctx := context.Background() - assertion := &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + assertion := &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: []byte{1, 2, 3, 4, 5}, // arbitrary RelyingPartyID: rpID, UserVerification: protocol.VerificationRequired, @@ -745,8 +746,8 @@ func TestFIDO2Login_singleResidentCredential(t *testing.T) { const rpID = "example.com" const origin = "https://example.com" ctx := context.Background() - assertion := &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + assertion := &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: []byte{1, 2, 3, 4, 5}, // arbitrary RelyingPartyID: rpID, UserVerification: protocol.VerificationRequired, @@ -862,11 +863,11 @@ func TestFIDO2Login_PromptTouch(t *testing.T) { }, }) - mfaAssertion := &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + mfaAssertion := &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: make([]byte, 32), RelyingPartyID: rpID, - AllowedCredentials: []protocol.CredentialDescriptor{ + AllowedCredentials: []wantypes.CredentialDescriptor{ { Type: protocol.PublicKeyCredentialType, CredentialID: auth1.credentialID(), @@ -882,8 +883,8 @@ func TestFIDO2Login_PromptTouch(t *testing.T) { }, }, } - pwdlessAssertion := &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + pwdlessAssertion := &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: make([]byte, 32), RelyingPartyID: rpID, UserVerification: protocol.VerificationRequired, @@ -893,7 +894,7 @@ func TestFIDO2Login_PromptTouch(t *testing.T) { tests := []struct { name string fido2 *fakeFIDO2 - assertion *wanlib.CredentialAssertion + assertion *wantypes.CredentialAssertion prompt wancli.LoginPrompt opts *wancli.LoginOpts wantTouches int @@ -971,29 +972,29 @@ func TestFIDO2Login_u2fDevice(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() - cc := &wanlib.CredentialCreation{ - Response: protocol.PublicKeyCredentialCreationOptions{ + cc := &wantypes.CredentialCreation{ + Response: wantypes.PublicKeyCredentialCreationOptions{ Challenge: []byte{1, 2, 3, 4, 5}, // arbitrary - RelyingParty: protocol.RelyingPartyEntity{ + RelyingParty: wantypes.RelyingPartyEntity{ ID: rpID, - CredentialEntity: protocol.CredentialEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "rp name", }, }, - Parameters: []protocol.CredentialParameter{ + Parameters: []wantypes.CredentialParameter{ { Type: protocol.PublicKeyCredentialType, Algorithm: webauthncose.AlgES256, }, }, - User: protocol.UserEntity{ + User: wantypes.UserEntity{ ID: []byte{1, 2, 3, 4, 1}, // arbitrary, - CredentialEntity: protocol.CredentialEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "user name", }, DisplayName: "user display name", }, - AuthenticatorSelection: protocol.AuthenticatorSelection{ + AuthenticatorSelection: wantypes.AuthenticatorSelection{ UserVerification: protocol.VerificationDiscouraged, }, Attestation: protocol.PreferNoAttestation, @@ -1004,11 +1005,11 @@ func TestFIDO2Login_u2fDevice(t *testing.T) { ccr, err := wancli.FIDO2Register(ctx, origin, cc, dev /* prompt */) require.NoError(t, err, "FIDO2Register errored") - assertion := &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + assertion := &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: []byte{1, 2, 3, 4, 5}, // arbitrary RelyingPartyID: rpID, - AllowedCredentials: []protocol.CredentialDescriptor{ + AllowedCredentials: []wantypes.CredentialDescriptor{ { Type: protocol.PublicKeyCredentialType, CredentialID: ccr.GetWebauthn().GetRawId(), @@ -1042,8 +1043,8 @@ func TestFIDO2Login_bioErrorHandling(t *testing.T) { // Prepare a passwordless assertion. // MFA would do as well; both are realistic here. const origin = "https://example.com" - assertion := &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + assertion := &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: []byte{1, 2, 3, 4, 5}, RelyingPartyID: "example.com", AllowedCredentials: nil, // passwordless @@ -1128,11 +1129,11 @@ func TestFIDO2Login_errors(t *testing.T) { f2.setCallbacks() const origin = "https://example.com" - okAssertion := &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + okAssertion := &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: make([]byte, 32), RelyingPartyID: "example.com", - AllowedCredentials: []protocol.CredentialDescriptor{ + AllowedCredentials: []wantypes.CredentialDescriptor{ {Type: protocol.PublicKeyCredentialType, CredentialID: []byte{1, 2, 3, 4, 5}}, }, }, @@ -1148,7 +1149,7 @@ func TestFIDO2Login_errors(t *testing.T) { tests := []struct { name string origin string - assertion *wanlib.CredentialAssertion + assertion *wantypes.CredentialAssertion prompt wancli.LoginPrompt wantErr string }{ @@ -1222,29 +1223,29 @@ func TestFIDO2_LoginRegister_interactionErrors(t *testing.T) { const rpID = "goteleport.com" const origin = "https://goteleport.com" - mfaCC := &wanlib.CredentialCreation{ - Response: protocol.PublicKeyCredentialCreationOptions{ + mfaCC := &wantypes.CredentialCreation{ + Response: wantypes.PublicKeyCredentialCreationOptions{ Challenge: []byte{1, 2, 3, 4, 5}, // arbitrary - RelyingParty: protocol.RelyingPartyEntity{ - CredentialEntity: protocol.CredentialEntity{ + RelyingParty: wantypes.RelyingPartyEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "Teleport", }, ID: rpID, }, - User: protocol.UserEntity{ - CredentialEntity: protocol.CredentialEntity{ + User: wantypes.UserEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "llama", }, DisplayName: "Llama", ID: []byte{1, 1, 1, 1, 1}, // arbitrary }, - Parameters: []protocol.CredentialParameter{ + Parameters: []wantypes.CredentialParameter{ { Type: protocol.PublicKeyCredentialType, Algorithm: webauthncose.AlgES256, }, }, - AuthenticatorSelection: protocol.AuthenticatorSelection{ + AuthenticatorSelection: wantypes.AuthenticatorSelection{ RequireResidentKey: protocol.ResidentKeyNotRequired(), ResidentKey: protocol.ResidentKeyRequirementDiscouraged, UserVerification: protocol.VerificationDiscouraged, @@ -1265,15 +1266,15 @@ func TestFIDO2_LoginRegister_interactionErrors(t *testing.T) { registeredCreds = append(registeredCreds, resp.GetWebauthn().RawId) } - mfaAssertion := &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + mfaAssertion := &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: []byte{1, 2, 3, 4, 5}, // arbitrary RelyingPartyID: rpID, UserVerification: protocol.VerificationDiscouraged, }, } for _, cred := range registeredCreds { - mfaAssertion.Response.AllowedCredentials = append(mfaAssertion.Response.AllowedCredentials, protocol.CredentialDescriptor{ + mfaAssertion.Response.AllowedCredentials = append(mfaAssertion.Response.AllowedCredentials, wantypes.CredentialDescriptor{ Type: protocol.PublicKeyCredentialType, CredentialID: cred, }) @@ -1286,20 +1287,20 @@ func TestFIDO2_LoginRegister_interactionErrors(t *testing.T) { // FIDO2Login interaction tests. for _, test := range []struct { name string - createAssertion func() *wanlib.CredentialAssertion + createAssertion func() *wantypes.CredentialAssertion prompt wancli.LoginPrompt wantErr string }{ { name: "no registered credential", - createAssertion: func() *wanlib.CredentialAssertion { return mfaAssertion }, + createAssertion: func() *wantypes.CredentialAssertion { return mfaAssertion }, prompt: notRegistered, wantErr: wancli.ErrUsingNonRegisteredDevice.Error(), }, { // Theoretically could happen, but not something we do today. name: "mfa lacks UV", - createAssertion: func() *wanlib.CredentialAssertion { + createAssertion: func() *wantypes.CredentialAssertion { mfaUV := *mfaAssertion mfaUV.Response.UserVerification = protocol.VerificationRequired return &mfaUV @@ -1309,20 +1310,20 @@ func TestFIDO2_LoginRegister_interactionErrors(t *testing.T) { }, { name: "passwordless lacks UV", - createAssertion: func() *wanlib.CredentialAssertion { return &pwdlessAssertion }, + createAssertion: func() *wantypes.CredentialAssertion { return &pwdlessAssertion }, prompt: noPIN, // PIN unset means it cannot do UV wantErr: "passwordless", }, { // Fictional scenario, no real-world authenticators match. name: "passwordless lacks RK", - createAssertion: func() *wanlib.CredentialAssertion { return &pwdlessAssertion }, + createAssertion: func() *wantypes.CredentialAssertion { return &pwdlessAssertion }, prompt: noRK, wantErr: "passwordless", }, { name: "passwordless U2F", - createAssertion: func() *wanlib.CredentialAssertion { return &pwdlessAssertion }, + createAssertion: func() *wantypes.CredentialAssertion { return &pwdlessAssertion }, prompt: u2f, wantErr: context.DeadlineExceeded.Error(), // silently filtered, times out }, @@ -1338,7 +1339,7 @@ func TestFIDO2_LoginRegister_interactionErrors(t *testing.T) { excludeCC := *mfaCC for _, cred := range registeredCreds { - excludeCC.Response.CredentialExcludeList = append(excludeCC.Response.CredentialExcludeList, protocol.CredentialDescriptor{ + excludeCC.Response.CredentialExcludeList = append(excludeCC.Response.CredentialExcludeList, wantypes.CredentialDescriptor{ Type: protocol.PublicKeyCredentialType, CredentialID: cred, }) @@ -1352,38 +1353,38 @@ func TestFIDO2_LoginRegister_interactionErrors(t *testing.T) { // FIDO2Register interaction tests. for _, test := range []struct { name string - createCC func() *wanlib.CredentialCreation + createCC func() *wantypes.CredentialCreation prompt wancli.RegisterPrompt wantErr string }{ { name: "excluded credential", - createCC: func() *wanlib.CredentialCreation { return &excludeCC }, + createCC: func() *wantypes.CredentialCreation { return &excludeCC }, prompt: noPIN, wantErr: "registered credential", }, { name: "excluded credential (U2F)", - createCC: func() *wanlib.CredentialCreation { return &excludeCC }, + createCC: func() *wantypes.CredentialCreation { return &excludeCC }, prompt: u2f, wantErr: context.DeadlineExceeded.Error(), // silently filtered, times out }, { name: "passwordless lacks UV", - createCC: func() *wanlib.CredentialCreation { return &pwdlessCC }, + createCC: func() *wantypes.CredentialCreation { return &pwdlessCC }, prompt: noPIN, // PIN unset means it cannot do UV wantErr: "user verification", }, { // Fictional scenario, no real-world authenticators match. name: "passwordless lacks RK", - createCC: func() *wanlib.CredentialCreation { return &pwdlessCC }, + createCC: func() *wantypes.CredentialCreation { return &pwdlessCC }, prompt: noRK, wantErr: "resident key", }, { name: "passwordless U2F", - createCC: func() *wanlib.CredentialCreation { return &pwdlessCC }, + createCC: func() *wantypes.CredentialCreation { return &pwdlessCC }, prompt: u2f, wantErr: context.DeadlineExceeded.Error(), // silently filtered, times out }, @@ -1435,25 +1436,25 @@ func TestFIDO2Register(t *testing.T) { none1 := mustNewFIDO2Device("/none1", "" /* pin */, &libfido2.DeviceInfo{Options: authOpts}) none1.format = "none" - challenge, err := protocol.CreateChallenge() + challenge, err := wantypes.CreateChallenge() require.NoError(t, err, "CreateChallenge failed") - baseCC := &wanlib.CredentialCreation{ - Response: protocol.PublicKeyCredentialCreationOptions{ + baseCC := &wantypes.CredentialCreation{ + Response: wantypes.PublicKeyCredentialCreationOptions{ Challenge: challenge, - RelyingParty: protocol.RelyingPartyEntity{ + RelyingParty: wantypes.RelyingPartyEntity{ ID: rpID, - CredentialEntity: protocol.CredentialEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "rp name", }, }, - Parameters: []protocol.CredentialParameter{ + Parameters: []wantypes.CredentialParameter{ {Type: protocol.PublicKeyCredentialType, Algorithm: webauthncose.AlgES256}, }, - AuthenticatorSelection: protocol.AuthenticatorSelection{ + AuthenticatorSelection: wantypes.AuthenticatorSelection{ UserVerification: protocol.VerificationDiscouraged, }, - User: protocol.UserEntity{ + User: wantypes.UserEntity{ ID: []byte{1, 2, 3, 4, 1}, // arbitrary, CredentialEntity: protocol.CredentialEntity{ Name: "user name", @@ -1465,8 +1466,8 @@ func TestFIDO2Register(t *testing.T) { } pwdlessCC := *baseCC pwdlessCC.Response.RelyingParty.Name = "Teleport" - pwdlessCC.Response.User = protocol.UserEntity{ - CredentialEntity: protocol.CredentialEntity{ + pwdlessCC.Response.User = wantypes.UserEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "llama", }, DisplayName: "Llama", @@ -1481,7 +1482,7 @@ func TestFIDO2Register(t *testing.T) { timeout time.Duration fido2 *fakeFIDO2 setUP func() - createCredential func() *wanlib.CredentialCreation + createCredential func() *wantypes.CredentialCreation prompt wancli.RegisterPrompt wantErr error assertResponse func(t *testing.T, ccr *wanpb.CredentialCreationResponse, attObj *protocol.AttestationObject) @@ -1490,7 +1491,7 @@ func TestFIDO2Register(t *testing.T) { name: "single device, packed attestation", fido2: newFakeFIDO2(auth1), setUP: auth1.setUP, - createCredential: func() *wanlib.CredentialCreation { + createCredential: func() *wantypes.CredentialCreation { cp := *baseCC return &cp }, @@ -1514,7 +1515,7 @@ func TestFIDO2Register(t *testing.T) { name: "fido-u2f attestation", fido2: newFakeFIDO2(u2f1), setUP: u2f1.setUP, - createCredential: func() *wanlib.CredentialCreation { + createCredential: func() *wantypes.CredentialCreation { cp := *baseCC return &cp }, @@ -1535,7 +1536,7 @@ func TestFIDO2Register(t *testing.T) { name: "none attestation", fido2: newFakeFIDO2(none1), setUP: none1.setUP, - createCredential: func() *wanlib.CredentialCreation { + createCredential: func() *wantypes.CredentialCreation { cp := *baseCC return &cp }, @@ -1547,7 +1548,7 @@ func TestFIDO2Register(t *testing.T) { name: "pin device", fido2: newFakeFIDO2(pin1), setUP: pin1.setUP, - createCredential: func() *wanlib.CredentialCreation { + createCredential: func() *wantypes.CredentialCreation { cp := *baseCC return &cp }, @@ -1557,7 +1558,7 @@ func TestFIDO2Register(t *testing.T) { name: "multiple valid devices", fido2: newFakeFIDO2(auth1, pin1, pin2, bio1), setUP: bio1.setUP, - createCredential: func() *wanlib.CredentialCreation { + createCredential: func() *wantypes.CredentialCreation { cp := *baseCC return &cp }, @@ -1569,7 +1570,7 @@ func TestFIDO2Register(t *testing.T) { name: "multiple devices, uses pin", fido2: newFakeFIDO2(auth1, pin1, pin2, bio1), setUP: pin2.setUP, - createCredential: func() *wanlib.CredentialCreation { + createCredential: func() *wantypes.CredentialCreation { cp := *baseCC return &cp }, @@ -1582,9 +1583,9 @@ func TestFIDO2Register(t *testing.T) { name: "excluded devices, single valid", fido2: newFakeFIDO2(auth1, bio1), setUP: bio1.setUP, - createCredential: func() *wanlib.CredentialCreation { + createCredential: func() *wantypes.CredentialCreation { cp := *baseCC - cp.Response.CredentialExcludeList = []protocol.CredentialDescriptor{ + cp.Response.CredentialExcludeList = []wantypes.CredentialDescriptor{ { Type: protocol.PublicKeyCredentialType, CredentialID: auth1.credentialID(), @@ -1600,9 +1601,9 @@ func TestFIDO2Register(t *testing.T) { name: "excluded devices, multiple valid", fido2: newFakeFIDO2(auth1, pin1, pin2, bio1), setUP: bio1.setUP, - createCredential: func() *wanlib.CredentialCreation { + createCredential: func() *wantypes.CredentialCreation { cp := *baseCC - cp.Response.CredentialExcludeList = []protocol.CredentialDescriptor{ + cp.Response.CredentialExcludeList = []wantypes.CredentialDescriptor{ { Type: protocol.PublicKeyCredentialType, CredentialID: pin1.credentialID(), @@ -1623,7 +1624,7 @@ func TestFIDO2Register(t *testing.T) { timeout: 10 * time.Millisecond, fido2: newFakeFIDO2(), setUP: func() {}, - createCredential: func() *wanlib.CredentialCreation { + createCredential: func() *wantypes.CredentialCreation { cp := *baseCC return &cp }, @@ -1633,7 +1634,7 @@ func TestFIDO2Register(t *testing.T) { name: "passwordless pin device", fido2: newFakeFIDO2(pin2), setUP: pin2.setUP, - createCredential: func() *wanlib.CredentialCreation { + createCredential: func() *wantypes.CredentialCreation { cp := pwdlessCC return &cp }, @@ -1648,7 +1649,7 @@ func TestFIDO2Register(t *testing.T) { name: "passwordless bio device", fido2: newFakeFIDO2(bio1), setUP: bio1.setUP, - createCredential: func() *wanlib.CredentialCreation { + createCredential: func() *wantypes.CredentialCreation { cp := pwdlessCC return &cp }, @@ -1663,7 +1664,7 @@ func TestFIDO2Register(t *testing.T) { name: "passwordless ResidentKey=required", fido2: newFakeFIDO2(pin2), setUP: pin2.setUP, - createCredential: func() *wanlib.CredentialCreation { + createCredential: func() *wantypes.CredentialCreation { cp := pwdlessCC cp.Response.AuthenticatorSelection.RequireResidentKey = nil cp.Response.AuthenticatorSelection.ResidentKey = protocol.ResidentKeyRequirementRequired @@ -1743,24 +1744,24 @@ func TestFIDO2Register_errors(t *testing.T) { f2.setCallbacks() const origin = "https://example.com" - okCC := &wanlib.CredentialCreation{ - Response: protocol.PublicKeyCredentialCreationOptions{ + okCC := &wantypes.CredentialCreation{ + Response: wantypes.PublicKeyCredentialCreationOptions{ Challenge: make([]byte, 32), - RelyingParty: protocol.RelyingPartyEntity{ + RelyingParty: wantypes.RelyingPartyEntity{ ID: "example.com", - CredentialEntity: protocol.CredentialEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "rp name", }, }, - Parameters: []protocol.CredentialParameter{ + Parameters: []wantypes.CredentialParameter{ {Type: protocol.PublicKeyCredentialType, Algorithm: webauthncose.AlgES256}, }, - AuthenticatorSelection: protocol.AuthenticatorSelection{ + AuthenticatorSelection: wantypes.AuthenticatorSelection{ UserVerification: protocol.VerificationDiscouraged, }, - User: protocol.UserEntity{ + User: wantypes.UserEntity{ ID: []byte{1, 2, 3, 4, 1}, // arbitrary, - CredentialEntity: protocol.CredentialEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "user name", }, DisplayName: "user display name", @@ -1771,8 +1772,8 @@ func TestFIDO2Register_errors(t *testing.T) { pwdlessOK := *okCC pwdlessOK.Response.RelyingParty.Name = "Teleport" - pwdlessOK.Response.User = protocol.UserEntity{ - CredentialEntity: protocol.CredentialEntity{ + pwdlessOK.Response.User = wantypes.UserEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "llama", }, DisplayName: "Llama", @@ -1787,27 +1788,27 @@ func TestFIDO2Register_errors(t *testing.T) { tests := []struct { name string origin string - createCC func() *wanlib.CredentialCreation + createCC func() *wantypes.CredentialCreation prompt wancli.RegisterPrompt wantErr string }{ { name: "ok - timeout", // check that good params are good origin: origin, - createCC: func() *wanlib.CredentialCreation { return okCC }, + createCC: func() *wantypes.CredentialCreation { return okCC }, prompt: prompt, wantErr: context.DeadlineExceeded.Error(), }, { name: "nil origin", - createCC: func() *wanlib.CredentialCreation { return okCC }, + createCC: func() *wantypes.CredentialCreation { return okCC }, prompt: prompt, wantErr: "origin", }, { name: "cc without challenge", origin: origin, - createCC: func() *wanlib.CredentialCreation { + createCC: func() *wantypes.CredentialCreation { cp := *okCC cp.Response.Challenge = nil return &cp @@ -1818,9 +1819,9 @@ func TestFIDO2Register_errors(t *testing.T) { { name: "cc unsupported parameters", origin: origin, - createCC: func() *wanlib.CredentialCreation { + createCC: func() *wantypes.CredentialCreation { cp := *okCC - cp.Response.Parameters = []protocol.CredentialParameter{ + cp.Response.Parameters = []wantypes.CredentialParameter{ {Type: protocol.PublicKeyCredentialType, Algorithm: webauthncose.AlgEdDSA}, } return &cp @@ -1831,7 +1832,7 @@ func TestFIDO2Register_errors(t *testing.T) { { name: "nil pinPrompt", origin: origin, - createCC: func() *wanlib.CredentialCreation { return okCC }, + createCC: func() *wantypes.CredentialCreation { return okCC }, wantErr: "prompt", }, } @@ -1862,22 +1863,22 @@ func TestFIDO2Register_u2fExcludedCredentials(t *testing.T) { f2.setCallbacks() const origin = "https://example.com" - cc := &wanlib.CredentialCreation{ - Response: protocol.PublicKeyCredentialCreationOptions{ + cc := &wantypes.CredentialCreation{ + Response: wantypes.PublicKeyCredentialCreationOptions{ Challenge: make([]byte, 32), - RelyingParty: protocol.RelyingPartyEntity{ + RelyingParty: wantypes.RelyingPartyEntity{ ID: "example.com", - CredentialEntity: protocol.CredentialEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "rp name", }, }, - Parameters: []protocol.CredentialParameter{ + Parameters: []wantypes.CredentialParameter{ {Type: protocol.PublicKeyCredentialType, Algorithm: webauthncose.AlgES256}, }, - AuthenticatorSelection: protocol.AuthenticatorSelection{ + AuthenticatorSelection: wantypes.AuthenticatorSelection{ UserVerification: protocol.VerificationDiscouraged, }, - User: protocol.UserEntity{ + User: wantypes.UserEntity{ ID: []byte{1, 2, 3, 4, 1}, // arbitrary, CredentialEntity: protocol.CredentialEntity{ Name: "user name", @@ -1895,7 +1896,7 @@ func TestFIDO2Register_u2fExcludedCredentials(t *testing.T) { require.NoError(t, err, "FIDO2Register errored") // Setup: mark the registered credential as excluded. - cc.Response.CredentialExcludeList = append(cc.Response.CredentialExcludeList, protocol.CredentialDescriptor{ + cc.Response.CredentialExcludeList = append(cc.Response.CredentialExcludeList, wantypes.CredentialDescriptor{ Type: protocol.PublicKeyCredentialType, CredentialID: resp.GetWebauthn().GetRawId(), }) diff --git a/lib/auth/webauthncli/u2f_login.go b/lib/auth/webauthncli/u2f_login.go index 50f875243902c..5c563c746117a 100644 --- a/lib/auth/webauthncli/u2f_login.go +++ b/lib/auth/webauthncli/u2f_login.go @@ -27,13 +27,13 @@ import ( "github.com/gravitational/trace" "github.com/gravitational/teleport/api/client/proto" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) // U2FLogin implements Login for U2F/CTAP1 devices. // The implementation is backed exclusively by Go code, making it useful in // scenarios where libfido2 is unavailable. -func U2FLogin(ctx context.Context, origin string, assertion *wanlib.CredentialAssertion) (*proto.MFAAuthenticateResponse, error) { +func U2FLogin(ctx context.Context, origin string, assertion *wantypes.CredentialAssertion) (*proto.MFAAuthenticateResponse, error) { switch { case origin == "": return nil, trace.BadParameter("origin required") @@ -72,16 +72,16 @@ func U2FLogin(ctx context.Context, origin string, assertion *wanlib.CredentialAs // Did we get the App ID extension? var appID string var appIDHash [32]byte - if value, ok := assertion.Response.Extensions[wanlib.AppIDExtension]; ok { + if value, ok := assertion.Response.Extensions[wantypes.AppIDExtension]; ok { appID = fmt.Sprint(value) appIDHash = sha256.Sum256([]byte(appID)) } // Variables below are filled by the callback on success. - var authCred protocol.CredentialDescriptor + var authCred wantypes.CredentialDescriptor var authResp *u2ftoken.AuthenticateResponse var usedAppID bool - makeAuthU2F := func(cred protocol.CredentialDescriptor, req u2ftoken.AuthenticateRequest, appID bool) func(Token) error { + makeAuthU2F := func(cred wantypes.CredentialDescriptor, req u2ftoken.AuthenticateRequest, appID bool) func(Token) error { return func(token Token) error { if err := token.CheckAuthenticate(req); err != nil { return err // don't wrap, inspected by RunOnU2FDevices @@ -118,9 +118,9 @@ func U2FLogin(ctx context.Context, origin string, assertion *wanlib.CredentialAs } // Assemble extensions. - var exts *wanlib.AuthenticationExtensionsClientOutputs + var exts *wantypes.AuthenticationExtensionsClientOutputs if usedAppID { - exts = &wanlib.AuthenticationExtensionsClientOutputs{AppID: true} + exts = &wantypes.AuthenticationExtensionsClientOutputs{AppID: true} } // Assemble authenticator data. @@ -133,17 +133,17 @@ func U2FLogin(ctx context.Context, origin string, assertion *wanlib.CredentialAs } authData.Write(authResp.RawResponse[:5]) // User Presence (1) + Counter (4) - resp := &wanlib.CredentialAssertionResponse{ - PublicKeyCredential: wanlib.PublicKeyCredential{ - Credential: wanlib.Credential{ + resp := &wantypes.CredentialAssertionResponse{ + PublicKeyCredential: wantypes.PublicKeyCredential{ + Credential: wantypes.Credential{ ID: base64.RawURLEncoding.EncodeToString(authCred.CredentialID), Type: string(protocol.PublicKeyCredentialType), }, RawID: authCred.CredentialID, Extensions: exts, }, - AssertionResponse: wanlib.AuthenticatorAssertionResponse{ - AuthenticatorResponse: wanlib.AuthenticatorResponse{ + AssertionResponse: wantypes.AuthenticatorAssertionResponse{ + AuthenticatorResponse: wantypes.AuthenticatorResponse{ ClientDataJSON: ccdJSON, }, AuthenticatorData: authData.Bytes(), @@ -152,7 +152,7 @@ func U2FLogin(ctx context.Context, origin string, assertion *wanlib.CredentialAs } return &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(resp), + Webauthn: wantypes.CredentialAssertionResponseToProto(resp), }, }, nil } diff --git a/lib/auth/webauthncli/u2f_login_test.go b/lib/auth/webauthncli/u2f_login_test.go index 9547dc7871345..6b63c07fec4d6 100644 --- a/lib/auth/webauthncli/u2f_login_test.go +++ b/lib/auth/webauthncli/u2f_login_test.go @@ -33,10 +33,11 @@ import ( "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/types" - wantypes "github.com/gravitational/teleport/api/types/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" "github.com/gravitational/teleport/lib/auth/mocku2f" wanlib "github.com/gravitational/teleport/lib/auth/webauthn" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) func TestLogin(t *testing.T) { @@ -155,7 +156,7 @@ func TestLogin(t *testing.T) { require.NotNil(t, mfaResp.GetWebauthn()) require.Equal(t, test.wantRawID, mfaResp.GetWebauthn().RawId) - _, err = loginFlow.Finish(ctx, username, wanlib.CredentialAssertionResponseFromProto(mfaResp.GetWebauthn())) + _, err = loginFlow.Finish(ctx, username, wantypes.CredentialAssertionResponseFromProto(mfaResp.GetWebauthn())) require.NoError(t, err) }) } @@ -184,33 +185,33 @@ func TestLogin_errors(t *testing.T) { tests := []struct { name string origin string - getAssertion func() *wanlib.CredentialAssertion + getAssertion func() *wantypes.CredentialAssertion }{ { name: "NOK origin empty", origin: "", - getAssertion: func() *wanlib.CredentialAssertion { + getAssertion: func() *wantypes.CredentialAssertion { return okAssertion }, }, { name: "NOK assertion nil", origin: origin, - getAssertion: func() *wanlib.CredentialAssertion { + getAssertion: func() *wantypes.CredentialAssertion { return nil }, }, { name: "NOK assertion empty", origin: origin, - getAssertion: func() *wanlib.CredentialAssertion { - return &wanlib.CredentialAssertion{} + getAssertion: func() *wantypes.CredentialAssertion { + return &wantypes.CredentialAssertion{} }, }, { name: "NOK assertion missing challenge", origin: origin, - getAssertion: func() *wanlib.CredentialAssertion { + getAssertion: func() *wantypes.CredentialAssertion { assertion, err := loginFlow.Begin(ctx, user) require.NoError(t, err) assertion.Response.Challenge = nil @@ -220,7 +221,7 @@ func TestLogin_errors(t *testing.T) { { name: "NOK assertion missing RPID", origin: origin, - getAssertion: func() *wanlib.CredentialAssertion { + getAssertion: func() *wantypes.CredentialAssertion { assertion, err := loginFlow.Begin(ctx, user) require.NoError(t, err) assertion.Response.RelyingPartyID = "" @@ -230,7 +231,7 @@ func TestLogin_errors(t *testing.T) { { name: "NOK assertion missing credentials", origin: origin, - getAssertion: func() *wanlib.CredentialAssertion { + getAssertion: func() *wantypes.CredentialAssertion { assertion, err := loginFlow.Begin(ctx, user) require.NoError(t, err) assertion.Response.AllowedCredentials = nil @@ -240,7 +241,7 @@ func TestLogin_errors(t *testing.T) { { name: "NOK assertion invalid user verification requirement", origin: origin, - getAssertion: func() *wanlib.CredentialAssertion { + getAssertion: func() *wantypes.CredentialAssertion { assertion, err := loginFlow.Begin(ctx, user) require.NoError(t, err) assertion.Response.UserVerification = protocol.VerificationRequired @@ -421,7 +422,7 @@ type fakeIdentity struct { User string Devices []*types.MFADevice LocalAuth *types.WebauthnLocalAuth - SessionData *wantypes.SessionData + SessionData *wanpb.SessionData } func (f *fakeIdentity) UpsertWebauthnLocalAuth(ctx context.Context, user string, wla *types.WebauthnLocalAuth) error { @@ -452,12 +453,12 @@ func (f *fakeIdentity) UpsertMFADevice(ctx context.Context, user string, d *type return nil } -func (f *fakeIdentity) UpsertWebauthnSessionData(ctx context.Context, user, sessionID string, sd *wantypes.SessionData) error { +func (f *fakeIdentity) UpsertWebauthnSessionData(ctx context.Context, user, sessionID string, sd *wanpb.SessionData) error { f.SessionData = sd return nil } -func (f *fakeIdentity) GetWebauthnSessionData(ctx context.Context, user, sessionID string) (*wantypes.SessionData, error) { +func (f *fakeIdentity) GetWebauthnSessionData(ctx context.Context, user, sessionID string) (*wanpb.SessionData, error) { return f.SessionData, nil } diff --git a/lib/auth/webauthncli/u2f_register.go b/lib/auth/webauthncli/u2f_register.go index 9ff84d0972d2e..04bc02cef15a9 100644 --- a/lib/auth/webauthncli/u2f_register.go +++ b/lib/auth/webauthncli/u2f_register.go @@ -36,12 +36,13 @@ import ( "github.com/gravitational/teleport/api/client/proto" wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) // U2FRegister implements Register for U2F/CTAP1 devices. // The implementation is backed exclusively by Go code, making it useful in // scenarios where libfido2 is unavailable. -func U2FRegister(ctx context.Context, origin string, cc *wanlib.CredentialCreation) (*proto.MFARegisterResponse, error) { +func U2FRegister(ctx context.Context, origin string, cc *wantypes.CredentialCreation) (*proto.MFARegisterResponse, error) { // Preliminary checks, more below. switch { case origin == "": @@ -88,7 +89,7 @@ func U2FRegister(ctx context.Context, origin string, cc *wanlib.CredentialCreati rpIDHash := sha256.Sum256([]byte(cc.Response.RelyingParty.ID)) var appIDHash []byte - if value, ok := cc.Response.Extensions[wanlib.AppIDExtension]; ok { + if value, ok := cc.Response.Extensions[wantypes.AppIDExtension]; ok { appID := fmt.Sprint(value) h := sha256.Sum256([]byte(appID)) appIDHash = h[:] @@ -144,7 +145,7 @@ func U2FRegister(ctx context.Context, origin string, cc *wanlib.CredentialCreati return &proto.MFARegisterResponse{ Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: wanlib.CredentialCreationResponseToProto(ccr), + Webauthn: wantypes.CredentialCreationResponseToProto(ccr), }, }, nil } @@ -223,7 +224,7 @@ func parseU2FRegistrationResponse(resp []byte) (*u2fRegistrationResponse, error) }, nil } -func credentialResponseFromU2F(ccdJSON, appIDHash []byte, resp *u2fRegistrationResponse) (*wanlib.CredentialCreationResponse, error) { +func credentialResponseFromU2F(ccdJSON, appIDHash []byte, resp *u2fRegistrationResponse) (*wantypes.CredentialCreationResponse, error) { // Reference: // https://fidoalliance.org/specs/fido-v2.1-ps-20210615/fido-client-to-authenticator-protocol-v2.1-ps-20210615.html#fig-u2f-compat-makeCredential @@ -258,16 +259,16 @@ func credentialResponseFromU2F(ccdJSON, appIDHash []byte, resp *u2fRegistrationR return nil, trace.Wrap(err) } - return &wanlib.CredentialCreationResponse{ - PublicKeyCredential: wanlib.PublicKeyCredential{ - Credential: wanlib.Credential{ + return &wantypes.CredentialCreationResponse{ + PublicKeyCredential: wantypes.PublicKeyCredential{ + Credential: wantypes.Credential{ ID: base64.RawURLEncoding.EncodeToString(resp.KeyHandle), Type: string(protocol.PublicKeyCredentialType), }, RawID: resp.KeyHandle, }, - AttestationResponse: wanlib.AuthenticatorAttestationResponse{ - AuthenticatorResponse: wanlib.AuthenticatorResponse{ + AttestationResponse: wantypes.AuthenticatorAttestationResponse{ + AuthenticatorResponse: wantypes.AuthenticatorResponse{ ClientDataJSON: ccdJSON, }, AttestationObject: attestationObj, diff --git a/lib/auth/webauthncli/u2f_register_test.go b/lib/auth/webauthncli/u2f_register_test.go index 5ff4ae7831c7f..27492909cff6f 100644 --- a/lib/auth/webauthncli/u2f_register_test.go +++ b/lib/auth/webauthncli/u2f_register_test.go @@ -26,6 +26,7 @@ import ( "github.com/gravitational/teleport/api/types" wanlib "github.com/gravitational/teleport/lib/auth/webauthn" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) func TestRegister(t *testing.T) { @@ -113,7 +114,7 @@ func TestRegister(t *testing.T) { _, err = webRegistration.Finish(ctx, wanlib.RegisterResponse{ User: user, DeviceName: u2fKey.name, - CreationResponse: wanlib.CredentialCreationResponseFromProto(resp.GetWebauthn()), + CreationResponse: wantypes.CredentialCreationResponseFromProto(resp.GetWebauthn()), }) require.NoError(t, err, "server-side registration failed") }) @@ -136,33 +137,33 @@ func TestRegister_errors(t *testing.T) { tests := []struct { name string origin string - makeCC func() *wanlib.CredentialCreation + makeCC func() *wantypes.CredentialCreation wantErr string }{ { name: "NOK empty origin", origin: "", - makeCC: func() *wanlib.CredentialCreation { return okCC }, + makeCC: func() *wantypes.CredentialCreation { return okCC }, wantErr: "origin", }, { name: "NOK nil credential creation", origin: origin, - makeCC: func() *wanlib.CredentialCreation { return nil }, + makeCC: func() *wantypes.CredentialCreation { return nil }, wantErr: "credential creation required", }, { name: "NOK nil empty creation", origin: origin, - makeCC: func() *wanlib.CredentialCreation { return &wanlib.CredentialCreation{} }, + makeCC: func() *wantypes.CredentialCreation { return &wantypes.CredentialCreation{} }, wantErr: "relying party", }, { name: "NOK ES256 algorithm not allowed", origin: origin, - makeCC: func() *wanlib.CredentialCreation { + makeCC: func() *wantypes.CredentialCreation { cp := *okCC - var params []protocol.CredentialParameter + var params []wantypes.CredentialParameter for _, p := range cp.Response.Parameters { if p.Algorithm == webauthncose.AlgES256 { continue @@ -177,7 +178,7 @@ func TestRegister_errors(t *testing.T) { { name: "NOK platform attachment required", origin: origin, - makeCC: func() *wanlib.CredentialCreation { + makeCC: func() *wantypes.CredentialCreation { cp := *okCC cp.Response.AuthenticatorSelection.AuthenticatorAttachment = protocol.Platform return &cp @@ -187,7 +188,7 @@ func TestRegister_errors(t *testing.T) { { name: "NOK resident key required", origin: origin, - makeCC: func() *wanlib.CredentialCreation { + makeCC: func() *wantypes.CredentialCreation { cp := *okCC rrk := true cp.Response.AuthenticatorSelection.RequireResidentKey = &rrk @@ -198,7 +199,7 @@ func TestRegister_errors(t *testing.T) { { name: "NOK user verification required", origin: origin, - makeCC: func() *wanlib.CredentialCreation { + makeCC: func() *wantypes.CredentialCreation { cp := *okCC cp.Response.AuthenticatorSelection.UserVerification = protocol.VerificationRequired return &cp diff --git a/lib/auth/webauthntypes/doc.go b/lib/auth/webauthntypes/doc.go new file mode 100644 index 0000000000000..fafae46e496b4 --- /dev/null +++ b/lib/auth/webauthntypes/doc.go @@ -0,0 +1,20 @@ +// Copyright 2023 Gravitational, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package webauthntypes provides WebAuthn types and conversions for both +// client-side and server-side implementations. +// +// Many of the types found in the package are replicas of go-webauthn/webauthn +// types, "frozen" as to avoid changes in their JSON representation. +package webauthntypes diff --git a/lib/auth/webauthntypes/extensions.go b/lib/auth/webauthntypes/extensions.go new file mode 100644 index 0000000000000..adc2b7a4621f5 --- /dev/null +++ b/lib/auth/webauthntypes/extensions.go @@ -0,0 +1,19 @@ +// Copyright 2021 Gravitational, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package webauthntypes + +// AppIDExtension is the key for the appid extension. +// https://www.w3.org/TR/webauthn-2/#sctn-appid-extension. +const AppIDExtension = "appid" diff --git a/lib/auth/webauthn/proto.go b/lib/auth/webauthntypes/proto.go similarity index 65% rename from lib/auth/webauthn/proto.go rename to lib/auth/webauthntypes/proto.go index 87baa00ade00d..938896e8cf1f8 100644 --- a/lib/auth/webauthn/proto.go +++ b/lib/auth/webauthntypes/proto.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package webauthn +package webauthntypes import ( "encoding/base64" @@ -20,17 +20,17 @@ import ( "github.com/go-webauthn/webauthn/protocol" "github.com/go-webauthn/webauthn/protocol/webauthncose" - wantypes "github.com/gravitational/teleport/api/types/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" ) // CredentialAssertionToProto converts a CredentialAssertion to its proto // counterpart. -func CredentialAssertionToProto(assertion *CredentialAssertion) *wantypes.CredentialAssertion { +func CredentialAssertionToProto(assertion *CredentialAssertion) *wanpb.CredentialAssertion { if assertion == nil { return nil } - return &wantypes.CredentialAssertion{ - PublicKey: &wantypes.PublicKeyCredentialRequestOptions{ + return &wanpb.CredentialAssertion{ + PublicKey: &wanpb.PublicKeyCredentialRequestOptions{ Challenge: assertion.Response.Challenge, TimeoutMs: int64(assertion.Response.Timeout), RpId: assertion.Response.RelyingPartyID, @@ -43,14 +43,14 @@ func CredentialAssertionToProto(assertion *CredentialAssertion) *wantypes.Creden // CredentialAssertionResponseToProto converts a CredentialAssertionResponse to // its proto counterpart. -func CredentialAssertionResponseToProto(car *CredentialAssertionResponse) *wantypes.CredentialAssertionResponse { +func CredentialAssertionResponseToProto(car *CredentialAssertionResponse) *wanpb.CredentialAssertionResponse { if car == nil { return nil } - return &wantypes.CredentialAssertionResponse{ + return &wanpb.CredentialAssertionResponse{ Type: car.Type, RawId: car.RawID, - Response: &wantypes.AuthenticatorAssertionResponse{ + Response: &wanpb.AuthenticatorAssertionResponse{ ClientDataJson: car.AssertionResponse.ClientDataJSON, AuthenticatorData: car.AssertionResponse.AuthenticatorData, Signature: car.AssertionResponse.Signature, @@ -62,12 +62,12 @@ func CredentialAssertionResponseToProto(car *CredentialAssertionResponse) *wanty // CredentialCreationToProto converts a CredentialCreation to its proto // counterpart. -func CredentialCreationToProto(cc *CredentialCreation) *wantypes.CredentialCreation { +func CredentialCreationToProto(cc *CredentialCreation) *wanpb.CredentialCreation { if cc == nil { return nil } - return &wantypes.CredentialCreation{ - PublicKey: &wantypes.PublicKeyCredentialCreationOptions{ + return &wanpb.CredentialCreation{ + PublicKey: &wanpb.PublicKeyCredentialCreationOptions{ Challenge: cc.Response.Challenge, Rp: rpEntityToProto(cc.Response.RelyingParty), User: userEntityToProto(cc.Response.User), @@ -83,14 +83,14 @@ func CredentialCreationToProto(cc *CredentialCreation) *wantypes.CredentialCreat // CredentialCreationResponseToProto converts a CredentialCreationResponse to // its proto counterpart. -func CredentialCreationResponseToProto(ccr *CredentialCreationResponse) *wantypes.CredentialCreationResponse { +func CredentialCreationResponseToProto(ccr *CredentialCreationResponse) *wanpb.CredentialCreationResponse { if ccr == nil { return nil } - return &wantypes.CredentialCreationResponse{ + return &wanpb.CredentialCreationResponse{ Type: ccr.Type, RawId: ccr.RawID, - Response: &wantypes.AuthenticatorAttestationResponse{ + Response: &wanpb.AuthenticatorAttestationResponse{ ClientDataJson: ccr.AttestationResponse.ClientDataJSON, AttestationObject: ccr.AttestationResponse.AttestationObject, }, @@ -100,7 +100,7 @@ func CredentialCreationResponseToProto(ccr *CredentialCreationResponse) *wantype // CredentialAssertionFromProto converts a CredentialAssertion proto to its lib // counterpart. -func CredentialAssertionFromProto(assertion *wantypes.CredentialAssertion) *CredentialAssertion { +func CredentialAssertionFromProto(assertion *wanpb.CredentialAssertion) *CredentialAssertion { if assertion == nil { return nil } @@ -111,7 +111,7 @@ func CredentialAssertionFromProto(assertion *wantypes.CredentialAssertion) *Cred // CredentialAssertionResponseFromProto converts a CredentialAssertionResponse // proto to its lib counterpart. -func CredentialAssertionResponseFromProto(car *wantypes.CredentialAssertionResponse) *CredentialAssertionResponse { +func CredentialAssertionResponseFromProto(car *wanpb.CredentialAssertionResponse) *CredentialAssertionResponse { if car == nil { return nil } @@ -130,7 +130,7 @@ func CredentialAssertionResponseFromProto(car *wantypes.CredentialAssertionRespo // CredentialCreationFromProto converts a CredentialCreation proto to its lib // counterpart. -func CredentialCreationFromProto(cc *wantypes.CredentialCreation) *CredentialCreation { +func CredentialCreationFromProto(cc *wanpb.CredentialCreation) *CredentialCreation { if cc == nil { return nil } @@ -141,7 +141,7 @@ func CredentialCreationFromProto(cc *wantypes.CredentialCreation) *CredentialCre // CredentialCreationResponseFromProto converts a CredentialCreationResponse // proto to its lib counterpart. -func CredentialCreationResponseFromProto(ccr *wantypes.CredentialCreationResponse) *CredentialCreationResponse { +func CredentialCreationResponseFromProto(ccr *wanpb.CredentialCreationResponse) *CredentialCreationResponse { if ccr == nil { return nil } @@ -158,18 +158,18 @@ func CredentialCreationResponseFromProto(ccr *wantypes.CredentialCreationRespons } } -func authenticatorSelectionToProto(a protocol.AuthenticatorSelection) *wantypes.AuthenticatorSelection { - return &wantypes.AuthenticatorSelection{ +func authenticatorSelectionToProto(a AuthenticatorSelection) *wanpb.AuthenticatorSelection { + return &wanpb.AuthenticatorSelection{ AuthenticatorAttachment: string(a.AuthenticatorAttachment), RequireResidentKey: a.RequireResidentKey != nil && *a.RequireResidentKey, UserVerification: string(a.UserVerification), } } -func credentialDescriptorsToProto(creds []protocol.CredentialDescriptor) []*wantypes.CredentialDescriptor { - res := make([]*wantypes.CredentialDescriptor, len(creds)) +func credentialDescriptorsToProto(creds []CredentialDescriptor) []*wanpb.CredentialDescriptor { + res := make([]*wanpb.CredentialDescriptor, len(creds)) for i, cred := range creds { - res[i] = &wantypes.CredentialDescriptor{ + res[i] = &wanpb.CredentialDescriptor{ Type: string(cred.Type), Id: cred.CredentialID, } @@ -177,10 +177,10 @@ func credentialDescriptorsToProto(creds []protocol.CredentialDescriptor) []*want return res } -func credentialParametersToProto(params []protocol.CredentialParameter) []*wantypes.CredentialParameter { - res := make([]*wantypes.CredentialParameter, len(params)) +func credentialParametersToProto(params []CredentialParameter) []*wanpb.CredentialParameter { + res := make([]*wanpb.CredentialParameter, len(params)) for i, p := range params { - res[i] = &wantypes.CredentialParameter{ + res[i] = &wanpb.CredentialParameter{ Type: string(p.Type), Alg: int32(p.Algorithm), } @@ -188,11 +188,11 @@ func credentialParametersToProto(params []protocol.CredentialParameter) []*wanty return res } -func inputExtensionsToProto(exts protocol.AuthenticationExtensions) *wantypes.AuthenticationExtensionsClientInputs { +func inputExtensionsToProto(exts AuthenticationExtensions) *wanpb.AuthenticationExtensionsClientInputs { if len(exts) == 0 { return nil } - res := &wantypes.AuthenticationExtensionsClientInputs{} + res := &wanpb.AuthenticationExtensionsClientInputs{} if value, ok := exts[AppIDExtension]; ok { // Type should always be string, since we are the ones setting it, but let's // play it safe and check anyway. @@ -203,33 +203,31 @@ func inputExtensionsToProto(exts protocol.AuthenticationExtensions) *wantypes.Au return res } -func outputExtensionsToProto(exts *AuthenticationExtensionsClientOutputs) *wantypes.AuthenticationExtensionsClientOutputs { +func outputExtensionsToProto(exts *AuthenticationExtensionsClientOutputs) *wanpb.AuthenticationExtensionsClientOutputs { if exts == nil { return nil } - return &wantypes.AuthenticationExtensionsClientOutputs{ + return &wanpb.AuthenticationExtensionsClientOutputs{ AppId: exts.AppID, } } -func rpEntityToProto(rp protocol.RelyingPartyEntity) *wantypes.RelyingPartyEntity { - return &wantypes.RelyingPartyEntity{ +func rpEntityToProto(rp RelyingPartyEntity) *wanpb.RelyingPartyEntity { + return &wanpb.RelyingPartyEntity{ Id: rp.ID, Name: rp.Name, - Icon: rp.Icon, } } -func userEntityToProto(user protocol.UserEntity) *wantypes.UserEntity { - return &wantypes.UserEntity{ +func userEntityToProto(user UserEntity) *wanpb.UserEntity { + return &wanpb.UserEntity{ Id: user.ID, Name: user.Name, DisplayName: user.DisplayName, - Icon: user.Icon, } } -func authenticatorAssertionResponseFromProto(resp *wantypes.AuthenticatorAssertionResponse) AuthenticatorAssertionResponse { +func authenticatorAssertionResponseFromProto(resp *wanpb.AuthenticatorAssertionResponse) AuthenticatorAssertionResponse { if resp == nil { return AuthenticatorAssertionResponse{} } @@ -243,7 +241,7 @@ func authenticatorAssertionResponseFromProto(resp *wantypes.AuthenticatorAsserti } } -func authenticatorAttestationResponseFromProto(resp *wantypes.AuthenticatorAttestationResponse) AuthenticatorAttestationResponse { +func authenticatorAttestationResponseFromProto(resp *wanpb.AuthenticatorAttestationResponse) AuthenticatorAttestationResponse { if resp == nil { return AuthenticatorAttestationResponse{} } @@ -255,25 +253,25 @@ func authenticatorAttestationResponseFromProto(resp *wantypes.AuthenticatorAttes } } -func authenticatorSelectionFromProto(a *wantypes.AuthenticatorSelection) protocol.AuthenticatorSelection { +func authenticatorSelectionFromProto(a *wanpb.AuthenticatorSelection) AuthenticatorSelection { if a == nil { - return protocol.AuthenticatorSelection{} + return AuthenticatorSelection{} } rrk := a.RequireResidentKey - return protocol.AuthenticatorSelection{ + return AuthenticatorSelection{ AuthenticatorAttachment: protocol.AuthenticatorAttachment(a.AuthenticatorAttachment), RequireResidentKey: &rrk, UserVerification: protocol.UserVerificationRequirement(a.UserVerification), } } -func credentialDescriptorsFromProto(creds []*wantypes.CredentialDescriptor) []protocol.CredentialDescriptor { - var res []protocol.CredentialDescriptor +func credentialDescriptorsFromProto(creds []*wanpb.CredentialDescriptor) []CredentialDescriptor { + var res []CredentialDescriptor for _, cred := range creds { if cred == nil { continue } - res = append(res, protocol.CredentialDescriptor{ + res = append(res, CredentialDescriptor{ Type: protocol.CredentialType(cred.Type), CredentialID: cred.Id, }) @@ -281,13 +279,13 @@ func credentialDescriptorsFromProto(creds []*wantypes.CredentialDescriptor) []pr return res } -func credentialParametersFromProto(params []*wantypes.CredentialParameter) []protocol.CredentialParameter { - var res []protocol.CredentialParameter +func credentialParametersFromProto(params []*wanpb.CredentialParameter) []CredentialParameter { + var res []CredentialParameter for _, p := range params { if p == nil { continue } - res = append(res, protocol.CredentialParameter{ + res = append(res, CredentialParameter{ Type: protocol.CredentialType(p.Type), Algorithm: webauthncose.COSEAlgorithmIdentifier(p.Alg), }) @@ -295,7 +293,7 @@ func credentialParametersFromProto(params []*wantypes.CredentialParameter) []pro return res } -func inputExtensionsFromProto(exts *wantypes.AuthenticationExtensionsClientInputs) protocol.AuthenticationExtensions { +func inputExtensionsFromProto(exts *wanpb.AuthenticationExtensionsClientInputs) AuthenticationExtensions { if exts == nil { return nil } @@ -306,7 +304,7 @@ func inputExtensionsFromProto(exts *wantypes.AuthenticationExtensionsClientInput return res } -func outputExtensionsFromProto(exts *wantypes.AuthenticationExtensionsClientOutputs) *AuthenticationExtensionsClientOutputs { +func outputExtensionsFromProto(exts *wanpb.AuthenticationExtensionsClientOutputs) *AuthenticationExtensionsClientOutputs { if exts == nil { return nil } @@ -315,11 +313,11 @@ func outputExtensionsFromProto(exts *wantypes.AuthenticationExtensionsClientOutp } } -func publicKeyCredentialCreationOptionsFromProto(pubKey *wantypes.PublicKeyCredentialCreationOptions) protocol.PublicKeyCredentialCreationOptions { +func publicKeyCredentialCreationOptionsFromProto(pubKey *wanpb.PublicKeyCredentialCreationOptions) PublicKeyCredentialCreationOptions { if pubKey == nil { - return protocol.PublicKeyCredentialCreationOptions{} + return PublicKeyCredentialCreationOptions{} } - return protocol.PublicKeyCredentialCreationOptions{ + return PublicKeyCredentialCreationOptions{ Challenge: pubKey.Challenge, RelyingParty: rpEntityFromProto(pubKey.Rp), User: userEntityFromProto(pubKey.User), @@ -332,11 +330,11 @@ func publicKeyCredentialCreationOptionsFromProto(pubKey *wantypes.PublicKeyCrede } } -func publicKeyCredentialRequestOptionsFromProto(pubKey *wantypes.PublicKeyCredentialRequestOptions) protocol.PublicKeyCredentialRequestOptions { +func publicKeyCredentialRequestOptionsFromProto(pubKey *wanpb.PublicKeyCredentialRequestOptions) PublicKeyCredentialRequestOptions { if pubKey == nil { - return protocol.PublicKeyCredentialRequestOptions{} + return PublicKeyCredentialRequestOptions{} } - return protocol.PublicKeyCredentialRequestOptions{ + return PublicKeyCredentialRequestOptions{ Challenge: pubKey.Challenge, Timeout: int(pubKey.TimeoutMs), RelyingPartyID: pubKey.RpId, @@ -346,27 +344,25 @@ func publicKeyCredentialRequestOptionsFromProto(pubKey *wantypes.PublicKeyCreden } } -func rpEntityFromProto(rp *wantypes.RelyingPartyEntity) protocol.RelyingPartyEntity { +func rpEntityFromProto(rp *wanpb.RelyingPartyEntity) RelyingPartyEntity { if rp == nil { - return protocol.RelyingPartyEntity{} + return RelyingPartyEntity{} } - return protocol.RelyingPartyEntity{ - CredentialEntity: protocol.CredentialEntity{ + return RelyingPartyEntity{ + CredentialEntity: CredentialEntity{ Name: rp.Name, - Icon: rp.Icon, }, ID: rp.Id, } } -func userEntityFromProto(user *wantypes.UserEntity) protocol.UserEntity { +func userEntityFromProto(user *wanpb.UserEntity) UserEntity { if user == nil { - return protocol.UserEntity{} + return UserEntity{} } - return protocol.UserEntity{ - CredentialEntity: protocol.CredentialEntity{ + return UserEntity{ + CredentialEntity: CredentialEntity{ Name: user.Name, - Icon: user.Icon, }, DisplayName: user.DisplayName, ID: user.Id, diff --git a/lib/auth/webauthn/proto_test.go b/lib/auth/webauthntypes/proto_test.go similarity index 59% rename from lib/auth/webauthn/proto_test.go rename to lib/auth/webauthntypes/proto_test.go index ff1c60f1fbd78..66d82662bbccd 100644 --- a/lib/auth/webauthn/proto_test.go +++ b/lib/auth/webauthntypes/proto_test.go @@ -12,15 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -package webauthn_test +package webauthntypes_test import ( "testing" "github.com/stretchr/testify/require" - wantypes "github.com/gravitational/teleport/api/types/webauthn" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) func TestConversionFromProto_nils(t *testing.T) { @@ -37,29 +37,29 @@ func TestConversionFromProto_nils(t *testing.T) { { name: "CredentialAssertion nil", fn: func() { - wanlib.CredentialAssertionFromProto(nil) + wantypes.CredentialAssertionFromProto(nil) }, }, { name: "CredentialAssertion empty", fn: func() { - wanlib.CredentialAssertionFromProto(&wantypes.CredentialAssertion{}) + wantypes.CredentialAssertionFromProto(&wanpb.CredentialAssertion{}) }, }, { name: "CredentialAssertion.PublicKey empty", fn: func() { - wanlib.CredentialAssertionFromProto(&wantypes.CredentialAssertion{ - PublicKey: &wantypes.PublicKeyCredentialRequestOptions{}, + wantypes.CredentialAssertionFromProto(&wanpb.CredentialAssertion{ + PublicKey: &wanpb.PublicKeyCredentialRequestOptions{}, }) }, }, { name: "CredentialAssertion.PublicKey slice elements nil", fn: func() { - wanlib.CredentialAssertionFromProto(&wantypes.CredentialAssertion{ - PublicKey: &wantypes.PublicKeyCredentialRequestOptions{ - AllowCredentials: []*wantypes.CredentialDescriptor{ + wantypes.CredentialAssertionFromProto(&wanpb.CredentialAssertion{ + PublicKey: &wanpb.PublicKeyCredentialRequestOptions{ + AllowCredentials: []*wanpb.CredentialDescriptor{ {}, nil, {}, }, }, @@ -69,52 +69,52 @@ func TestConversionFromProto_nils(t *testing.T) { { name: "CredentialAssertionResponse nil", fn: func() { - wanlib.CredentialAssertionResponseFromProto(nil) + wantypes.CredentialAssertionResponseFromProto(nil) }, }, { name: "CredentialAssertionResponse empty", fn: func() { - wanlib.CredentialAssertionResponseFromProto(&wantypes.CredentialAssertionResponse{}) + wantypes.CredentialAssertionResponseFromProto(&wanpb.CredentialAssertionResponse{}) }, }, { name: "CredentialAssertionResponse.Response empty", fn: func() { - wanlib.CredentialAssertionResponseFromProto(&wantypes.CredentialAssertionResponse{ - Response: &wantypes.AuthenticatorAssertionResponse{}, + wantypes.CredentialAssertionResponseFromProto(&wanpb.CredentialAssertionResponse{ + Response: &wanpb.AuthenticatorAssertionResponse{}, }) }, }, { name: "CredentialCreation nil", fn: func() { - wanlib.CredentialCreationFromProto(nil) + wantypes.CredentialCreationFromProto(nil) }, }, { name: "CredentialCreation empty", fn: func() { - wanlib.CredentialCreationFromProto(&wantypes.CredentialCreation{}) + wantypes.CredentialCreationFromProto(&wanpb.CredentialCreation{}) }, }, { name: "CredentialCreation.PublicKey empty", fn: func() { - wanlib.CredentialCreationFromProto(&wantypes.CredentialCreation{ - PublicKey: &wantypes.PublicKeyCredentialCreationOptions{}, + wantypes.CredentialCreationFromProto(&wanpb.CredentialCreation{ + PublicKey: &wanpb.PublicKeyCredentialCreationOptions{}, }) }, }, { name: "CredentialCreation.PublicKey slice elements nil", fn: func() { - wanlib.CredentialCreationFromProto(&wantypes.CredentialCreation{ - PublicKey: &wantypes.PublicKeyCredentialCreationOptions{ - CredentialParameters: []*wantypes.CredentialParameter{ + wantypes.CredentialCreationFromProto(&wanpb.CredentialCreation{ + PublicKey: &wanpb.PublicKeyCredentialCreationOptions{ + CredentialParameters: []*wanpb.CredentialParameter{ {}, nil, {}, }, - ExcludeCredentials: []*wantypes.CredentialDescriptor{ + ExcludeCredentials: []*wanpb.CredentialDescriptor{ {}, nil, {}, }, }, @@ -124,20 +124,20 @@ func TestConversionFromProto_nils(t *testing.T) { { name: "CredentialCreationResponse nil", fn: func() { - wanlib.CredentialCreationResponseFromProto(nil) + wantypes.CredentialCreationResponseFromProto(nil) }, }, { name: "CredentialCreationResponse empty", fn: func() { - wanlib.CredentialCreationResponseFromProto(&wantypes.CredentialCreationResponse{}) + wantypes.CredentialCreationResponseFromProto(&wanpb.CredentialCreationResponse{}) }, }, { name: "CredentialCreationResponse.Response empty", fn: func() { - wanlib.CredentialCreationResponseFromProto(&wantypes.CredentialCreationResponse{ - Response: &wantypes.AuthenticatorAttestationResponse{}, + wantypes.CredentialCreationResponseFromProto(&wanpb.CredentialCreationResponse{ + Response: &wanpb.AuthenticatorAttestationResponse{}, }) }, }, diff --git a/lib/auth/webauthntypes/webauthn.go b/lib/auth/webauthntypes/webauthn.go new file mode 100644 index 0000000000000..01e636816326e --- /dev/null +++ b/lib/auth/webauthntypes/webauthn.go @@ -0,0 +1,359 @@ +// Copyright 2021 Gravitational, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package webauthntypes + +import ( + "encoding/base64" + + "github.com/go-webauthn/webauthn/protocol" + "github.com/go-webauthn/webauthn/protocol/webauthncose" + "github.com/gravitational/trace" +) + +// CredentialAssertion is the payload sent to authenticators to initiate login. +type CredentialAssertion struct { + Response PublicKeyCredentialRequestOptions `json:"publicKey"` +} + +// PublicKeyCredentialRequestOptions is a clone of +// [protocol.PublicKeyCredentialRequestOptions], materialized here to keep a +// stable JSON marshal/unmarshal representation. +type PublicKeyCredentialRequestOptions struct { + Challenge Challenge `json:"challenge"` + Timeout int `json:"timeout,omitempty"` + RelyingPartyID string `json:"rpId,omitempty"` + AllowedCredentials []CredentialDescriptor `json:"allowCredentials,omitempty"` + UserVerification protocol.UserVerificationRequirement `json:"userVerification,omitempty"` // Default is "preferred" + Extensions AuthenticationExtensions `json:"extensions,omitempty"` +} + +func (a *PublicKeyCredentialRequestOptions) GetAllowedCredentialIDs() [][]byte { + var allowedCredentialIDs = make([][]byte, len(a.AllowedCredentials)) + for i, credential := range a.AllowedCredentials { + allowedCredentialIDs[i] = credential.CredentialID + } + return allowedCredentialIDs +} + +// Validate performs client-side validation of CredentialAssertion. +// It makes sure that data are valid and can be sent to authenticator. +// This is general purpose validation and authenticator should add its own +// on top of it, if necessary. +func (ca *CredentialAssertion) Validate() error { + switch { + case ca == nil: + return trace.BadParameter("credential assertion required") + case len(ca.Response.Challenge) == 0: + return trace.BadParameter("credential assertion challenge required") + case ca.Response.RelyingPartyID == "": + return trace.BadParameter("credential assertion relying party ID required") + } + return nil +} + +// CredentialAssertionFromProtocol converts a [protocol.CredentialAssertion] to +// a [CredentialAssertion]. +func CredentialAssertionFromProtocol(a *protocol.CredentialAssertion) *CredentialAssertion { + if a == nil { + return nil + } + + return &CredentialAssertion{ + Response: PublicKeyCredentialRequestOptions{ + Challenge: Challenge(a.Response.Challenge), + Timeout: a.Response.Timeout, + RelyingPartyID: a.Response.RelyingPartyID, + AllowedCredentials: credentialDescriptorsFromProtocol(a.Response.AllowedCredentials), + UserVerification: a.Response.UserVerification, + Extensions: a.Response.Extensions, + }, + } +} + +func credentialDescriptorsFromProtocol(cs []protocol.CredentialDescriptor) []CredentialDescriptor { + if len(cs) == 0 { + return nil + } + + res := make([]CredentialDescriptor, len(cs)) + for i, c := range cs { + res[i] = CredentialDescriptor{ + Type: c.Type, + CredentialID: c.CredentialID, + Transport: c.Transport, + AttestationType: c.AttestationType, + } + } + return res +} + +// CredentialAssertionResponse is the reply from authenticators to complete +// login. +type CredentialAssertionResponse struct { + // CredentialAssertionResponse is redefined because, unlike + // CredentialAssertion, it is likely to be manually created by package users. + // Redefining allows us to 1) make sure it can be properly JSON-marshaled + // (protocol.CredentialAssertionResponse.Extensions can't) and 2) we avoid + // leaking the duo-labs/webauthn dependency. + // The nesting of types is identical to protocol.CredentialAssertionResponse. + + PublicKeyCredential + AssertionResponse AuthenticatorAssertionResponse `json:"response"` +} + +// AuthenticatorAssertionResponse is a clone of +// [protocol.AuthenticatorAssertionResponse], materialized here to keep a +// stable JSON marshal/unmarshal representation. +type AuthenticatorAssertionResponse struct { + AuthenticatorResponse + AuthenticatorData protocol.URLEncodedBase64 `json:"authenticatorData"` + Signature protocol.URLEncodedBase64 `json:"signature"` + UserHandle protocol.URLEncodedBase64 `json:"userHandle,omitempty"` +} + +// AuthenticatorResponse is a clone of [protocol.AuthenticatorResponse], +// materialized here to keep a stable JSON marshal/unmarshal representation. +type AuthenticatorResponse protocol.AuthenticatorResponse + +// CredentialCreation is the payload sent to authenticators to initiate +// registration. +type CredentialCreation struct { + Response PublicKeyCredentialCreationOptions `json:"publicKey"` +} + +// PublicKeyCredentialCreationOptions is a clone of +// [protocol.PublicKeyCredentialCreationOptions], materialized here to keep a +// stable JSON marshal/unmarshal representation. +type PublicKeyCredentialCreationOptions struct { + Challenge Challenge `json:"challenge"` + RelyingParty RelyingPartyEntity `json:"rp"` + User UserEntity `json:"user"` + Parameters []CredentialParameter `json:"pubKeyCredParams,omitempty"` + AuthenticatorSelection AuthenticatorSelection `json:"authenticatorSelection,omitempty"` + Timeout int `json:"timeout,omitempty"` + CredentialExcludeList []CredentialDescriptor `json:"excludeCredentials,omitempty"` + Extensions AuthenticationExtensions `json:"extensions,omitempty"` + Attestation protocol.ConveyancePreference `json:"attestation,omitempty"` +} + +// RelyingPartyEntity is a clone of [protocol.RelyingPartyEntity], materialized +// here to keep a stable JSON marshal/unmarshal representation. +type RelyingPartyEntity struct { + CredentialEntity + ID string `json:"id"` +} + +// UserEntity is a clone of [protocol.UserEntity], materialized here to keep a +// stable JSON marshal/unmarshal representation. +type UserEntity struct { + CredentialEntity + DisplayName string `json:"displayName,omitempty"` + ID []byte `json:"id"` +} + +// CredentialEntity is a clone of [protocol.CredentialEntity], materialized here +// to keep a stable JSON marshal/unmarshal representation. +type CredentialEntity = protocol.CredentialEntity + +// CredentialParameter is a clone of +// [protocol.CredentialParameter], materialized here to keep a stable JSON +// marshal/unmarshal representation. +type CredentialParameter struct { + Type protocol.CredentialType `json:"type"` + Algorithm webauthncose.COSEAlgorithmIdentifier `json:"alg"` +} + +// AuthenticatorSelection is a clone of +// [protocol.AuthenticatorSelection], materialized here to keep a stable JSON +// marshal/unmarshal representation. +type AuthenticatorSelection struct { + AuthenticatorAttachment protocol.AuthenticatorAttachment `json:"authenticatorAttachment,omitempty"` + RequireResidentKey *bool `json:"requireResidentKey,omitempty"` + ResidentKey protocol.ResidentKeyRequirement `json:"residentKey,omitempty"` + UserVerification protocol.UserVerificationRequirement `json:"userVerification,omitempty"` +} + +// AuthenticationExtensions is a clone of [protocol.AuthenticationExtensions], +// materialized here to keep a stable JSON marshal/unmarshal representation. +type AuthenticationExtensions = protocol.AuthenticationExtensions + +// RequireResidentKey returns information whether resident key is required or +// not. It checks ResidentKey and fallbacks to RequireResidentKey. +func (cc *CredentialCreation) RequireResidentKey() (bool, error) { + as := cc.Response.AuthenticatorSelection + switch as.ResidentKey { + case protocol.ResidentKeyRequirementRequired: + if as.RequireResidentKey != nil && !*as.RequireResidentKey { + return false, trace.BadParameter("invalid combination of ResidentKey: %v and RequireResidentKey: %v", as.ResidentKey, *as.RequireResidentKey) + } + return true, nil + case protocol.ResidentKeyRequirementDiscouraged: + if as.RequireResidentKey != nil && *as.RequireResidentKey { + return false, trace.BadParameter("invalid combination of ResidentKey: %v and RequireResidentKey: %v", as.ResidentKey, *as.RequireResidentKey) + } + return false, nil + case protocol.ResidentKeyRequirementPreferred: + return false, nil + } + // If ResidentKey is not set, then fallback to the legacy RequireResidentKey + // field. + return as.RequireResidentKey != nil && *as.RequireResidentKey, nil +} + +// Validate performs client-side validation of CredentialCreation. +// It makes sure that data are valid and can be sent to authenticator. +// This is general purpose validation and authenticator should add its own +// on top of it, if necessary. +func (cc *CredentialCreation) Validate() error { + switch { + case cc == nil: + return trace.BadParameter("credential creation required") + case len(cc.Response.Challenge) == 0: + return trace.BadParameter("credential creation challenge required") + case cc.Response.RelyingParty.ID == "": + return trace.BadParameter("credential creation relying party ID required") + case len(cc.Response.RelyingParty.Name) == 0: + return trace.BadParameter("relying party name required") + case len(cc.Response.User.Name) == 0: + return trace.BadParameter("user name required") + case len(cc.Response.User.DisplayName) == 0: + return trace.BadParameter("user display name required") + case len(cc.Response.User.ID) == 0: + return trace.BadParameter("user ID required") + default: + return nil + } +} + +// CredentialCreationFromProtocol converts a [protocol.CredentialCreation] to a +// [CredentialCreation]. +func CredentialCreationFromProtocol(cc *protocol.CredentialCreation) *CredentialCreation { + if cc == nil { + return nil + } + + return &CredentialCreation{ + Response: PublicKeyCredentialCreationOptions{ + Challenge: Challenge(cc.Response.Challenge), + RelyingParty: RelyingPartyEntity{ + CredentialEntity: cc.Response.RelyingParty.CredentialEntity, + ID: cc.Response.RelyingParty.ID, + }, + User: UserEntity{ + CredentialEntity: cc.Response.User.CredentialEntity, + DisplayName: cc.Response.User.Name, + ID: cc.Response.User.ID, + }, + Parameters: credentialParametersFromProtocol(cc.Response.Parameters), + AuthenticatorSelection: AuthenticatorSelection{ + AuthenticatorAttachment: cc.Response.AuthenticatorSelection.AuthenticatorAttachment, + RequireResidentKey: cc.Response.AuthenticatorSelection.RequireResidentKey, + ResidentKey: cc.Response.AuthenticatorSelection.ResidentKey, + UserVerification: cc.Response.AuthenticatorSelection.UserVerification, + }, + Timeout: cc.Response.Timeout, + CredentialExcludeList: credentialDescriptorsFromProtocol(cc.Response.CredentialExcludeList), + Extensions: cc.Response.Extensions, + Attestation: cc.Response.Attestation, + }, + } +} + +func credentialParametersFromProtocol(ps []protocol.CredentialParameter) []CredentialParameter { + if len(ps) == 0 { + return nil + } + + res := make([]CredentialParameter, len(ps)) + for i, p := range ps { + res[i] = CredentialParameter{ + Type: p.Type, + Algorithm: p.Algorithm, + } + } + return res +} + +// CredentialCreationResponse is the reply from authenticators to complete +// registration. +type CredentialCreationResponse struct { + // CredentialCreationResponse is manually redefined, instead of directly based + // in protocol.CredentialCreationResponse, for the same reasoning that + // CredentialAssertionResponse is - in short, we want a clean package. + // The nesting of types is identical to protocol.CredentialCreationResponse. + + PublicKeyCredential + AttestationResponse AuthenticatorAttestationResponse `json:"response"` +} + +// AuthenticatorAttestationResponse is a clone of +// [protocol.AuthenticatorAttestationResponse], materialized here to keep a +// stable JSON marshal/unmarshal representation. +type AuthenticatorAttestationResponse struct { + AuthenticatorResponse + AttestationObject protocol.URLEncodedBase64 `json:"attestationObject"` + + // Added by go-webauthn/webauthn v0.7.2. + // Transports []string `json:"transports,omitempty"` +} + +// Challenge represents a WebAuthn challenge. +// It is used instead of [protocol.URLEncodedBase64] so its JSON +// marshal/unmarshal representation won't change in relation to older Teleport +// versions. +type Challenge []byte + +func CreateChallenge() (Challenge, error) { + chal, err := protocol.CreateChallenge() + if err != nil { + return nil, err + } + return Challenge(chal), nil +} + +func (c Challenge) String() string { + return base64.RawURLEncoding.EncodeToString(c) +} + +// CredentialDescriptor is a clone of [protocol.CredentialDescriptor], +// materialized here to keep a stable JSON marshal/unmarshal representation. +type CredentialDescriptor struct { + Type protocol.CredentialType `json:"type"` + CredentialID []byte `json:"id"` + Transport []protocol.AuthenticatorTransport `json:"transports,omitempty"` + AttestationType string `json:"-"` +} + +// PublicKeyCredential is a clone of [protocol.PublicKeyCredential], +// materialized here to keep a stable JSON marshal/unmarshal representation. +type PublicKeyCredential struct { + Credential + RawID protocol.URLEncodedBase64 `json:"rawId"` + Extensions *AuthenticationExtensionsClientOutputs `json:"extensions,omitempty"` + + // Added by go-webauthn/webauthn v0.7.2. + // AuthenticatorAttachment string `json:"authenticatorAttachment,omitempty"` +} + +// Credential is a clone of [protocol.Credential], materialized here to keep a +// stable JSON marshal/unmarshal representation. +type Credential protocol.Credential + +// AuthenticationExtensionsClientOutputs is a clone of +// [protocol.AuthenticationExtensionsClientOutputs], materialized here to keep a +// stable JSON marshal/unmarshal representation. +type AuthenticationExtensionsClientOutputs struct { + AppID bool `json:"appid,omitempty"` +} diff --git a/lib/auth/webauthn/messages_test.go b/lib/auth/webauthntypes/webauthn_test.go similarity index 75% rename from lib/auth/webauthn/messages_test.go rename to lib/auth/webauthntypes/webauthn_test.go index 19808f4aaa50e..a7f578e08dbeb 100644 --- a/lib/auth/webauthn/messages_test.go +++ b/lib/auth/webauthntypes/webauthn_test.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package webauthn_test +package webauthntypes_test import ( "encoding/base64" @@ -25,23 +25,23 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) func TestCredentialAssertionResponse_json(t *testing.T) { - resp := &wanlib.CredentialAssertionResponse{ - PublicKeyCredential: wanlib.PublicKeyCredential{ - Credential: wanlib.Credential{ + resp := &wantypes.CredentialAssertionResponse{ + PublicKeyCredential: wantypes.PublicKeyCredential{ + Credential: wantypes.Credential{ ID: base64.RawURLEncoding.EncodeToString([]byte("credentialid")), Type: "public-key", }, RawID: []byte("credentialid"), - Extensions: &wanlib.AuthenticationExtensionsClientOutputs{ + Extensions: &wantypes.AuthenticationExtensionsClientOutputs{ AppID: true, }, }, - AssertionResponse: wanlib.AuthenticatorAssertionResponse{ - AuthenticatorResponse: wanlib.AuthenticatorResponse{ + AssertionResponse: wantypes.AuthenticatorAssertionResponse{ + AuthenticatorResponse: wantypes.AuthenticatorResponse{ ClientDataJSON: []byte("clientdatajson"), }, AuthenticatorData: []byte("authdata"), @@ -53,7 +53,7 @@ func TestCredentialAssertionResponse_json(t *testing.T) { respJSON, err := json.Marshal(resp) require.NoError(t, err) - got := &wanlib.CredentialAssertionResponse{} + got := &wantypes.CredentialAssertionResponse{} require.NoError(t, json.Unmarshal(respJSON, got)) if diff := cmp.Diff(resp, got); diff != "" { t.Errorf("Unmarshal() mismatch (-want +got):\n%s", diff) @@ -61,24 +61,24 @@ func TestCredentialAssertionResponse_json(t *testing.T) { } func TestCredentialCreation_Validate(t *testing.T) { - okCC := &wanlib.CredentialCreation{ - Response: protocol.PublicKeyCredentialCreationOptions{ + okCC := &wantypes.CredentialCreation{ + Response: wantypes.PublicKeyCredentialCreationOptions{ Challenge: make([]byte, 32), - RelyingParty: protocol.RelyingPartyEntity{ + RelyingParty: wantypes.RelyingPartyEntity{ ID: "example.com", - CredentialEntity: protocol.CredentialEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "Teleport", }, }, - Parameters: []protocol.CredentialParameter{ + Parameters: []wantypes.CredentialParameter{ {Type: protocol.PublicKeyCredentialType, Algorithm: webauthncose.AlgES256}, }, - AuthenticatorSelection: protocol.AuthenticatorSelection{ + AuthenticatorSelection: wantypes.AuthenticatorSelection{ UserVerification: protocol.VerificationDiscouraged, }, Attestation: protocol.PreferNoAttestation, - User: protocol.UserEntity{ - CredentialEntity: protocol.CredentialEntity{ + User: wantypes.UserEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "llama", }, DisplayName: "Llama", @@ -89,23 +89,23 @@ func TestCredentialCreation_Validate(t *testing.T) { tests := []struct { name string - createCC func() *wanlib.CredentialCreation + createCC func() *wantypes.CredentialCreation alwaysCreateRK bool wantErr string }{ { name: "ok", // check that good params are good - createCC: func() *wanlib.CredentialCreation { return okCC }, + createCC: func() *wantypes.CredentialCreation { return okCC }, wantErr: "", }, { name: "nil cc", - createCC: func() *wanlib.CredentialCreation { return nil }, + createCC: func() *wantypes.CredentialCreation { return nil }, wantErr: "credential creation required", }, { name: "nil challenge", - createCC: func() *wanlib.CredentialCreation { + createCC: func() *wantypes.CredentialCreation { cp := *okCC cp.Response.Challenge = nil return &cp @@ -114,7 +114,7 @@ func TestCredentialCreation_Validate(t *testing.T) { }, { name: "empty RPID", - createCC: func() *wanlib.CredentialCreation { + createCC: func() *wantypes.CredentialCreation { cp := *okCC cp.Response.RelyingParty.ID = "" return &cp @@ -123,7 +123,7 @@ func TestCredentialCreation_Validate(t *testing.T) { }, { name: "empty RP name", - createCC: func() *wanlib.CredentialCreation { + createCC: func() *wantypes.CredentialCreation { cp := *okCC cp.Response.RelyingParty.Name = "" return &cp @@ -132,7 +132,7 @@ func TestCredentialCreation_Validate(t *testing.T) { }, { name: "empty user name", - createCC: func() *wanlib.CredentialCreation { + createCC: func() *wantypes.CredentialCreation { cp := *okCC cp.Response.User.Name = "" return &cp @@ -141,7 +141,7 @@ func TestCredentialCreation_Validate(t *testing.T) { }, { name: "empty user display name", - createCC: func() *wanlib.CredentialCreation { + createCC: func() *wantypes.CredentialCreation { cp := *okCC cp.Response.User.DisplayName = "" return &cp @@ -150,7 +150,7 @@ func TestCredentialCreation_Validate(t *testing.T) { }, { name: "nil user ID", - createCC: func() *wanlib.CredentialCreation { + createCC: func() *wantypes.CredentialCreation { cp := *okCC cp.Response.User.ID = nil return &cp @@ -171,11 +171,11 @@ func TestCredentialCreation_Validate(t *testing.T) { } func TestCredentialAssertion_Validate(t *testing.T) { - okAssertion := &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + okAssertion := &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: make([]byte, 32), RelyingPartyID: "example.com", - AllowedCredentials: []protocol.CredentialDescriptor{ + AllowedCredentials: []wantypes.CredentialDescriptor{ {Type: protocol.PublicKeyCredentialType, CredentialID: []byte{1, 2, 3, 4, 5}}, }, }, @@ -188,7 +188,7 @@ func TestCredentialAssertion_Validate(t *testing.T) { emptyRPIDAssertion.Response.RelyingPartyID = "" tests := []struct { name string - assertion *wanlib.CredentialAssertion + assertion *wantypes.CredentialAssertion wantErr string }{ { @@ -226,18 +226,18 @@ func TestCredentialAssertion_Validate(t *testing.T) { func TestRequireResidentKey(t *testing.T) { tests := []struct { name string - in protocol.AuthenticatorSelection + in wantypes.AuthenticatorSelection want bool wantErr string }{ { name: "nothing set", - in: protocol.AuthenticatorSelection{}, + in: wantypes.AuthenticatorSelection{}, want: false, }, { name: "discouraged and rrk=true", - in: protocol.AuthenticatorSelection{ + in: wantypes.AuthenticatorSelection{ ResidentKey: protocol.ResidentKeyRequirementDiscouraged, RequireResidentKey: protocol.ResidentKeyRequired(), }, @@ -245,7 +245,7 @@ func TestRequireResidentKey(t *testing.T) { }, { name: "required and rrk=false", - in: protocol.AuthenticatorSelection{ + in: wantypes.AuthenticatorSelection{ ResidentKey: protocol.ResidentKeyRequirementRequired, RequireResidentKey: protocol.ResidentKeyNotRequired(), }, @@ -253,7 +253,7 @@ func TestRequireResidentKey(t *testing.T) { }, { name: "support nil RequireResidentKey", - in: protocol.AuthenticatorSelection{ + in: wantypes.AuthenticatorSelection{ ResidentKey: "", RequireResidentKey: nil, }, @@ -261,7 +261,7 @@ func TestRequireResidentKey(t *testing.T) { }, { name: "ResidentKey preferred result in false", - in: protocol.AuthenticatorSelection{ + in: wantypes.AuthenticatorSelection{ ResidentKey: protocol.ResidentKeyRequirementPreferred, RequireResidentKey: nil, }, @@ -269,21 +269,21 @@ func TestRequireResidentKey(t *testing.T) { }, { name: "ResidentKey required", - in: protocol.AuthenticatorSelection{ + in: wantypes.AuthenticatorSelection{ ResidentKey: protocol.ResidentKeyRequirementRequired, }, want: true, }, { name: "ResidentKey discouraged", - in: protocol.AuthenticatorSelection{ + in: wantypes.AuthenticatorSelection{ ResidentKey: protocol.ResidentKeyRequirementDiscouraged, }, want: false, }, { name: "use RequireResidentKey required if ResidentKey empty", - in: protocol.AuthenticatorSelection{ + in: wantypes.AuthenticatorSelection{ ResidentKey: "", RequireResidentKey: protocol.ResidentKeyRequired(), }, @@ -292,8 +292,8 @@ func TestRequireResidentKey(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - req := &wanlib.CredentialCreation{ - Response: protocol.PublicKeyCredentialCreationOptions{ + req := &wantypes.CredentialCreation{ + Response: wantypes.PublicKeyCredentialCreationOptions{ AuthenticatorSelection: test.in, }, } diff --git a/lib/auth/webauthnwin/api.go b/lib/auth/webauthnwin/api.go index a9597c68d3a92..3a216eba62148 100644 --- a/lib/auth/webauthnwin/api.go +++ b/lib/auth/webauthnwin/api.go @@ -31,7 +31,7 @@ import ( log "github.com/sirupsen/logrus" "github.com/gravitational/teleport/api/client/proto" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) // LoginOpts groups non-mandatory options for Login. @@ -52,8 +52,8 @@ const ( // Implementors must provide a global variable called `native`. type nativeWebauthn interface { CheckSupport() CheckSupportResult - GetAssertion(origin string, in *getAssertionRequest) (*wanlib.CredentialAssertionResponse, error) - MakeCredential(origin string, in *makeCredentialRequest) (*wanlib.CredentialCreationResponse, error) + GetAssertion(origin string, in *getAssertionRequest) (*wantypes.CredentialAssertionResponse, error) + MakeCredential(origin string, in *makeCredentialRequest) (*wantypes.CredentialCreationResponse, error) } type getAssertionRequest struct { @@ -73,7 +73,7 @@ type makeCredentialRequest struct { } // Login implements Login for Windows Webauthn API. -func Login(_ context.Context, origin string, assertion *wanlib.CredentialAssertion, loginOpts *LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { +func Login(_ context.Context, origin string, assertion *wantypes.CredentialAssertion, loginOpts *LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { if origin == "" { return nil, "", trace.BadParameter("origin required") } @@ -106,13 +106,13 @@ func Login(_ context.Context, origin string, assertion *wanlib.CredentialAsserti return &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(resp), + Webauthn: wantypes.CredentialAssertionResponseToProto(resp), }, }, "", nil } // Register implements Register for Windows Webauthn API. -func Register(_ context.Context, origin string, cc *wanlib.CredentialCreation) (*proto.MFARegisterResponse, error) { +func Register(_ context.Context, origin string, cc *wantypes.CredentialCreation) (*proto.MFARegisterResponse, error) { if origin == "" { return nil, trace.BadParameter("origin required") } @@ -155,7 +155,7 @@ func Register(_ context.Context, origin string, cc *wanlib.CredentialCreation) ( return &proto.MFARegisterResponse{ Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: wanlib.CredentialCreationResponseToProto(resp), + Webauthn: wantypes.CredentialCreationResponseToProto(resp), }, }, nil } @@ -224,23 +224,23 @@ func Diag(ctx context.Context) (*DiagResult, error) { // Attempt registration. const origin = "localhost" - cc := &wanlib.CredentialCreation{ - Response: protocol.PublicKeyCredentialCreationOptions{ + cc := &wantypes.CredentialCreation{ + Response: wantypes.PublicKeyCredentialCreationOptions{ Challenge: make([]byte, 32), - RelyingParty: protocol.RelyingPartyEntity{ + RelyingParty: wantypes.RelyingPartyEntity{ ID: "localhost", - CredentialEntity: protocol.CredentialEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "test RP", }, }, - User: protocol.UserEntity{ - CredentialEntity: protocol.CredentialEntity{ + User: wantypes.UserEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "test", }, ID: []byte("test"), DisplayName: "test", }, - Parameters: []protocol.CredentialParameter{ + Parameters: []wantypes.CredentialParameter{ { Type: protocol.PublicKeyCredentialType, Algorithm: webauthncose.AlgES256, @@ -260,11 +260,11 @@ func Diag(ctx context.Context) (*DiagResult, error) { res.RegisterSuccessful = true // Attempt login. - assertion := &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + assertion := &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: make([]byte, 32), RelyingPartyID: cc.Response.RelyingParty.ID, - AllowedCredentials: []protocol.CredentialDescriptor{ + AllowedCredentials: []wantypes.CredentialDescriptor{ { Type: protocol.PublicKeyCredentialType, CredentialID: ccr.GetWebauthn().GetRawId(), diff --git a/lib/auth/webauthnwin/api_test.go b/lib/auth/webauthnwin/api_test.go index 1aa4e254977fd..b6ccb06502cdc 100644 --- a/lib/auth/webauthnwin/api_test.go +++ b/lib/auth/webauthnwin/api_test.go @@ -25,8 +25,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/gravitational/teleport/api/types/webauthn" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) func init() { @@ -38,27 +38,27 @@ func TestRegister(t *testing.T) { resetNativeAfterTests(t) const origin = "https://example.com" - okCC := &wanlib.CredentialCreation{ - Response: protocol.PublicKeyCredentialCreationOptions{ + okCC := &wantypes.CredentialCreation{ + Response: wantypes.PublicKeyCredentialCreationOptions{ Challenge: make([]byte, 32), - RelyingParty: protocol.RelyingPartyEntity{ + RelyingParty: wantypes.RelyingPartyEntity{ ID: "example.com", - CredentialEntity: protocol.CredentialEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "Teleport", }, }, - User: protocol.UserEntity{ + User: wantypes.UserEntity{ ID: []byte{1, 2, 3, 4}, DisplayName: "display name", - CredentialEntity: protocol.CredentialEntity{ + CredentialEntity: wantypes.CredentialEntity{ Name: "user name", }, }, - Parameters: []protocol.CredentialParameter{ + Parameters: []wantypes.CredentialParameter{ {Type: protocol.PublicKeyCredentialType, Algorithm: webauthncose.AlgES256}, {Type: protocol.PublicKeyCredentialType, Algorithm: webauthncose.AlgRS256}, }, - AuthenticatorSelection: protocol.AuthenticatorSelection{ + AuthenticatorSelection: wantypes.AuthenticatorSelection{ UserVerification: protocol.VerificationDiscouraged, }, Attestation: protocol.PreferNoAttestation, @@ -68,14 +68,14 @@ func TestRegister(t *testing.T) { tests := []struct { name string origin string - createCC func() *wanlib.CredentialCreation - assertFn func(t *testing.T, ccr *webauthn.CredentialCreationResponse, req *makeCredentialRequest) + createCC func() *wantypes.CredentialCreation + assertFn func(t *testing.T, ccr *wanpb.CredentialCreationResponse, req *makeCredentialRequest) }{ { name: "flow with auto attachment and discouraged UV", origin: origin, - createCC: func() *wanlib.CredentialCreation { return okCC }, - assertFn: func(t *testing.T, ccr *webauthn.CredentialCreationResponse, req *makeCredentialRequest) { + createCC: func() *wantypes.CredentialCreation { return okCC }, + assertFn: func(t *testing.T, ccr *wanpb.CredentialCreationResponse, req *makeCredentialRequest) { assert.Equal(t, webauthnAttachmentAny, req.opts.dwAuthenticatorAttachment) assert.Equal(t, webauthnUserVerificationDiscouraged, req.opts.dwUserVerificationRequirement) @@ -86,7 +86,7 @@ func TestRegister(t *testing.T) { { name: "with UV required and cross-platform and RRK", origin: origin, - createCC: func() *wanlib.CredentialCreation { + createCC: func() *wantypes.CredentialCreation { cc := *okCC cc.Response.User.DisplayName = "display name" cc.Response.AuthenticatorSelection.UserVerification = protocol.VerificationRequired @@ -94,7 +94,7 @@ func TestRegister(t *testing.T) { cc.Response.AuthenticatorSelection.ResidentKey = protocol.ResidentKeyRequirementRequired return &cc }, - assertFn: func(t *testing.T, ccr *webauthn.CredentialCreationResponse, req *makeCredentialRequest) { + assertFn: func(t *testing.T, ccr *wanpb.CredentialCreationResponse, req *makeCredentialRequest) { assert.Equal(t, webauthnUserVerificationRequired, req.opts.dwUserVerificationRequirement) assert.Equal(t, webauthnAttachmentCrossPlatform, req.opts.dwAuthenticatorAttachment) @@ -105,13 +105,13 @@ func TestRegister(t *testing.T) { { name: "with UV preferred and platform", origin: origin, - createCC: func() *wanlib.CredentialCreation { + createCC: func() *wantypes.CredentialCreation { cc := *okCC cc.Response.AuthenticatorSelection.UserVerification = protocol.VerificationPreferred cc.Response.AuthenticatorSelection.AuthenticatorAttachment = protocol.Platform return &cc }, - assertFn: func(t *testing.T, ccr *webauthn.CredentialCreationResponse, req *makeCredentialRequest) { + assertFn: func(t *testing.T, ccr *wanpb.CredentialCreationResponse, req *makeCredentialRequest) { assert.Equal(t, webauthnUserVerificationPreferred, req.opts.dwUserVerificationRequirement) assert.Equal(t, webauthnAttachmentPlatform, req.opts.dwAuthenticatorAttachment) @@ -120,24 +120,24 @@ func TestRegister(t *testing.T) { { name: "with UV discouraged and platform", origin: origin, - createCC: func() *wanlib.CredentialCreation { + createCC: func() *wantypes.CredentialCreation { cc := *okCC cc.Response.AuthenticatorSelection.UserVerification = protocol.VerificationDiscouraged return &cc }, - assertFn: func(t *testing.T, ccr *webauthn.CredentialCreationResponse, req *makeCredentialRequest) { + assertFn: func(t *testing.T, ccr *wanpb.CredentialCreationResponse, req *makeCredentialRequest) { assert.Equal(t, webauthnUserVerificationDiscouraged, req.opts.dwUserVerificationRequirement) }, }, { name: "RRK from RequireResidentKey if is empty ResidentKey", origin: origin, - createCC: func() *wanlib.CredentialCreation { + createCC: func() *wantypes.CredentialCreation { cc := *okCC cc.Response.AuthenticatorSelection.RequireResidentKey = protocol.ResidentKeyRequired() return &cc }, - assertFn: func(t *testing.T, ccr *webauthn.CredentialCreationResponse, req *makeCredentialRequest) { + assertFn: func(t *testing.T, ccr *wanpb.CredentialCreationResponse, req *makeCredentialRequest) { assert.Equal(t, uint32(1), req.opts.bRequireResidentKey) }, }, @@ -163,11 +163,11 @@ func TestLogin(t *testing.T) { resetNativeAfterTests(t) const origin = "https://example.com" - okAssertion := &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + okAssertion := &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: make([]byte, 32), RelyingPartyID: "example.com", - AllowedCredentials: []protocol.CredentialDescriptor{ + AllowedCredentials: []wantypes.CredentialDescriptor{ {Type: protocol.PublicKeyCredentialType, CredentialID: []byte{1, 2, 3, 4, 5}}, }, UserVerification: protocol.VerificationDiscouraged, @@ -177,16 +177,16 @@ func TestLogin(t *testing.T) { tests := []struct { name string origin string - assertionIn func() *wanlib.CredentialAssertion + assertionIn func() *wantypes.CredentialAssertion opts LoginOpts wantErr string - assertFn func(t *testing.T, car *webauthn.CredentialAssertionResponse, req *getAssertionRequest) + assertFn func(t *testing.T, car *wanpb.CredentialAssertionResponse, req *getAssertionRequest) }{ { name: "uv discouraged, attachment auto", origin: origin, - assertionIn: func() *wanlib.CredentialAssertion { return okAssertion }, - assertFn: func(t *testing.T, car *webauthn.CredentialAssertionResponse, req *getAssertionRequest) { + assertionIn: func() *wantypes.CredentialAssertion { return okAssertion }, + assertFn: func(t *testing.T, car *wanpb.CredentialAssertionResponse, req *getAssertionRequest) { assert.Equal(t, uint32(6), req.opts.dwVersion) assert.Equal(t, webauthnUserVerificationDiscouraged, req.opts.dwUserVerificationRequirement) @@ -197,13 +197,13 @@ func TestLogin(t *testing.T) { { name: "uv required, attachment platform", origin: origin, - assertionIn: func() *wanlib.CredentialAssertion { + assertionIn: func() *wantypes.CredentialAssertion { out := *okAssertion out.Response.UserVerification = protocol.VerificationRequired return &out }, opts: LoginOpts{AuthenticatorAttachment: AttachmentPlatform}, - assertFn: func(t *testing.T, car *webauthn.CredentialAssertionResponse, req *getAssertionRequest) { + assertFn: func(t *testing.T, car *wanpb.CredentialAssertionResponse, req *getAssertionRequest) { assert.Equal(t, uint32(6), req.opts.dwVersion) assert.Equal(t, webauthnUserVerificationRequired, req.opts.dwUserVerificationRequirement) @@ -214,13 +214,13 @@ func TestLogin(t *testing.T) { { name: "uv preferred, attachment cross-platform", origin: origin, - assertionIn: func() *wanlib.CredentialAssertion { + assertionIn: func() *wantypes.CredentialAssertion { out := *okAssertion out.Response.UserVerification = protocol.VerificationPreferred return &out }, opts: LoginOpts{AuthenticatorAttachment: AttachmentCrossPlatform}, - assertFn: func(t *testing.T, car *webauthn.CredentialAssertionResponse, req *getAssertionRequest) { + assertFn: func(t *testing.T, car *wanpb.CredentialAssertionResponse, req *getAssertionRequest) { assert.Equal(t, uint32(6), req.opts.dwVersion) assert.Equal(t, webauthnUserVerificationPreferred, req.opts.dwUserVerificationRequirement) @@ -231,13 +231,13 @@ func TestLogin(t *testing.T) { { name: "uv discouraged", origin: origin, - assertionIn: func() *wanlib.CredentialAssertion { + assertionIn: func() *wantypes.CredentialAssertion { out := *okAssertion out.Response.UserVerification = protocol.VerificationDiscouraged return &out }, opts: LoginOpts{AuthenticatorAttachment: AttachmentCrossPlatform}, - assertFn: func(t *testing.T, car *webauthn.CredentialAssertionResponse, req *getAssertionRequest) { + assertFn: func(t *testing.T, car *wanpb.CredentialAssertionResponse, req *getAssertionRequest) { assert.Equal(t, uint32(6), req.opts.dwVersion) assert.Equal(t, webauthnUserVerificationDiscouraged, req.opts.dwUserVerificationRequirement) @@ -280,12 +280,12 @@ func (m *mockNative) CheckSupport() CheckSupportResult { } } -func (m *mockNative) GetAssertion(origin string, in *getAssertionRequest) (*wanlib.CredentialAssertionResponse, error) { +func (m *mockNative) GetAssertion(origin string, in *getAssertionRequest) (*wantypes.CredentialAssertionResponse, error) { m.getAssersionReq = in - return &wanlib.CredentialAssertionResponse{}, nil + return &wantypes.CredentialAssertionResponse{}, nil } -func (m *mockNative) MakeCredential(origin string, in *makeCredentialRequest) (*wanlib.CredentialCreationResponse, error) { +func (m *mockNative) MakeCredential(origin string, in *makeCredentialRequest) (*wantypes.CredentialCreationResponse, error) { m.makeCredentialReq = in - return &wanlib.CredentialCreationResponse{}, nil + return &wantypes.CredentialCreationResponse{}, nil } diff --git a/lib/auth/webauthnwin/conv.go b/lib/auth/webauthnwin/conv.go index ad293b5bc1e8d..55e2cdf57bca5 100644 --- a/lib/auth/webauthnwin/conv.go +++ b/lib/auth/webauthnwin/conv.go @@ -22,9 +22,11 @@ import ( "github.com/go-webauthn/webauthn/protocol" "github.com/gravitational/trace" + + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) -func assertOptionsToCType(in protocol.PublicKeyCredentialRequestOptions, loginOpts *LoginOpts) (*webauthnAuthenticatorGetAssertionOptions, error) { +func assertOptionsToCType(in wantypes.PublicKeyCredentialRequestOptions, loginOpts *LoginOpts) (*webauthnAuthenticatorGetAssertionOptions, error) { allowCredList, err := credentialsExToCType(in.AllowedCredentials) if err != nil { return nil, err @@ -53,7 +55,7 @@ func assertOptionsToCType(in protocol.PublicKeyCredentialRequestOptions, loginOp }, nil } -func rpToCType(in protocol.RelyingPartyEntity) (*webauthnRPEntityInformation, error) { +func rpToCType(in wantypes.RelyingPartyEntity) (*webauthnRPEntityInformation, error) { if in.ID == "" { return nil, trace.BadParameter("missing RelyingPartyEntity.Id") } @@ -68,22 +70,14 @@ func rpToCType(in protocol.RelyingPartyEntity) (*webauthnRPEntityInformation, er if err != nil { return nil, err } - var icon *uint16 - if in.Icon != "" { - icon, err = utf16PtrFromString(in.Icon) - if err != nil { - return nil, err - } - } return &webauthnRPEntityInformation{ dwVersion: 1, pwszID: id, pwszName: name, - pwszIcon: icon, }, nil } -func userToCType(in protocol.UserEntity) (*webauthnUserEntityInformation, error) { +func userToCType(in wantypes.UserEntity) (*webauthnUserEntityInformation, error) { if len(in.ID) == 0 { return nil, trace.BadParameter("missing UserEntity.Id") } @@ -102,24 +96,16 @@ func userToCType(in protocol.UserEntity) (*webauthnUserEntityInformation, error) return nil, err } } - var icon *uint16 - if in.Icon != "" { - icon, err = utf16PtrFromString(in.Icon) - if err != nil { - return nil, err - } - } return &webauthnUserEntityInformation{ dwVersion: 1, cbID: uint32(len(in.ID)), pbID: &in.ID[0], pwszName: name, pwszDisplayName: displayName, - pwszIcon: icon, }, nil } -func credParamToCType(in []protocol.CredentialParameter) (*webauthnCoseCredentialParameters, error) { +func credParamToCType(in []wantypes.CredentialParameter) (*webauthnCoseCredentialParameters, error) { if len(in) == 0 { return nil, trace.BadParameter("missing CredentialParameter") } @@ -174,7 +160,7 @@ func clientDataToCType(challenge, origin, cdType string) (*webauthnClientData, [ }, jsonCD, nil } -func credentialsExToCType(in []protocol.CredentialDescriptor) (*webauthnCredentialList, error) { +func credentialsExToCType(in []wantypes.CredentialDescriptor) (*webauthnCredentialList, error) { exCredList := make([]*webauthnCredentialEX, 0, len(in)) for _, e := range in { if e.Type == "" { @@ -270,7 +256,7 @@ func userVerificationToCType(in protocol.UserVerificationRequirement) uint32 { } } -func requirePreferResidentKey(in protocol.AuthenticatorSelection) (requireRK bool, preferRK bool) { +func requirePreferResidentKey(in wantypes.AuthenticatorSelection) (requireRK bool, preferRK bool) { switch in.ResidentKey { case protocol.ResidentKeyRequirementRequired: return true, false @@ -286,7 +272,7 @@ func requirePreferResidentKey(in protocol.AuthenticatorSelection) (requireRK boo } } -func makeCredOptionsToCType(in protocol.PublicKeyCredentialCreationOptions) (*webauthnAuthenticatorMakeCredentialOptions, error) { +func makeCredOptionsToCType(in wantypes.PublicKeyCredentialCreationOptions) (*webauthnAuthenticatorMakeCredentialOptions, error) { exCredList, err := credentialsExToCType(in.CredentialExcludeList) if err != nil { return nil, err diff --git a/lib/auth/webauthnwin/ctypes.go b/lib/auth/webauthnwin/ctypes.go index 2da0c0a7e3948..a97113b45aebc 100644 --- a/lib/auth/webauthnwin/ctypes.go +++ b/lib/auth/webauthnwin/ctypes.go @@ -35,8 +35,6 @@ type webauthnRPEntityInformation struct { // "Acme Corporation", "Widgets Inc" or "Awesome Site". // This field is required. pwszName *uint16 - // Optional URL pointing to RP's logo. - pwszIcon *uint16 } type webauthnUserEntityInformation struct { @@ -48,9 +46,6 @@ type webauthnUserEntityInformation struct { // "john.p.smith@example.com". // It holds the Teleport user name. pwszName *uint16 - // Optional URL that can be used to retrieve an image containing the user's current avatar, - // or a data URI that contains the image data. - pwszIcon *uint16 // For User: Contains the friendly name associated with the user account by the Relying Party, such as "John P. Smith". pwszDisplayName *uint16 } diff --git a/lib/auth/webauthnwin/webauthn_other.go b/lib/auth/webauthnwin/webauthn_other.go index 6b557718413e7..35854f79e50b3 100644 --- a/lib/auth/webauthnwin/webauthn_other.go +++ b/lib/auth/webauthnwin/webauthn_other.go @@ -20,7 +20,7 @@ package webauthnwin import ( "errors" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) var native nativeWebauthn = noopNative{} @@ -35,10 +35,10 @@ func (n noopNative) CheckSupport() CheckSupportResult { } } -func (n noopNative) GetAssertion(origin string, in *getAssertionRequest) (*wanlib.CredentialAssertionResponse, error) { +func (n noopNative) GetAssertion(origin string, in *getAssertionRequest) (*wantypes.CredentialAssertionResponse, error) { return nil, errUnavailable } -func (n noopNative) MakeCredential(origin string, in *makeCredentialRequest) (*wanlib.CredentialCreationResponse, error) { +func (n noopNative) MakeCredential(origin string, in *makeCredentialRequest) (*wantypes.CredentialCreationResponse, error) { return nil, errUnavailable } diff --git a/lib/auth/webauthnwin/webauthn_windows.go b/lib/auth/webauthnwin/webauthn_windows.go index ac59071db044a..49d08e4773b0f 100644 --- a/lib/auth/webauthnwin/webauthn_windows.go +++ b/lib/auth/webauthnwin/webauthn_windows.go @@ -26,7 +26,7 @@ import ( log "github.com/sirupsen/logrus" "golang.org/x/sys/windows" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" ) var ( @@ -100,7 +100,7 @@ func (n *nativeImpl) CheckSupport() CheckSupportResult { // either security key or Windows Hello). // It does not accept username - during passwordless login webauthn.dll provides // its own dialog with credentials selection. -func (n *nativeImpl) GetAssertion(origin string, in *getAssertionRequest) (*wanlib.CredentialAssertionResponse, error) { +func (n *nativeImpl) GetAssertion(origin string, in *getAssertionRequest) (*wantypes.CredentialAssertionResponse, error) { hwnd, err := getForegroundWindow() if err != nil { return nil, trace.Wrap(err) @@ -132,19 +132,19 @@ func (n *nativeImpl) GetAssertion(origin string, in *getAssertionRequest) (*wanl credential := bytesFromCBytes(out.Credential.cbID, out.Credential.pbID) credType := windows.UTF16PtrToString(out.Credential.pwszCredentialType) - return &wanlib.CredentialAssertionResponse{ - PublicKeyCredential: wanlib.PublicKeyCredential{ + return &wantypes.CredentialAssertionResponse{ + PublicKeyCredential: wantypes.PublicKeyCredential{ RawID: credential, - Credential: wanlib.Credential{ + Credential: wantypes.Credential{ ID: base64.RawURLEncoding.EncodeToString(credential), Type: credType, }, }, - AssertionResponse: wanlib.AuthenticatorAssertionResponse{ + AssertionResponse: wantypes.AuthenticatorAssertionResponse{ AuthenticatorData: authData, Signature: signature, UserHandle: userID, - AuthenticatorResponse: wanlib.AuthenticatorResponse{ + AuthenticatorResponse: wantypes.AuthenticatorResponse{ ClientDataJSON: in.jsonEncodedClientData, }, }, @@ -157,7 +157,7 @@ func (n *nativeImpl) GetAssertion(origin string, in *getAssertionRequest) (*wanl // (using auto starts with Windows Hello but there is // option to select other devices). // Windows Hello keys are always resident. -func (n *nativeImpl) MakeCredential(origin string, in *makeCredentialRequest) (*wanlib.CredentialCreationResponse, error) { +func (n *nativeImpl) MakeCredential(origin string, in *makeCredentialRequest) (*wantypes.CredentialCreationResponse, error) { hwnd, err := getForegroundWindow() if err != nil { return nil, trace.Wrap(err) @@ -187,16 +187,16 @@ func (n *nativeImpl) MakeCredential(origin string, in *makeCredentialRequest) (* credential := bytesFromCBytes(out.cbCredentialID, out.pbCredentialID) - return &wanlib.CredentialCreationResponse{ - PublicKeyCredential: wanlib.PublicKeyCredential{ - Credential: wanlib.Credential{ + return &wantypes.CredentialCreationResponse{ + PublicKeyCredential: wantypes.PublicKeyCredential{ + Credential: wantypes.Credential{ ID: base64.RawURLEncoding.EncodeToString(credential), Type: string(protocol.PublicKeyCredentialType), }, RawID: credential, }, - AttestationResponse: wanlib.AuthenticatorAttestationResponse{ - AuthenticatorResponse: wanlib.AuthenticatorResponse{ + AttestationResponse: wantypes.AuthenticatorAttestationResponse{ + AuthenticatorResponse: wantypes.AuthenticatorResponse{ ClientDataJSON: in.jsonEncodedClientData, }, AttestationObject: bytesFromCBytes(out.cbAttestationObject, out.pbAttestationObject), diff --git a/lib/client/api_login_test.go b/lib/client/api_login_test.go index e1b1c972a8cac..e1af337560d91 100644 --- a/lib/client/api_login_test.go +++ b/lib/client/api_login_test.go @@ -41,8 +41,8 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/auth/mocku2f" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/cloud" @@ -98,7 +98,7 @@ func TestTeleportClient_Login_local(t *testing.T) { <-ctx.Done() // wait for timeout return "", ctx.Err() } - noopWebauthnFn := func(ctx context.Context, origin string, assertion *wanlib.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) { + noopWebauthnFn := func(ctx context.Context, origin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) { <-ctx.Done() // wait for timeout return nil, ctx.Err() } @@ -106,18 +106,18 @@ func TestTeleportClient_Login_local(t *testing.T) { solveOTP := func(ctx context.Context) (string, error) { return totp.GenerateCode(otpKey, clock.Now()) } - solveWebauthn := func(ctx context.Context, origin string, assertion *wanlib.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) { + solveWebauthn := func(ctx context.Context, origin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) { car, err := device.SignAssertion(origin, assertion) if err != nil { return nil, err } return &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(car), + Webauthn: wantypes.CredentialAssertionResponseToProto(car), }, }, nil } - solvePwdless := func(ctx context.Context, origin string, assertion *wanlib.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) { + solvePwdless := func(ctx context.Context, origin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) { resp, err := solveWebauthn(ctx, origin, assertion, prompt) if err == nil { resp.GetWebauthn().Response.UserHandle = webID @@ -129,7 +129,7 @@ func TestTeleportClient_Login_local(t *testing.T) { userPINFn := func(ctx context.Context) (string, error) { return pin, nil } - solvePIN := func(ctx context.Context, origin string, assertion *wanlib.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) { + solvePIN := func(ctx context.Context, origin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) { // Ask and verify the PIN. Usually the authenticator would verify the PIN, // but we are faking it here. got, err := prompt.PromptPIN() @@ -158,7 +158,7 @@ func TestTeleportClient_Login_local(t *testing.T) { name string secondFactor constants.SecondFactorType inputReader *prompt.FakeReader - solveWebauthn func(ctx context.Context, origin string, assertion *wanlib.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) + solveWebauthn func(ctx context.Context, origin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) authConnector string allowStdinHijack bool preferOTP bool @@ -190,7 +190,7 @@ func TestTeleportClient_Login_local(t *testing.T) { name: "OTP preferred", secondFactor: constants.SecondFactorOptional, inputReader: prompt.NewFakeReader().AddString(password).AddReply(solveOTP), - solveWebauthn: func(ctx context.Context, origin string, assertion *wanlib.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) { + solveWebauthn: func(ctx context.Context, origin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) { panic("this should not be called") }, preferOTP: true, @@ -249,7 +249,7 @@ func TestTeleportClient_Login_local(t *testing.T) { inputReader: prompt.NewFakeReader(). AddString(password). AddReply(solveOTP), - solveWebauthn: func(ctx context.Context, origin string, assertion *wanlib.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) { + solveWebauthn: func(ctx context.Context, origin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) { panic("this should not be called") }, preferOTP: true, @@ -264,7 +264,7 @@ func TestTeleportClient_Login_local(t *testing.T) { prompt.SetStdin(test.inputReader) *client.PromptWebauthn = func( ctx context.Context, - origin string, assertion *wanlib.CredentialAssertion, prompt wancli.LoginPrompt, _ *wancli.LoginOpts, + origin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt, _ *wancli.LoginOpts, ) (*proto.MFAAuthenticateResponse, string, error) { resp, err := test.solveWebauthn(ctx, origin, assertion, prompt) return resp, "", err @@ -617,7 +617,7 @@ func newStandaloneTeleport(t *testing.T, clock clockwork.Clock) *standaloneBundl DeviceUsage: proto.DeviceUsage_DEVICE_USAGE_PASSWORDLESS, }) require.NoError(t, err) - cc := wanlib.CredentialCreationFromProto(res.GetWebauthn()) + cc := wantypes.CredentialCreationFromProto(res.GetWebauthn()) webID := cc.Response.User.ID device, err := mocku2f.Create() require.NoError(t, err) @@ -630,7 +630,7 @@ func newStandaloneTeleport(t *testing.T, clock clockwork.Clock) *standaloneBundl NewPassword: []byte(password), NewMFARegisterResponse: &proto.MFARegisterResponse{ Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: wanlib.CredentialCreationResponseToProto(ccr), + Webauthn: wantypes.CredentialCreationResponseToProto(ccr), }, }, }) diff --git a/lib/client/mfa.go b/lib/client/mfa.go index 5ef70e8653ef8..5adb73e6a8818 100644 --- a/lib/client/mfa.go +++ b/lib/client/mfa.go @@ -29,9 +29,9 @@ import ( oteltrace "go.opentelemetry.io/otel/trace" "github.com/gravitational/teleport/api/client/proto" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" - "github.com/gravitational/teleport/lib/auth/webauthnwin" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" + wanwin "github.com/gravitational/teleport/lib/auth/webauthnwin" "github.com/gravitational/teleport/lib/utils/prompt" ) @@ -239,8 +239,8 @@ func PromptMFAChallenge(ctx context.Context, c *proto.MFAAuthenticateChallenge, // Customize Windows prompt directly. // Note that the platform popup is a modal and will only go away if // canceled. - webauthnwin.PromptPlatformMessage = "Follow the OS dialogs for platform authentication, or enter an OTP code here:" - defer webauthnwin.ResetPromptPlatformMessage() + wanwin.PromptPlatformMessage = "Follow the OS dialogs for platform authentication, or enter an OTP code here:" + defer wanwin.ResetPromptPlatformMessage() default: // Webauthn only prompt.FirstTouchMessage = fmt.Sprintf("Tap any %ssecurity key", promptDevicePrefix) @@ -250,7 +250,7 @@ func PromptMFAChallenge(ctx context.Context, c *proto.MFAAuthenticateChallenge, otpWait.Wait() }} - resp, _, err := promptWebauthn(ctx, origin, wanlib.CredentialAssertionFromProto(c.WebauthnChallenge), mfaPrompt, &wancli.LoginOpts{ + resp, _, err := promptWebauthn(ctx, origin, wantypes.CredentialAssertionFromProto(c.WebauthnChallenge), mfaPrompt, &wancli.LoginOpts{ AuthenticatorAttachment: opts.AuthenticatorAttachment, }) respC <- response{kind: "WEBAUTHN", resp: resp, err: err} @@ -286,7 +286,7 @@ func PromptMFAChallenge(ctx context.Context, c *proto.MFAAuthenticateChallenge, type MFAAuthenticateChallenge struct { // WebauthnChallenge contains a WebAuthn credential assertion used for // login/authentication ceremonies. - WebauthnChallenge *wanlib.CredentialAssertion `json:"webauthn_challenge"` + WebauthnChallenge *wantypes.CredentialAssertion `json:"webauthn_challenge"` // TOTPChallenge specifies whether TOTP is supported for this user. TOTPChallenge bool `json:"totp_challenge"` } @@ -297,7 +297,7 @@ func MakeAuthenticateChallenge(protoChal *proto.MFAAuthenticateChallenge) *MFAAu TOTPChallenge: protoChal.GetTOTP() != nil, } if protoChal.GetWebauthnChallenge() != nil { - chal.WebauthnChallenge = wanlib.CredentialAssertionFromProto(protoChal.WebauthnChallenge) + chal.WebauthnChallenge = wantypes.CredentialAssertionFromProto(protoChal.WebauthnChallenge) } return chal } @@ -309,7 +309,7 @@ type TOTPRegisterChallenge struct { // MFARegisterChallenge is an MFA register challenge sent on new MFA register. type MFARegisterChallenge struct { // Webauthn contains webauthn challenge. - Webauthn *wanlib.CredentialCreation `json:"webauthn"` + Webauthn *wantypes.CredentialCreation `json:"webauthn"` // TOTP contains TOTP challenge. TOTP *TOTPRegisterChallenge `json:"totp"` } @@ -325,7 +325,7 @@ func MakeRegisterChallenge(protoChal *proto.MFARegisterChallenge) *MFARegisterCh } case *proto.MFARegisterChallenge_Webauthn: return &MFARegisterChallenge{ - Webauthn: wanlib.CredentialCreationFromProto(protoChal.GetWebauthn()), + Webauthn: wantypes.CredentialCreationFromProto(protoChal.GetWebauthn()), } } return nil diff --git a/lib/client/mfa_test.go b/lib/client/mfa_test.go index b267853eaaac3..a68520d305f33 100644 --- a/lib/client/mfa_test.go +++ b/lib/client/mfa_test.go @@ -24,8 +24,8 @@ import ( "github.com/gravitational/teleport/api/client/proto" wanpb "github.com/gravitational/teleport/api/types/webauthn" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/utils/prompt" ) @@ -45,7 +45,7 @@ func TestPromptMFAChallenge_usingNonRegisteredDevice(t *testing.T) { }) // User always picks a non-registered device. - *client.PromptWebauthn = func(ctx context.Context, origin string, assertion *wanlib.CredentialAssertion, prompt wancli.LoginPrompt, opts *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { + *client.PromptWebauthn = func(ctx context.Context, origin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt, opts *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { return nil, "", wancli.ErrUsingNonRegisteredDevice } // Support always enabled. diff --git a/lib/client/weblogin.go b/lib/client/weblogin.go index 6ca06870ef304..65fe2d7ef191f 100644 --- a/lib/client/weblogin.go +++ b/lib/client/weblogin.go @@ -42,8 +42,8 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/auth" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/defaults" ) @@ -104,7 +104,7 @@ type MFAChallengeResponse struct { // TOTPCode is a code for a otp device. TOTPCode string `json:"totp_code,omitempty"` // WebauthnResponse is a response from a webauthn device. - WebauthnResponse *wanlib.CredentialAssertionResponse `json:"webauthn_response,omitempty"` + WebauthnResponse *wantypes.CredentialAssertionResponse `json:"webauthn_response,omitempty"` } // GetOptionalMFAResponseProtoReq converts response to a type proto.MFAAuthenticateResponse, @@ -122,7 +122,7 @@ func (r *MFAChallengeResponse) GetOptionalMFAResponseProtoReq() (*proto.MFAAuthe if r.WebauthnResponse != nil { return &proto.MFAAuthenticateResponse{Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(r.WebauthnResponse), + Webauthn: wantypes.CredentialAssertionResponseToProto(r.WebauthnResponse), }}, nil } @@ -167,7 +167,7 @@ type AuthenticateSSHUserRequest struct { // performed. Password string `json:"password"` // WebauthnChallengeResponse is a signed WebAuthn credential assertion. - WebauthnChallengeResponse *wanlib.CredentialAssertionResponse `json:"webauthn_challenge_response"` + WebauthnChallengeResponse *wantypes.CredentialAssertionResponse `json:"webauthn_challenge_response"` // TOTPCode is a code from the TOTP device. TOTPCode string `json:"totp_code"` // PubKey is a public key user wishes to sign @@ -191,14 +191,14 @@ type AuthenticateWebUserRequest struct { // User is a teleport username. User string `json:"user"` // WebauthnAssertionResponse is a signed WebAuthn credential assertion. - WebauthnAssertionResponse *wanlib.CredentialAssertionResponse `json:"webauthnAssertionResponse,omitempty"` + WebauthnAssertionResponse *wantypes.CredentialAssertionResponse `json:"webauthnAssertionResponse,omitempty"` } type HeadlessRequest struct { // Actions can be either accept or deny. Action string `json:"action"` // WebauthnAssertionResponse is a signed WebAuthn credential assertion. - WebauthnAssertionResponse *wanlib.CredentialAssertionResponse `json:"webauthnAssertionResponse,omitempty"` + WebauthnAssertionResponse *wantypes.CredentialAssertionResponse `json:"webauthnAssertionResponse,omitempty"` } // SSHLogin contains common SSH login parameters. @@ -521,7 +521,7 @@ func SSHAgentPasswordlessLogin(ctx context.Context, login SSHLoginPasswordless) ctx, webClient.Endpoint("webapi", "mfa", "login", "finish"), &AuthenticateSSHUserRequest{ User: "", // User carried on WebAuthn assertion. - WebauthnChallengeResponse: wanlib.CredentialAssertionResponseFromProto(mfaResp.GetWebauthn()), + WebauthnChallengeResponse: wantypes.CredentialAssertionResponseFromProto(mfaResp.GetWebauthn()), PubKey: login.PubKey, TTL: login.TTL, Compatibility: login.Compatibility, @@ -570,7 +570,7 @@ func SSHAgentMFALogin(ctx context.Context, login SSHLoginMFA) (*auth.SSHLoginRes challengePB.TOTP = &proto.TOTPChallenge{} } if challenge.WebauthnChallenge != nil { - challengePB.WebauthnChallenge = wanlib.CredentialAssertionToProto(challenge.WebauthnChallenge) + challengePB.WebauthnChallenge = wantypes.CredentialAssertionToProto(challenge.WebauthnChallenge) } respPB, err := PromptMFAChallenge(ctx, challengePB, login.ProxyAddr, &PromptMFAChallengeOpts{ @@ -597,7 +597,7 @@ func SSHAgentMFALogin(ctx context.Context, login SSHLoginMFA) (*auth.SSHLoginRes case *proto.MFAAuthenticateResponse_TOTP: challengeResp.TOTPCode = r.TOTP.Code case *proto.MFAAuthenticateResponse_Webauthn: - challengeResp.WebauthnChallengeResponse = wanlib.CredentialAssertionResponseFromProto(r.Webauthn) + challengeResp.WebauthnChallengeResponse = wantypes.CredentialAssertionResponseFromProto(r.Webauthn) default: // No challenge was sent, so we send back just username/password. } diff --git a/lib/client/weblogin_test.go b/lib/client/weblogin_test.go index 984d925589d6e..ce6ae66df25b2 100644 --- a/lib/client/weblogin_test.go +++ b/lib/client/weblogin_test.go @@ -31,8 +31,8 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/client" ) @@ -139,14 +139,14 @@ func TestSSHAgentPasswordlessLogin(t *testing.T) { *client.PromptWebauthn = oldWebauthn }) - solvePwdless := func(ctx context.Context, origin string, assertion *wanlib.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) { + solvePwdless := func(ctx context.Context, origin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt) (*proto.MFAAuthenticateResponse, error) { car, err := device.SignAssertion(origin, assertion) if err != nil { return nil, err } resp := &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(car), + Webauthn: wantypes.CredentialAssertionResponseToProto(car), }, } resp.GetWebauthn().Response.UserHandle = webID @@ -165,12 +165,12 @@ func TestSSHAgentPasswordlessLogin(t *testing.T) { tests := []struct { name string - customPromptWebauthn func(ctx context.Context, origin string, assert *wanlib.CredentialAssertion, p wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) + customPromptWebauthn func(ctx context.Context, origin string, assert *wantypes.CredentialAssertion, p wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) customPromptLogin wancli.LoginPrompt }{ { name: "with custom prompt", - customPromptWebauthn: func(ctx context.Context, origin string, assert *wanlib.CredentialAssertion, p wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { + customPromptWebauthn: func(ctx context.Context, origin string, assert *wantypes.CredentialAssertion, p wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { _, ok := p.(*customPromptLogin) require.True(t, ok) customPromptCalled = true @@ -197,7 +197,7 @@ func TestSSHAgentPasswordlessLogin(t *testing.T) { }, { name: "without custom prompt", - customPromptWebauthn: func(ctx context.Context, origin string, assert *wanlib.CredentialAssertion, p wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { + customPromptWebauthn: func(ctx context.Context, origin string, assert *wantypes.CredentialAssertion, p wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { _, ok := p.(*wancli.DefaultPrompt) require.True(t, ok) customPromptCalled = true diff --git a/lib/services/identity.go b/lib/services/identity.go index 9812037da3b65..0a797eb7819e8 100644 --- a/lib/services/identity.go +++ b/lib/services/identity.go @@ -29,7 +29,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" - wantypes "github.com/gravitational/teleport/api/types/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/defaults" ) @@ -129,11 +129,11 @@ type Identity interface { // storage, for the purpose of later verifying an authentication or // registration challenge. // Session data is expected to expire according to backend settings. - UpsertWebauthnSessionData(ctx context.Context, user, sessionID string, sd *wantypes.SessionData) error + UpsertWebauthnSessionData(ctx context.Context, user, sessionID string, sd *wanpb.SessionData) error // GetWebauthnSessionData retrieves a previously-stored session data by ID, // if it exists and has not expired. - GetWebauthnSessionData(ctx context.Context, user, sessionID string) (*wantypes.SessionData, error) + GetWebauthnSessionData(ctx context.Context, user, sessionID string) (*wanpb.SessionData, error) // DeleteWebauthnSessionData deletes session data by ID, if it exists and has // not expired. @@ -143,12 +143,12 @@ type Identity interface { // storage, for the purpose of later verifying an authentication challenge. // Session data is expected to expire according to backend settings. // Used for passwordless challenges. - UpsertGlobalWebauthnSessionData(ctx context.Context, scope, id string, sd *wantypes.SessionData) error + UpsertGlobalWebauthnSessionData(ctx context.Context, scope, id string, sd *wanpb.SessionData) error // GetGlobalWebauthnSessionData retrieves previously-stored session data by ID, // if it exists and has not expired. // Used for passwordless challenges. - GetGlobalWebauthnSessionData(ctx context.Context, scope, id string) (*wantypes.SessionData, error) + GetGlobalWebauthnSessionData(ctx context.Context, scope, id string) (*wanpb.SessionData, error) // DeleteGlobalWebauthnSessionData deletes session data by ID, if it exists // and has not expired. diff --git a/lib/services/local/users.go b/lib/services/local/users.go index b5e2e0ab916b9..52cfea9b7dffe 100644 --- a/lib/services/local/users.go +++ b/lib/services/local/users.go @@ -38,7 +38,7 @@ import ( "golang.org/x/crypto/ssh" "github.com/gravitational/teleport/api/types" - wantypes "github.com/gravitational/teleport/api/types/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/defaults" @@ -616,7 +616,7 @@ func (s *IdentityService) UpsertWebauthnLocalAuth(ctx context.Context, user stri if err != nil { return trace.Wrap(err, "marshal webauthn local auth") } - userJSON, err := json.Marshal(&wantypes.User{ + userJSON, err := json.Marshal(&wanpb.User{ TeleportUser: user, }) if err != nil { @@ -673,7 +673,7 @@ func (s *IdentityService) GetTeleportUserByWebauthnID(ctx context.Context, webID if err != nil { return "", trace.Wrap(err) } - user := &wantypes.User{} + user := &wanpb.User{} if err := json.Unmarshal(item.Value, user); err != nil { return "", trace.Wrap(err) } @@ -689,7 +689,7 @@ func webauthnUserKey(id []byte) []byte { return backend.Key(webauthnPrefix, usersPrefix, key) } -func (s *IdentityService) UpsertWebauthnSessionData(ctx context.Context, user, sessionID string, sd *wantypes.SessionData) error { +func (s *IdentityService) UpsertWebauthnSessionData(ctx context.Context, user, sessionID string, sd *wanpb.SessionData) error { switch { case user == "": return trace.BadParameter("missing parameter user") @@ -711,7 +711,7 @@ func (s *IdentityService) UpsertWebauthnSessionData(ctx context.Context, user, s return trace.Wrap(err) } -func (s *IdentityService) GetWebauthnSessionData(ctx context.Context, user, sessionID string) (*wantypes.SessionData, error) { +func (s *IdentityService) GetWebauthnSessionData(ctx context.Context, user, sessionID string) (*wanpb.SessionData, error) { switch { case user == "": return nil, trace.BadParameter("missing parameter user") @@ -723,7 +723,7 @@ func (s *IdentityService) GetWebauthnSessionData(ctx context.Context, user, sess if err != nil { return nil, trace.Wrap(err) } - sd := &wantypes.SessionData{} + sd := &wanpb.SessionData{} return sd, trace.Wrap(json.Unmarshal(item.Value, sd)) } @@ -784,7 +784,7 @@ var sdLimiter = &globalSessionDataLimiter{ scopeCount: make(map[string]int), } -func (s *IdentityService) UpsertGlobalWebauthnSessionData(ctx context.Context, scope, id string, sd *wantypes.SessionData) error { +func (s *IdentityService) UpsertGlobalWebauthnSessionData(ctx context.Context, scope, id string, sd *wanpb.SessionData) error { switch { case scope == "": return trace.BadParameter("missing parameter scope") @@ -817,7 +817,7 @@ func (s *IdentityService) UpsertGlobalWebauthnSessionData(ctx context.Context, s return nil } -func (s *IdentityService) GetGlobalWebauthnSessionData(ctx context.Context, scope, id string) (*wantypes.SessionData, error) { +func (s *IdentityService) GetGlobalWebauthnSessionData(ctx context.Context, scope, id string) (*wanpb.SessionData, error) { switch { case scope == "": return nil, trace.BadParameter("missing parameter scope") @@ -829,7 +829,7 @@ func (s *IdentityService) GetGlobalWebauthnSessionData(ctx context.Context, scop if err != nil { return nil, trace.Wrap(err) } - sd := &wantypes.SessionData{} + sd := &wanpb.SessionData{} return sd, trace.Wrap(json.Unmarshal(item.Value, sd)) } diff --git a/lib/services/local/users_test.go b/lib/services/local/users_test.go index c183dd5ec4376..48764449012c6 100644 --- a/lib/services/local/users_test.go +++ b/lib/services/local/users_test.go @@ -35,7 +35,7 @@ import ( "github.com/stretchr/testify/require" "github.com/gravitational/teleport/api/types" - wantypes "github.com/gravitational/teleport/api/types/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/backend" @@ -613,16 +613,16 @@ func TestIdentityService_WebauthnSessionDataCRUD(t *testing.T) { const user2 = "alpaca" // Prepare a few different objects so we can assert that both "user" and // "session" key components are used correctly. - user1Reg := &wantypes.SessionData{ + user1Reg := &wanpb.SessionData{ Challenge: []byte("challenge1-reg"), UserId: []byte("llamaid"), } - user1Login := &wantypes.SessionData{ + user1Login := &wanpb.SessionData{ Challenge: []byte("challenge1-login"), UserId: []byte("llamaid"), AllowCredentials: [][]byte{[]byte("cred1"), []byte("cred2")}, } - user2Login := &wantypes.SessionData{ + user2Login := &wanpb.SessionData{ Challenge: []byte("challenge2"), UserId: []byte("alpacaid"), } @@ -632,7 +632,7 @@ func TestIdentityService_WebauthnSessionDataCRUD(t *testing.T) { const loginSession = "login" params := []struct { user, session string - sd *wantypes.SessionData + sd *wanpb.SessionData }{ {user: user1, session: registerSession, sd: user1Reg}, {user: user1, session: loginSession, sd: user1Login}, @@ -656,7 +656,7 @@ func TestIdentityService_WebauthnSessionDataCRUD(t *testing.T) { } // Verify upsert/update. - user1Reg = &wantypes.SessionData{ + user1Reg = &wanpb.SessionData{ Challenge: []byte("challenge1reg--another"), UserId: []byte("llamaid"), } @@ -684,23 +684,23 @@ func TestIdentityService_GlobalWebauthnSessionDataCRUD(t *testing.T) { t.Parallel() identity := newIdentityService(t, clockwork.NewFakeClock()) - user1Login1 := &wantypes.SessionData{ + user1Login1 := &wanpb.SessionData{ Challenge: []byte("challenge1"), UserId: []byte("user1-web-id"), UserVerification: string(protocol.VerificationRequired), } - user1Login2 := &wantypes.SessionData{ + user1Login2 := &wanpb.SessionData{ Challenge: []byte("challenge2"), UserId: []byte("user1-web-id"), UserVerification: string(protocol.VerificationRequired), } - user1Registration := &wantypes.SessionData{ + user1Registration := &wanpb.SessionData{ Challenge: []byte("challenge3"), UserId: []byte("user1-web-id"), ResidentKey: true, UserVerification: string(protocol.VerificationRequired), } - user2Login := &wantypes.SessionData{ + user2Login := &wanpb.SessionData{ Challenge: []byte("challenge4"), UserId: []byte("user2-web-id"), ResidentKey: true, @@ -713,7 +713,7 @@ func TestIdentityService_GlobalWebauthnSessionDataCRUD(t *testing.T) { const scopeRegister = "register" params := []struct { scope, id string - sd *wantypes.SessionData + sd *wanpb.SessionData }{ {scope: scopeLogin, id: base64.RawURLEncoding.EncodeToString(user1Login1.Challenge), sd: user1Login1}, {scope: scopeLogin, id: base64.RawURLEncoding.EncodeToString(user1Login2.Challenge), sd: user1Login2}, @@ -780,7 +780,7 @@ func TestIdentityService_UpsertGlobalWebauthnSessionData_maxLimit(t *testing.T) const id2 = "challenge2" const id3 = "challenge3" const id4 = "challenge4" - sd := &wantypes.SessionData{ + sd := &wanpb.SessionData{ Challenge: []byte("supersecretchallenge"), // typically matches the key UserVerification: "required", } diff --git a/lib/srv/desktop/tdp/proto.go b/lib/srv/desktop/tdp/proto.go index c937e598b82ae..a35fdba3997f3 100644 --- a/lib/srv/desktop/tdp/proto.go +++ b/lib/srv/desktop/tdp/proto.go @@ -34,7 +34,7 @@ import ( "github.com/gravitational/trace" authproto "github.com/gravitational/teleport/api/client/proto" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/web/mfajson" @@ -550,7 +550,7 @@ func (m MFA) Encode() ([]byte, error) { } else if m.MFAAuthenticateResponse != nil { switch t := m.MFAAuthenticateResponse.Response.(type) { case *authproto.MFAAuthenticateResponse_Webauthn: - buff, err = json.Marshal(wanlib.CredentialAssertionResponseFromProto(m.MFAAuthenticateResponse.GetWebauthn())) + buff, err = json.Marshal(wantypes.CredentialAssertionResponseFromProto(m.MFAAuthenticateResponse.GetWebauthn())) if err != nil { return nil, trace.Wrap(err) } diff --git a/lib/srv/desktop/tdp/proto_test.go b/lib/srv/desktop/tdp/proto_test.go index 7f7b2119dc9eb..1a58bca28064f 100644 --- a/lib/srv/desktop/tdp/proto_test.go +++ b/lib/srv/desktop/tdp/proto_test.go @@ -31,8 +31,8 @@ import ( "github.com/stretchr/testify/require" authproto "github.com/gravitational/teleport/api/client/proto" - wantypes "github.com/gravitational/teleport/api/types/webauthn" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wanpb "github.com/gravitational/teleport/api/types/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/defaults" ) @@ -125,12 +125,12 @@ func TestMFA(t *testing.T) { mfaWant := &MFA{ Type: defaults.WebsocketWebauthnChallenge[0], MFAAuthenticateChallenge: &client.MFAAuthenticateChallenge{ - WebauthnChallenge: &wanlib.CredentialAssertion{ - Response: protocol.PublicKeyCredentialRequestOptions{ + WebauthnChallenge: &wantypes.CredentialAssertion{ + Response: wantypes.PublicKeyCredentialRequestOptions{ Challenge: []byte("challenge"), Timeout: 10, RelyingPartyID: "teleport", - AllowedCredentials: []protocol.CredentialDescriptor{ + AllowedCredentials: []wantypes.CredentialDescriptor{ { Type: "public-key", CredentialID: []byte("credential id"), @@ -138,7 +138,7 @@ func TestMFA(t *testing.T) { }, }, UserVerification: "discouraged", - Extensions: protocol.AuthenticationExtensions{ + Extensions: wantypes.AuthenticationExtensions{ "ext1": "value1", }, }, @@ -160,16 +160,16 @@ func TestMFA(t *testing.T) { Type: defaults.WebsocketWebauthnChallenge[0], MFAAuthenticateResponse: &authproto.MFAAuthenticateResponse{ Response: &authproto.MFAAuthenticateResponse_Webauthn{ - Webauthn: &wantypes.CredentialAssertionResponse{ + Webauthn: &wanpb.CredentialAssertionResponse{ Type: "public-key", RawId: []byte("credential id"), - Response: &wantypes.AuthenticatorAssertionResponse{ + Response: &wanpb.AuthenticatorAssertionResponse{ ClientDataJson: []byte("client data json"), AuthenticatorData: []byte("authenticator data"), Signature: []byte("signature"), UserHandle: []byte("user handle"), }, - Extensions: &wantypes.AuthenticationExtensionsClientOutputs{ + Extensions: &wanpb.AuthenticationExtensionsClientOutputs{ AppId: true, }, }, diff --git a/lib/web/apiserver.go b/lib/web/apiserver.go index 9515d66834221..f63aea19c2098 100644 --- a/lib/web/apiserver.go +++ b/lib/web/apiserver.go @@ -67,7 +67,7 @@ import ( "github.com/gravitational/teleport/api/utils/keys" apisshutils "github.com/gravitational/teleport/api/utils/sshutils" "github.com/gravitational/teleport/lib/auth" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/defaults" dtconfig "github.com/gravitational/teleport/lib/devicetrust/config" @@ -2038,7 +2038,7 @@ type changeUserAuthenticationRequest struct { // Password is user password string converted to bytes. Password []byte `json:"password"` // WebauthnCreationResponse is the signed credential creation response. - WebauthnCreationResponse *wanlib.CredentialCreationResponse `json:"webauthnCreationResponse"` + WebauthnCreationResponse *wantypes.CredentialCreationResponse `json:"webauthnCreationResponse"` } func (h *Handler) changeUserAuthentication(w http.ResponseWriter, r *http.Request, p httprouter.Params) (interface{}, error) { @@ -2056,7 +2056,7 @@ func (h *Handler) changeUserAuthentication(w http.ResponseWriter, r *http.Reques case req.WebauthnCreationResponse != nil: protoReq.NewMFARegisterResponse = &proto.MFARegisterResponse{ Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: wanlib.CredentialCreationResponseToProto(req.WebauthnCreationResponse), + Webauthn: wantypes.CredentialCreationResponseToProto(req.WebauthnCreationResponse), }, } case req.SecondFactorToken != "": diff --git a/lib/web/apiserver_login_test.go b/lib/web/apiserver_login_test.go index 6231c6dafb071..c265107c0cc22 100644 --- a/lib/web/apiserver_login_test.go +++ b/lib/web/apiserver_login_test.go @@ -34,7 +34,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/auth" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/modules" @@ -237,11 +237,11 @@ func TestAuthenticate_passwordless(t *testing.T) { tests := []struct { name string - login func(t *testing.T, assertionResp *wanlib.CredentialAssertionResponse) + login func(t *testing.T, assertionResp *wantypes.CredentialAssertionResponse) }{ { name: "ssh", - login: func(t *testing.T, assertionResp *wanlib.CredentialAssertionResponse) { + login: func(t *testing.T, assertionResp *wantypes.CredentialAssertionResponse) { ep := clt.Endpoint("webapi", "mfa", "login", "finish") sshResp, err := clt.PostJSON(ctx, ep, &client.AuthenticateSSHUserRequest{ WebauthnChallengeResponse: assertionResp, // no username @@ -256,7 +256,7 @@ func TestAuthenticate_passwordless(t *testing.T) { }, { name: "web", - login: func(t *testing.T, assertionResp *wanlib.CredentialAssertionResponse) { + login: func(t *testing.T, assertionResp *wantypes.CredentialAssertionResponse) { ep := clt.Endpoint("webapi", "mfa", "login", "finishsession") sessionResp, err := clt.PostJSON(ctx, ep, &client.AuthenticateWebUserRequest{ WebauthnAssertionResponse: assertionResp, // no username diff --git a/lib/web/apiserver_test.go b/lib/web/apiserver_test.go index e4c71c74bf5fd..5a3b6bd293df0 100644 --- a/lib/web/apiserver_test.go +++ b/lib/web/apiserver_test.go @@ -96,7 +96,7 @@ import ( "github.com/gravitational/teleport/lib/auth/mocku2f" "github.com/gravitational/teleport/lib/auth/native" "github.com/gravitational/teleport/lib/auth/testauthority" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/authz" "github.com/gravitational/teleport/lib/bpf" "github.com/gravitational/teleport/lib/client" @@ -1697,12 +1697,12 @@ func TestTerminalRequireSessionMFA(t *testing.T) { }, getChallengeResponseBytes: func(chals *client.MFAAuthenticateChallenge, dev *auth.TestDevice) []byte { res, err := dev.SolveAuthn(&authproto.MFAAuthenticateChallenge{ - WebauthnChallenge: wanlib.CredentialAssertionToProto(chals.WebauthnChallenge), + WebauthnChallenge: wantypes.CredentialAssertionToProto(chals.WebauthnChallenge), }) require.Nil(t, err) - webauthnResBytes, err := json.Marshal(wanlib.CredentialAssertionResponseFromProto(res.GetWebauthn())) - require.Nil(t, err) + webauthnResBytes, err := json.Marshal(wantypes.CredentialAssertionResponseFromProto(res.GetWebauthn())) + require.NoError(t, err) return webauthnResBytes }, @@ -1901,7 +1901,7 @@ func handleMFAWebauthnChallenge(t *testing.T, ws *websocket.Conn, dev *auth.Test mfaChallange, err := tdp.DecodeMFAChallenge(br) require.NoError(t, err) res, err := dev.SolveAuthn(&authproto.MFAAuthenticateChallenge{ - WebauthnChallenge: wanlib.CredentialAssertionToProto(mfaChallange.WebauthnChallenge), + WebauthnChallenge: wantypes.CredentialAssertionToProto(mfaChallange.WebauthnChallenge), }) require.NoError(t, err) err = tdp.NewConn(&WebsocketIO{Conn: ws}).WriteMessage(tdp.MFA{ @@ -4251,7 +4251,7 @@ func TestAddMFADevice(t *testing.T) { name string deviceName string getTOTPCode func() string - getWebauthnResp func() *wanlib.CredentialCreationResponse + getWebauthnResp func() *wantypes.CredentialCreationResponse }{ { name: "new TOTP device", @@ -4273,7 +4273,7 @@ func TestAddMFADevice(t *testing.T) { { name: "new Webauthn device", deviceName: "new-webauthn", - getWebauthnResp: func() *wanlib.CredentialCreationResponse { + getWebauthnResp: func() *wantypes.CredentialCreationResponse { // Get webauthn register challenge. res, err := env.server.Auth().CreateRegisterChallenge(ctx, &authproto.CreateRegisterChallengeRequest{ TokenID: privilegeToken, @@ -4284,7 +4284,7 @@ func TestAddMFADevice(t *testing.T) { _, regRes, err := auth.NewTestDeviceFromChallenge(res) require.NoError(t, err) - return wanlib.CredentialCreationResponseFromProto(regRes.GetWebauthn()) + return wantypes.CredentialCreationResponseFromProto(regRes.GetWebauthn()) }, }, } @@ -4294,7 +4294,7 @@ func TestAddMFADevice(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() var totpCode string - var webauthnRegResp *wanlib.CredentialCreationResponse + var webauthnRegResp *wantypes.CredentialCreationResponse if tc.getWebauthnResp != nil { webauthnRegResp = tc.getWebauthnResp() @@ -5208,7 +5208,7 @@ func TestChangeUserAuthentication_settingDefaultClusterAuthPreference(t *testing }) require.NoError(t, err) - cc := wanlib.CredentialCreationFromProto(res.GetWebauthn()) + cc := wantypes.CredentialCreationFromProto(res.GetWebauthn()) // use passwordless as auth method device, err := mocku2f.Create() diff --git a/lib/web/files.go b/lib/web/files.go index cb442b0075898..f579beff1283f 100644 --- a/lib/web/files.go +++ b/lib/web/files.go @@ -29,7 +29,7 @@ import ( "github.com/gravitational/teleport/api/defaults" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/auth" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/reversetunnel" "github.com/gravitational/teleport/lib/sshutils/sftp" @@ -184,7 +184,7 @@ func (f *fileTransfer) createClient(req fileTransferRequest, httpReq *http.Reque type mfaRequest struct { // WebauthnResponse is the response from authenticators. - WebauthnAssertionResponse *wanlib.CredentialAssertionResponse `json:"webauthnAssertionResponse"` + WebauthnAssertionResponse *wantypes.CredentialAssertionResponse `json:"webauthnAssertionResponse"` } // issueSingleUseCert will take an assertion response sent from a solved challenge in the web UI @@ -209,7 +209,7 @@ func (f *fileTransfer) issueSingleUseCert(webauthn string, httpReq *http.Request Expires: time.Now().Add(time.Minute).UTC(), MFAResponse: &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(mfaReq.WebauthnAssertionResponse), + Webauthn: wantypes.CredentialAssertionResponseToProto(mfaReq.WebauthnAssertionResponse), }, }, }) diff --git a/lib/web/headless.go b/lib/web/headless.go index 527bec83b33fd..4cab41fda017a 100644 --- a/lib/web/headless.go +++ b/lib/web/headless.go @@ -27,7 +27,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/httplib" ) @@ -76,7 +76,7 @@ func (h *Handler) putHeadlessState(_ http.ResponseWriter, r *http.Request, param action = types.HeadlessAuthenticationState_HEADLESS_AUTHENTICATION_STATE_APPROVED resp = &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(req.WebauthnAssertionResponse), + Webauthn: wantypes.CredentialAssertionResponseToProto(req.WebauthnAssertionResponse), }, } case "denied": diff --git a/lib/web/mfa.go b/lib/web/mfa.go index b9cd7ca7fde68..a0f5b6812cee0 100644 --- a/lib/web/mfa.go +++ b/lib/web/mfa.go @@ -24,7 +24,7 @@ import ( "github.com/julienschmidt/httprouter" "github.com/gravitational/teleport/api/client/proto" - "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/httplib" "github.com/gravitational/teleport/lib/reversetunnel" @@ -78,7 +78,7 @@ type addMFADeviceRequest struct { // SecondFactorToken is the totp code. SecondFactorToken string `json:"secondFactorToken"` // WebauthnRegisterResponse is a WebAuthn registration challenge response. - WebauthnRegisterResponse *webauthn.CredentialCreationResponse `json:"webauthnRegisterResponse"` + WebauthnRegisterResponse *wantypes.CredentialCreationResponse `json:"webauthnRegisterResponse"` // DeviceUsage is the intended usage of the device (MFA, Passwordless, etc). // It mimics the proto.DeviceUsage enum. // Defaults to MFA. @@ -110,7 +110,7 @@ func (h *Handler) addMFADeviceHandle(w http.ResponseWriter, r *http.Request, par }} case req.WebauthnRegisterResponse != nil: protoReq.NewMFAResponse = &proto.MFARegisterResponse{Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: webauthn.CredentialCreationResponseToProto(req.WebauthnRegisterResponse), + Webauthn: wantypes.CredentialCreationResponseToProto(req.WebauthnRegisterResponse), }} default: return nil, trace.BadParameter("missing new mfa credentials") diff --git a/lib/web/mfajson/mfajson.go b/lib/web/mfajson/mfajson.go index 09b6c6aa32f35..474df03f14006 100644 --- a/lib/web/mfajson/mfajson.go +++ b/lib/web/mfajson/mfajson.go @@ -22,7 +22,7 @@ import ( "github.com/gravitational/trace" authproto "github.com/gravitational/teleport/api/client/proto" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/defaults" ) @@ -33,13 +33,13 @@ func Decode(b []byte, typ string) (*authproto.MFAAuthenticateResponse, error) { switch typ { case defaults.WebsocketWebauthnChallenge: - var webauthnResponse wanlib.CredentialAssertionResponse + var webauthnResponse wantypes.CredentialAssertionResponse if err := json.Unmarshal(b, &webauthnResponse); err != nil { return nil, trace.Wrap(err) } resp = &authproto.MFAAuthenticateResponse{ Response: &authproto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(&webauthnResponse), + Webauthn: wantypes.CredentialAssertionResponseToProto(&webauthnResponse), }, } diff --git a/lib/web/password.go b/lib/web/password.go index 00ec6c874b639..00a624ad25b71 100644 --- a/lib/web/password.go +++ b/lib/web/password.go @@ -23,7 +23,7 @@ import ( "github.com/julienschmidt/httprouter" "github.com/gravitational/teleport/api/client/proto" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/httplib" ) @@ -37,7 +37,7 @@ type changePasswordReq struct { // SecondFactorToken is user 2nd factor token SecondFactorToken string `json:"second_factor_token"` // WebauthnAssertionResponse is a Webauthn response - WebauthnAssertionResponse *wanlib.CredentialAssertionResponse `json:"webauthnAssertionResponse"` + WebauthnAssertionResponse *wantypes.CredentialAssertionResponse `json:"webauthnAssertionResponse"` } // changePassword updates users password based on the old password. @@ -57,7 +57,7 @@ func (h *Handler) changePassword(w http.ResponseWriter, r *http.Request, p httpr OldPassword: req.OldPassword, NewPassword: req.NewPassword, SecondFactorToken: req.SecondFactorToken, - Webauthn: wanlib.CredentialAssertionResponseToProto( + Webauthn: wantypes.CredentialAssertionResponseToProto( req.WebauthnAssertionResponse, ), } diff --git a/lib/web/terminal.go b/lib/web/terminal.go index 80bfd0643f962..fb317cb07eaa2 100644 --- a/lib/web/terminal.go +++ b/lib/web/terminal.go @@ -46,7 +46,7 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/api/utils/keys" "github.com/gravitational/teleport/lib/auth" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/events" @@ -609,7 +609,7 @@ func promptMFAChallenge( switch { case c.GetWebauthnChallenge() != nil: challenge = &client.MFAAuthenticateChallenge{ - WebauthnChallenge: wanlib.CredentialAssertionFromProto(c.WebauthnChallenge), + WebauthnChallenge: wantypes.CredentialAssertionFromProto(c.WebauthnChallenge), } default: return nil, trace.AccessDenied("only hardware keys are supported on the web terminal, please register a hardware device to connect to this server") diff --git a/lib/web/users.go b/lib/web/users.go index 3f0d56ca559a0..35ed56c8702dc 100644 --- a/lib/web/users.go +++ b/lib/web/users.go @@ -26,7 +26,7 @@ import ( "github.com/gravitational/teleport/api/client/proto" "github.com/gravitational/teleport/api/types" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/httplib" "github.com/gravitational/teleport/lib/web/ui" ) @@ -221,7 +221,7 @@ type privilegeTokenRequest struct { // SecondFactorToken is the totp code. SecondFactorToken string `json:"secondFactorToken"` // WebauthnResponse is the response from authenticators. - WebauthnResponse *wanlib.CredentialAssertionResponse `json:"webauthnAssertionResponse"` + WebauthnResponse *wantypes.CredentialAssertionResponse `json:"webauthnAssertionResponse"` } // createPrivilegeTokenHandle creates and returns a privilege token. @@ -240,7 +240,7 @@ func (h *Handler) createPrivilegeTokenHandle(w http.ResponseWriter, r *http.Requ }} case req.WebauthnResponse != nil: protoReq.ExistingMFAResponse = &proto.MFAAuthenticateResponse{Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(req.WebauthnResponse), + Webauthn: wantypes.CredentialAssertionResponseToProto(req.WebauthnResponse), }} default: // Can be empty, which means user did not have a second factor registered. diff --git a/tool/tsh/mfa.go b/tool/tsh/mfa.go index 6871603f0d96e..eb72ea56decfb 100644 --- a/tool/tsh/mfa.go +++ b/tool/tsh/mfa.go @@ -35,9 +35,9 @@ import ( "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/asciitable" "github.com/gravitational/teleport/lib/auth/touchid" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" - "github.com/gravitational/teleport/lib/auth/webauthnwin" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" + wanwin "github.com/gravitational/teleport/lib/auth/webauthnwin" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/defaults" "github.com/gravitational/teleport/lib/utils" @@ -358,8 +358,8 @@ func (c *mfaAddCommand) addDeviceRPC(ctx context.Context, tc *client.TeleportCli // of finding out whether it is a Windows prompt or not). const registeredMsg = "Using platform authentication for *registered* device, follow the OS dialogs" const newMsg = "Using platform authentication for *new* device, follow the OS dialogs" - defer webauthnwin.ResetPromptPlatformMessage() - webauthnwin.PromptPlatformMessage = registeredMsg + defer wanwin.ResetPromptPlatformMessage() + wanwin.PromptPlatformMessage = registeredMsg authResp, err := tc.PromptMFAChallenge(ctx, "" /* proxyAddr */, authChallenge, func(opts *client.PromptMFAChallengeOpts) { opts.PromptDevicePrefix = "*registered* " @@ -383,7 +383,7 @@ func (c *mfaAddCommand) addDeviceRPC(ctx context.Context, tc *client.TeleportCli return trace.BadParameter("server bug: server sent %T when client expected AddMFADeviceResponse_NewMFARegisterChallenge", resp.Response) } - webauthnwin.PromptPlatformMessage = newMsg + wanwin.PromptPlatformMessage = newMsg regResp, regCallback, err := promptRegisterChallenge(ctx, tc.WebProxyAddr, c.devType, regChallenge) if err != nil { return trace.Wrap(err) @@ -441,7 +441,7 @@ func promptRegisterChallenge(ctx context.Context, proxyAddr, devType string, c * if !strings.HasPrefix(proxyAddr, "https://") { origin = "https://" + origin } - cc := wanlib.CredentialCreationFromProto(c.GetWebauthn()) + cc := wantypes.CredentialCreationFromProto(c.GetWebauthn()) if devType == touchIDDeviceType { return promptTouchIDRegisterChallenge(origin, cc) @@ -531,7 +531,7 @@ func promptTOTPRegisterChallenge(ctx context.Context, c *proto.TOTPRegisterChall }}, nil } -func promptWebauthnRegisterChallenge(ctx context.Context, origin string, cc *wanlib.CredentialCreation) (*proto.MFARegisterResponse, error) { +func promptWebauthnRegisterChallenge(ctx context.Context, origin string, cc *wantypes.CredentialCreation) (*proto.MFARegisterResponse, error) { log.Debugf("WebAuthn: prompting MFA devices with origin %q", origin) prompt := wancli.NewDefaultPrompt(ctx, os.Stdout) @@ -543,7 +543,7 @@ func promptWebauthnRegisterChallenge(ctx context.Context, origin string, cc *wan return resp, trace.Wrap(err) } -func promptTouchIDRegisterChallenge(origin string, cc *wanlib.CredentialCreation) (*proto.MFARegisterResponse, registerCallback, error) { +func promptTouchIDRegisterChallenge(origin string, cc *wantypes.CredentialCreation) (*proto.MFARegisterResponse, registerCallback, error) { log.Debugf("Touch ID: prompting registration with origin %q", origin) reg, err := touchid.Register(origin, cc) @@ -552,7 +552,7 @@ func promptTouchIDRegisterChallenge(origin string, cc *wanlib.CredentialCreation } return &proto.MFARegisterResponse{ Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: wanlib.CredentialCreationResponseToProto(reg.CCR), + Webauthn: wantypes.CredentialCreationResponseToProto(reg.CCR), }, }, reg, nil } diff --git a/tool/tsh/tsh_test.go b/tool/tsh/tsh_test.go index 4e90d9ddc86ba..84127f9cc2675 100644 --- a/tool/tsh/tsh_test.go +++ b/tool/tsh/tsh_test.go @@ -60,8 +60,8 @@ import ( "github.com/gravitational/teleport/lib" "github.com/gravitational/teleport/lib/auth" "github.com/gravitational/teleport/lib/auth/mocku2f" - wanlib "github.com/gravitational/teleport/lib/auth/webauthn" wancli "github.com/gravitational/teleport/lib/auth/webauthncli" + wantypes "github.com/gravitational/teleport/lib/auth/webauthntypes" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/client" "github.com/gravitational/teleport/lib/client/identityfile" @@ -1125,7 +1125,7 @@ func TestSSHOnMultipleNodes(t *testing.T) { DeviceUsage: proto.DeviceUsage_DEVICE_USAGE_PASSWORDLESS, }) require.NoError(t, err) - cc := wanlib.CredentialCreationFromProto(res.GetWebauthn()) + cc := wantypes.CredentialCreationFromProto(res.GetWebauthn()) ccr, err := device.SignCredentialCreation(origin(cluster), cc) require.NoError(t, err) @@ -1134,7 +1134,7 @@ func TestSSHOnMultipleNodes(t *testing.T) { NewPassword: []byte(password), NewMFARegisterResponse: &proto.MFARegisterResponse{ Response: &proto.MFARegisterResponse_Webauthn{ - Webauthn: wanlib.CredentialCreationResponseToProto(ccr), + Webauthn: wantypes.CredentialCreationResponseToProto(ccr), }, }, }) @@ -1145,28 +1145,28 @@ func TestSSHOnMultipleNodes(t *testing.T) { setupUser("leafcluster", "alice", true, leafAuth.GetAuthServer()) setupUser("localhost", "bob", false, rootAuth.GetAuthServer()) - successfulChallenge := func(cluster string) func(ctx context.Context, realOrigin string, assertion *wanlib.CredentialAssertion, prompt wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { - return func(ctx context.Context, realOrigin string, assertion *wanlib.CredentialAssertion, prompt wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { + successfulChallenge := func(cluster string) func(ctx context.Context, realOrigin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { + return func(ctx context.Context, realOrigin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { car, err := device.SignAssertion(origin(cluster), assertion) // use the fake origin to prevent a mismatch if err != nil { return nil, "", err } return &proto.MFAAuthenticateResponse{ Response: &proto.MFAAuthenticateResponse_Webauthn{ - Webauthn: wanlib.CredentialAssertionResponseToProto(car), + Webauthn: wantypes.CredentialAssertionResponseToProto(car), }, }, "", nil } } - failedChallenge := func(cluster string) func(ctx context.Context, realOrigin string, assertion *wanlib.CredentialAssertion, prompt wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { - return func(ctx context.Context, realOrigin string, assertion *wanlib.CredentialAssertion, prompt wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { + failedChallenge := func(cluster string) func(ctx context.Context, realOrigin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { + return func(ctx context.Context, realOrigin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) { car, err := device.SignAssertion(origin(cluster), assertion) // use the fake origin to prevent a mismatch if err != nil { return nil, "", err } - carProto := wanlib.CredentialAssertionResponseToProto(car) + carProto := wantypes.CredentialAssertionResponseToProto(car) carProto.Type = "NOT A VALID TYPE" // set to an invalid type so the ceremony fails return &proto.MFAAuthenticateResponse{ @@ -1177,7 +1177,7 @@ func TestSSHOnMultipleNodes(t *testing.T) { } } - type mfaPrompt = func(ctx context.Context, origin string, assertion *wanlib.CredentialAssertion, prompt wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) + type mfaPrompt = func(ctx context.Context, origin string, assertion *wantypes.CredentialAssertion, prompt wancli.LoginPrompt, _ *wancli.LoginOpts) (*proto.MFAAuthenticateResponse, string, error) setupChallengeSolver := func(mfaPrompt mfaPrompt) func(t *testing.T) { return func(t *testing.T) { inputReader := prompt.NewFakeReader(). diff --git a/tool/tsh/winwebauthn.go b/tool/tsh/webauthnwin.go similarity index 87% rename from tool/tsh/winwebauthn.go rename to tool/tsh/webauthnwin.go index 24b1c136191d3..7b1f5c261ea81 100644 --- a/tool/tsh/winwebauthn.go +++ b/tool/tsh/webauthnwin.go @@ -21,7 +21,7 @@ import ( "github.com/alecthomas/kingpin/v2" "github.com/gravitational/trace" - "github.com/gravitational/teleport/lib/auth/webauthnwin" + wanwin "github.com/gravitational/teleport/lib/auth/webauthnwin" ) type webauthnwinCommand struct { @@ -49,7 +49,7 @@ func newWebauthnwinDiagCommand(app *kingpin.CmdClause) *webauthnwinDiagCommand { } func (w *webauthnwinDiagCommand) run(cf *CLIConf) error { - diag := webauthnwin.CheckSupport() + diag := wanwin.CheckSupport() fmt.Printf("\nWebauthnWin available: %v\n", diag.IsAvailable) fmt.Printf("Compile support: %v\n", diag.HasCompileSupport) fmt.Printf("DLL API version: %v\n", diag.WebAuthnAPIVersion) @@ -59,11 +59,11 @@ func (w *webauthnwinDiagCommand) run(cf *CLIConf) error { return nil } - promptBefore := webauthnwin.PromptWriter - defer func() { webauthnwin.PromptWriter = promptBefore }() - webauthnwin.PromptWriter = os.Stderr + promptBefore := wanwin.PromptWriter + defer func() { wanwin.PromptWriter = promptBefore }() + wanwin.PromptWriter = os.Stderr - resp, err := webauthnwin.Diag(cf.Context) + resp, err := wanwin.Diag(cf.Context) // Abort if we got a nil diagnostic, otherwise print as much as we can. if resp == nil { return trace.Wrap(err)