feat: next
This commit is contained in:
49
server/internal/auth/creds.go
Normal file
49
server/internal/auth/creds.go
Normal file
@ -0,0 +1,49 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
"github.com/spotdemo4/trevstack/server/internal/sqlc"
|
||||
)
|
||||
|
||||
func NewCreds(creds []sqlc.Credential) []webauthn.Credential {
|
||||
webauthnCreds := []webauthn.Credential{}
|
||||
|
||||
for _, c := range creds {
|
||||
transports := []protocol.AuthenticatorTransport{}
|
||||
if c.Transports != nil {
|
||||
for t := range strings.SplitSeq(*c.Transports, " ") {
|
||||
transports = append(transports, protocol.AuthenticatorTransport(t))
|
||||
}
|
||||
}
|
||||
|
||||
flags := webauthn.CredentialFlags{}
|
||||
if c.UserVerified != nil {
|
||||
flags.UserVerified = *c.UserVerified
|
||||
}
|
||||
if c.BackupEligible != nil {
|
||||
flags.BackupEligible = *c.BackupEligible
|
||||
}
|
||||
if c.BackupState != nil {
|
||||
flags.BackupState = *c.BackupState
|
||||
}
|
||||
|
||||
webauthnCreds = append(webauthnCreds, webauthn.Credential{
|
||||
ID: []byte(c.CredID),
|
||||
PublicKey: c.CredPublicKey,
|
||||
Authenticator: webauthn.Authenticator{
|
||||
SignCount: uint32(c.SignCount),
|
||||
},
|
||||
Transport: transports,
|
||||
Flags: flags,
|
||||
Attestation: webauthn.CredentialAttestation{
|
||||
Object: c.AttestationObject,
|
||||
ClientDataJSON: c.AttestationClientData,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return webauthnCreds
|
||||
}
|
33
server/internal/auth/user.go
Normal file
33
server/internal/auth/user.go
Normal file
@ -0,0 +1,33 @@
|
||||
package auth
|
||||
|
||||
import "github.com/go-webauthn/webauthn/webauthn"
|
||||
|
||||
type User struct {
|
||||
id string
|
||||
username string
|
||||
credentials []webauthn.Credential
|
||||
}
|
||||
|
||||
func NewUser(id string, username string, credentials []webauthn.Credential) User {
|
||||
return User{
|
||||
id: id,
|
||||
username: username,
|
||||
credentials: credentials,
|
||||
}
|
||||
}
|
||||
|
||||
func (u User) WebAuthnID() []byte {
|
||||
return []byte(u.id)
|
||||
}
|
||||
|
||||
func (u User) WebAuthnName() string {
|
||||
return u.username
|
||||
}
|
||||
|
||||
func (u User) WebAuthnDisplayName() string {
|
||||
return u.username
|
||||
}
|
||||
|
||||
func (u User) WebAuthnCredentials() []webauthn.Credential {
|
||||
return u.credentials
|
||||
}
|
@ -7,6 +7,7 @@
|
||||
package itemv1
|
||||
|
||||
import (
|
||||
_ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
timestamppb "google.golang.org/protobuf/types/known/timestamppb"
|
||||
@ -638,10 +639,10 @@ var File_item_v1_item_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_item_v1_item_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x12item/v1/item.proto\x12\aitem.v1\x1a\x1fgoogle/protobuf/timestamp.proto\"\xb0\x01\n" +
|
||||
"\x12item/v1/item.proto\x12\aitem.v1\x1a\x1bbuf/validate/validate.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xb9\x01\n" +
|
||||
"\x04Item\x12\x0e\n" +
|
||||
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x12\n" +
|
||||
"\x04name\x18\x02 \x01(\tR\x04name\x120\n" +
|
||||
"\x02id\x18\x01 \x01(\x03R\x02id\x12\x1b\n" +
|
||||
"\x04name\x18\x02 \x01(\tB\a\xbaH\x04r\x02\x10\x03R\x04name\x120\n" +
|
||||
"\x05added\x18\x03 \x01(\v2\x1a.google.protobuf.TimestampR\x05added\x12 \n" +
|
||||
"\vdescription\x18\x04 \x01(\tR\vdescription\x12\x14\n" +
|
||||
"\x05price\x18\x05 \x01(\x02R\x05price\x12\x1a\n" +
|
||||
|
@ -7,6 +7,7 @@
|
||||
package userv1
|
||||
|
||||
import (
|
||||
_ "buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go/buf/validate"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
@ -285,27 +286,222 @@ func (*LogoutResponse) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_auth_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
type BeginPasskeyLoginRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyLoginRequest) Reset() {
|
||||
*x = BeginPasskeyLoginRequest{}
|
||||
mi := &file_user_v1_auth_proto_msgTypes[6]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyLoginRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*BeginPasskeyLoginRequest) ProtoMessage() {}
|
||||
|
||||
func (x *BeginPasskeyLoginRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_auth_proto_msgTypes[6]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use BeginPasskeyLoginRequest.ProtoReflect.Descriptor instead.
|
||||
func (*BeginPasskeyLoginRequest) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_auth_proto_rawDescGZIP(), []int{6}
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyLoginRequest) GetUsername() string {
|
||||
if x != nil {
|
||||
return x.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type BeginPasskeyLoginResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
OptionsJson string `protobuf:"bytes,1,opt,name=options_json,json=optionsJson,proto3" json:"options_json,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyLoginResponse) Reset() {
|
||||
*x = BeginPasskeyLoginResponse{}
|
||||
mi := &file_user_v1_auth_proto_msgTypes[7]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyLoginResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*BeginPasskeyLoginResponse) ProtoMessage() {}
|
||||
|
||||
func (x *BeginPasskeyLoginResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_auth_proto_msgTypes[7]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use BeginPasskeyLoginResponse.ProtoReflect.Descriptor instead.
|
||||
func (*BeginPasskeyLoginResponse) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_auth_proto_rawDescGZIP(), []int{7}
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyLoginResponse) GetOptionsJson() string {
|
||||
if x != nil {
|
||||
return x.OptionsJson
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type FinishPasskeyLoginRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Username string `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
|
||||
Attestation string `protobuf:"bytes,2,opt,name=attestation,proto3" json:"attestation,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyLoginRequest) Reset() {
|
||||
*x = FinishPasskeyLoginRequest{}
|
||||
mi := &file_user_v1_auth_proto_msgTypes[8]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyLoginRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*FinishPasskeyLoginRequest) ProtoMessage() {}
|
||||
|
||||
func (x *FinishPasskeyLoginRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_auth_proto_msgTypes[8]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use FinishPasskeyLoginRequest.ProtoReflect.Descriptor instead.
|
||||
func (*FinishPasskeyLoginRequest) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_auth_proto_rawDescGZIP(), []int{8}
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyLoginRequest) GetUsername() string {
|
||||
if x != nil {
|
||||
return x.Username
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyLoginRequest) GetAttestation() string {
|
||||
if x != nil {
|
||||
return x.Attestation
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type FinishPasskeyLoginResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyLoginResponse) Reset() {
|
||||
*x = FinishPasskeyLoginResponse{}
|
||||
mi := &file_user_v1_auth_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyLoginResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*FinishPasskeyLoginResponse) ProtoMessage() {}
|
||||
|
||||
func (x *FinishPasskeyLoginResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_auth_proto_msgTypes[9]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use FinishPasskeyLoginResponse.ProtoReflect.Descriptor instead.
|
||||
func (*FinishPasskeyLoginResponse) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_auth_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyLoginResponse) GetToken() string {
|
||||
if x != nil {
|
||||
return x.Token
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_user_v1_auth_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_user_v1_auth_proto_rawDesc = "" +
|
||||
"\n" +
|
||||
"\x12user/v1/auth.proto\x12\auser.v1\"F\n" +
|
||||
"\fLoginRequest\x12\x1a\n" +
|
||||
"\busername\x18\x01 \x01(\tR\busername\x12\x1a\n" +
|
||||
"\x12user/v1/auth.proto\x12\auser.v1\x1a\x1bbuf/validate/validate.proto\"O\n" +
|
||||
"\fLoginRequest\x12#\n" +
|
||||
"\busername\x18\x01 \x01(\tB\a\xbaH\x04r\x02\x10\x03R\busername\x12\x1a\n" +
|
||||
"\bpassword\x18\x02 \x01(\tR\bpassword\"%\n" +
|
||||
"\rLoginResponse\x12\x14\n" +
|
||||
"\x05token\x18\x01 \x01(\tR\x05token\"r\n" +
|
||||
"\rSignUpRequest\x12\x1a\n" +
|
||||
"\busername\x18\x01 \x01(\tR\busername\x12\x1a\n" +
|
||||
"\x05token\x18\x01 \x01(\tR\x05token\"{\n" +
|
||||
"\rSignUpRequest\x12#\n" +
|
||||
"\busername\x18\x01 \x01(\tB\a\xbaH\x04r\x02\x10\x03R\busername\x12\x1a\n" +
|
||||
"\bpassword\x18\x02 \x01(\tR\bpassword\x12)\n" +
|
||||
"\x10confirm_password\x18\x03 \x01(\tR\x0fconfirmPassword\"\x10\n" +
|
||||
"\x0eSignUpResponse\"\x0f\n" +
|
||||
"\rLogoutRequest\"\x10\n" +
|
||||
"\x0eLogoutResponse2\xc1\x01\n" +
|
||||
"\x0eLogoutResponse\"6\n" +
|
||||
"\x18BeginPasskeyLoginRequest\x12\x1a\n" +
|
||||
"\busername\x18\x01 \x01(\tR\busername\">\n" +
|
||||
"\x19BeginPasskeyLoginResponse\x12!\n" +
|
||||
"\foptions_json\x18\x01 \x01(\tR\voptionsJson\"Y\n" +
|
||||
"\x19FinishPasskeyLoginRequest\x12\x1a\n" +
|
||||
"\busername\x18\x01 \x01(\tR\busername\x12 \n" +
|
||||
"\vattestation\x18\x02 \x01(\tR\vattestation\"2\n" +
|
||||
"\x1aFinishPasskeyLoginResponse\x12\x14\n" +
|
||||
"\x05token\x18\x01 \x01(\tR\x05token2\x80\x03\n" +
|
||||
"\vAuthService\x128\n" +
|
||||
"\x05Login\x12\x15.user.v1.LoginRequest\x1a\x16.user.v1.LoginResponse\"\x00\x12;\n" +
|
||||
"\x06SignUp\x12\x16.user.v1.SignUpRequest\x1a\x17.user.v1.SignUpResponse\"\x00\x12;\n" +
|
||||
"\x06Logout\x12\x16.user.v1.LogoutRequest\x1a\x17.user.v1.LogoutResponse\"\x00B\x9c\x01\n" +
|
||||
"\x06Logout\x12\x16.user.v1.LogoutRequest\x1a\x17.user.v1.LogoutResponse\"\x00\x12\\\n" +
|
||||
"\x11BeginPasskeyLogin\x12!.user.v1.BeginPasskeyLoginRequest\x1a\".user.v1.BeginPasskeyLoginResponse\"\x00\x12_\n" +
|
||||
"\x12FinishPasskeyLogin\x12\".user.v1.FinishPasskeyLoginRequest\x1a#.user.v1.FinishPasskeyLoginResponse\"\x00B\x9c\x01\n" +
|
||||
"\vcom.user.v1B\tAuthProtoP\x01ZEgithub.com/spotdemo4/trevstack/server/internal/connect/user/v1;userv1\xa2\x02\x03UXX\xaa\x02\aUser.V1\xca\x02\aUser\\V1\xe2\x02\x13User\\V1\\GPBMetadata\xea\x02\bUser::V1b\x06proto3"
|
||||
|
||||
var (
|
||||
@ -320,24 +516,32 @@ func file_user_v1_auth_proto_rawDescGZIP() []byte {
|
||||
return file_user_v1_auth_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_user_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||
var file_user_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 10)
|
||||
var file_user_v1_auth_proto_goTypes = []any{
|
||||
(*LoginRequest)(nil), // 0: user.v1.LoginRequest
|
||||
(*LoginResponse)(nil), // 1: user.v1.LoginResponse
|
||||
(*SignUpRequest)(nil), // 2: user.v1.SignUpRequest
|
||||
(*SignUpResponse)(nil), // 3: user.v1.SignUpResponse
|
||||
(*LogoutRequest)(nil), // 4: user.v1.LogoutRequest
|
||||
(*LogoutResponse)(nil), // 5: user.v1.LogoutResponse
|
||||
(*LoginRequest)(nil), // 0: user.v1.LoginRequest
|
||||
(*LoginResponse)(nil), // 1: user.v1.LoginResponse
|
||||
(*SignUpRequest)(nil), // 2: user.v1.SignUpRequest
|
||||
(*SignUpResponse)(nil), // 3: user.v1.SignUpResponse
|
||||
(*LogoutRequest)(nil), // 4: user.v1.LogoutRequest
|
||||
(*LogoutResponse)(nil), // 5: user.v1.LogoutResponse
|
||||
(*BeginPasskeyLoginRequest)(nil), // 6: user.v1.BeginPasskeyLoginRequest
|
||||
(*BeginPasskeyLoginResponse)(nil), // 7: user.v1.BeginPasskeyLoginResponse
|
||||
(*FinishPasskeyLoginRequest)(nil), // 8: user.v1.FinishPasskeyLoginRequest
|
||||
(*FinishPasskeyLoginResponse)(nil), // 9: user.v1.FinishPasskeyLoginResponse
|
||||
}
|
||||
var file_user_v1_auth_proto_depIdxs = []int32{
|
||||
0, // 0: user.v1.AuthService.Login:input_type -> user.v1.LoginRequest
|
||||
2, // 1: user.v1.AuthService.SignUp:input_type -> user.v1.SignUpRequest
|
||||
4, // 2: user.v1.AuthService.Logout:input_type -> user.v1.LogoutRequest
|
||||
1, // 3: user.v1.AuthService.Login:output_type -> user.v1.LoginResponse
|
||||
3, // 4: user.v1.AuthService.SignUp:output_type -> user.v1.SignUpResponse
|
||||
5, // 5: user.v1.AuthService.Logout:output_type -> user.v1.LogoutResponse
|
||||
3, // [3:6] is the sub-list for method output_type
|
||||
0, // [0:3] is the sub-list for method input_type
|
||||
6, // 3: user.v1.AuthService.BeginPasskeyLogin:input_type -> user.v1.BeginPasskeyLoginRequest
|
||||
8, // 4: user.v1.AuthService.FinishPasskeyLogin:input_type -> user.v1.FinishPasskeyLoginRequest
|
||||
1, // 5: user.v1.AuthService.Login:output_type -> user.v1.LoginResponse
|
||||
3, // 6: user.v1.AuthService.SignUp:output_type -> user.v1.SignUpResponse
|
||||
5, // 7: user.v1.AuthService.Logout:output_type -> user.v1.LogoutResponse
|
||||
7, // 8: user.v1.AuthService.BeginPasskeyLogin:output_type -> user.v1.BeginPasskeyLoginResponse
|
||||
9, // 9: user.v1.AuthService.FinishPasskeyLogin:output_type -> user.v1.FinishPasskeyLoginResponse
|
||||
5, // [5:10] is the sub-list for method output_type
|
||||
0, // [0:5] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
@ -354,7 +558,7 @@ func file_user_v1_auth_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_v1_auth_proto_rawDesc), len(file_user_v1_auth_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 6,
|
||||
NumMessages: 10,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
@ -457,6 +457,166 @@ func (x *UpdateProfilePictureResponse) GetUser() *User {
|
||||
return nil
|
||||
}
|
||||
|
||||
type BeginPasskeyRegistrationRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyRegistrationRequest) Reset() {
|
||||
*x = BeginPasskeyRegistrationRequest{}
|
||||
mi := &file_user_v1_user_proto_msgTypes[9]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyRegistrationRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*BeginPasskeyRegistrationRequest) ProtoMessage() {}
|
||||
|
||||
func (x *BeginPasskeyRegistrationRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_user_proto_msgTypes[9]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use BeginPasskeyRegistrationRequest.ProtoReflect.Descriptor instead.
|
||||
func (*BeginPasskeyRegistrationRequest) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_user_proto_rawDescGZIP(), []int{9}
|
||||
}
|
||||
|
||||
type BeginPasskeyRegistrationResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
OptionsJson string `protobuf:"bytes,1,opt,name=options_json,json=optionsJson,proto3" json:"options_json,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyRegistrationResponse) Reset() {
|
||||
*x = BeginPasskeyRegistrationResponse{}
|
||||
mi := &file_user_v1_user_proto_msgTypes[10]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyRegistrationResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*BeginPasskeyRegistrationResponse) ProtoMessage() {}
|
||||
|
||||
func (x *BeginPasskeyRegistrationResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_user_proto_msgTypes[10]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use BeginPasskeyRegistrationResponse.ProtoReflect.Descriptor instead.
|
||||
func (*BeginPasskeyRegistrationResponse) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_user_proto_rawDescGZIP(), []int{10}
|
||||
}
|
||||
|
||||
func (x *BeginPasskeyRegistrationResponse) GetOptionsJson() string {
|
||||
if x != nil {
|
||||
return x.OptionsJson
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type FinishPasskeyRegistrationRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Attestation string `protobuf:"bytes,1,opt,name=attestation,proto3" json:"attestation,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyRegistrationRequest) Reset() {
|
||||
*x = FinishPasskeyRegistrationRequest{}
|
||||
mi := &file_user_v1_user_proto_msgTypes[11]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyRegistrationRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*FinishPasskeyRegistrationRequest) ProtoMessage() {}
|
||||
|
||||
func (x *FinishPasskeyRegistrationRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_user_proto_msgTypes[11]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use FinishPasskeyRegistrationRequest.ProtoReflect.Descriptor instead.
|
||||
func (*FinishPasskeyRegistrationRequest) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_user_proto_rawDescGZIP(), []int{11}
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyRegistrationRequest) GetAttestation() string {
|
||||
if x != nil {
|
||||
return x.Attestation
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type FinishPasskeyRegistrationResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyRegistrationResponse) Reset() {
|
||||
*x = FinishPasskeyRegistrationResponse{}
|
||||
mi := &file_user_v1_user_proto_msgTypes[12]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *FinishPasskeyRegistrationResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*FinishPasskeyRegistrationResponse) ProtoMessage() {}
|
||||
|
||||
func (x *FinishPasskeyRegistrationResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_user_v1_user_proto_msgTypes[12]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use FinishPasskeyRegistrationResponse.ProtoReflect.Descriptor instead.
|
||||
func (*FinishPasskeyRegistrationResponse) Descriptor() ([]byte, []int) {
|
||||
return file_user_v1_user_proto_rawDescGZIP(), []int{12}
|
||||
}
|
||||
|
||||
var File_user_v1_user_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_user_v1_user_proto_rawDesc = "" +
|
||||
@ -485,12 +645,20 @@ const file_user_v1_user_proto_rawDesc = "" +
|
||||
"\tfile_name\x18\x01 \x01(\tR\bfileName\x12\x12\n" +
|
||||
"\x04data\x18\x02 \x01(\fR\x04data\"A\n" +
|
||||
"\x1cUpdateProfilePictureResponse\x12!\n" +
|
||||
"\x04user\x18\x01 \x01(\v2\r.user.v1.UserR\x04user2\xcf\x02\n" +
|
||||
"\x04user\x18\x01 \x01(\v2\r.user.v1.UserR\x04user\"!\n" +
|
||||
"\x1fBeginPasskeyRegistrationRequest\"E\n" +
|
||||
" BeginPasskeyRegistrationResponse\x12!\n" +
|
||||
"\foptions_json\x18\x01 \x01(\tR\voptionsJson\"D\n" +
|
||||
" FinishPasskeyRegistrationRequest\x12 \n" +
|
||||
"\vattestation\x18\x01 \x01(\tR\vattestation\"#\n" +
|
||||
"!FinishPasskeyRegistrationResponse2\xb8\x04\n" +
|
||||
"\vUserService\x12>\n" +
|
||||
"\aGetUser\x12\x17.user.v1.GetUserRequest\x1a\x18.user.v1.GetUserResponse\"\x00\x12S\n" +
|
||||
"\x0eUpdatePassword\x12\x1e.user.v1.UpdatePasswordRequest\x1a\x1f.user.v1.UpdatePasswordResponse\"\x00\x12D\n" +
|
||||
"\tGetAPIKey\x12\x19.user.v1.GetAPIKeyRequest\x1a\x1a.user.v1.GetAPIKeyResponse\"\x00\x12e\n" +
|
||||
"\x14UpdateProfilePicture\x12$.user.v1.UpdateProfilePictureRequest\x1a%.user.v1.UpdateProfilePictureResponse\"\x00B\x9c\x01\n" +
|
||||
"\x14UpdateProfilePicture\x12$.user.v1.UpdateProfilePictureRequest\x1a%.user.v1.UpdateProfilePictureResponse\"\x00\x12q\n" +
|
||||
"\x18BeginPasskeyRegistration\x12(.user.v1.BeginPasskeyRegistrationRequest\x1a).user.v1.BeginPasskeyRegistrationResponse\"\x00\x12t\n" +
|
||||
"\x19FinishPasskeyRegistration\x12).user.v1.FinishPasskeyRegistrationRequest\x1a*.user.v1.FinishPasskeyRegistrationResponse\"\x00B\x9c\x01\n" +
|
||||
"\vcom.user.v1B\tUserProtoP\x01ZEgithub.com/spotdemo4/trevstack/server/internal/connect/user/v1;userv1\xa2\x02\x03UXX\xaa\x02\aUser.V1\xca\x02\aUser\\V1\xe2\x02\x13User\\V1\\GPBMetadata\xea\x02\bUser::V1b\x06proto3"
|
||||
|
||||
var (
|
||||
@ -505,35 +673,43 @@ func file_user_v1_user_proto_rawDescGZIP() []byte {
|
||||
return file_user_v1_user_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_user_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
|
||||
var file_user_v1_user_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
|
||||
var file_user_v1_user_proto_goTypes = []any{
|
||||
(*User)(nil), // 0: user.v1.User
|
||||
(*GetUserRequest)(nil), // 1: user.v1.GetUserRequest
|
||||
(*GetUserResponse)(nil), // 2: user.v1.GetUserResponse
|
||||
(*UpdatePasswordRequest)(nil), // 3: user.v1.UpdatePasswordRequest
|
||||
(*UpdatePasswordResponse)(nil), // 4: user.v1.UpdatePasswordResponse
|
||||
(*GetAPIKeyRequest)(nil), // 5: user.v1.GetAPIKeyRequest
|
||||
(*GetAPIKeyResponse)(nil), // 6: user.v1.GetAPIKeyResponse
|
||||
(*UpdateProfilePictureRequest)(nil), // 7: user.v1.UpdateProfilePictureRequest
|
||||
(*UpdateProfilePictureResponse)(nil), // 8: user.v1.UpdateProfilePictureResponse
|
||||
(*User)(nil), // 0: user.v1.User
|
||||
(*GetUserRequest)(nil), // 1: user.v1.GetUserRequest
|
||||
(*GetUserResponse)(nil), // 2: user.v1.GetUserResponse
|
||||
(*UpdatePasswordRequest)(nil), // 3: user.v1.UpdatePasswordRequest
|
||||
(*UpdatePasswordResponse)(nil), // 4: user.v1.UpdatePasswordResponse
|
||||
(*GetAPIKeyRequest)(nil), // 5: user.v1.GetAPIKeyRequest
|
||||
(*GetAPIKeyResponse)(nil), // 6: user.v1.GetAPIKeyResponse
|
||||
(*UpdateProfilePictureRequest)(nil), // 7: user.v1.UpdateProfilePictureRequest
|
||||
(*UpdateProfilePictureResponse)(nil), // 8: user.v1.UpdateProfilePictureResponse
|
||||
(*BeginPasskeyRegistrationRequest)(nil), // 9: user.v1.BeginPasskeyRegistrationRequest
|
||||
(*BeginPasskeyRegistrationResponse)(nil), // 10: user.v1.BeginPasskeyRegistrationResponse
|
||||
(*FinishPasskeyRegistrationRequest)(nil), // 11: user.v1.FinishPasskeyRegistrationRequest
|
||||
(*FinishPasskeyRegistrationResponse)(nil), // 12: user.v1.FinishPasskeyRegistrationResponse
|
||||
}
|
||||
var file_user_v1_user_proto_depIdxs = []int32{
|
||||
0, // 0: user.v1.GetUserResponse.user:type_name -> user.v1.User
|
||||
0, // 1: user.v1.UpdatePasswordResponse.user:type_name -> user.v1.User
|
||||
0, // 2: user.v1.UpdateProfilePictureResponse.user:type_name -> user.v1.User
|
||||
1, // 3: user.v1.UserService.GetUser:input_type -> user.v1.GetUserRequest
|
||||
3, // 4: user.v1.UserService.UpdatePassword:input_type -> user.v1.UpdatePasswordRequest
|
||||
5, // 5: user.v1.UserService.GetAPIKey:input_type -> user.v1.GetAPIKeyRequest
|
||||
7, // 6: user.v1.UserService.UpdateProfilePicture:input_type -> user.v1.UpdateProfilePictureRequest
|
||||
2, // 7: user.v1.UserService.GetUser:output_type -> user.v1.GetUserResponse
|
||||
4, // 8: user.v1.UserService.UpdatePassword:output_type -> user.v1.UpdatePasswordResponse
|
||||
6, // 9: user.v1.UserService.GetAPIKey:output_type -> user.v1.GetAPIKeyResponse
|
||||
8, // 10: user.v1.UserService.UpdateProfilePicture:output_type -> user.v1.UpdateProfilePictureResponse
|
||||
7, // [7:11] is the sub-list for method output_type
|
||||
3, // [3:7] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
0, // 0: user.v1.GetUserResponse.user:type_name -> user.v1.User
|
||||
0, // 1: user.v1.UpdatePasswordResponse.user:type_name -> user.v1.User
|
||||
0, // 2: user.v1.UpdateProfilePictureResponse.user:type_name -> user.v1.User
|
||||
1, // 3: user.v1.UserService.GetUser:input_type -> user.v1.GetUserRequest
|
||||
3, // 4: user.v1.UserService.UpdatePassword:input_type -> user.v1.UpdatePasswordRequest
|
||||
5, // 5: user.v1.UserService.GetAPIKey:input_type -> user.v1.GetAPIKeyRequest
|
||||
7, // 6: user.v1.UserService.UpdateProfilePicture:input_type -> user.v1.UpdateProfilePictureRequest
|
||||
9, // 7: user.v1.UserService.BeginPasskeyRegistration:input_type -> user.v1.BeginPasskeyRegistrationRequest
|
||||
11, // 8: user.v1.UserService.FinishPasskeyRegistration:input_type -> user.v1.FinishPasskeyRegistrationRequest
|
||||
2, // 9: user.v1.UserService.GetUser:output_type -> user.v1.GetUserResponse
|
||||
4, // 10: user.v1.UserService.UpdatePassword:output_type -> user.v1.UpdatePasswordResponse
|
||||
6, // 11: user.v1.UserService.GetAPIKey:output_type -> user.v1.GetAPIKeyResponse
|
||||
8, // 12: user.v1.UserService.UpdateProfilePicture:output_type -> user.v1.UpdateProfilePictureResponse
|
||||
10, // 13: user.v1.UserService.BeginPasskeyRegistration:output_type -> user.v1.BeginPasskeyRegistrationResponse
|
||||
12, // 14: user.v1.UserService.FinishPasskeyRegistration:output_type -> user.v1.FinishPasskeyRegistrationResponse
|
||||
9, // [9:15] is the sub-list for method output_type
|
||||
3, // [3:9] is the sub-list for method input_type
|
||||
3, // [3:3] is the sub-list for extension type_name
|
||||
3, // [3:3] is the sub-list for extension extendee
|
||||
0, // [0:3] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_user_v1_user_proto_init() }
|
||||
@ -548,7 +724,7 @@ func file_user_v1_user_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_user_v1_user_proto_rawDesc), len(file_user_v1_user_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 9,
|
||||
NumMessages: 13,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
@ -39,6 +39,12 @@ const (
|
||||
AuthServiceSignUpProcedure = "/user.v1.AuthService/SignUp"
|
||||
// AuthServiceLogoutProcedure is the fully-qualified name of the AuthService's Logout RPC.
|
||||
AuthServiceLogoutProcedure = "/user.v1.AuthService/Logout"
|
||||
// AuthServiceBeginPasskeyLoginProcedure is the fully-qualified name of the AuthService's
|
||||
// BeginPasskeyLogin RPC.
|
||||
AuthServiceBeginPasskeyLoginProcedure = "/user.v1.AuthService/BeginPasskeyLogin"
|
||||
// AuthServiceFinishPasskeyLoginProcedure is the fully-qualified name of the AuthService's
|
||||
// FinishPasskeyLogin RPC.
|
||||
AuthServiceFinishPasskeyLoginProcedure = "/user.v1.AuthService/FinishPasskeyLogin"
|
||||
)
|
||||
|
||||
// AuthServiceClient is a client for the user.v1.AuthService service.
|
||||
@ -46,6 +52,8 @@ type AuthServiceClient interface {
|
||||
Login(context.Context, *connect.Request[v1.LoginRequest]) (*connect.Response[v1.LoginResponse], error)
|
||||
SignUp(context.Context, *connect.Request[v1.SignUpRequest]) (*connect.Response[v1.SignUpResponse], error)
|
||||
Logout(context.Context, *connect.Request[v1.LogoutRequest]) (*connect.Response[v1.LogoutResponse], error)
|
||||
BeginPasskeyLogin(context.Context, *connect.Request[v1.BeginPasskeyLoginRequest]) (*connect.Response[v1.BeginPasskeyLoginResponse], error)
|
||||
FinishPasskeyLogin(context.Context, *connect.Request[v1.FinishPasskeyLoginRequest]) (*connect.Response[v1.FinishPasskeyLoginResponse], error)
|
||||
}
|
||||
|
||||
// NewAuthServiceClient constructs a client for the user.v1.AuthService service. By default, it uses
|
||||
@ -77,14 +85,28 @@ func NewAuthServiceClient(httpClient connect.HTTPClient, baseURL string, opts ..
|
||||
connect.WithSchema(authServiceMethods.ByName("Logout")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
beginPasskeyLogin: connect.NewClient[v1.BeginPasskeyLoginRequest, v1.BeginPasskeyLoginResponse](
|
||||
httpClient,
|
||||
baseURL+AuthServiceBeginPasskeyLoginProcedure,
|
||||
connect.WithSchema(authServiceMethods.ByName("BeginPasskeyLogin")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
finishPasskeyLogin: connect.NewClient[v1.FinishPasskeyLoginRequest, v1.FinishPasskeyLoginResponse](
|
||||
httpClient,
|
||||
baseURL+AuthServiceFinishPasskeyLoginProcedure,
|
||||
connect.WithSchema(authServiceMethods.ByName("FinishPasskeyLogin")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// authServiceClient implements AuthServiceClient.
|
||||
type authServiceClient struct {
|
||||
login *connect.Client[v1.LoginRequest, v1.LoginResponse]
|
||||
signUp *connect.Client[v1.SignUpRequest, v1.SignUpResponse]
|
||||
logout *connect.Client[v1.LogoutRequest, v1.LogoutResponse]
|
||||
login *connect.Client[v1.LoginRequest, v1.LoginResponse]
|
||||
signUp *connect.Client[v1.SignUpRequest, v1.SignUpResponse]
|
||||
logout *connect.Client[v1.LogoutRequest, v1.LogoutResponse]
|
||||
beginPasskeyLogin *connect.Client[v1.BeginPasskeyLoginRequest, v1.BeginPasskeyLoginResponse]
|
||||
finishPasskeyLogin *connect.Client[v1.FinishPasskeyLoginRequest, v1.FinishPasskeyLoginResponse]
|
||||
}
|
||||
|
||||
// Login calls user.v1.AuthService.Login.
|
||||
@ -102,11 +124,23 @@ func (c *authServiceClient) Logout(ctx context.Context, req *connect.Request[v1.
|
||||
return c.logout.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// BeginPasskeyLogin calls user.v1.AuthService.BeginPasskeyLogin.
|
||||
func (c *authServiceClient) BeginPasskeyLogin(ctx context.Context, req *connect.Request[v1.BeginPasskeyLoginRequest]) (*connect.Response[v1.BeginPasskeyLoginResponse], error) {
|
||||
return c.beginPasskeyLogin.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// FinishPasskeyLogin calls user.v1.AuthService.FinishPasskeyLogin.
|
||||
func (c *authServiceClient) FinishPasskeyLogin(ctx context.Context, req *connect.Request[v1.FinishPasskeyLoginRequest]) (*connect.Response[v1.FinishPasskeyLoginResponse], error) {
|
||||
return c.finishPasskeyLogin.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// AuthServiceHandler is an implementation of the user.v1.AuthService service.
|
||||
type AuthServiceHandler interface {
|
||||
Login(context.Context, *connect.Request[v1.LoginRequest]) (*connect.Response[v1.LoginResponse], error)
|
||||
SignUp(context.Context, *connect.Request[v1.SignUpRequest]) (*connect.Response[v1.SignUpResponse], error)
|
||||
Logout(context.Context, *connect.Request[v1.LogoutRequest]) (*connect.Response[v1.LogoutResponse], error)
|
||||
BeginPasskeyLogin(context.Context, *connect.Request[v1.BeginPasskeyLoginRequest]) (*connect.Response[v1.BeginPasskeyLoginResponse], error)
|
||||
FinishPasskeyLogin(context.Context, *connect.Request[v1.FinishPasskeyLoginRequest]) (*connect.Response[v1.FinishPasskeyLoginResponse], error)
|
||||
}
|
||||
|
||||
// NewAuthServiceHandler builds an HTTP handler from the service implementation. It returns the path
|
||||
@ -134,6 +168,18 @@ func NewAuthServiceHandler(svc AuthServiceHandler, opts ...connect.HandlerOption
|
||||
connect.WithSchema(authServiceMethods.ByName("Logout")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
authServiceBeginPasskeyLoginHandler := connect.NewUnaryHandler(
|
||||
AuthServiceBeginPasskeyLoginProcedure,
|
||||
svc.BeginPasskeyLogin,
|
||||
connect.WithSchema(authServiceMethods.ByName("BeginPasskeyLogin")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
authServiceFinishPasskeyLoginHandler := connect.NewUnaryHandler(
|
||||
AuthServiceFinishPasskeyLoginProcedure,
|
||||
svc.FinishPasskeyLogin,
|
||||
connect.WithSchema(authServiceMethods.ByName("FinishPasskeyLogin")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
return "/user.v1.AuthService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case AuthServiceLoginProcedure:
|
||||
@ -142,6 +188,10 @@ func NewAuthServiceHandler(svc AuthServiceHandler, opts ...connect.HandlerOption
|
||||
authServiceSignUpHandler.ServeHTTP(w, r)
|
||||
case AuthServiceLogoutProcedure:
|
||||
authServiceLogoutHandler.ServeHTTP(w, r)
|
||||
case AuthServiceBeginPasskeyLoginProcedure:
|
||||
authServiceBeginPasskeyLoginHandler.ServeHTTP(w, r)
|
||||
case AuthServiceFinishPasskeyLoginProcedure:
|
||||
authServiceFinishPasskeyLoginHandler.ServeHTTP(w, r)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
@ -162,3 +212,11 @@ func (UnimplementedAuthServiceHandler) SignUp(context.Context, *connect.Request[
|
||||
func (UnimplementedAuthServiceHandler) Logout(context.Context, *connect.Request[v1.LogoutRequest]) (*connect.Response[v1.LogoutResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.AuthService.Logout is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedAuthServiceHandler) BeginPasskeyLogin(context.Context, *connect.Request[v1.BeginPasskeyLoginRequest]) (*connect.Response[v1.BeginPasskeyLoginResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.AuthService.BeginPasskeyLogin is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedAuthServiceHandler) FinishPasskeyLogin(context.Context, *connect.Request[v1.FinishPasskeyLoginRequest]) (*connect.Response[v1.FinishPasskeyLoginResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.AuthService.FinishPasskeyLogin is not implemented"))
|
||||
}
|
||||
|
@ -43,6 +43,12 @@ const (
|
||||
// UserServiceUpdateProfilePictureProcedure is the fully-qualified name of the UserService's
|
||||
// UpdateProfilePicture RPC.
|
||||
UserServiceUpdateProfilePictureProcedure = "/user.v1.UserService/UpdateProfilePicture"
|
||||
// UserServiceBeginPasskeyRegistrationProcedure is the fully-qualified name of the UserService's
|
||||
// BeginPasskeyRegistration RPC.
|
||||
UserServiceBeginPasskeyRegistrationProcedure = "/user.v1.UserService/BeginPasskeyRegistration"
|
||||
// UserServiceFinishPasskeyRegistrationProcedure is the fully-qualified name of the UserService's
|
||||
// FinishPasskeyRegistration RPC.
|
||||
UserServiceFinishPasskeyRegistrationProcedure = "/user.v1.UserService/FinishPasskeyRegistration"
|
||||
)
|
||||
|
||||
// UserServiceClient is a client for the user.v1.UserService service.
|
||||
@ -51,6 +57,8 @@ type UserServiceClient interface {
|
||||
UpdatePassword(context.Context, *connect.Request[v1.UpdatePasswordRequest]) (*connect.Response[v1.UpdatePasswordResponse], error)
|
||||
GetAPIKey(context.Context, *connect.Request[v1.GetAPIKeyRequest]) (*connect.Response[v1.GetAPIKeyResponse], error)
|
||||
UpdateProfilePicture(context.Context, *connect.Request[v1.UpdateProfilePictureRequest]) (*connect.Response[v1.UpdateProfilePictureResponse], error)
|
||||
BeginPasskeyRegistration(context.Context, *connect.Request[v1.BeginPasskeyRegistrationRequest]) (*connect.Response[v1.BeginPasskeyRegistrationResponse], error)
|
||||
FinishPasskeyRegistration(context.Context, *connect.Request[v1.FinishPasskeyRegistrationRequest]) (*connect.Response[v1.FinishPasskeyRegistrationResponse], error)
|
||||
}
|
||||
|
||||
// NewUserServiceClient constructs a client for the user.v1.UserService service. By default, it uses
|
||||
@ -88,15 +96,29 @@ func NewUserServiceClient(httpClient connect.HTTPClient, baseURL string, opts ..
|
||||
connect.WithSchema(userServiceMethods.ByName("UpdateProfilePicture")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
beginPasskeyRegistration: connect.NewClient[v1.BeginPasskeyRegistrationRequest, v1.BeginPasskeyRegistrationResponse](
|
||||
httpClient,
|
||||
baseURL+UserServiceBeginPasskeyRegistrationProcedure,
|
||||
connect.WithSchema(userServiceMethods.ByName("BeginPasskeyRegistration")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
finishPasskeyRegistration: connect.NewClient[v1.FinishPasskeyRegistrationRequest, v1.FinishPasskeyRegistrationResponse](
|
||||
httpClient,
|
||||
baseURL+UserServiceFinishPasskeyRegistrationProcedure,
|
||||
connect.WithSchema(userServiceMethods.ByName("FinishPasskeyRegistration")),
|
||||
connect.WithClientOptions(opts...),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// userServiceClient implements UserServiceClient.
|
||||
type userServiceClient struct {
|
||||
getUser *connect.Client[v1.GetUserRequest, v1.GetUserResponse]
|
||||
updatePassword *connect.Client[v1.UpdatePasswordRequest, v1.UpdatePasswordResponse]
|
||||
getAPIKey *connect.Client[v1.GetAPIKeyRequest, v1.GetAPIKeyResponse]
|
||||
updateProfilePicture *connect.Client[v1.UpdateProfilePictureRequest, v1.UpdateProfilePictureResponse]
|
||||
getUser *connect.Client[v1.GetUserRequest, v1.GetUserResponse]
|
||||
updatePassword *connect.Client[v1.UpdatePasswordRequest, v1.UpdatePasswordResponse]
|
||||
getAPIKey *connect.Client[v1.GetAPIKeyRequest, v1.GetAPIKeyResponse]
|
||||
updateProfilePicture *connect.Client[v1.UpdateProfilePictureRequest, v1.UpdateProfilePictureResponse]
|
||||
beginPasskeyRegistration *connect.Client[v1.BeginPasskeyRegistrationRequest, v1.BeginPasskeyRegistrationResponse]
|
||||
finishPasskeyRegistration *connect.Client[v1.FinishPasskeyRegistrationRequest, v1.FinishPasskeyRegistrationResponse]
|
||||
}
|
||||
|
||||
// GetUser calls user.v1.UserService.GetUser.
|
||||
@ -119,12 +141,24 @@ func (c *userServiceClient) UpdateProfilePicture(ctx context.Context, req *conne
|
||||
return c.updateProfilePicture.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// BeginPasskeyRegistration calls user.v1.UserService.BeginPasskeyRegistration.
|
||||
func (c *userServiceClient) BeginPasskeyRegistration(ctx context.Context, req *connect.Request[v1.BeginPasskeyRegistrationRequest]) (*connect.Response[v1.BeginPasskeyRegistrationResponse], error) {
|
||||
return c.beginPasskeyRegistration.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// FinishPasskeyRegistration calls user.v1.UserService.FinishPasskeyRegistration.
|
||||
func (c *userServiceClient) FinishPasskeyRegistration(ctx context.Context, req *connect.Request[v1.FinishPasskeyRegistrationRequest]) (*connect.Response[v1.FinishPasskeyRegistrationResponse], error) {
|
||||
return c.finishPasskeyRegistration.CallUnary(ctx, req)
|
||||
}
|
||||
|
||||
// UserServiceHandler is an implementation of the user.v1.UserService service.
|
||||
type UserServiceHandler interface {
|
||||
GetUser(context.Context, *connect.Request[v1.GetUserRequest]) (*connect.Response[v1.GetUserResponse], error)
|
||||
UpdatePassword(context.Context, *connect.Request[v1.UpdatePasswordRequest]) (*connect.Response[v1.UpdatePasswordResponse], error)
|
||||
GetAPIKey(context.Context, *connect.Request[v1.GetAPIKeyRequest]) (*connect.Response[v1.GetAPIKeyResponse], error)
|
||||
UpdateProfilePicture(context.Context, *connect.Request[v1.UpdateProfilePictureRequest]) (*connect.Response[v1.UpdateProfilePictureResponse], error)
|
||||
BeginPasskeyRegistration(context.Context, *connect.Request[v1.BeginPasskeyRegistrationRequest]) (*connect.Response[v1.BeginPasskeyRegistrationResponse], error)
|
||||
FinishPasskeyRegistration(context.Context, *connect.Request[v1.FinishPasskeyRegistrationRequest]) (*connect.Response[v1.FinishPasskeyRegistrationResponse], error)
|
||||
}
|
||||
|
||||
// NewUserServiceHandler builds an HTTP handler from the service implementation. It returns the path
|
||||
@ -158,6 +192,18 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption
|
||||
connect.WithSchema(userServiceMethods.ByName("UpdateProfilePicture")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
userServiceBeginPasskeyRegistrationHandler := connect.NewUnaryHandler(
|
||||
UserServiceBeginPasskeyRegistrationProcedure,
|
||||
svc.BeginPasskeyRegistration,
|
||||
connect.WithSchema(userServiceMethods.ByName("BeginPasskeyRegistration")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
userServiceFinishPasskeyRegistrationHandler := connect.NewUnaryHandler(
|
||||
UserServiceFinishPasskeyRegistrationProcedure,
|
||||
svc.FinishPasskeyRegistration,
|
||||
connect.WithSchema(userServiceMethods.ByName("FinishPasskeyRegistration")),
|
||||
connect.WithHandlerOptions(opts...),
|
||||
)
|
||||
return "/user.v1.UserService/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case UserServiceGetUserProcedure:
|
||||
@ -168,6 +214,10 @@ func NewUserServiceHandler(svc UserServiceHandler, opts ...connect.HandlerOption
|
||||
userServiceGetAPIKeyHandler.ServeHTTP(w, r)
|
||||
case UserServiceUpdateProfilePictureProcedure:
|
||||
userServiceUpdateProfilePictureHandler.ServeHTTP(w, r)
|
||||
case UserServiceBeginPasskeyRegistrationProcedure:
|
||||
userServiceBeginPasskeyRegistrationHandler.ServeHTTP(w, r)
|
||||
case UserServiceFinishPasskeyRegistrationProcedure:
|
||||
userServiceFinishPasskeyRegistrationHandler.ServeHTTP(w, r)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
@ -192,3 +242,11 @@ func (UnimplementedUserServiceHandler) GetAPIKey(context.Context, *connect.Reque
|
||||
func (UnimplementedUserServiceHandler) UpdateProfilePicture(context.Context, *connect.Request[v1.UpdateProfilePictureRequest]) (*connect.Response[v1.UpdateProfilePictureResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.UserService.UpdateProfilePicture is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedUserServiceHandler) BeginPasskeyRegistration(context.Context, *connect.Request[v1.BeginPasskeyRegistrationRequest]) (*connect.Response[v1.BeginPasskeyRegistrationResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.UserService.BeginPasskeyRegistration is not implemented"))
|
||||
}
|
||||
|
||||
func (UnimplementedUserServiceHandler) FinishPasskeyRegistration(context.Context, *connect.Request[v1.FinishPasskeyRegistrationRequest]) (*connect.Response[v1.FinishPasskeyRegistrationResponse], error) {
|
||||
return nil, connect.NewError(connect.CodeUnimplemented, errors.New("user.v1.UserService.FinishPasskeyRegistration is not implemented"))
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package database
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io"
|
||||
"log"
|
||||
"net/url"
|
||||
|
||||
@ -11,8 +10,8 @@ import (
|
||||
)
|
||||
|
||||
func Migrate(dsn string, dbFS embed.FS) error {
|
||||
_, err := dbFS.ReadDir(".")
|
||||
if err == io.EOF {
|
||||
entries, err := dbFS.ReadDir(".")
|
||||
if err != nil || len(entries) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -35,7 +34,7 @@ func Migrate(dsn string, dbFS embed.FS) error {
|
||||
log.Println(m.Version, m.FilePath)
|
||||
}
|
||||
|
||||
log.Println("\nApplying...")
|
||||
log.Println("Applying...")
|
||||
err = db.CreateAndMigrate()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -2,7 +2,6 @@ package client
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"io"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
|
||||
@ -10,8 +9,8 @@ import (
|
||||
)
|
||||
|
||||
func NewClientHandler(key string, clientFS embed.FS) http.Handler {
|
||||
_, err := clientFS.ReadDir(".")
|
||||
if err == io.EOF {
|
||||
entries, err := clientFS.ReadDir(".")
|
||||
if err != nil || len(entries) == 0 {
|
||||
return http.NotFoundHandler()
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"time"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"connectrpc.com/validate"
|
||||
itemv1 "github.com/spotdemo4/trevstack/server/internal/connect/item/v1"
|
||||
"github.com/spotdemo4/trevstack/server/internal/connect/item/v1/itemv1connect"
|
||||
"github.com/spotdemo4/trevstack/server/internal/interceptors"
|
||||
@ -190,8 +191,8 @@ func (h *Handler) DeleteItem(ctx context.Context, req *connect.Request[itemv1.De
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func NewHandler(db *sqlc.Queries, key string) (string, http.Handler) {
|
||||
interceptors := connect.WithInterceptors(interceptors.NewAuthInterceptor(key))
|
||||
func NewHandler(vi *validate.Interceptor, db *sqlc.Queries, key string) (string, http.Handler) {
|
||||
interceptors := connect.WithInterceptors(vi, interceptors.NewAuthInterceptor(key))
|
||||
|
||||
return itemv1connect.NewItemServiceHandler(
|
||||
&Handler{
|
||||
|
@ -3,15 +3,20 @@ package user
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
_ "crypto/sha256" // Crypto
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"connectrpc.com/validate"
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/spotdemo4/trevstack/server/internal/auth"
|
||||
userv1 "github.com/spotdemo4/trevstack/server/internal/connect/user/v1"
|
||||
"github.com/spotdemo4/trevstack/server/internal/connect/user/v1/userv1connect"
|
||||
"github.com/spotdemo4/trevstack/server/internal/interceptors"
|
||||
@ -20,8 +25,13 @@ import (
|
||||
)
|
||||
|
||||
type AuthHandler struct {
|
||||
db *sqlc.Queries
|
||||
key []byte
|
||||
db *sqlc.Queries
|
||||
webAuthn *webauthn.WebAuthn
|
||||
key []byte
|
||||
name string
|
||||
|
||||
sessions *map[string]*webauthn.SessionData
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (h *AuthHandler) Login(ctx context.Context, req *connect.Request[userv1.LoginRequest]) (*connect.Response[userv1.LoginResponse], error) {
|
||||
@ -40,37 +50,18 @@ func (h *AuthHandler) Login(ctx context.Context, req *connect.Request[userv1.Log
|
||||
return nil, connect.NewError(connect.CodePermissionDenied, errors.New("invalid username or password"))
|
||||
}
|
||||
|
||||
// Generate JWT
|
||||
t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
|
||||
Issuer: "trevstack",
|
||||
Subject: strconv.FormatInt(user.ID, 10),
|
||||
IssuedAt: &jwt.NumericDate{
|
||||
Time: time.Now(),
|
||||
},
|
||||
ExpiresAt: &jwt.NumericDate{
|
||||
Time: time.Now().Add(time.Hour * 24),
|
||||
},
|
||||
})
|
||||
ss, err := t.SignedString(h.key)
|
||||
// Create JWT
|
||||
token, cookie, err := h.createJWT(user.ID)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
|
||||
// Create cookie
|
||||
cookie := http.Cookie{
|
||||
Name: "token",
|
||||
Value: ss,
|
||||
Path: "/",
|
||||
MaxAge: 86400,
|
||||
HttpOnly: true,
|
||||
Secure: true,
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
}
|
||||
|
||||
// Create response
|
||||
res := connect.NewResponse(&userv1.LoginResponse{
|
||||
Token: ss,
|
||||
Token: token,
|
||||
})
|
||||
res.Header().Set("Set-Cookie", cookie.String())
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
@ -82,7 +73,7 @@ func (h *AuthHandler) SignUp(ctx context.Context, req *connect.Request[userv1.Si
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
} else {
|
||||
return nil, connect.NewError(connect.CodeAlreadyExists, err)
|
||||
return nil, connect.NewError(connect.CodeAlreadyExists, errors.New("user already exists"))
|
||||
}
|
||||
|
||||
// Check if confirmation passwords match
|
||||
@ -98,8 +89,9 @@ func (h *AuthHandler) SignUp(ctx context.Context, req *connect.Request[userv1.Si
|
||||
|
||||
// Create user
|
||||
_, err = h.db.InsertUser(ctx, sqlc.InsertUserParams{
|
||||
Username: req.Msg.Username,
|
||||
Password: string(hash),
|
||||
Username: req.Msg.Username,
|
||||
Password: string(hash),
|
||||
WebauthnID: uuid.New().String(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
@ -123,104 +115,173 @@ func (h *AuthHandler) Logout(_ context.Context, _ *connect.Request[userv1.Logout
|
||||
|
||||
res := connect.NewResponse(&userv1.LogoutResponse{})
|
||||
res.Header().Set("Set-Cookie", cookie.String())
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// func (h *AuthHandler) GetPasskeyIDs(_ context.Context, req *connect.Request[userv1.GetPasskeyIDsRequest]) (*connect.Response[userv1.GetPasskeyIDsResponse], error) {
|
||||
// // Get user
|
||||
// user := models.User{}
|
||||
// if err := h.db.Preload("Passkeys").First(&user, "username = ?", req.Msg.Username).Error; err != nil {
|
||||
// return nil, connect.NewError(connect.CodeNotFound, err)
|
||||
// }
|
||||
func (h *AuthHandler) BeginPasskeyLogin(ctx context.Context, req *connect.Request[userv1.BeginPasskeyLoginRequest]) (*connect.Response[userv1.BeginPasskeyLoginResponse], error) {
|
||||
// Get user
|
||||
pUser, _, err := h.getPasskeyUser(ctx, req.Msg.Username)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeUnauthenticated, err)
|
||||
}
|
||||
|
||||
// // Get IDs
|
||||
// ids := []string{}
|
||||
// for _, passkey := range user.Passkeys {
|
||||
// ids = append(ids, passkey.ID)
|
||||
// }
|
||||
// Get options for user
|
||||
options, session, err := h.webAuthn.BeginLogin(pUser)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// return connect.NewResponse(&userv1.GetPasskeyIDsResponse{
|
||||
// PasskeyIds: ids,
|
||||
// }), nil
|
||||
// }
|
||||
// Turn the options into json
|
||||
optionsJSON, err := json.Marshal(options)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
|
||||
// func (h *AuthHandler) PasskeyLogin(_ context.Context, req *connect.Request[userv1.PasskeyLoginRequest]) (*connect.Response[userv1.PasskeyLoginResponse], error) {
|
||||
// // Get passkey
|
||||
// passkey := models.Passkey{}
|
||||
// if err := h.db.First(&passkey, "id = ?", req.Msg.Id).Error; err != nil {
|
||||
// return nil, connect.NewError(connect.CodeNotFound, err)
|
||||
// }
|
||||
// Set session for validation later
|
||||
h.setSession(req.Msg.Username, session)
|
||||
|
||||
// // create a verifier from a trusted private key
|
||||
// var verifier cose.Verifier
|
||||
// var err error
|
||||
// switch req.Msg.Algorithm {
|
||||
// case -7:
|
||||
// verifier, err = cose.NewVerifier(cose.AlgorithmES256, passkey.PublicKey)
|
||||
return connect.NewResponse(&userv1.BeginPasskeyLoginResponse{
|
||||
OptionsJson: string(optionsJSON),
|
||||
}), nil
|
||||
}
|
||||
|
||||
// case -257:
|
||||
// verifier, err = cose.NewVerifier(cose.AlgorithmRS256, passkey.PublicKey)
|
||||
func (h *AuthHandler) FinishPasskeyLogin(ctx context.Context, req *connect.Request[userv1.FinishPasskeyLoginRequest]) (*connect.Response[userv1.FinishPasskeyLoginResponse], error) {
|
||||
// Get user
|
||||
pUser, userID, err := h.getPasskeyUser(ctx, req.Msg.Username)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// default:
|
||||
// return nil, connect.NewError(connect.CodeInternal, errors.New("decode algorithm not implemented"))
|
||||
// }
|
||||
// if err != nil {
|
||||
// return nil, connect.NewError(connect.CodeInternal, err)
|
||||
// }
|
||||
// Get the session data previously set
|
||||
session, err := h.getSession(req.Msg.Username)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// // create a sign message from a raw signature payload
|
||||
// var msg cose.Sign1Message
|
||||
// if err = msg.UnmarshalCBOR(req.Msg.Signature); err != nil {
|
||||
// return nil, connect.NewError(connect.CodeInternal, err)
|
||||
// }
|
||||
// Parse the attestation response
|
||||
parsedResponse, err := protocol.ParseCredentialRequestResponseBytes([]byte(req.Msg.Attestation))
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// // Validate passkey
|
||||
// err = msg.Verify(nil, verifier)
|
||||
// if err != nil {
|
||||
// return nil, connect.NewError(connect.CodeUnauthenticated, err)
|
||||
// }
|
||||
// Validate the login
|
||||
credential, err := h.webAuthn.ValidateLogin(pUser, *session, parsedResponse)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeUnauthenticated, err)
|
||||
}
|
||||
|
||||
// // Generate JWT
|
||||
// t := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
|
||||
// Issuer: "trevstack",
|
||||
// Subject: strconv.FormatUint(uint64(passkey.UserID), 10),
|
||||
// IssuedAt: &jwt.NumericDate{
|
||||
// Time: time.Now(),
|
||||
// },
|
||||
// ExpiresAt: &jwt.NumericDate{
|
||||
// Time: time.Now().Add(time.Hour * 24),
|
||||
// },
|
||||
// })
|
||||
// ss, err := t.SignedString(h.key)
|
||||
// if err != nil {
|
||||
// return nil, connect.NewError(connect.CodeInternal, err)
|
||||
// }
|
||||
// Update the credential in the database
|
||||
lastUsed := time.Now()
|
||||
signCount := int64(credential.Authenticator.SignCount)
|
||||
err = h.db.UpdateCredential(ctx, sqlc.UpdateCredentialParams{
|
||||
// set
|
||||
LastUsed: &lastUsed,
|
||||
SignCount: &signCount,
|
||||
|
||||
// // Create cookie
|
||||
// cookie := http.Cookie{
|
||||
// Name: "token",
|
||||
// Value: ss,
|
||||
// Path: "/",
|
||||
// MaxAge: 86400,
|
||||
// HttpOnly: true,
|
||||
// Secure: true,
|
||||
// SameSite: http.SameSiteStrictMode,
|
||||
// }
|
||||
// where
|
||||
ID: string(credential.ID),
|
||||
UserID: userID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
|
||||
// res := connect.NewResponse(&userv1.PasskeyLoginResponse{
|
||||
// Token: ss,
|
||||
// })
|
||||
// res.Header().Set("Set-Cookie", cookie.String())
|
||||
// return res, nil
|
||||
// }
|
||||
// Create JWT
|
||||
token, cookie, err := h.createJWT(userID)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
|
||||
func NewAuthHandler(db *sqlc.Queries, key string) (string, http.Handler) {
|
||||
interceptors := connect.WithInterceptors(interceptors.NewRateLimitInterceptor(key))
|
||||
// Create response
|
||||
resp := connect.NewResponse(&userv1.FinishPasskeyLoginResponse{
|
||||
Token: token,
|
||||
})
|
||||
resp.Header().Set("Set-Cookie", cookie.String())
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (h *AuthHandler) getPasskeyUser(ctx context.Context, username string) (*auth.User, int64, error) {
|
||||
user, err := h.db.GetUserbyUsername(ctx, username)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
creds, err := h.db.GetCredentials(ctx, user.ID)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
webCreds := auth.NewCreds(creds)
|
||||
webUser := auth.NewUser(user.WebauthnID, user.Username, webCreds)
|
||||
|
||||
return &webUser, user.ID, nil
|
||||
}
|
||||
|
||||
func (h *AuthHandler) getSession(username string) (*webauthn.SessionData, error) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
session, ok := (*h.sessions)[username]
|
||||
if !ok {
|
||||
return nil, errors.New("session does not exist")
|
||||
}
|
||||
|
||||
delete(*h.sessions, username)
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func (h *AuthHandler) setSession(username string, data *webauthn.SessionData) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
(*h.sessions)[username] = data
|
||||
}
|
||||
|
||||
func (h *AuthHandler) createJWT(userid int64) (string, *http.Cookie, error) {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{
|
||||
Issuer: h.name,
|
||||
Subject: strconv.Itoa(int(userid)),
|
||||
IssuedAt: &jwt.NumericDate{
|
||||
Time: time.Now(),
|
||||
},
|
||||
ExpiresAt: &jwt.NumericDate{
|
||||
Time: time.Now().Add(time.Hour * 24),
|
||||
},
|
||||
})
|
||||
|
||||
tokenString, err := token.SignedString(h.key)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
cookie := http.Cookie{
|
||||
Name: "token",
|
||||
Value: tokenString,
|
||||
Path: "/",
|
||||
MaxAge: 86400,
|
||||
HttpOnly: true,
|
||||
Secure: true,
|
||||
SameSite: http.SameSiteStrictMode,
|
||||
}
|
||||
|
||||
return tokenString, &cookie, nil
|
||||
}
|
||||
|
||||
func NewAuthHandler(vi *validate.Interceptor, db *sqlc.Queries, webauth *webauthn.WebAuthn, name string, key string) (string, http.Handler) {
|
||||
interceptors := connect.WithInterceptors(vi, interceptors.NewRateLimitInterceptor(key))
|
||||
|
||||
sd := map[string]*webauthn.SessionData{}
|
||||
return userv1connect.NewAuthServiceHandler(
|
||||
&AuthHandler{
|
||||
db: db,
|
||||
key: []byte(key),
|
||||
db: db,
|
||||
webAuthn: webauth,
|
||||
name: name,
|
||||
key: []byte(key),
|
||||
|
||||
sessions: &sd,
|
||||
mu: sync.Mutex{},
|
||||
},
|
||||
interceptors,
|
||||
)
|
||||
|
@ -3,13 +3,19 @@ package user
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"connectrpc.com/validate"
|
||||
"github.com/go-webauthn/webauthn/protocol"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/spotdemo4/trevstack/server/internal/auth"
|
||||
userv1 "github.com/spotdemo4/trevstack/server/internal/connect/user/v1"
|
||||
"github.com/spotdemo4/trevstack/server/internal/connect/user/v1/userv1connect"
|
||||
"github.com/spotdemo4/trevstack/server/internal/interceptors"
|
||||
@ -27,8 +33,12 @@ func userToConnect(item sqlc.User) *userv1.User {
|
||||
}
|
||||
|
||||
type Handler struct {
|
||||
db *sqlc.Queries
|
||||
key []byte
|
||||
db *sqlc.Queries
|
||||
webAuthn *webauthn.WebAuthn
|
||||
key []byte
|
||||
|
||||
sessions *map[int64]*webauthn.SessionData
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func (h *Handler) GetUser(ctx context.Context, _ *connect.Request[userv1.GetUserRequest]) (*connect.Response[userv1.GetUserResponse], error) {
|
||||
@ -200,75 +210,149 @@ func (h *Handler) UpdateProfilePicture(ctx context.Context, req *connect.Request
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// func (h *Handler) BeginPasskeyRegistration(ctx context.Context, req *connect.Request[userv1.BeginPasskeyRegistrationRequest]) (*connect.Response[userv1.BeginPasskeyRegistrationResponse], error) {
|
||||
// // Get user ID from context
|
||||
// userid, ok := interceptors.GetUserContext(ctx)
|
||||
// if !ok {
|
||||
// return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("user not authenticated"))
|
||||
// }
|
||||
func (h *Handler) BeginPasskeyRegistration(ctx context.Context, _ *connect.Request[userv1.BeginPasskeyRegistrationRequest]) (*connect.Response[userv1.BeginPasskeyRegistrationResponse], error) {
|
||||
userid, ok := interceptors.GetUserContext(ctx)
|
||||
if !ok {
|
||||
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("unauthenticated"))
|
||||
}
|
||||
|
||||
// // Get user
|
||||
// user := models.User{}
|
||||
// if err := h.db.First(&user, "id = ?", userid).Error; err != nil {
|
||||
// return nil, connect.NewError(connect.CodeInternal, err)
|
||||
// }
|
||||
// Get user
|
||||
pUser, err := h.getPasskeyUser(ctx, userid)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// return connect.NewResponse(&userv1.BeginPasskeyRegistrationResponse{}), nil
|
||||
// }
|
||||
// Get options for user
|
||||
options, session, err := h.webAuthn.BeginRegistration(pUser)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// func (h *Handler) FinishPasskeyRegistration(ctx context.Context, req *connect.Request[userv1.FinishPasskeyRegistrationRequest]) (*connect.Response[userv1.FinishPasskeyRegistrationResponse], error) {
|
||||
// // Get user ID from context
|
||||
// userid, ok := interceptors.GetUserContext(ctx)
|
||||
// if !ok {
|
||||
// return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("user not authenticated"))
|
||||
// }
|
||||
// Turn options into json
|
||||
optionsJSON, err := json.Marshal(options)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
|
||||
// // Get user
|
||||
// user := models.User{}
|
||||
// if err := h.db.First(&user, "id = ?", userid).Error; err != nil {
|
||||
// return nil, connect.NewError(connect.CodeInternal, err)
|
||||
// }
|
||||
// Set session for validation later
|
||||
h.setSession(userid, session)
|
||||
|
||||
// return connect.NewResponse(&userv1.FinishPasskeyRegistrationResponse{}), nil
|
||||
// }
|
||||
return connect.NewResponse(&userv1.BeginPasskeyRegistrationResponse{
|
||||
OptionsJson: string(optionsJSON),
|
||||
}), nil
|
||||
}
|
||||
|
||||
// func BeginRegistration(ctx context.Context) error {
|
||||
// userid, ok := interceptors.GetUserContext(ctx)
|
||||
// if !ok {
|
||||
// return nil
|
||||
// }
|
||||
func (h *Handler) FinishPasskeyRegistration(ctx context.Context, req *connect.Request[userv1.FinishPasskeyRegistrationRequest]) (*connect.Response[userv1.FinishPasskeyRegistrationResponse], error) {
|
||||
userid, ok := interceptors.GetUserContext(ctx)
|
||||
if !ok {
|
||||
return nil, connect.NewError(connect.CodeUnauthenticated, errors.New("unauthenticated"))
|
||||
}
|
||||
|
||||
// wconfig := &webauthn.Config{
|
||||
// RPDisplayName: "Go Webauthn", // Display Name for your site
|
||||
// RPID: "go-webauthn.local", // Generally the FQDN for your site
|
||||
// RPOrigins: []string{"https://login.go-webauthn.local"}, // The origin URLs allowed for WebAuthn requests
|
||||
// }
|
||||
// webAuthn, err := webauthn.New(wconfig)
|
||||
// if err != nil {
|
||||
// return nil
|
||||
// }
|
||||
// Get user
|
||||
pUser, err := h.getPasskeyUser(ctx, userid)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// var user webauthn.User
|
||||
// user.WebAuthnCredentials()
|
||||
// Get the session data previously set
|
||||
session, err := h.getSession(userid)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// var cred webauthn.Credential
|
||||
// cred.Verify()
|
||||
// Parse the attestation response
|
||||
parsedResponse, err := protocol.ParseCredentialCreationResponseBytes([]byte(req.Msg.Attestation))
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInvalidArgument, err)
|
||||
}
|
||||
|
||||
// var test metadata.Provider
|
||||
// test.
|
||||
// Create the credential
|
||||
credential, err := h.webAuthn.CreateCredential(pUser, *session, parsedResponse)
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
|
||||
// options, session, err := webAuthn.BeginRegistration(user)
|
||||
transports := transportsToString(credential.Transport)
|
||||
|
||||
// return nil
|
||||
// }
|
||||
// Save the credential
|
||||
err = h.db.InsertCredential(ctx, sqlc.InsertCredentialParams{
|
||||
CredID: string(credential.ID),
|
||||
CredPublicKey: credential.PublicKey,
|
||||
SignCount: int64(credential.Authenticator.SignCount),
|
||||
Transports: &transports,
|
||||
UserVerified: &credential.Flags.UserVerified,
|
||||
BackupEligible: &credential.Flags.BackupEligible,
|
||||
BackupState: &credential.Flags.BackupState,
|
||||
AttestationObject: credential.Attestation.Object,
|
||||
AttestationClientData: credential.Attestation.ClientDataJSON,
|
||||
CreatedAt: time.Now(),
|
||||
LastUsed: time.Now(),
|
||||
UserID: userid,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, connect.NewError(connect.CodeInternal, err)
|
||||
}
|
||||
|
||||
func NewHandler(db *sqlc.Queries, key string) (string, http.Handler) {
|
||||
interceptors := connect.WithInterceptors(interceptors.NewAuthInterceptor(key))
|
||||
return connect.NewResponse(&userv1.FinishPasskeyRegistrationResponse{}), nil
|
||||
}
|
||||
|
||||
func (h *Handler) getPasskeyUser(ctx context.Context, userid int64) (*auth.User, error) {
|
||||
user, err := h.db.GetUser(ctx, userid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
creds, err := h.db.GetCredentials(ctx, user.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
webCreds := auth.NewCreds(creds)
|
||||
webUser := auth.NewUser(user.WebauthnID, user.Username, webCreds)
|
||||
|
||||
return &webUser, nil
|
||||
}
|
||||
|
||||
func (h *Handler) getSession(userid int64) (*webauthn.SessionData, error) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
session, ok := (*h.sessions)[userid]
|
||||
if !ok {
|
||||
return nil, errors.New("session does not exist")
|
||||
}
|
||||
|
||||
delete(*h.sessions, userid)
|
||||
return session, nil
|
||||
}
|
||||
|
||||
func (h *Handler) setSession(userid int64, data *webauthn.SessionData) {
|
||||
h.mu.Lock()
|
||||
defer h.mu.Unlock()
|
||||
|
||||
(*h.sessions)[userid] = data
|
||||
}
|
||||
|
||||
func transportsToString(transports []protocol.AuthenticatorTransport) string {
|
||||
s := ""
|
||||
for _, transport := range transports {
|
||||
s += string(transport) + ", "
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func NewHandler(vi *validate.Interceptor, db *sqlc.Queries, webauth *webauthn.WebAuthn, key string) (string, http.Handler) {
|
||||
interceptors := connect.WithInterceptors(interceptors.NewAuthInterceptor(key), vi)
|
||||
|
||||
sd := map[int64]*webauthn.SessionData{}
|
||||
return userv1connect.NewUserServiceHandler(
|
||||
&Handler{
|
||||
db: db,
|
||||
key: []byte(key),
|
||||
db: db,
|
||||
webAuthn: webauth,
|
||||
key: []byte(key),
|
||||
|
||||
sessions: &sd,
|
||||
mu: sync.Mutex{},
|
||||
},
|
||||
interceptors,
|
||||
)
|
||||
|
@ -96,7 +96,13 @@ func (i *AuthInterceptor) WrapUnary(next connect.UnaryFunc) connect.UnaryFunc {
|
||||
ctx, err = newUserContext(ctx, subject)
|
||||
if err == nil {
|
||||
return next(ctx, req)
|
||||
} else {
|
||||
log.Println("huh")
|
||||
log.Println(err)
|
||||
}
|
||||
} else {
|
||||
log.Println("what")
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -176,7 +182,7 @@ func getCookies(rawCookies string) []*http.Cookie {
|
||||
}
|
||||
|
||||
func validateToken(tokenString string, key string) (subject string, err error) {
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) {
|
||||
// Don't forget to validate the alg is what you expect:
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
|
221
server/internal/sqlc/credential.sql.go
Normal file
221
server/internal/sqlc/credential.sql.go
Normal file
@ -0,0 +1,221 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.29.0
|
||||
// source: credential.sql
|
||||
|
||||
package sqlc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
const deleteCredential = `-- name: DeleteCredential :exec
|
||||
DELETE FROM credential
|
||||
WHERE
|
||||
cred_id = ?1
|
||||
AND
|
||||
user_id = ?2
|
||||
`
|
||||
|
||||
type DeleteCredentialParams struct {
|
||||
ID string
|
||||
UserID int64
|
||||
}
|
||||
|
||||
func (q *Queries) DeleteCredential(ctx context.Context, arg DeleteCredentialParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteCredential, arg.ID, arg.UserID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getCredential = `-- name: GetCredential :one
|
||||
SELECT
|
||||
cred_id,
|
||||
cred_public_key,
|
||||
sign_count,
|
||||
transports,
|
||||
user_verified,
|
||||
backup_eligible,
|
||||
backup_state,
|
||||
attestation_object,
|
||||
attestation_client_data,
|
||||
created_at,
|
||||
last_used,
|
||||
user_id
|
||||
FROM credential
|
||||
WHERE
|
||||
cred_id = ?1
|
||||
AND
|
||||
user_id = ?2
|
||||
LIMIT 1
|
||||
`
|
||||
|
||||
type GetCredentialParams struct {
|
||||
ID string
|
||||
UserID int64
|
||||
}
|
||||
|
||||
func (q *Queries) GetCredential(ctx context.Context, arg GetCredentialParams) (Credential, error) {
|
||||
row := q.db.QueryRowContext(ctx, getCredential, arg.ID, arg.UserID)
|
||||
var i Credential
|
||||
err := row.Scan(
|
||||
&i.CredID,
|
||||
&i.CredPublicKey,
|
||||
&i.SignCount,
|
||||
&i.Transports,
|
||||
&i.UserVerified,
|
||||
&i.BackupEligible,
|
||||
&i.BackupState,
|
||||
&i.AttestationObject,
|
||||
&i.AttestationClientData,
|
||||
&i.CreatedAt,
|
||||
&i.LastUsed,
|
||||
&i.UserID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getCredentials = `-- name: GetCredentials :many
|
||||
SELECT
|
||||
cred_id,
|
||||
cred_public_key,
|
||||
sign_count,
|
||||
transports,
|
||||
user_verified,
|
||||
backup_eligible,
|
||||
backup_state,
|
||||
attestation_object,
|
||||
attestation_client_data,
|
||||
created_at,
|
||||
last_used,
|
||||
user_id
|
||||
FROM credential
|
||||
WHERE user_id = ?1
|
||||
`
|
||||
|
||||
func (q *Queries) GetCredentials(ctx context.Context, userID int64) ([]Credential, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getCredentials, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []Credential
|
||||
for rows.Next() {
|
||||
var i Credential
|
||||
if err := rows.Scan(
|
||||
&i.CredID,
|
||||
&i.CredPublicKey,
|
||||
&i.SignCount,
|
||||
&i.Transports,
|
||||
&i.UserVerified,
|
||||
&i.BackupEligible,
|
||||
&i.BackupState,
|
||||
&i.AttestationObject,
|
||||
&i.AttestationClientData,
|
||||
&i.CreatedAt,
|
||||
&i.LastUsed,
|
||||
&i.UserID,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const insertCredential = `-- name: InsertCredential :exec
|
||||
INSERT INTO credential (
|
||||
cred_id,
|
||||
cred_public_key,
|
||||
sign_count,
|
||||
transports,
|
||||
user_verified,
|
||||
backup_eligible,
|
||||
backup_state,
|
||||
attestation_object,
|
||||
attestation_client_data,
|
||||
created_at,
|
||||
last_used,
|
||||
user_id
|
||||
) VALUES (
|
||||
?1,
|
||||
?2,
|
||||
?3,
|
||||
?4,
|
||||
?5,
|
||||
?6,
|
||||
?7,
|
||||
?8,
|
||||
?9,
|
||||
?10,
|
||||
?11,
|
||||
?12
|
||||
)
|
||||
`
|
||||
|
||||
type InsertCredentialParams struct {
|
||||
CredID string
|
||||
CredPublicKey []byte
|
||||
SignCount int64
|
||||
Transports *string
|
||||
UserVerified *bool
|
||||
BackupEligible *bool
|
||||
BackupState *bool
|
||||
AttestationObject []byte
|
||||
AttestationClientData []byte
|
||||
CreatedAt time.Time
|
||||
LastUsed time.Time
|
||||
UserID int64
|
||||
}
|
||||
|
||||
func (q *Queries) InsertCredential(ctx context.Context, arg InsertCredentialParams) error {
|
||||
_, err := q.db.ExecContext(ctx, insertCredential,
|
||||
arg.CredID,
|
||||
arg.CredPublicKey,
|
||||
arg.SignCount,
|
||||
arg.Transports,
|
||||
arg.UserVerified,
|
||||
arg.BackupEligible,
|
||||
arg.BackupState,
|
||||
arg.AttestationObject,
|
||||
arg.AttestationClientData,
|
||||
arg.CreatedAt,
|
||||
arg.LastUsed,
|
||||
arg.UserID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateCredential = `-- name: UpdateCredential :exec
|
||||
UPDATE credential
|
||||
SET
|
||||
last_used = COALESCE(?1, last_used),
|
||||
sign_count = COALESCE(?2, sign_count)
|
||||
WHERE
|
||||
cred_id = ?3
|
||||
AND
|
||||
user_id = ?4
|
||||
`
|
||||
|
||||
type UpdateCredentialParams struct {
|
||||
LastUsed *time.Time
|
||||
SignCount *int64
|
||||
ID string
|
||||
UserID int64
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateCredential(ctx context.Context, arg UpdateCredentialParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateCredential,
|
||||
arg.LastUsed,
|
||||
arg.SignCount,
|
||||
arg.ID,
|
||||
arg.UserID,
|
||||
)
|
||||
return err
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.28.0
|
||||
// sqlc v1.29.0
|
||||
|
||||
package sqlc
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.28.0
|
||||
// sqlc v1.29.0
|
||||
// source: file.sql
|
||||
|
||||
package sqlc
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.28.0
|
||||
// sqlc v1.29.0
|
||||
// source: item.sql
|
||||
|
||||
package sqlc
|
||||
@ -85,6 +85,7 @@ WHERE
|
||||
AND
|
||||
(added <= ?4 OR ?4 IS NULL)
|
||||
)
|
||||
ORDER BY added DESC
|
||||
LIMIT
|
||||
?6
|
||||
OFFSET
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.28.0
|
||||
// sqlc v1.29.0
|
||||
|
||||
package sqlc
|
||||
|
||||
@ -8,6 +8,21 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Credential struct {
|
||||
CredID string
|
||||
CredPublicKey []byte
|
||||
SignCount int64
|
||||
Transports *string
|
||||
UserVerified *bool
|
||||
BackupEligible *bool
|
||||
BackupState *bool
|
||||
AttestationObject []byte
|
||||
AttestationClientData []byte
|
||||
CreatedAt time.Time
|
||||
LastUsed time.Time
|
||||
UserID int64
|
||||
}
|
||||
|
||||
type File struct {
|
||||
ID int64
|
||||
Name string
|
||||
@ -34,4 +49,5 @@ type User struct {
|
||||
Username string
|
||||
Password string
|
||||
ProfilePictureID *int64
|
||||
WebauthnID string
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.28.0
|
||||
// sqlc v1.29.0
|
||||
// source: user.sql
|
||||
|
||||
package sqlc
|
||||
@ -24,7 +24,8 @@ SELECT
|
||||
id,
|
||||
username,
|
||||
password,
|
||||
profile_picture_id
|
||||
profile_picture_id,
|
||||
webauthn_id
|
||||
FROM user
|
||||
WHERE
|
||||
id = ?1
|
||||
@ -39,6 +40,7 @@ func (q *Queries) GetUser(ctx context.Context, id int64) (User, error) {
|
||||
&i.Username,
|
||||
&i.Password,
|
||||
&i.ProfilePictureID,
|
||||
&i.WebauthnID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@ -48,7 +50,8 @@ SELECT
|
||||
id,
|
||||
username,
|
||||
password,
|
||||
profile_picture_id
|
||||
profile_picture_id,
|
||||
webauthn_id
|
||||
FROM user
|
||||
WHERE
|
||||
username = ?1
|
||||
@ -63,6 +66,7 @@ func (q *Queries) GetUserbyUsername(ctx context.Context, username string) (User,
|
||||
&i.Username,
|
||||
&i.Password,
|
||||
&i.ProfilePictureID,
|
||||
&i.WebauthnID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@ -70,21 +74,24 @@ func (q *Queries) GetUserbyUsername(ctx context.Context, username string) (User,
|
||||
const insertUser = `-- name: InsertUser :one
|
||||
INSERT INTO user (
|
||||
username,
|
||||
password
|
||||
password,
|
||||
webauthn_id
|
||||
) VALUES (
|
||||
?1,
|
||||
?2
|
||||
?2,
|
||||
?3
|
||||
)
|
||||
RETURNING id
|
||||
`
|
||||
|
||||
type InsertUserParams struct {
|
||||
Username string
|
||||
Password string
|
||||
Username string
|
||||
Password string
|
||||
WebauthnID string
|
||||
}
|
||||
|
||||
func (q *Queries) InsertUser(ctx context.Context, arg InsertUserParams) (int64, error) {
|
||||
row := q.db.QueryRowContext(ctx, insertUser, arg.Username, arg.Password)
|
||||
row := q.db.QueryRowContext(ctx, insertUser, arg.Username, arg.Password, arg.WebauthnID)
|
||||
var id int64
|
||||
err := row.Scan(&id)
|
||||
return id, err
|
||||
|
Reference in New Issue
Block a user