diff --git a/api/proto/user/v1/auth.pb.go b/api/proto/user/v1/auth.pb.go index 3c178cc3..81739263 100644 --- a/api/proto/user/v1/auth.pb.go +++ b/api/proto/user/v1/auth.pb.go @@ -280,6 +280,205 @@ func (x *LoginThirdPartyRequest) GetCampaign() string { return "" } +// RegisterThirdPartyRequest 是资料页完成时使用的三方注册入口。 +// 它不会先返回 profile_required 登录态,而是在服务端一次性校验 credential、写用户资料并签发完整 session。 +type RegisterThirdPartyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"` + Provider string `protobuf:"bytes,2,opt,name=provider,proto3" json:"provider,omitempty"` + Credential string `protobuf:"bytes,3,opt,name=credential,proto3" json:"credential,omitempty"` + DeviceId string `protobuf:"bytes,4,opt,name=device_id,json=deviceId,proto3" json:"device_id,omitempty"` + Username string `protobuf:"bytes,5,opt,name=username,proto3" json:"username,omitempty"` + Avatar string `protobuf:"bytes,6,opt,name=avatar,proto3" json:"avatar,omitempty"` + Gender string `protobuf:"bytes,7,opt,name=gender,proto3" json:"gender,omitempty"` + Country string `protobuf:"bytes,8,opt,name=country,proto3" json:"country,omitempty"` + InviteCode string `protobuf:"bytes,9,opt,name=invite_code,json=inviteCode,proto3" json:"invite_code,omitempty"` + Device string `protobuf:"bytes,10,opt,name=device,proto3" json:"device,omitempty"` + OsVersion string `protobuf:"bytes,11,opt,name=os_version,json=osVersion,proto3" json:"os_version,omitempty"` + Birth string `protobuf:"bytes,12,opt,name=birth,proto3" json:"birth,omitempty"` + AppVersion string `protobuf:"bytes,13,opt,name=app_version,json=appVersion,proto3" json:"app_version,omitempty"` + BuildNumber string `protobuf:"bytes,14,opt,name=build_number,json=buildNumber,proto3" json:"build_number,omitempty"` + Source string `protobuf:"bytes,15,opt,name=source,proto3" json:"source,omitempty"` + InstallChannel string `protobuf:"bytes,16,opt,name=install_channel,json=installChannel,proto3" json:"install_channel,omitempty"` + Campaign string `protobuf:"bytes,17,opt,name=campaign,proto3" json:"campaign,omitempty"` + Platform string `protobuf:"bytes,18,opt,name=platform,proto3" json:"platform,omitempty"` + Language string `protobuf:"bytes,19,opt,name=language,proto3" json:"language,omitempty"` + Timezone string `protobuf:"bytes,20,opt,name=timezone,proto3" json:"timezone,omitempty"` +} + +func (x *RegisterThirdPartyRequest) Reset() { + *x = RegisterThirdPartyRequest{} + mi := &file_proto_user_v1_auth_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *RegisterThirdPartyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterThirdPartyRequest) ProtoMessage() {} + +func (x *RegisterThirdPartyRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_user_v1_auth_proto_msgTypes[2] + 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 RegisterThirdPartyRequest.ProtoReflect.Descriptor instead. +func (*RegisterThirdPartyRequest) Descriptor() ([]byte, []int) { + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{2} +} + +func (x *RegisterThirdPartyRequest) GetMeta() *RequestMeta { + if x != nil { + return x.Meta + } + return nil +} + +func (x *RegisterThirdPartyRequest) GetProvider() string { + if x != nil { + return x.Provider + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetCredential() string { + if x != nil { + return x.Credential + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetDeviceId() string { + if x != nil { + return x.DeviceId + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetAvatar() string { + if x != nil { + return x.Avatar + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetGender() string { + if x != nil { + return x.Gender + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetCountry() string { + if x != nil { + return x.Country + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetInviteCode() string { + if x != nil { + return x.InviteCode + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetDevice() string { + if x != nil { + return x.Device + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetOsVersion() string { + if x != nil { + return x.OsVersion + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetBirth() string { + if x != nil { + return x.Birth + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetAppVersion() string { + if x != nil { + return x.AppVersion + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetBuildNumber() string { + if x != nil { + return x.BuildNumber + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetSource() string { + if x != nil { + return x.Source + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetInstallChannel() string { + if x != nil { + return x.InstallChannel + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetCampaign() string { + if x != nil { + return x.Campaign + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetPlatform() string { + if x != nil { + return x.Platform + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetLanguage() string { + if x != nil { + return x.Language + } + return "" +} + +func (x *RegisterThirdPartyRequest) GetTimezone() string { + if x != nil { + return x.Timezone + } + return "" +} + // AuthResponse 返回标准认证令牌。 type AuthResponse struct { state protoimpl.MessageState @@ -294,7 +493,7 @@ type AuthResponse struct { func (x *AuthResponse) Reset() { *x = AuthResponse{} - mi := &file_proto_user_v1_auth_proto_msgTypes[2] + mi := &file_proto_user_v1_auth_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -306,7 +505,7 @@ func (x *AuthResponse) String() string { func (*AuthResponse) ProtoMessage() {} func (x *AuthResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[2] + mi := &file_proto_user_v1_auth_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -319,7 +518,7 @@ func (x *AuthResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AuthResponse.ProtoReflect.Descriptor instead. func (*AuthResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{2} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{3} } func (x *AuthResponse) GetToken() *AuthToken { @@ -363,7 +562,7 @@ type SetPasswordRequest struct { func (x *SetPasswordRequest) Reset() { *x = SetPasswordRequest{} - mi := &file_proto_user_v1_auth_proto_msgTypes[3] + mi := &file_proto_user_v1_auth_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -375,7 +574,7 @@ func (x *SetPasswordRequest) String() string { func (*SetPasswordRequest) ProtoMessage() {} func (x *SetPasswordRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[3] + mi := &file_proto_user_v1_auth_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -388,7 +587,7 @@ func (x *SetPasswordRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetPasswordRequest.ProtoReflect.Descriptor instead. func (*SetPasswordRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{3} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{4} } func (x *SetPasswordRequest) GetMeta() *RequestMeta { @@ -423,7 +622,7 @@ type SetPasswordResponse struct { func (x *SetPasswordResponse) Reset() { *x = SetPasswordResponse{} - mi := &file_proto_user_v1_auth_proto_msgTypes[4] + mi := &file_proto_user_v1_auth_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -435,7 +634,7 @@ func (x *SetPasswordResponse) String() string { func (*SetPasswordResponse) ProtoMessage() {} func (x *SetPasswordResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[4] + mi := &file_proto_user_v1_auth_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -448,7 +647,7 @@ func (x *SetPasswordResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SetPasswordResponse.ProtoReflect.Descriptor instead. func (*SetPasswordResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{4} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{5} } func (x *SetPasswordResponse) GetPasswordSet() bool { @@ -487,7 +686,7 @@ type QuickCreateAccountRequest struct { func (x *QuickCreateAccountRequest) Reset() { *x = QuickCreateAccountRequest{} - mi := &file_proto_user_v1_auth_proto_msgTypes[5] + mi := &file_proto_user_v1_auth_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -499,7 +698,7 @@ func (x *QuickCreateAccountRequest) String() string { func (*QuickCreateAccountRequest) ProtoMessage() {} func (x *QuickCreateAccountRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[5] + mi := &file_proto_user_v1_auth_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -512,7 +711,7 @@ func (x *QuickCreateAccountRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use QuickCreateAccountRequest.ProtoReflect.Descriptor instead. func (*QuickCreateAccountRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{5} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{6} } func (x *QuickCreateAccountRequest) GetMeta() *RequestMeta { @@ -660,7 +859,7 @@ type QuickCreateAccountResponse struct { func (x *QuickCreateAccountResponse) Reset() { *x = QuickCreateAccountResponse{} - mi := &file_proto_user_v1_auth_proto_msgTypes[6] + mi := &file_proto_user_v1_auth_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -672,7 +871,7 @@ func (x *QuickCreateAccountResponse) String() string { func (*QuickCreateAccountResponse) ProtoMessage() {} func (x *QuickCreateAccountResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[6] + mi := &file_proto_user_v1_auth_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -685,7 +884,7 @@ func (x *QuickCreateAccountResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use QuickCreateAccountResponse.ProtoReflect.Descriptor instead. func (*QuickCreateAccountResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{6} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{7} } func (x *QuickCreateAccountResponse) GetToken() *AuthToken { @@ -714,7 +913,7 @@ type RefreshTokenRequest struct { func (x *RefreshTokenRequest) Reset() { *x = RefreshTokenRequest{} - mi := &file_proto_user_v1_auth_proto_msgTypes[7] + mi := &file_proto_user_v1_auth_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -726,7 +925,7 @@ func (x *RefreshTokenRequest) String() string { func (*RefreshTokenRequest) ProtoMessage() {} func (x *RefreshTokenRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[7] + mi := &file_proto_user_v1_auth_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -739,7 +938,7 @@ func (x *RefreshTokenRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RefreshTokenRequest.ProtoReflect.Descriptor instead. func (*RefreshTokenRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{7} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{8} } func (x *RefreshTokenRequest) GetMeta() *RequestMeta { @@ -767,7 +966,7 @@ type RefreshTokenResponse struct { func (x *RefreshTokenResponse) Reset() { *x = RefreshTokenResponse{} - mi := &file_proto_user_v1_auth_proto_msgTypes[8] + mi := &file_proto_user_v1_auth_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -779,7 +978,7 @@ func (x *RefreshTokenResponse) String() string { func (*RefreshTokenResponse) ProtoMessage() {} func (x *RefreshTokenResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[8] + mi := &file_proto_user_v1_auth_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -792,7 +991,7 @@ func (x *RefreshTokenResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RefreshTokenResponse.ProtoReflect.Descriptor instead. func (*RefreshTokenResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{8} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{9} } func (x *RefreshTokenResponse) GetToken() *AuthToken { @@ -815,7 +1014,7 @@ type LogoutRequest struct { func (x *LogoutRequest) Reset() { *x = LogoutRequest{} - mi := &file_proto_user_v1_auth_proto_msgTypes[9] + mi := &file_proto_user_v1_auth_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -827,7 +1026,7 @@ func (x *LogoutRequest) String() string { func (*LogoutRequest) ProtoMessage() {} func (x *LogoutRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[9] + mi := &file_proto_user_v1_auth_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -840,7 +1039,7 @@ func (x *LogoutRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use LogoutRequest.ProtoReflect.Descriptor instead. func (*LogoutRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{9} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{10} } func (x *LogoutRequest) GetMeta() *RequestMeta { @@ -875,7 +1074,7 @@ type LogoutResponse struct { func (x *LogoutResponse) Reset() { *x = LogoutResponse{} - mi := &file_proto_user_v1_auth_proto_msgTypes[10] + mi := &file_proto_user_v1_auth_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -887,7 +1086,7 @@ func (x *LogoutResponse) String() string { func (*LogoutResponse) ProtoMessage() {} func (x *LogoutResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[10] + mi := &file_proto_user_v1_auth_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -900,7 +1099,7 @@ func (x *LogoutResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use LogoutResponse.ProtoReflect.Descriptor instead. func (*LogoutResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{10} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{11} } func (x *LogoutResponse) GetRevoked() bool { @@ -926,7 +1125,7 @@ type RecordLoginBlockedRequest struct { func (x *RecordLoginBlockedRequest) Reset() { *x = RecordLoginBlockedRequest{} - mi := &file_proto_user_v1_auth_proto_msgTypes[11] + mi := &file_proto_user_v1_auth_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -938,7 +1137,7 @@ func (x *RecordLoginBlockedRequest) String() string { func (*RecordLoginBlockedRequest) ProtoMessage() {} func (x *RecordLoginBlockedRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[11] + mi := &file_proto_user_v1_auth_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -951,7 +1150,7 @@ func (x *RecordLoginBlockedRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RecordLoginBlockedRequest.ProtoReflect.Descriptor instead. func (*RecordLoginBlockedRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{11} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{12} } func (x *RecordLoginBlockedRequest) GetMeta() *RequestMeta { @@ -1007,7 +1206,7 @@ type RecordLoginBlockedResponse struct { func (x *RecordLoginBlockedResponse) Reset() { *x = RecordLoginBlockedResponse{} - mi := &file_proto_user_v1_auth_proto_msgTypes[12] + mi := &file_proto_user_v1_auth_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1019,7 +1218,7 @@ func (x *RecordLoginBlockedResponse) String() string { func (*RecordLoginBlockedResponse) ProtoMessage() {} func (x *RecordLoginBlockedResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[12] + mi := &file_proto_user_v1_auth_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1032,7 +1231,7 @@ func (x *RecordLoginBlockedResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RecordLoginBlockedResponse.ProtoReflect.Descriptor instead. func (*RecordLoginBlockedResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{12} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{13} } func (x *RecordLoginBlockedResponse) GetRecorded() bool { @@ -1054,7 +1253,7 @@ type CheckLoginRiskIPWhitelistRequest struct { func (x *CheckLoginRiskIPWhitelistRequest) Reset() { *x = CheckLoginRiskIPWhitelistRequest{} - mi := &file_proto_user_v1_auth_proto_msgTypes[13] + mi := &file_proto_user_v1_auth_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1066,7 +1265,7 @@ func (x *CheckLoginRiskIPWhitelistRequest) String() string { func (*CheckLoginRiskIPWhitelistRequest) ProtoMessage() {} func (x *CheckLoginRiskIPWhitelistRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[13] + mi := &file_proto_user_v1_auth_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1079,7 +1278,7 @@ func (x *CheckLoginRiskIPWhitelistRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckLoginRiskIPWhitelistRequest.ProtoReflect.Descriptor instead. func (*CheckLoginRiskIPWhitelistRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{13} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{14} } func (x *CheckLoginRiskIPWhitelistRequest) GetMeta() *RequestMeta { @@ -1107,7 +1306,7 @@ type CheckLoginRiskIPWhitelistResponse struct { func (x *CheckLoginRiskIPWhitelistResponse) Reset() { *x = CheckLoginRiskIPWhitelistResponse{} - mi := &file_proto_user_v1_auth_proto_msgTypes[14] + mi := &file_proto_user_v1_auth_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1119,7 +1318,7 @@ func (x *CheckLoginRiskIPWhitelistResponse) String() string { func (*CheckLoginRiskIPWhitelistResponse) ProtoMessage() {} func (x *CheckLoginRiskIPWhitelistResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[14] + mi := &file_proto_user_v1_auth_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1132,7 +1331,7 @@ func (x *CheckLoginRiskIPWhitelistResponse) ProtoReflect() protoreflect.Message // Deprecated: Use CheckLoginRiskIPWhitelistResponse.ProtoReflect.Descriptor instead. func (*CheckLoginRiskIPWhitelistResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{14} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{15} } func (x *CheckLoginRiskIPWhitelistResponse) GetWhitelisted() bool { @@ -1156,7 +1355,7 @@ type RegisterRiskConfig struct { func (x *RegisterRiskConfig) Reset() { *x = RegisterRiskConfig{} - mi := &file_proto_user_v1_auth_proto_msgTypes[15] + mi := &file_proto_user_v1_auth_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1168,7 +1367,7 @@ func (x *RegisterRiskConfig) String() string { func (*RegisterRiskConfig) ProtoMessage() {} func (x *RegisterRiskConfig) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[15] + mi := &file_proto_user_v1_auth_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1181,7 +1380,7 @@ func (x *RegisterRiskConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisterRiskConfig.ProtoReflect.Descriptor instead. func (*RegisterRiskConfig) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{15} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{16} } func (x *RegisterRiskConfig) GetAppCode() string { @@ -1223,7 +1422,7 @@ type GetRegisterRiskConfigRequest struct { func (x *GetRegisterRiskConfigRequest) Reset() { *x = GetRegisterRiskConfigRequest{} - mi := &file_proto_user_v1_auth_proto_msgTypes[16] + mi := &file_proto_user_v1_auth_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1235,7 +1434,7 @@ func (x *GetRegisterRiskConfigRequest) String() string { func (*GetRegisterRiskConfigRequest) ProtoMessage() {} func (x *GetRegisterRiskConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[16] + mi := &file_proto_user_v1_auth_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1248,7 +1447,7 @@ func (x *GetRegisterRiskConfigRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRegisterRiskConfigRequest.ProtoReflect.Descriptor instead. func (*GetRegisterRiskConfigRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{16} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{17} } func (x *GetRegisterRiskConfigRequest) GetMeta() *RequestMeta { @@ -1268,7 +1467,7 @@ type GetRegisterRiskConfigResponse struct { func (x *GetRegisterRiskConfigResponse) Reset() { *x = GetRegisterRiskConfigResponse{} - mi := &file_proto_user_v1_auth_proto_msgTypes[17] + mi := &file_proto_user_v1_auth_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1280,7 +1479,7 @@ func (x *GetRegisterRiskConfigResponse) String() string { func (*GetRegisterRiskConfigResponse) ProtoMessage() {} func (x *GetRegisterRiskConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[17] + mi := &file_proto_user_v1_auth_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1293,7 +1492,7 @@ func (x *GetRegisterRiskConfigResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetRegisterRiskConfigResponse.ProtoReflect.Descriptor instead. func (*GetRegisterRiskConfigResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{17} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{18} } func (x *GetRegisterRiskConfigResponse) GetConfig() *RegisterRiskConfig { @@ -1316,7 +1515,7 @@ type UpdateRegisterRiskConfigRequest struct { func (x *UpdateRegisterRiskConfigRequest) Reset() { *x = UpdateRegisterRiskConfigRequest{} - mi := &file_proto_user_v1_auth_proto_msgTypes[18] + mi := &file_proto_user_v1_auth_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1328,7 +1527,7 @@ func (x *UpdateRegisterRiskConfigRequest) String() string { func (*UpdateRegisterRiskConfigRequest) ProtoMessage() {} func (x *UpdateRegisterRiskConfigRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[18] + mi := &file_proto_user_v1_auth_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1341,7 +1540,7 @@ func (x *UpdateRegisterRiskConfigRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateRegisterRiskConfigRequest.ProtoReflect.Descriptor instead. func (*UpdateRegisterRiskConfigRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{18} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{19} } func (x *UpdateRegisterRiskConfigRequest) GetMeta() *RequestMeta { @@ -1375,7 +1574,7 @@ type UpdateRegisterRiskConfigResponse struct { func (x *UpdateRegisterRiskConfigResponse) Reset() { *x = UpdateRegisterRiskConfigResponse{} - mi := &file_proto_user_v1_auth_proto_msgTypes[19] + mi := &file_proto_user_v1_auth_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1387,7 +1586,7 @@ func (x *UpdateRegisterRiskConfigResponse) String() string { func (*UpdateRegisterRiskConfigResponse) ProtoMessage() {} func (x *UpdateRegisterRiskConfigResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[19] + mi := &file_proto_user_v1_auth_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1400,7 +1599,7 @@ func (x *UpdateRegisterRiskConfigResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateRegisterRiskConfigResponse.ProtoReflect.Descriptor instead. func (*UpdateRegisterRiskConfigResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{19} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{20} } func (x *UpdateRegisterRiskConfigResponse) GetConfig() *RegisterRiskConfig { @@ -1423,7 +1622,7 @@ type AppHeartbeatRequest struct { func (x *AppHeartbeatRequest) Reset() { *x = AppHeartbeatRequest{} - mi := &file_proto_user_v1_auth_proto_msgTypes[20] + mi := &file_proto_user_v1_auth_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1435,7 +1634,7 @@ func (x *AppHeartbeatRequest) String() string { func (*AppHeartbeatRequest) ProtoMessage() {} func (x *AppHeartbeatRequest) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[20] + mi := &file_proto_user_v1_auth_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1448,7 +1647,7 @@ func (x *AppHeartbeatRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AppHeartbeatRequest.ProtoReflect.Descriptor instead. func (*AppHeartbeatRequest) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{20} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{21} } func (x *AppHeartbeatRequest) GetMeta() *RequestMeta { @@ -1486,7 +1685,7 @@ type AppHeartbeatResponse struct { func (x *AppHeartbeatResponse) Reset() { *x = AppHeartbeatResponse{} - mi := &file_proto_user_v1_auth_proto_msgTypes[21] + mi := &file_proto_user_v1_auth_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1498,7 +1697,7 @@ func (x *AppHeartbeatResponse) String() string { func (*AppHeartbeatResponse) ProtoMessage() {} func (x *AppHeartbeatResponse) ProtoReflect() protoreflect.Message { - mi := &file_proto_user_v1_auth_proto_msgTypes[21] + mi := &file_proto_user_v1_auth_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1511,7 +1710,7 @@ func (x *AppHeartbeatResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AppHeartbeatResponse.ProtoReflect.Descriptor instead. func (*AppHeartbeatResponse) Descriptor() ([]byte, []int) { - return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{21} + return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{22} } func (x *AppHeartbeatResponse) GetAccepted() bool { @@ -1596,7 +1795,46 @@ var file_proto_user_v1_auth_proto_rawDesc = []byte{ 0x61, 0x6c, 0x6c, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x61, 0x6d, 0x70, 0x61, 0x69, 0x67, 0x6e, 0x18, 0x14, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x6d, 0x70, 0x61, 0x69, 0x67, 0x6e, 0x22, 0xb8, 0x01, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x61, 0x6d, 0x70, 0x61, 0x69, 0x67, 0x6e, 0x22, 0xed, 0x04, + 0x0a, 0x19, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x68, 0x69, 0x72, 0x64, 0x50, + 0x61, 0x72, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, + 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61, 0x70, + 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x1a, 0x0a, 0x08, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, + 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x64, 0x65, + 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x63, 0x72, 0x65, + 0x64, 0x65, 0x6e, 0x74, 0x69, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x76, 0x69, 0x63, + 0x65, 0x5f, 0x69, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x64, 0x65, 0x76, 0x69, + 0x63, 0x65, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x67, 0x65, 0x6e, 0x64, + 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x67, 0x65, 0x6e, 0x64, 0x65, 0x72, + 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x1f, 0x0a, 0x0b, 0x69, 0x6e, + 0x76, 0x69, 0x74, 0x65, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0a, 0x69, 0x6e, 0x76, 0x69, 0x74, 0x65, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x6f, 0x73, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, + 0x6e, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6f, 0x73, 0x56, 0x65, 0x72, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x69, 0x72, 0x74, 0x68, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x62, 0x69, 0x72, 0x74, 0x68, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, 0x70, 0x5f, + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, + 0x70, 0x70, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, + 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x18, 0x10, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x69, + 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x1a, 0x0a, + 0x08, 0x63, 0x61, 0x6d, 0x70, 0x61, 0x69, 0x67, 0x6e, 0x18, 0x11, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x63, 0x61, 0x6d, 0x70, 0x61, 0x69, 0x67, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6c, 0x61, + 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x6c, 0x61, + 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, + 0x65, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, + 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x18, 0x14, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x69, 0x6d, 0x65, 0x7a, 0x6f, 0x6e, 0x65, 0x22, 0xb8, 0x01, 0x0a, 0x0c, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, @@ -1774,7 +2012,7 @@ var file_proto_user_v1_auth_proto_rawDesc = []byte{ 0x69, 0x6d, 0x65, 0x4d, 0x73, 0x12, 0x2e, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x05, - 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0xcd, 0x08, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0xaa, 0x09, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x51, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, @@ -1786,67 +2024,73 @@ var file_proto_user_v1_auth_proto_rawDesc = []byte{ 0x6e, 0x54, 0x68, 0x69, 0x72, 0x64, 0x50, 0x61, 0x72, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x54, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x21, + 0x5b, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x68, 0x69, 0x72, 0x64, + 0x50, 0x61, 0x72, 0x74, 0x79, 0x12, 0x28, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, + 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x68, + 0x69, 0x72, 0x64, 0x50, 0x61, 0x72, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1b, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, + 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0b, + 0x53, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x2e, 0x68, 0x79, + 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50, + 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, - 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x12, 0x51, 0x75, 0x69, 0x63, 0x6b, 0x43, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x2e, 0x68, 0x79, - 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x69, 0x63, - 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, - 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x69, 0x63, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x57, 0x0a, 0x0c, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, - 0x12, 0x22, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, - 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x06, 0x4c, 0x6f, 0x67, - 0x6f, 0x75, 0x74, 0x12, 0x1c, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, - 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x1d, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, - 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x12, 0x69, 0x0a, 0x12, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x12, 0x28, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, - 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67, - 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x29, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7e, 0x0a, 0x19, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x69, 0x73, 0x6b, 0x49, 0x50, 0x57, - 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, 0x73, 0x74, 0x12, 0x2f, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, - 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x6f, - 0x67, 0x69, 0x6e, 0x52, 0x69, 0x73, 0x6b, 0x49, 0x50, 0x57, 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, - 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x68, 0x79, 0x61, 0x70, - 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, - 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x69, 0x73, 0x6b, 0x49, 0x50, 0x57, 0x68, 0x69, 0x74, 0x65, 0x6c, - 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x72, 0x0a, 0x15, 0x47, - 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x69, 0x73, 0x6b, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2b, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, - 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x52, 0x69, 0x73, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x2c, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, + 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x69, 0x0a, 0x12, 0x51, 0x75, 0x69, 0x63, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x69, 0x63, 0x6b, 0x43, 0x72, + 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, + 0x76, 0x31, 0x2e, 0x51, 0x75, 0x69, 0x63, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, + 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, + 0x0c, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x22, 0x2e, + 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x1a, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, + 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, + 0x12, 0x1c, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, + 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, + 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, + 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, + 0x12, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x65, 0x64, 0x12, 0x28, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, + 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, + 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7e, 0x0a, 0x19, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x69, 0x73, 0x6b, 0x49, 0x50, 0x57, 0x68, 0x69, 0x74, + 0x65, 0x6c, 0x69, 0x73, 0x74, 0x12, 0x2f, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, + 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x6f, 0x67, 0x69, 0x6e, + 0x52, 0x69, 0x73, 0x6b, 0x49, 0x50, 0x57, 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, + 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x6f, 0x67, 0x69, + 0x6e, 0x52, 0x69, 0x73, 0x6b, 0x49, 0x50, 0x57, 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, 0x73, 0x74, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x72, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x69, 0x73, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x12, 0x2b, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x69, 0x73, - 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x7b, 0x0a, 0x18, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x52, 0x69, 0x73, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x2e, 0x68, 0x79, - 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x69, 0x73, 0x6b, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x68, 0x79, - 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x69, 0x73, 0x6b, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0c, - 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x22, 0x2e, 0x68, - 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, - 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, - 0x2e, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x26, 0x5a, 0x24, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x6c, - 0x6f, 0x63, 0x61, 0x6c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x75, - 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x76, 0x31, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, + 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x47, + 0x65, 0x74, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x69, 0x73, 0x6b, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7b, 0x0a, 0x18, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x69, + 0x73, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2e, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x69, 0x73, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2f, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, + 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x52, 0x69, 0x73, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0c, 0x41, 0x70, 0x70, + 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x22, 0x2e, 0x68, 0x79, 0x61, 0x70, + 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, + 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, + 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, + 0x70, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x42, 0x26, 0x5a, 0x24, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x63, 0x61, + 0x6c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x75, 0x73, 0x65, 0x72, + 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -1861,78 +2105,82 @@ func file_proto_user_v1_auth_proto_rawDescGZIP() []byte { return file_proto_user_v1_auth_proto_rawDescData } -var file_proto_user_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 22) +var file_proto_user_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 23) var file_proto_user_v1_auth_proto_goTypes = []any{ (*LoginPasswordRequest)(nil), // 0: hyapp.user.v1.LoginPasswordRequest (*LoginThirdPartyRequest)(nil), // 1: hyapp.user.v1.LoginThirdPartyRequest - (*AuthResponse)(nil), // 2: hyapp.user.v1.AuthResponse - (*SetPasswordRequest)(nil), // 3: hyapp.user.v1.SetPasswordRequest - (*SetPasswordResponse)(nil), // 4: hyapp.user.v1.SetPasswordResponse - (*QuickCreateAccountRequest)(nil), // 5: hyapp.user.v1.QuickCreateAccountRequest - (*QuickCreateAccountResponse)(nil), // 6: hyapp.user.v1.QuickCreateAccountResponse - (*RefreshTokenRequest)(nil), // 7: hyapp.user.v1.RefreshTokenRequest - (*RefreshTokenResponse)(nil), // 8: hyapp.user.v1.RefreshTokenResponse - (*LogoutRequest)(nil), // 9: hyapp.user.v1.LogoutRequest - (*LogoutResponse)(nil), // 10: hyapp.user.v1.LogoutResponse - (*RecordLoginBlockedRequest)(nil), // 11: hyapp.user.v1.RecordLoginBlockedRequest - (*RecordLoginBlockedResponse)(nil), // 12: hyapp.user.v1.RecordLoginBlockedResponse - (*CheckLoginRiskIPWhitelistRequest)(nil), // 13: hyapp.user.v1.CheckLoginRiskIPWhitelistRequest - (*CheckLoginRiskIPWhitelistResponse)(nil), // 14: hyapp.user.v1.CheckLoginRiskIPWhitelistResponse - (*RegisterRiskConfig)(nil), // 15: hyapp.user.v1.RegisterRiskConfig - (*GetRegisterRiskConfigRequest)(nil), // 16: hyapp.user.v1.GetRegisterRiskConfigRequest - (*GetRegisterRiskConfigResponse)(nil), // 17: hyapp.user.v1.GetRegisterRiskConfigResponse - (*UpdateRegisterRiskConfigRequest)(nil), // 18: hyapp.user.v1.UpdateRegisterRiskConfigRequest - (*UpdateRegisterRiskConfigResponse)(nil), // 19: hyapp.user.v1.UpdateRegisterRiskConfigResponse - (*AppHeartbeatRequest)(nil), // 20: hyapp.user.v1.AppHeartbeatRequest - (*AppHeartbeatResponse)(nil), // 21: hyapp.user.v1.AppHeartbeatResponse - (*RequestMeta)(nil), // 22: hyapp.user.v1.RequestMeta - (*AuthToken)(nil), // 23: hyapp.user.v1.AuthToken + (*RegisterThirdPartyRequest)(nil), // 2: hyapp.user.v1.RegisterThirdPartyRequest + (*AuthResponse)(nil), // 3: hyapp.user.v1.AuthResponse + (*SetPasswordRequest)(nil), // 4: hyapp.user.v1.SetPasswordRequest + (*SetPasswordResponse)(nil), // 5: hyapp.user.v1.SetPasswordResponse + (*QuickCreateAccountRequest)(nil), // 6: hyapp.user.v1.QuickCreateAccountRequest + (*QuickCreateAccountResponse)(nil), // 7: hyapp.user.v1.QuickCreateAccountResponse + (*RefreshTokenRequest)(nil), // 8: hyapp.user.v1.RefreshTokenRequest + (*RefreshTokenResponse)(nil), // 9: hyapp.user.v1.RefreshTokenResponse + (*LogoutRequest)(nil), // 10: hyapp.user.v1.LogoutRequest + (*LogoutResponse)(nil), // 11: hyapp.user.v1.LogoutResponse + (*RecordLoginBlockedRequest)(nil), // 12: hyapp.user.v1.RecordLoginBlockedRequest + (*RecordLoginBlockedResponse)(nil), // 13: hyapp.user.v1.RecordLoginBlockedResponse + (*CheckLoginRiskIPWhitelistRequest)(nil), // 14: hyapp.user.v1.CheckLoginRiskIPWhitelistRequest + (*CheckLoginRiskIPWhitelistResponse)(nil), // 15: hyapp.user.v1.CheckLoginRiskIPWhitelistResponse + (*RegisterRiskConfig)(nil), // 16: hyapp.user.v1.RegisterRiskConfig + (*GetRegisterRiskConfigRequest)(nil), // 17: hyapp.user.v1.GetRegisterRiskConfigRequest + (*GetRegisterRiskConfigResponse)(nil), // 18: hyapp.user.v1.GetRegisterRiskConfigResponse + (*UpdateRegisterRiskConfigRequest)(nil), // 19: hyapp.user.v1.UpdateRegisterRiskConfigRequest + (*UpdateRegisterRiskConfigResponse)(nil), // 20: hyapp.user.v1.UpdateRegisterRiskConfigResponse + (*AppHeartbeatRequest)(nil), // 21: hyapp.user.v1.AppHeartbeatRequest + (*AppHeartbeatResponse)(nil), // 22: hyapp.user.v1.AppHeartbeatResponse + (*RequestMeta)(nil), // 23: hyapp.user.v1.RequestMeta + (*AuthToken)(nil), // 24: hyapp.user.v1.AuthToken } var file_proto_user_v1_auth_proto_depIdxs = []int32{ - 22, // 0: hyapp.user.v1.LoginPasswordRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 22, // 1: hyapp.user.v1.LoginThirdPartyRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 23, // 2: hyapp.user.v1.AuthResponse.token:type_name -> hyapp.user.v1.AuthToken - 22, // 3: hyapp.user.v1.SetPasswordRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 22, // 4: hyapp.user.v1.QuickCreateAccountRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 23, // 5: hyapp.user.v1.QuickCreateAccountResponse.token:type_name -> hyapp.user.v1.AuthToken - 22, // 6: hyapp.user.v1.RefreshTokenRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 23, // 7: hyapp.user.v1.RefreshTokenResponse.token:type_name -> hyapp.user.v1.AuthToken - 22, // 8: hyapp.user.v1.LogoutRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 22, // 9: hyapp.user.v1.RecordLoginBlockedRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 22, // 10: hyapp.user.v1.CheckLoginRiskIPWhitelistRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 22, // 11: hyapp.user.v1.GetRegisterRiskConfigRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 15, // 12: hyapp.user.v1.GetRegisterRiskConfigResponse.config:type_name -> hyapp.user.v1.RegisterRiskConfig - 22, // 13: hyapp.user.v1.UpdateRegisterRiskConfigRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 15, // 14: hyapp.user.v1.UpdateRegisterRiskConfigResponse.config:type_name -> hyapp.user.v1.RegisterRiskConfig - 22, // 15: hyapp.user.v1.AppHeartbeatRequest.meta:type_name -> hyapp.user.v1.RequestMeta - 23, // 16: hyapp.user.v1.AppHeartbeatResponse.token:type_name -> hyapp.user.v1.AuthToken - 0, // 17: hyapp.user.v1.AuthService.LoginPassword:input_type -> hyapp.user.v1.LoginPasswordRequest - 1, // 18: hyapp.user.v1.AuthService.LoginThirdParty:input_type -> hyapp.user.v1.LoginThirdPartyRequest - 3, // 19: hyapp.user.v1.AuthService.SetPassword:input_type -> hyapp.user.v1.SetPasswordRequest - 5, // 20: hyapp.user.v1.AuthService.QuickCreateAccount:input_type -> hyapp.user.v1.QuickCreateAccountRequest - 7, // 21: hyapp.user.v1.AuthService.RefreshToken:input_type -> hyapp.user.v1.RefreshTokenRequest - 9, // 22: hyapp.user.v1.AuthService.Logout:input_type -> hyapp.user.v1.LogoutRequest - 11, // 23: hyapp.user.v1.AuthService.RecordLoginBlocked:input_type -> hyapp.user.v1.RecordLoginBlockedRequest - 13, // 24: hyapp.user.v1.AuthService.CheckLoginRiskIPWhitelist:input_type -> hyapp.user.v1.CheckLoginRiskIPWhitelistRequest - 16, // 25: hyapp.user.v1.AuthService.GetRegisterRiskConfig:input_type -> hyapp.user.v1.GetRegisterRiskConfigRequest - 18, // 26: hyapp.user.v1.AuthService.UpdateRegisterRiskConfig:input_type -> hyapp.user.v1.UpdateRegisterRiskConfigRequest - 20, // 27: hyapp.user.v1.AuthService.AppHeartbeat:input_type -> hyapp.user.v1.AppHeartbeatRequest - 2, // 28: hyapp.user.v1.AuthService.LoginPassword:output_type -> hyapp.user.v1.AuthResponse - 2, // 29: hyapp.user.v1.AuthService.LoginThirdParty:output_type -> hyapp.user.v1.AuthResponse - 4, // 30: hyapp.user.v1.AuthService.SetPassword:output_type -> hyapp.user.v1.SetPasswordResponse - 6, // 31: hyapp.user.v1.AuthService.QuickCreateAccount:output_type -> hyapp.user.v1.QuickCreateAccountResponse - 8, // 32: hyapp.user.v1.AuthService.RefreshToken:output_type -> hyapp.user.v1.RefreshTokenResponse - 10, // 33: hyapp.user.v1.AuthService.Logout:output_type -> hyapp.user.v1.LogoutResponse - 12, // 34: hyapp.user.v1.AuthService.RecordLoginBlocked:output_type -> hyapp.user.v1.RecordLoginBlockedResponse - 14, // 35: hyapp.user.v1.AuthService.CheckLoginRiskIPWhitelist:output_type -> hyapp.user.v1.CheckLoginRiskIPWhitelistResponse - 17, // 36: hyapp.user.v1.AuthService.GetRegisterRiskConfig:output_type -> hyapp.user.v1.GetRegisterRiskConfigResponse - 19, // 37: hyapp.user.v1.AuthService.UpdateRegisterRiskConfig:output_type -> hyapp.user.v1.UpdateRegisterRiskConfigResponse - 21, // 38: hyapp.user.v1.AuthService.AppHeartbeat:output_type -> hyapp.user.v1.AppHeartbeatResponse - 28, // [28:39] is the sub-list for method output_type - 17, // [17:28] is the sub-list for method input_type - 17, // [17:17] is the sub-list for extension type_name - 17, // [17:17] is the sub-list for extension extendee - 0, // [0:17] is the sub-list for field type_name + 23, // 0: hyapp.user.v1.LoginPasswordRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 23, // 1: hyapp.user.v1.LoginThirdPartyRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 23, // 2: hyapp.user.v1.RegisterThirdPartyRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 24, // 3: hyapp.user.v1.AuthResponse.token:type_name -> hyapp.user.v1.AuthToken + 23, // 4: hyapp.user.v1.SetPasswordRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 23, // 5: hyapp.user.v1.QuickCreateAccountRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 24, // 6: hyapp.user.v1.QuickCreateAccountResponse.token:type_name -> hyapp.user.v1.AuthToken + 23, // 7: hyapp.user.v1.RefreshTokenRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 24, // 8: hyapp.user.v1.RefreshTokenResponse.token:type_name -> hyapp.user.v1.AuthToken + 23, // 9: hyapp.user.v1.LogoutRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 23, // 10: hyapp.user.v1.RecordLoginBlockedRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 23, // 11: hyapp.user.v1.CheckLoginRiskIPWhitelistRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 23, // 12: hyapp.user.v1.GetRegisterRiskConfigRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 16, // 13: hyapp.user.v1.GetRegisterRiskConfigResponse.config:type_name -> hyapp.user.v1.RegisterRiskConfig + 23, // 14: hyapp.user.v1.UpdateRegisterRiskConfigRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 16, // 15: hyapp.user.v1.UpdateRegisterRiskConfigResponse.config:type_name -> hyapp.user.v1.RegisterRiskConfig + 23, // 16: hyapp.user.v1.AppHeartbeatRequest.meta:type_name -> hyapp.user.v1.RequestMeta + 24, // 17: hyapp.user.v1.AppHeartbeatResponse.token:type_name -> hyapp.user.v1.AuthToken + 0, // 18: hyapp.user.v1.AuthService.LoginPassword:input_type -> hyapp.user.v1.LoginPasswordRequest + 1, // 19: hyapp.user.v1.AuthService.LoginThirdParty:input_type -> hyapp.user.v1.LoginThirdPartyRequest + 2, // 20: hyapp.user.v1.AuthService.RegisterThirdParty:input_type -> hyapp.user.v1.RegisterThirdPartyRequest + 4, // 21: hyapp.user.v1.AuthService.SetPassword:input_type -> hyapp.user.v1.SetPasswordRequest + 6, // 22: hyapp.user.v1.AuthService.QuickCreateAccount:input_type -> hyapp.user.v1.QuickCreateAccountRequest + 8, // 23: hyapp.user.v1.AuthService.RefreshToken:input_type -> hyapp.user.v1.RefreshTokenRequest + 10, // 24: hyapp.user.v1.AuthService.Logout:input_type -> hyapp.user.v1.LogoutRequest + 12, // 25: hyapp.user.v1.AuthService.RecordLoginBlocked:input_type -> hyapp.user.v1.RecordLoginBlockedRequest + 14, // 26: hyapp.user.v1.AuthService.CheckLoginRiskIPWhitelist:input_type -> hyapp.user.v1.CheckLoginRiskIPWhitelistRequest + 17, // 27: hyapp.user.v1.AuthService.GetRegisterRiskConfig:input_type -> hyapp.user.v1.GetRegisterRiskConfigRequest + 19, // 28: hyapp.user.v1.AuthService.UpdateRegisterRiskConfig:input_type -> hyapp.user.v1.UpdateRegisterRiskConfigRequest + 21, // 29: hyapp.user.v1.AuthService.AppHeartbeat:input_type -> hyapp.user.v1.AppHeartbeatRequest + 3, // 30: hyapp.user.v1.AuthService.LoginPassword:output_type -> hyapp.user.v1.AuthResponse + 3, // 31: hyapp.user.v1.AuthService.LoginThirdParty:output_type -> hyapp.user.v1.AuthResponse + 3, // 32: hyapp.user.v1.AuthService.RegisterThirdParty:output_type -> hyapp.user.v1.AuthResponse + 5, // 33: hyapp.user.v1.AuthService.SetPassword:output_type -> hyapp.user.v1.SetPasswordResponse + 7, // 34: hyapp.user.v1.AuthService.QuickCreateAccount:output_type -> hyapp.user.v1.QuickCreateAccountResponse + 9, // 35: hyapp.user.v1.AuthService.RefreshToken:output_type -> hyapp.user.v1.RefreshTokenResponse + 11, // 36: hyapp.user.v1.AuthService.Logout:output_type -> hyapp.user.v1.LogoutResponse + 13, // 37: hyapp.user.v1.AuthService.RecordLoginBlocked:output_type -> hyapp.user.v1.RecordLoginBlockedResponse + 15, // 38: hyapp.user.v1.AuthService.CheckLoginRiskIPWhitelist:output_type -> hyapp.user.v1.CheckLoginRiskIPWhitelistResponse + 18, // 39: hyapp.user.v1.AuthService.GetRegisterRiskConfig:output_type -> hyapp.user.v1.GetRegisterRiskConfigResponse + 20, // 40: hyapp.user.v1.AuthService.UpdateRegisterRiskConfig:output_type -> hyapp.user.v1.UpdateRegisterRiskConfigResponse + 22, // 41: hyapp.user.v1.AuthService.AppHeartbeat:output_type -> hyapp.user.v1.AppHeartbeatResponse + 30, // [30:42] is the sub-list for method output_type + 18, // [18:30] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name } func init() { file_proto_user_v1_auth_proto_init() } @@ -1947,7 +2195,7 @@ func file_proto_user_v1_auth_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_user_v1_auth_proto_rawDesc, NumEnums: 0, - NumMessages: 22, + NumMessages: 23, NumExtensions: 0, NumServices: 1, }, diff --git a/api/proto/user/v1/auth.proto b/api/proto/user/v1/auth.proto index 668e25c6..f52fd524 100644 --- a/api/proto/user/v1/auth.proto +++ b/api/proto/user/v1/auth.proto @@ -37,6 +37,31 @@ message LoginThirdPartyRequest { string campaign = 20; } +// RegisterThirdPartyRequest 是资料页完成时使用的三方注册入口。 +// 它不会先返回 profile_required 登录态,而是在服务端一次性校验 credential、写用户资料并签发完整 session。 +message RegisterThirdPartyRequest { + RequestMeta meta = 1; + string provider = 2; + string credential = 3; + string device_id = 4; + string username = 5; + string avatar = 6; + string gender = 7; + string country = 8; + string invite_code = 9; + string device = 10; + string os_version = 11; + string birth = 12; + string app_version = 13; + string build_number = 14; + string source = 15; + string install_channel = 16; + string campaign = 17; + string platform = 18; + string language = 19; + string timezone = 20; +} + // AuthResponse 返回标准认证令牌。 message AuthResponse { AuthToken token = 1; @@ -182,6 +207,7 @@ message AppHeartbeatResponse { service AuthService { rpc LoginPassword(LoginPasswordRequest) returns (AuthResponse); rpc LoginThirdParty(LoginThirdPartyRequest) returns (AuthResponse); + rpc RegisterThirdParty(RegisterThirdPartyRequest) returns (AuthResponse); rpc SetPassword(SetPasswordRequest) returns (SetPasswordResponse); rpc QuickCreateAccount(QuickCreateAccountRequest) returns (QuickCreateAccountResponse); rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse); diff --git a/api/proto/user/v1/auth_grpc.pb.go b/api/proto/user/v1/auth_grpc.pb.go index e8d7a6ee..ca909166 100644 --- a/api/proto/user/v1/auth_grpc.pb.go +++ b/api/proto/user/v1/auth_grpc.pb.go @@ -21,6 +21,7 @@ const _ = grpc.SupportPackageIsVersion9 const ( AuthService_LoginPassword_FullMethodName = "/hyapp.user.v1.AuthService/LoginPassword" AuthService_LoginThirdParty_FullMethodName = "/hyapp.user.v1.AuthService/LoginThirdParty" + AuthService_RegisterThirdParty_FullMethodName = "/hyapp.user.v1.AuthService/RegisterThirdParty" AuthService_SetPassword_FullMethodName = "/hyapp.user.v1.AuthService/SetPassword" AuthService_QuickCreateAccount_FullMethodName = "/hyapp.user.v1.AuthService/QuickCreateAccount" AuthService_RefreshToken_FullMethodName = "/hyapp.user.v1.AuthService/RefreshToken" @@ -40,6 +41,7 @@ const ( type AuthServiceClient interface { LoginPassword(ctx context.Context, in *LoginPasswordRequest, opts ...grpc.CallOption) (*AuthResponse, error) LoginThirdParty(ctx context.Context, in *LoginThirdPartyRequest, opts ...grpc.CallOption) (*AuthResponse, error) + RegisterThirdParty(ctx context.Context, in *RegisterThirdPartyRequest, opts ...grpc.CallOption) (*AuthResponse, error) SetPassword(ctx context.Context, in *SetPasswordRequest, opts ...grpc.CallOption) (*SetPasswordResponse, error) QuickCreateAccount(ctx context.Context, in *QuickCreateAccountRequest, opts ...grpc.CallOption) (*QuickCreateAccountResponse, error) RefreshToken(ctx context.Context, in *RefreshTokenRequest, opts ...grpc.CallOption) (*RefreshTokenResponse, error) @@ -79,6 +81,16 @@ func (c *authServiceClient) LoginThirdParty(ctx context.Context, in *LoginThirdP return out, nil } +func (c *authServiceClient) RegisterThirdParty(ctx context.Context, in *RegisterThirdPartyRequest, opts ...grpc.CallOption) (*AuthResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(AuthResponse) + err := c.cc.Invoke(ctx, AuthService_RegisterThirdParty_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *authServiceClient) SetPassword(ctx context.Context, in *SetPasswordRequest, opts ...grpc.CallOption) (*SetPasswordResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(SetPasswordResponse) @@ -177,6 +189,7 @@ func (c *authServiceClient) AppHeartbeat(ctx context.Context, in *AppHeartbeatRe type AuthServiceServer interface { LoginPassword(context.Context, *LoginPasswordRequest) (*AuthResponse, error) LoginThirdParty(context.Context, *LoginThirdPartyRequest) (*AuthResponse, error) + RegisterThirdParty(context.Context, *RegisterThirdPartyRequest) (*AuthResponse, error) SetPassword(context.Context, *SetPasswordRequest) (*SetPasswordResponse, error) QuickCreateAccount(context.Context, *QuickCreateAccountRequest) (*QuickCreateAccountResponse, error) RefreshToken(context.Context, *RefreshTokenRequest) (*RefreshTokenResponse, error) @@ -202,6 +215,9 @@ func (UnimplementedAuthServiceServer) LoginPassword(context.Context, *LoginPassw func (UnimplementedAuthServiceServer) LoginThirdParty(context.Context, *LoginThirdPartyRequest) (*AuthResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method LoginThirdParty not implemented") } +func (UnimplementedAuthServiceServer) RegisterThirdParty(context.Context, *RegisterThirdPartyRequest) (*AuthResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method RegisterThirdParty not implemented") +} func (UnimplementedAuthServiceServer) SetPassword(context.Context, *SetPasswordRequest) (*SetPasswordResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SetPassword not implemented") } @@ -286,6 +302,24 @@ func _AuthService_LoginThirdParty_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _AuthService_RegisterThirdParty_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(RegisterThirdPartyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AuthServiceServer).RegisterThirdParty(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AuthService_RegisterThirdParty_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AuthServiceServer).RegisterThirdParty(ctx, req.(*RegisterThirdPartyRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _AuthService_SetPassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(SetPasswordRequest) if err := dec(in); err != nil { @@ -463,6 +497,10 @@ var AuthService_ServiceDesc = grpc.ServiceDesc{ MethodName: "LoginThirdParty", Handler: _AuthService_LoginThirdParty_Handler, }, + { + MethodName: "RegisterThirdParty", + Handler: _AuthService_RegisterThirdParty_Handler, + }, { MethodName: "SetPassword", Handler: _AuthService_SetPassword_Handler, diff --git a/docs/flutter对接/Google三方注册Flutter对接.md b/docs/flutter对接/Google三方注册Flutter对接.md new file mode 100644 index 00000000..db01783c --- /dev/null +++ b/docs/flutter对接/Google三方注册Flutter对接.md @@ -0,0 +1,249 @@ +# Google 三方注册 Flutter 对接 + +## 边界 + +Google 登录后,Flutter 只暂存 Firebase ID token 和 Google 基本信息,不写入 `AppAuthSession`。 + +资料页允许 pending third-party auth 进入,不要求 `_authService.isSignedIn`。用户点击完成后,Flutter 调注册接口;后端校验 Firebase ID token、创建或补齐用户资料、返回 access/refresh token。Flutter 收到 token 后再写入现有 `AppAuthSession`。 + +旧接口 `POST /api/v1/auth/third-party/login` 仍保留兼容,不用于新的 Google 完整注册流程。 + +## 调用流程 + +1. Flutter 调 Google Sign-In / Firebase Auth,拿到 Firebase ID token。 +2. Flutter 暂存: + - `provider=firebase` + - `credential=` + - Google 返回的昵称、头像等基础信息 + - `device_id`、`platform`、`language`、`timezone` +3. 进入资料页,用户补齐 `username`、`avatar`、`gender`、`country`、`invite_code`。 +4. 如果用户选择本地头像,先调用注册头像上传接口拿 URL。 +5. 调 `POST /api/v1/auth/third-party/register`。 +6. 成功后把返回的 token 写入 `AppAuthSession`。 + +## 注册头像上传 + +地址: + +```http +POST /api/v1/files/registration/avatar/upload +``` + +Headers: + +| Header | 必填 | 说明 | +| --- | --- | --- | +| `Content-Type` | 是 | `multipart/form-data` | +| `X-App-Code` | 否 | App 编码,默认 `lalu` | + +参数: + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `file` | file | 是 | 头像文件;也支持字段名 `avatar`。 | + +限制: + +| 项 | 说明 | +| --- | --- | +| 登录态 | 不需要 `Authorization`。 | +| 大小 | 最大 5MB。 | +| 格式 | `jpg`、`jpeg`、`png`、`webp`。 | +| 校验 | 后端按文件头识别图片类型,不信任客户端传入的 Content-Type。 | + +返回值: + +```json +{ + "code": 0, + "message": "ok", + "request_id": "req_xxx", + "data": { + "url": "https://media.example.com/app/avatars/registration/20260626/avatar_xxx.png", + "object_key": "app/avatars/registration/20260626/avatar_xxx.png", + "content_type": "image/png", + "size_bytes": 204800 + } +} +``` + +Flutter 使用 `data.url` 作为注册接口的 `avatar`。 + +## 三方注册 + +地址: + +```http +POST /api/v1/auth/third-party/register +``` + +Headers: + +| Header | 必填 | 说明 | +| --- | --- | --- | +| `Content-Type` | 是 | `application/json` | +| `X-App-Code` | 否 | App 编码,默认 `lalu` | +| `X-App-Package` | 否 | 包名;后端可按包名解析 app_code。 | + +参数: + +| 字段 | 类型 | 必填 | 说明 | +| --- | --- | --- | --- | +| `provider` | string | 是 | 固定传 `firebase`。 | +| `credential` | string | 是 | Firebase ID token。 | +| `device_id` | string | 是 | 设备 ID,用于 refresh session 绑定和注册风控。 | +| `username` | string | 是 | 用户昵称。 | +| `avatar` | string | 是 | HTTP(S) 头像 URL,可以是上传接口返回的 `url`。 | +| `gender` | string | 是 | 性别值,后端只保存原值。 | +| `country` | string | 是 | 国家码,例如 `SG`、`CN`。 | +| `invite_code` | string | 否 | 邀请码。 | +| `device` | string | 否 | 设备型号。 | +| `os_version` | string | 否 | 系统版本。 | +| `birth` | string | 否 | 生日,格式 `yyyy-mm-dd`。 | +| `app_version` | string | 否 | App 版本。 | +| `build_number` | string | 否 | 构建号。 | +| `source` | string | 否 | 注册来源。 | +| `install_channel` | string | 否 | 安装渠道。 | +| `campaign` | string | 否 | 投放活动。 | +| `platform` | string | 是 | `android` 或 `ios`。 | +| `language` | string | 否 | 客户端语言,例如 `en-US`。 | +| `timezone` | string | 否 | IANA 时区,例如 `Asia/Shanghai`。 | + +请求示例: + +```json +{ + "provider": "firebase", + "credential": "firebase_id_token", + "device_id": "ios_device_001", + "username": "Alice", + "avatar": "https://media.example.com/app/avatars/registration/20260626/avatar_xxx.png", + "gender": "female", + "country": "SG", + "invite_code": "", + "device": "iPhone 15", + "os_version": "iOS 18.1", + "birth": "2000-01-02", + "app_version": "1.2.3", + "build_number": "123", + "source": "google", + "install_channel": "app_store", + "campaign": "", + "platform": "ios", + "language": "en-US", + "timezone": "Asia/Shanghai" +} +``` + +返回值: + +```json +{ + "code": 0, + "message": "ok", + "request_id": "req_xxx", + "data": { + "app_code": "lalu", + "user_id": "10001", + "display_user_id": "163001", + "default_display_user_id": "163001", + "display_user_id_kind": "default", + "display_user_id_expires_at_ms": 0, + "session_id": "sess_xxx", + "access_token": "access_token", + "refresh_token": "refresh_token", + "expires_in_sec": 1800, + "token_type": "Bearer", + "is_new_user": true, + "profile_completed": true, + "onboarding_status": "completed" + } +} +``` + +Flutter 成功处理: + +| 字段 | 处理 | +| --- | --- | +| `access_token` | 写入现有 `AppAuthSession.accessToken`。 | +| `refresh_token` | 写入现有 `AppAuthSession.refreshToken`。 | +| `session_id` | 写入现有 session 标识。 | +| `user_id` | 字符串保存,不要转 JS number。 | +| `display_user_id` | 展示号。 | +| `profile_completed` | 必须为 `true`;否则不要进入首页。 | +| `onboarding_status` | 成功应为 `completed`。 | + +## 重试和兼容 + +同一个 Firebase subject 如果之前已经被旧流程创建成 pending 用户,新注册接口会补齐这个用户并返回 token,不会再创建第二个用户。 + +同一个 Firebase subject 如果已经完成注册,再调该接口会按已有用户登录返回 token,不会覆盖用户后续改过的资料。 + +## 错误处理 + +| HTTP | code | 场景 | Flutter 处理 | +| --- | --- | --- | --- | +| `400` | `INVALID_ARGUMENT` | 缺必填字段、国家格式错误、头像 URL 非 HTTP(S)、生日格式错误。 | 停留资料页,提示用户修正。 | +| `400` | `INVALID_INVITE_CODE` | 邀请码格式错误或不可用。 | 提示邀请码无效,可允许清空后重试。 | +| `401` | `AUTH_FAILED` | Firebase ID token 无效、过期或 provider 不允许。 | 回到 Google 登录重新拿 token。 | +| `429` | `RATE_LIMITED` | 登录注册入口限流。 | 稍后重试。 | +| `409` | `DEVICE_ALREADY_REGISTERED` | 当前设备已达到注册账号上限。 | 提示当前设备无法继续注册。 | +| `502` | `UPSTREAM_ERROR` | user-service 或对象存储不可用。 | 稍后重试。 | + +## Dart 调用示例 + +```dart +Future registerFirebaseUser({ + required AppNetworkService network, + required String firebaseIdToken, + required String deviceId, + required String username, + required String avatar, + required String gender, + required String country, + String inviteCode = '', +}) async { + final envelope = await network.postEnvelope( + '/api/v1/auth/third-party/register', + data: { + 'provider': 'firebase', + 'credential': firebaseIdToken, + 'device_id': deviceId, + 'username': username, + 'avatar': avatar, + 'gender': gender, + 'country': country, + 'invite_code': inviteCode, + 'platform': Platform.isIOS ? 'ios' : 'android', + 'language': PlatformDispatcher.instance.locale.toLanguageTag(), + 'timezone': await currentIanaTimezone(), + }, + requiresAuthorization: false, + ); + + if (envelope.code != 0) { + throw ApiException(envelope.code, envelope.message); + } + + final data = envelope.data as Map; + if (data['profile_completed'] != true || + data['onboarding_status'] != 'completed') { + throw StateError('third-party register did not complete profile'); + } + + return AppAuthSession( + appCode: data['app_code'] as String? ?? '', + userId: data['user_id'] as String? ?? '', + displayUserId: data['display_user_id'] as String? ?? '', + sessionId: data['session_id'] as String? ?? '', + accessToken: data['access_token'] as String? ?? '', + refreshToken: data['refresh_token'] as String? ?? '', + expiresInSec: (data['expires_in_sec'] as num? ?? 0).toInt(), + tokenType: data['token_type'] as String? ?? 'Bearer', + profileCompleted: data['profile_completed'] == true, + onboardingStatus: data['onboarding_status'] as String? ?? '', + ); +} +``` + +相关 IM:注册接口成功后,后端会导入腾讯 IM 用户资料;Flutter 不需要额外调用 IM 导入接口。 diff --git a/services/gateway-service/internal/client/user_client.go b/services/gateway-service/internal/client/user_client.go index e09e6d66..b0fb3e06 100644 --- a/services/gateway-service/internal/client/user_client.go +++ b/services/gateway-service/internal/client/user_client.go @@ -12,6 +12,7 @@ import ( type UserAuthClient interface { LoginPassword(ctx context.Context, req *userv1.LoginPasswordRequest) (*userv1.AuthResponse, error) LoginThirdParty(ctx context.Context, req *userv1.LoginThirdPartyRequest) (*userv1.AuthResponse, error) + RegisterThirdParty(ctx context.Context, req *userv1.RegisterThirdPartyRequest) (*userv1.AuthResponse, error) SetPassword(ctx context.Context, req *userv1.SetPasswordRequest) (*userv1.SetPasswordResponse, error) QuickCreateAccount(ctx context.Context, req *userv1.QuickCreateAccountRequest) (*userv1.QuickCreateAccountResponse, error) RefreshToken(ctx context.Context, req *userv1.RefreshTokenRequest) (*userv1.RefreshTokenResponse, error) @@ -255,6 +256,10 @@ func (c *grpcUserAuthClient) LoginThirdParty(ctx context.Context, req *userv1.Lo return c.client.LoginThirdParty(ctx, req) } +func (c *grpcUserAuthClient) RegisterThirdParty(ctx context.Context, req *userv1.RegisterThirdPartyRequest) (*userv1.AuthResponse, error) { + return c.client.RegisterThirdParty(ctx, req) +} + func (c *grpcUserAuthClient) SetPassword(ctx context.Context, req *userv1.SetPasswordRequest) (*userv1.SetPasswordResponse, error) { return c.client.SetPassword(ctx, req) } diff --git a/services/gateway-service/internal/transport/http/appapi/handler.go b/services/gateway-service/internal/transport/http/appapi/handler.go index 7fb2c1f6..b4b98727 100644 --- a/services/gateway-service/internal/transport/http/appapi/handler.go +++ b/services/gateway-service/internal/transport/http/appapi/handler.go @@ -89,6 +89,10 @@ func (h *Handler) UploadAvatar(writer http.ResponseWriter, request *http.Request h.uploadAvatar(writer, request) } +func (h *Handler) UploadRegistrationAvatar(writer http.ResponseWriter, request *http.Request) { + h.uploadRegistrationAvatar(writer, request) +} + func (h *Handler) ProxyEffectMedia(writer http.ResponseWriter, request *http.Request) { h.proxyEffectMedia(writer, request) } diff --git a/services/gateway-service/internal/transport/http/appapi/upload_handler.go b/services/gateway-service/internal/transport/http/appapi/upload_handler.go index 6fc6e6a7..c52509f3 100644 --- a/services/gateway-service/internal/transport/http/appapi/upload_handler.go +++ b/services/gateway-service/internal/transport/http/appapi/upload_handler.go @@ -58,15 +58,20 @@ type uploadResponse struct { // uploadAvatar 上传用户头像文件,只接受常见静态图片格式。 func (h *Handler) uploadAvatar(writer http.ResponseWriter, request *http.Request) { - h.uploadObject(writer, request, avatarUploadPolicy) + h.uploadObject(writer, request, avatarUploadPolicy, fmt.Sprintf("%d", auth.UserIDFromContext(request.Context()))) +} + +// uploadRegistrationAvatar 上传注册页临时头像,供尚未拿到后端 session 的三方注册流程使用。 +func (h *Handler) uploadRegistrationAvatar(writer http.ResponseWriter, request *http.Request) { + h.uploadObject(writer, request, avatarUploadPolicy, "registration") } // uploadFile 上传 App 通用文件,不绑定用户资料更新语义。 func (h *Handler) uploadFile(writer http.ResponseWriter, request *http.Request) { - h.uploadObject(writer, request, fileUploadPolicy) + h.uploadObject(writer, request, fileUploadPolicy, fmt.Sprintf("%d", auth.UserIDFromContext(request.Context()))) } -func (h *Handler) uploadObject(writer http.ResponseWriter, request *http.Request, policy uploadPolicy) { +func (h *Handler) uploadObject(writer http.ResponseWriter, request *http.Request, policy uploadPolicy, ownerSegment string) { if request.Method != http.MethodPost { httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument") return @@ -106,7 +111,7 @@ func (h *Handler) uploadObject(writer http.ResponseWriter, request *http.Request return } - objectKey := buildUploadObjectKey(policy, auth.UserIDFromContext(request.Context()), header.Filename, contentType, time.Now().UTC()) + objectKey := buildUploadObjectKey(policy, ownerSegment, header.Filename, contentType, time.Now().UTC()) objectURL, err := h.objectUploader.PutObject(request.Context(), objectKey, io.MultiReader(bytes.NewReader(head), file), header.Size, contentType) if err != nil { httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error") @@ -180,7 +185,7 @@ func isAvatarContentType(contentType string) bool { } } -func buildUploadObjectKey(policy uploadPolicy, userID int64, filename string, contentType string, now time.Time) string { +func buildUploadObjectKey(policy uploadPolicy, ownerSegment string, filename string, contentType string, now time.Time) string { date := now.UTC().Format("20060102") extension := normalizedUploadExtension(filename, contentType) if extension == "" && policy.RequireImage { @@ -188,7 +193,12 @@ func buildUploadObjectKey(policy uploadPolicy, userID int64, filename string, co } idPrefix := strings.TrimSuffix(policy.Kind, "s") - return fmt.Sprintf("app/%s/%d/%s/%s%s", policy.Kind, userID, date, idgen.New(idPrefix), extension) + ownerSegment = strings.Trim(ownerSegment, "/") + if ownerSegment == "" { + // 调用方必须明确文件归属分区;空分区会让不同入口对象混在一起,直接回落到 unknown 便于排查。 + ownerSegment = "unknown" + } + return fmt.Sprintf("app/%s/%s/%s/%s%s", policy.Kind, ownerSegment, date, idgen.New(idPrefix), extension) } func normalizedUploadExtension(filename string, contentType string) string { diff --git a/services/gateway-service/internal/transport/http/auth_handler.go b/services/gateway-service/internal/transport/http/auth_handler.go index 1c504921..deb00509 100644 --- a/services/gateway-service/internal/transport/http/auth_handler.go +++ b/services/gateway-service/internal/transport/http/auth_handler.go @@ -287,6 +287,83 @@ func (h *Handler) loginThirdParty(writer http.ResponseWriter, request *http.Requ httpkit.WriteOK(writer, request, authData(resp.GetToken(), &isNewUser)) } +// registerThirdParty 处理资料页完成时的三方注册,不返回 profile_required 中间态。 +func (h *Handler) registerThirdParty(writer http.ResponseWriter, request *http.Request) { + var body struct { + Provider string `json:"provider"` + Credential string `json:"credential"` + DeviceID string `json:"device_id"` + Username string `json:"username"` + Avatar string `json:"avatar"` + Gender string `json:"gender"` + Country string `json:"country"` + InviteCode string `json:"invite_code"` + Device string `json:"device"` + OSVersion string `json:"os_version"` + Birth string `json:"birth"` + AppVersion string `json:"app_version"` + BuildNumber string `json:"build_number"` + Source string `json:"source"` + InstallChannel string `json:"install_channel"` + Campaign string `json:"campaign"` + Platform string `json:"platform"` + Language string `json:"language"` + Timezone string `json:"timezone"` + } + if !decode(writer, request, &body) { + return + } + if !h.allowPublicAuthRequest(writer, request, authRateInput{ + operation: authOperationThirdParty, + deviceID: body.DeviceID, + provider: body.Provider, + }) { + return + } + if !h.allowLoginRisk(writer, request, loginRiskInput{ + Channel: body.Provider, + Platform: body.Platform, + Language: body.Language, + Timezone: body.Timezone, + }) { + return + } + if h.userClient == nil { + httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error") + return + } + + resp, err := h.userClient.RegisterThirdParty(request.Context(), &userv1.RegisterThirdPartyRequest{ + Meta: authRequestMetaWithLoginContext(request, body.DeviceID, body.Platform, body.Language, body.Timezone), + Provider: body.Provider, + Credential: body.Credential, + DeviceId: body.DeviceID, + Username: body.Username, + Avatar: body.Avatar, + Gender: body.Gender, + Country: body.Country, + InviteCode: body.InviteCode, + Device: body.Device, + OsVersion: body.OSVersion, + Birth: body.Birth, + AppVersion: body.AppVersion, + BuildNumber: body.BuildNumber, + Source: body.Source, + InstallChannel: body.InstallChannel, + Campaign: body.Campaign, + Platform: body.Platform, + Language: body.Language, + Timezone: body.Timezone, + }) + if err != nil { + httpkit.WriteRPCError(writer, request, err) + return + } + + isNewUser := resp.GetIsNewUser() + httpkit.WriteOK(writer, request, authData(resp.GetToken(), &isNewUser)) +} + // setPassword 处理已登录用户首次设置密码。 func (h *Handler) setPassword(writer http.ResponseWriter, request *http.Request) { var body struct { diff --git a/services/gateway-service/internal/transport/http/httproutes/router.go b/services/gateway-service/internal/transport/http/httproutes/router.go index b0bf1254..07210429 100644 --- a/services/gateway-service/internal/transport/http/httproutes/router.go +++ b/services/gateway-service/internal/transport/http/httproutes/router.go @@ -33,6 +33,7 @@ type AuthHandlers struct { SetPassword http.HandlerFunc QuickCreateAccount http.HandlerFunc LoginThirdParty http.HandlerFunc + RegisterThirdParty http.HandlerFunc RefreshToken http.HandlerFunc Logout http.HandlerFunc } @@ -59,6 +60,7 @@ type AppHandlers struct { HandleTencentRTCCallback http.HandlerFunc UploadFile http.HandlerFunc UploadAvatar http.HandlerFunc + UploadRegistrationAvatar http.HandlerFunc ProxyEffectMedia http.HandlerFunc HandlePushToken http.HandlerFunc AppHeartbeat http.HandlerFunc @@ -369,6 +371,7 @@ func (r routes) registerAuthRoutes() { r.auth("/auth/password/set", "", h.SetPassword) r.public("/auth/account/quick-create", http.MethodPost, h.QuickCreateAccount) r.public("/auth/third-party/login", "", h.LoginThirdParty) + r.public("/auth/third-party/register", http.MethodPost, h.RegisterThirdParty) r.public("/auth/token/refresh", "", h.RefreshToken) r.public("/auth/logout", "", h.Logout) } @@ -396,6 +399,7 @@ func (r routes) registerAppRoutes() { r.public("/tencent-rtc/callback", "", h.HandleTencentRTCCallback) r.auth("/files/upload", "", h.UploadFile) r.auth("/files/avatar/upload", "", h.UploadAvatar) + r.public("/files/registration/avatar/upload", http.MethodPost, h.UploadRegistrationAvatar) r.public("/media/effects/proxy", http.MethodGet, h.ProxyEffectMedia) r.auth("/app/heartbeat", http.MethodPost, h.AppHeartbeat) r.profile("/devices/push-token", "", h.HandlePushToken) diff --git a/services/gateway-service/internal/transport/http/response_test.go b/services/gateway-service/internal/transport/http/response_test.go index b7cebc82..37dc98f4 100644 --- a/services/gateway-service/internal/transport/http/response_test.go +++ b/services/gateway-service/internal/transport/http/response_test.go @@ -288,6 +288,7 @@ func (f *fakeRoomClient) UnfollowRoom(_ context.Context, req *roomv1.UnfollowRoo type fakeUserAuthClient struct { lastLoginThirdParty *userv1.LoginThirdPartyRequest + lastRegisterThird *userv1.RegisterThirdPartyRequest lastLoginPassword *userv1.LoginPasswordRequest lastSetPassword *userv1.SetPasswordRequest lastQuickCreate *userv1.QuickCreateAccountRequest @@ -686,6 +687,31 @@ func (f *fakeUserAuthClient) LoginThirdParty(_ context.Context, req *userv1.Logi }, nil } +func (f *fakeUserAuthClient) RegisterThirdParty(_ context.Context, req *userv1.RegisterThirdPartyRequest) (*userv1.AuthResponse, error) { + f.lastRegisterThird = req + f.loginThirdCount++ + if f.loginErr != nil { + return nil, f.loginErr + } + + return &userv1.AuthResponse{ + Token: &userv1.AuthToken{ + UserId: 10001, + DisplayUserId: "100001", + SessionId: "sess-register", + AccessToken: "access-register", + RefreshToken: "refresh-register", + ExpiresInSec: 1800, + TokenType: "Bearer", + ProfileCompleted: true, + OnboardingStatus: "completed", + }, + IsNewUser: true, + ProfileCompleted: true, + OnboardingStatus: "completed", + }, nil +} + func (f *fakeUserAuthClient) SetPassword(_ context.Context, req *userv1.SetPasswordRequest) (*userv1.SetPasswordResponse, error) { f.lastSetPassword = req return &userv1.SetPasswordResponse{PasswordSet: true}, nil @@ -5460,6 +5486,55 @@ func TestThirdPartyLoginUsesPublicEnvelopeAndPropagatesRequestID(t *testing.T) { } } +func TestThirdPartyRegisterUsesPublicEnvelopeAndReturnsCompletedToken(t *testing.T) { + userClient := &fakeUserAuthClient{} + router := NewHandler(&fakeRoomClient{}, userClient).Routes(auth.NewVerifier("secret")) + body := []byte(`{"provider":"firebase","credential":"firebase-id-token-1","device_id":"dev-1","username":"hy","avatar":"https://cdn.example/avatar.png","gender":"male","country":"CN","invite_code":"INV-1","device":"iPhone 15","os_version":"iOS 18.1","birth":"2000-01-02","app_version":"1.2.3","build_number":"123","source":"campaign-a","install_channel":"app_store","campaign":"spring_launch","platform":"ios","language":"zh-CN","timezone":"Asia/Shanghai"}`) + request := httptest.NewRequest(http.MethodPost, "/api/v1/auth/third-party/register", bytes.NewReader(body)) + request.Header.Set("X-Request-ID", "req-auth-register") + request.Header.Set("X-Forwarded-For", "203.0.113.10") + request.Header.Set("User-Agent", "hyapp-test/1.0") + request.Header.Set("CF-IPCountry", "sg") + recorder := httptest.NewRecorder() + + router.ServeHTTP(recorder, request) + + if recorder.Code != http.StatusOK { + t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String()) + } + var response httpkit.ResponseEnvelope + if err := json.NewDecoder(recorder.Body).Decode(&response); err != nil { + t.Fatalf("decode response failed: %v", err) + } + generatedRequestID := assertGeneratedRequestID(t, recorder, "req-auth-register") + if response.Code != httpkit.CodeOK || response.RequestID != generatedRequestID { + t.Fatalf("unexpected envelope: %+v", response) + } + data, ok := response.Data.(map[string]any) + if !ok || data["profile_completed"] != true || data["onboarding_status"] != "completed" || data["access_token"] != "access-register" { + t.Fatalf("register must return completed token data: %+v", response.Data) + } + req := userClient.lastRegisterThird + if req == nil { + t.Fatal("RegisterThirdParty request was not sent") + } + if userClient.lastLoginThirdParty != nil { + t.Fatalf("register endpoint must not call LoginThirdParty: %+v", userClient.lastLoginThirdParty) + } + if req.GetMeta().GetRequestId() != generatedRequestID || req.GetMeta().GetClientIp() != "203.0.113.10" || req.GetMeta().GetCountryByIp() != "SG" { + t.Fatalf("auth meta was not propagated from gateway context: %+v", req.GetMeta()) + } + if req.GetProvider() != "firebase" || req.GetCredential() != "firebase-id-token-1" || req.GetDeviceId() != "dev-1" { + t.Fatalf("provider credential device fields mismatch: %+v", req) + } + if req.GetUsername() != "hy" || req.GetAvatar() == "" || req.GetGender() != "male" || req.GetCountry() != "CN" || req.GetInviteCode() != "INV-1" { + t.Fatalf("profile fields mismatch: %+v", req) + } + if req.GetPlatform() != "ios" || req.GetLanguage() != "zh-CN" || req.GetTimezone() != "Asia/Shanghai" || req.GetInstallChannel() != "app_store" || req.GetCampaign() != "spring_launch" { + t.Fatalf("source fields mismatch: %+v", req) + } +} + func TestAuthClientIPPrefersForwardedPublicIPOverProxyRealIP(t *testing.T) { request := httptest.NewRequest(http.MethodPost, "/api/v1/auth/third-party/login", nil) request.RemoteAddr = "10.0.0.9:54123" @@ -8566,6 +8641,39 @@ func TestUploadAvatarAllowsIncompleteProfileAndWritesObject(t *testing.T) { } } +func TestUploadRegistrationAvatarAllowsPublicPendingAuthUpload(t *testing.T) { + uploader := &fakeObjectUploader{} + handler := NewHandler(&fakeRoomClient{}) + handler.SetObjectUploader(uploader) + router := handler.Routes(auth.NewVerifier("secret")) + payload := append([]byte{0x89, 'P', 'N', 'G', '\r', '\n', 0x1a, '\n'}, bytes.Repeat([]byte{1}, 32)...) + body, contentType := multipartUploadBody(t, "avatar", "avatar.png", "image/png", payload) + request := httptest.NewRequest(http.MethodPost, "/api/v1/files/registration/avatar/upload", body) + request.Header.Set("Content-Type", contentType) + request.Header.Set("X-Request-ID", "req-upload-register-avatar") + recorder := httptest.NewRecorder() + + router.ServeHTTP(recorder, request) + + if recorder.Code != http.StatusOK { + t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String()) + } + var response httpkit.ResponseEnvelope + if err := json.NewDecoder(recorder.Body).Decode(&response); err != nil { + t.Fatalf("decode upload response failed: %v", err) + } + data, ok := response.Data.(map[string]any) + if response.Code != httpkit.CodeOK || !ok { + t.Fatalf("unexpected upload envelope: %+v", response) + } + if !strings.HasPrefix(uploader.lastKey, "app/avatars/registration/") || !strings.HasSuffix(uploader.lastKey, ".png") { + t.Fatalf("unexpected registration avatar object key: %q", uploader.lastKey) + } + if data["url"] != "https://media.example.com/"+uploader.lastKey || data["object_key"] != uploader.lastKey || data["content_type"] != "image/png" { + t.Fatalf("unexpected upload data: %+v", data) + } +} + func TestUploadAvatarRejectsSpoofedImageContentType(t *testing.T) { uploader := &fakeObjectUploader{} handler := NewHandler(&fakeRoomClient{}) diff --git a/services/gateway-service/internal/transport/http/router.go b/services/gateway-service/internal/transport/http/router.go index 00b5fbaa..a67bab09 100644 --- a/services/gateway-service/internal/transport/http/router.go +++ b/services/gateway-service/internal/transport/http/router.go @@ -169,6 +169,7 @@ func (h *Handler) Routes(jwtVerifier *auth.Verifier) http.Handler { SetPassword: h.setPassword, QuickCreateAccount: h.quickCreateAccount, LoginThirdParty: h.loginThirdParty, + RegisterThirdParty: h.registerThirdParty, RefreshToken: h.refreshToken, Logout: h.logout, }, @@ -194,6 +195,7 @@ func (h *Handler) Routes(jwtVerifier *auth.Verifier) http.Handler { HandleTencentRTCCallback: callbackAPI.HandleTencentRTCCallback, UploadFile: appAPI.UploadFile, UploadAvatar: appAPI.UploadAvatar, + UploadRegistrationAvatar: appAPI.UploadRegistrationAvatar, ProxyEffectMedia: appAPI.ProxyEffectMedia, HandlePushToken: appAPI.HandlePushToken, AppHeartbeat: appAPI.AppHeartbeat, diff --git a/services/user-service/internal/service/auth/service.go b/services/user-service/internal/service/auth/service.go index c82433ec..c518ff96 100644 --- a/services/user-service/internal/service/auth/service.go +++ b/services/user-service/internal/service/auth/service.go @@ -68,6 +68,8 @@ type AuthRepository interface { ListDisplayUserIDsByRegisterDeviceID(ctx context.Context, appCode string, deviceID string) ([]string, error) // CreateThirdPartyUser 原子创建用户、默认短号、三方身份和首个 session。 CreateThirdPartyUser(ctx context.Context, user userdomain.User, identity userdomain.Identity, thirdParty authdomain.ThirdPartyIdentity, session authdomain.Session, invite invitedomain.BindCommand, maxAccountsPerDevice int32) error + // CompleteThirdPartyRegistration 原子补齐旧三方 pending 用户资料并创建首个注册完成 session。 + CompleteThirdPartyRegistration(ctx context.Context, command userdomain.CompleteOnboardingCommand, session authdomain.Session) (userdomain.User, error) // GetRegisterRiskConfig 读取当前 App 的注册风控配置。 GetRegisterRiskConfig(ctx context.Context, appCode string) (authdomain.RegisterRiskConfig, error) // UpsertRegisterRiskConfig 保存当前 App 的注册风控配置。 diff --git a/services/user-service/internal/service/auth/service_test.go b/services/user-service/internal/service/auth/service_test.go index d63fa2fe..c3500cb2 100644 --- a/services/user-service/internal/service/auth/service_test.go +++ b/services/user-service/internal/service/auth/service_test.go @@ -1011,6 +1011,80 @@ func TestThirdPartyLoginOrRegister(t *testing.T) { } } +func TestRegisterThirdPartyCreatesCompletedUserAndSession(t *testing.T) { + // 新注册接口必须一次性完成资料、三方绑定、session 和注册事实,客户端不再拿 profile_required token。 + ctx := context.Background() + repository := mysqltest.NewRepository(t) + country := seedCountry(t, repository, "SG") + region := mustResolveRegionByCountry(t, repository, "SG") + now := time.UnixMilli(1000) + imImporter := &fakeIMAccountImporter{} + svc := newAuthService(repository, &now, []int64{900030}, []string{"100030"}, authservice.WithIMAccountImporter(imImporter)) + + token, isNewUser, err := svc.RegisterThirdParty(ctx, "wechat", "openid-register-complete", thirdPartyRegistration("ios"), authservice.Meta{RequestID: "req-third-register", ClientIP: "203.0.113.10", UserAgent: "hyapp-test/1.0"}) + if err != nil { + t.Fatalf("RegisterThirdParty failed: %v", err) + } + if !isNewUser || token.UserID != 900030 || token.ProfileCompleted != true || token.OnboardingStatus != string(userdomain.OnboardingStatusCompleted) || token.AccessToken == "" || token.RefreshToken == "" { + t.Fatalf("register must return completed token for new user: token=%+v isNew=%v", token, isNewUser) + } + user, err := repository.GetUser(ctx, token.UserID) + if err != nil { + t.Fatalf("GetUser failed: %v", err) + } + if !user.ProfileCompleted || user.ProfileCompletedAtMs != now.UnixMilli() || user.OnboardingStatus != userdomain.OnboardingStatusCompleted { + t.Fatalf("user must be completed immediately: %+v", user) + } + if user.Country != country.CountryCode || user.RegionID != region.RegionID || user.Username != "hy" || user.Avatar != "https://cdn.example/avatar.png" { + t.Fatalf("registration profile mismatch: %+v", user) + } + if imImporter.userID != token.UserID || imImporter.nickname != "hy" || imImporter.faceURL != "https://cdn.example/avatar.png" { + t.Fatalf("completed registration must import final IM profile: %+v", imImporter) + } + assertUserRegisteredOutboxCount(t, repository, token.UserID, 1) + assertUserRegisteredOutboxFact(t, repository, token.UserID, country.CountryID, region.RegionID, now.UnixMilli()) +} + +func TestRegisterThirdPartyCompletesExistingPendingUser(t *testing.T) { + // 兼容旧流程:同一 provider subject 已有 pending 用户时,新接口补齐资料并签新 session,不创建第二个账号。 + ctx := context.Background() + repository := mysqltest.NewRepository(t) + country := seedCountry(t, repository, "SG") + region := mustResolveRegionByCountry(t, repository, "SG") + now := time.UnixMilli(1000) + svc := newAuthService(repository, &now, []int64{900031, 900032}, []string{"100031", "100032"}) + + pendingToken, isNewUser, err := svc.LoginThirdParty(ctx, "wechat", "openid-pending-register", authdomain.ThirdPartyRegistration{DeviceID: "dev-ios", Platform: "ios"}, authservice.Meta{RequestID: "req-third-pending"}) + if err != nil { + t.Fatalf("LoginThirdParty pending seed failed: %v", err) + } + if !isNewUser || pendingToken.ProfileCompleted { + t.Fatalf("seed login must create pending user: token=%+v isNew=%v", pendingToken, isNewUser) + } + assertUserRegisteredOutboxCount(t, repository, pendingToken.UserID, 0) + + now = time.UnixMilli(2000) + register := thirdPartyRegistration("ios") + register.Username = "finished" + register.DeviceID = "dev-ios-finished" + completedToken, isNewUser, err := svc.RegisterThirdParty(ctx, "wechat", "openid-pending-register", register, authservice.Meta{RequestID: "req-third-complete"}) + if err != nil { + t.Fatalf("RegisterThirdParty completion failed: %v", err) + } + if isNewUser || completedToken.UserID != pendingToken.UserID || !completedToken.ProfileCompleted || completedToken.OnboardingStatus != string(userdomain.OnboardingStatusCompleted) { + t.Fatalf("pending subject must complete existing user: token=%+v isNew=%v pending=%+v", completedToken, isNewUser, pendingToken) + } + user, err := repository.GetUser(ctx, pendingToken.UserID) + if err != nil { + t.Fatalf("GetUser failed: %v", err) + } + if user.Username != "finished" || user.Country != country.CountryCode || user.RegionID != region.RegionID || user.ProfileCompletedAtMs != now.UnixMilli() { + t.Fatalf("pending user completion mismatch: %+v", user) + } + assertUserRegisteredOutboxCount(t, repository, pendingToken.UserID, 1) + assertUserRegisteredOutboxFact(t, repository, pendingToken.UserID, country.CountryID, region.RegionID, now.UnixMilli()) +} + func TestThirdPartyRegisterRejectsReusedDeviceID(t *testing.T) { // 设备号只限制新账号注册;同一个 provider subject 的再次登录仍然可以创建新 session。 ctx := context.Background() diff --git a/services/user-service/internal/service/auth/third_party.go b/services/user-service/internal/service/auth/third_party.go index 4e167b9b..6395d679 100644 --- a/services/user-service/internal/service/auth/third_party.go +++ b/services/user-service/internal/service/auth/third_party.go @@ -95,6 +95,69 @@ func (s *Service) LoginThirdParty(ctx context.Context, provider string, credenti return s.createThirdPartyUser(ctx, provider, profile, registration, meta, maxAccountsPerDevice) } +// RegisterThirdParty 校验三方凭证并一次性完成 App 自然注册。 +// 该入口服务 Flutter 资料页完成动作:资料未齐不建用户,不向客户端暴露 profile_required session。 +func (s *Service) RegisterThirdParty(ctx context.Context, provider string, credential string, registration authdomain.ThirdPartyRegistration, meta Meta) (authdomain.Token, bool, error) { + meta.AppCode = appcode.Normalize(meta.AppCode) + registration.AppCode = meta.AppCode + provider = strings.ToLower(strings.TrimSpace(provider)) + credential = strings.TrimSpace(credential) + registration = normalizeThirdPartyRegistration(registration, meta) + meta.Platform = registration.Platform + meta.Language = registration.Language + meta.Timezone = registration.Timezone + if provider == "" || credential == "" { + // provider/credential 缺失时不能进入 verifier,避免把客户端参数错误伪装成三方认证失败。 + return authdomain.Token{}, false, xerr.New(xerr.InvalidArgument, "provider and credential are required") + } + if registration.DeviceID == "" || registration.Platform == "" { + // 注册完成后会立即创建 refresh session;缺设备或平台会让 session 和注册来源失去可追踪性。 + return authdomain.Token{}, false, xerr.New(xerr.InvalidArgument, "device_id and platform are required") + } + if err := validateThirdPartyRegistrationRequiredForCreate(registration); err != nil { + // 新注册入口承诺不落 pending 用户,因此 username/avatar/gender/country 必须在事务前全部具备。 + return authdomain.Token{}, false, err + } + if err := validateThirdPartyRegistration(registration); err != nil { + return authdomain.Token{}, false, err + } + if s.authRepository == nil { + return authdomain.Token{}, false, xerr.New(xerr.Unavailable, "auth repository is not configured") + } + if !s.TokenConfigValid() { + // 资料和三方绑定一旦提交就必须返回可用 token;签名配置不完整时提前失败,避免写出不可登录用户。 + return authdomain.Token{}, false, xerr.New(xerr.Unavailable, "token service is not configured") + } + + profile, err := s.thirdPartyVerifier.Verify(ctx, provider, credential) + if err != nil { + s.audit(ctx, meta, 0, loginThird, provider, resultFailed, string(xerr.AuthFailed)) + return authdomain.Token{}, false, authFailed() + } + + identity, err := s.authRepository.FindThirdPartyIdentity(ctx, provider, profile.ProviderSubject) + if err == nil { + // 兼容旧 Flutter 流程已创建的 profile_required 占位用户;同一 provider subject 不能新建第二个账号。 + return s.registerExistingThirdParty(ctx, identity, registration, meta) + } + if !xerr.IsCode(err, xerr.NotFound) { + logx.Error(logx.With(ctx, slog.String("request_id", meta.RequestID), slog.String("app_code", meta.AppCode)), "third_party_register_lookup_failed", err, slog.String("component", "user_auth"), slog.String("provider", provider)) + s.audit(ctx, meta, 0, loginThird, provider, resultFailed, string(xerr.CodeOf(err))) + return authdomain.Token{}, false, err + } + + maxAccountsPerDevice, err := s.ensureDeviceCanRegisterThirdParty(ctx, registration, meta, provider) + if err != nil { + return authdomain.Token{}, false, err + } + registration, err = s.resolveRegistrationCountry(ctx, registration) + if err != nil { + return authdomain.Token{}, false, err + } + + return s.createRegisteredThirdPartyUser(ctx, provider, profile, registration, meta, maxAccountsPerDevice) +} + func (s *Service) ensureDeviceCanRegisterThirdParty(ctx context.Context, registration authdomain.ThirdPartyRegistration, meta Meta, provider string) (int32, error) { // 设备号限制只约束“创建新账号”路径;已绑定三方身份登录不经过这里,避免把正常跨设备登录误拦。 config, err := s.GetRegisterRiskConfig(ctx) @@ -149,6 +212,59 @@ func (s *Service) loginExistingThirdParty(ctx context.Context, identity authdoma return token, false, nil } +func (s *Service) registerExistingThirdParty(ctx context.Context, identity authdomain.ThirdPartyIdentity, registration authdomain.ThirdPartyRegistration, meta Meta) (authdomain.Token, bool, error) { + // 已完成资料的同一三方身份按登录处理,保持客户端重试和多端登录的幂等语义。 + user, err := s.freshUser(ctx, identity.UserID, s.now().UnixMilli(), meta.RequestID) + if err != nil { + s.audit(ctx, meta, identity.UserID, loginThird, identity.Provider, resultFailed, string(xerr.CodeOf(err))) + return authdomain.Token{}, false, err + } + if !user.CanLogin() { + s.audit(ctx, meta, user.UserID, loginThird, identity.Provider, resultFailed, string(xerr.UserDisabled)) + return authdomain.Token{}, false, xerr.New(xerr.UserDisabled, "user is disabled") + } + if user.ProfileCompleted { + return s.loginExistingThirdParty(ctx, identity, registration, meta) + } + + registration, err = s.resolveRegistrationCountry(ctx, registration) + if err != nil { + s.audit(ctx, meta, identity.UserID, loginThird, identity.Provider, resultFailed, string(xerr.CodeOf(err))) + return authdomain.Token{}, false, err + } + session, refreshToken, err := s.newSession(meta.AppCode, identity.UserID, registration.DeviceID) + if err != nil { + return authdomain.Token{}, false, err + } + updated, err := s.authRepository.CompleteThirdPartyRegistration(ctx, userdomain.CompleteOnboardingCommand{ + AppCode: meta.AppCode, + UserID: identity.UserID, + Username: registration.Username, + Avatar: registration.Avatar, + Gender: registration.Gender, + Country: registration.Country, + RegionID: registration.RegionID, + InviteCode: registration.InviteCode, + RequestID: meta.RequestID, + CompletedAtMs: s.now().UnixMilli(), + }, session) + if err != nil { + s.audit(ctx, meta, identity.UserID, loginThird, identity.Provider, resultFailed, string(xerr.CodeOf(err))) + return authdomain.Token{}, false, err + } + + s.audit(ctx, meta, updated.UserID, loginThird, identity.Provider, resultSuccess, "") + token, err := s.issueToken(updated, session.SessionID, refreshToken) + if err != nil { + return authdomain.Token{}, false, err + } + s.importIMAccountAfterCommit(ctx, updated, meta) + s.enqueueLoginIPRiskJob(ctx, meta, token, normalizeThirdPartyChannel(identity.Provider), registration.Platform, registration.Language, registration.Timezone) + s.issueRegistrationRewardAfterCommit(ctx, registration.AppCode, updated.UserID, meta) + s.issueRegistrationLevelBadgesAfterCommit(ctx, registration.AppCode, updated.UserID, meta) + return token, false, nil +} + func (s *Service) createThirdPartyUser(ctx context.Context, provider string, profile authdomain.ThirdPartyProfile, registration authdomain.ThirdPartyRegistration, meta Meta, maxAccountsPerDevice int32) (authdomain.Token, bool, error) { var lastErr error for attempt := 0; attempt < s.allocateMaxAttempts; attempt++ { @@ -231,6 +347,86 @@ func (s *Service) createThirdPartyUser(ctx context.Context, provider string, pro return authdomain.Token{}, false, xerr.New(xerr.DisplayUserIDAllocateFailed, "display_user_id allocation failed") } +func (s *Service) createRegisteredThirdPartyUser(ctx context.Context, provider string, profile authdomain.ThirdPartyProfile, registration authdomain.ThirdPartyRegistration, meta Meta, maxAccountsPerDevice int32) (authdomain.Token, bool, error) { + var lastErr error + for attempt := 0; attempt < s.allocateMaxAttempts; attempt++ { + // 注册专用入口直接创建 completed 用户;所有公开资料、绑定、session 和注册事实必须同事务提交。 + user, displayIdentity := s.newUserWithIdentity(attempt) + user = applyThirdPartyRegistration(user, registration) + user.ProfileCompleted = true + user.ProfileCompletedAtMs = user.CreatedAtMs + user.OnboardingStatus = userdomain.OnboardingStatusCompleted + displayUserID, err := s.authRepository.AllocateDisplayUserID(ctx, registration.AppCode) + if err != nil { + return authdomain.Token{}, false, err + } + user.DefaultDisplayUserID = displayUserID + user.CurrentDisplayUserID = displayUserID + displayIdentity.DisplayUserID = displayUserID + displayIdentity.DefaultDisplayUserID = displayUserID + displayIdentity.AppCode = registration.AppCode + if !userdomain.ValidDisplayUserID(displayIdentity.DisplayUserID) { + // 非法候选不写库,继续拿下一组 user_id/display_user_id,保持默认短号约束一致。 + continue + } + session, refreshToken, err := s.newSession(registration.AppCode, user.UserID, registration.DeviceID) + if err != nil { + return authdomain.Token{}, false, err + } + thirdParty := authdomain.ThirdPartyIdentity{ + AppCode: registration.AppCode, + UserID: user.UserID, + Provider: provider, + ProviderSubject: profile.ProviderSubject, + ProviderUnionID: profile.ProviderUnionID, + CreatedAtMs: user.CreatedAtMs, + UpdatedAtMs: user.UpdatedAtMs, + } + inviteBind := invitedomain.BindCommand{ + AppCode: registration.AppCode, + InvitedUserID: user.UserID, + InvitedRegionID: registration.RegionID, + InviteCode: registration.InviteCode, + Source: "third_party_register", + RequestID: meta.RequestID, + BoundAtMs: user.CreatedAtMs, + } + err = s.authRepository.CreateThirdPartyUser(ctx, user, displayIdentity, thirdParty, session, inviteBind, maxAccountsPerDevice) + if err == nil { + s.audit(ctx, meta, user.UserID, loginThird, provider, resultSuccess, "") + token, tokenErr := s.issueToken(user, session.SessionID, refreshToken) + if tokenErr != nil { + return authdomain.Token{}, false, tokenErr + } + s.importIMAccountAfterCommit(ctx, user, meta) + s.enqueueLoginIPRiskJob(ctx, meta, token, normalizeThirdPartyChannel(provider), registration.Platform, registration.Language, registration.Timezone) + s.issueRegistrationRewardAfterCommit(ctx, registration.AppCode, user.UserID, meta) + s.issueRegistrationLevelBadgesAfterCommit(ctx, registration.AppCode, user.UserID, meta) + return token, true, nil + } + if xerr.IsCode(err, xerr.DisplayUserIDExists) { + // 默认短号冲突可重试;同一次 Firebase subject 不会因为短号冲突暴露成注册失败。 + lastErr = err + continue + } + if xerr.IsCode(err, xerr.Conflict) { + identity, findErr := s.authRepository.FindThirdPartyIdentity(ctx, provider, profile.ProviderSubject) + if findErr == nil { + return s.registerExistingThirdParty(ctx, identity, registration, meta) + } + } + + s.audit(ctx, meta, 0, loginThird, provider, resultFailed, string(xerr.CodeOf(err))) + logx.Error(logx.With(ctx, slog.String("request_id", meta.RequestID), slog.String("app_code", meta.AppCode)), "third_party_complete_register_failed", err, slog.String("component", "user_auth"), slog.String("provider", provider)) + return authdomain.Token{}, false, err + } + + if lastErr != nil { + s.audit(ctx, meta, 0, loginThird, provider, resultFailed, string(xerr.DisplayUserIDAllocateFailed)) + } + return authdomain.Token{}, false, xerr.New(xerr.DisplayUserIDAllocateFailed, "display_user_id allocation failed") +} + func (s *Service) issueRegistrationRewardAfterCommit(ctx context.Context, appCode string, userID int64, meta Meta) { if s.registrationRewardIssuer == nil { return diff --git a/services/user-service/internal/storage/mysql/auth/third_party.go b/services/user-service/internal/storage/mysql/auth/third_party.go index 870da1e3..3ff3db2b 100644 --- a/services/user-service/internal/storage/mysql/auth/third_party.go +++ b/services/user-service/internal/storage/mysql/auth/third_party.go @@ -127,7 +127,112 @@ func (r *Repository) CreateThirdPartyUser(ctx context.Context, user userdomain.U // session 插入失败会回滚用户和三方绑定。 return err } + if err := userstorage.InsertUserRegisteredOutbox(ctx, tx, user); err != nil { + // 完整注册入口会传入 profile_completed=true;旧三方登录仍是 pending 用户,这里会按用户状态自动跳过。 + return err + } return tx.Commit() } +// CompleteThirdPartyRegistration 原子补齐旧三方 pending 用户资料并写入可续期 session。 +// 它只服务 provider subject 已绑定但尚未 completed 的兼容路径,避免 Flutter 完成资料时再创建第二个用户。 +func (r *Repository) CompleteThirdPartyRegistration(ctx context.Context, command userdomain.CompleteOnboardingCommand, session authdomain.Session) (userdomain.User, error) { + tx, err := r.db.BeginTx(ctx, nil) + if err != nil { + return userdomain.User{}, err + } + defer tx.Rollback() + + user, err := userstorage.QueryUser(ctx, tx, "WHERE user_id = ? FOR UPDATE", command.UserID) + if err == sql.ErrNoRows { + return userdomain.User{}, xerr.New(xerr.NotFound, "user not found") + } + if err != nil { + return userdomain.User{}, err + } + if !user.CanLogin() { + // pending 用户可能已被后台禁用;注册完成入口不能绕过用户状态限制创建新 session。 + return userdomain.User{}, xerr.New(xerr.UserDisabled, "user is disabled") + } + if user.ProfileCompleted { + // 幂等兼容:已完成用户只补 session,不重写用户资料,避免重复提交覆盖用户后续修改。 + if err := insertSession(ctx, tx, session); err != nil { + return userdomain.User{}, err + } + if err := tx.Commit(); err != nil { + return userdomain.User{}, err + } + return userstorage.New(r.db).GetUser(ctx, command.UserID) + } + + inviteSnapshot := command.InviteCode + if command.InviteCode != "" { + binding, err := invitestorage.BindRelation(ctx, tx, invitedomain.BindCommand{ + AppCode: command.AppCode, + InvitedUserID: command.UserID, + InvitedRegionID: command.RegionID, + InviteCode: command.InviteCode, + Source: "third_party_register", + RequestID: command.RequestID, + BoundAtMs: command.CompletedAtMs, + }) + if err != nil { + return userdomain.User{}, err + } + if binding.InviteCode != "" { + inviteSnapshot = binding.InviteCode + user.InviteBinding = binding + } + } + + _, err = tx.ExecContext(ctx, ` + UPDATE users + SET username = ?, + avatar = ?, + gender = ?, + country = ?, + region_id = ?, + invite_code = CASE WHEN ? = '' THEN invite_code ELSE ? END, + profile_completed = ?, + profile_completed_at_ms = ?, + onboarding_status = ?, + updated_at_ms = ? + WHERE user_id = ? + AND app_code = ? + `, command.Username, command.Avatar, command.Gender, command.Country, shared.NullableRegionID(command.RegionID), inviteSnapshot, inviteSnapshot, true, command.CompletedAtMs, string(userdomain.OnboardingStatusCompleted), command.CompletedAtMs, command.UserID, appcode.Normalize(command.AppCode)) + if err != nil { + return userdomain.User{}, err + } + user.Username = command.Username + user.Avatar = command.Avatar + user.Gender = command.Gender + user.Country = command.Country + user.RegionID = command.RegionID + if inviteSnapshot != "" { + user.InviteCode = inviteSnapshot + } + user.ProfileCompleted = true + user.ProfileCompletedAtMs = command.CompletedAtMs + user.OnboardingStatus = userdomain.OnboardingStatusCompleted + user.UpdatedAtMs = command.CompletedAtMs + if err := insertSession(ctx, tx, session); err != nil { + // session 和资料完成必须同事务提交,否则 Flutter 会拿不到可落 AppAuthSession 的 refresh token。 + return userdomain.User{}, err + } + if err := userstorage.InsertUserRegisteredOutbox(ctx, tx, user); err != nil { + // UserRegistered 是统计和注册完成事实,必须和首次资料完成一起提交。 + return userdomain.User{}, err + } + if err := tx.Commit(); err != nil { + return userdomain.User{}, err + } + + updated, err := userstorage.New(r.db).GetUser(ctx, command.UserID) + if err != nil { + return userdomain.User{}, err + } + updated.InviteBinding = user.InviteBinding + return updated, nil +} + // RecordLoginAudit 写登录审计;调用方不会让审计失败影响主链路。 diff --git a/services/user-service/internal/testutil/mysqltest/mysqltest.go b/services/user-service/internal/testutil/mysqltest/mysqltest.go index 94cb23f3..1c246ae8 100644 --- a/services/user-service/internal/testutil/mysqltest/mysqltest.go +++ b/services/user-service/internal/testutil/mysqltest/mysqltest.go @@ -369,6 +369,11 @@ func (r *Repository) CreateThirdPartyUser(ctx context.Context, user userdomain.U return r.Repository.AuthRepository().CreateThirdPartyUser(ctx, user, identity, thirdParty, session, invite, maxAccountsPerDevice) } +// CompleteThirdPartyRegistration 让测试 wrapper 继续满足注册完成兼容接口。 +func (r *Repository) CompleteThirdPartyRegistration(ctx context.Context, command userdomain.CompleteOnboardingCommand, session authdomain.Session) (userdomain.User, error) { + return r.Repository.AuthRepository().CompleteThirdPartyRegistration(ctx, command, session) +} + // GetRegisterRiskConfig 让测试 wrapper 继续满足认证接口。 func (r *Repository) GetRegisterRiskConfig(ctx context.Context, appCode string) (authdomain.RegisterRiskConfig, error) { return r.Repository.AuthRepository().GetRegisterRiskConfig(ctx, appCode) diff --git a/services/user-service/internal/transport/grpc/server.go b/services/user-service/internal/transport/grpc/server.go index d8c7249b..dad9e881 100644 --- a/services/user-service/internal/transport/grpc/server.go +++ b/services/user-service/internal/transport/grpc/server.go @@ -139,6 +139,38 @@ func (s *Server) LoginThirdParty(ctx context.Context, req *userv1.LoginThirdPart return &userv1.AuthResponse{Token: toProtoToken(token), IsNewUser: isNewUser, ProfileCompleted: token.ProfileCompleted, OnboardingStatus: token.OnboardingStatus}, nil } +// RegisterThirdParty 在资料页完成时一次性校验 provider credential、创建或补齐用户并签发完整 session。 +func (s *Server) RegisterThirdParty(ctx context.Context, req *userv1.RegisterThirdPartyRequest) (*userv1.AuthResponse, error) { + ctx = contextWithApp(ctx, req.GetMeta()) + registration := authdomain.ThirdPartyRegistration{ + Username: req.GetUsername(), + Gender: req.GetGender(), + Country: req.GetCountry(), + InviteCode: req.GetInviteCode(), + IP: req.GetMeta().GetClientIp(), + DeviceID: req.GetDeviceId(), + Device: req.GetDevice(), + OSVersion: req.GetOsVersion(), + Avatar: req.GetAvatar(), + BirthDate: req.GetBirth(), + AppVersion: req.GetAppVersion(), + BuildNumber: req.GetBuildNumber(), + Source: req.GetSource(), + InstallChannel: req.GetInstallChannel(), + Campaign: req.GetCampaign(), + Platform: req.GetPlatform(), + Language: req.GetLanguage(), + Timezone: req.GetTimezone(), + } + // 注册专用 RPC 不返回 profile_required token;service 层会在写库前验证资料完整度。 + token, isNewUser, err := s.authSvc.RegisterThirdParty(ctx, req.GetProvider(), req.GetCredential(), registration, authMeta(req.GetMeta())) + if err != nil { + return nil, xerr.ToGRPCError(err) + } + + return &userv1.AuthResponse{Token: toProtoToken(token), IsNewUser: isNewUser, ProfileCompleted: token.ProfileCompleted, OnboardingStatus: token.OnboardingStatus}, nil +} + // SetPassword 为已登录用户首次写入密码身份。 func (s *Server) SetPassword(ctx context.Context, req *userv1.SetPasswordRequest) (*userv1.SetPasswordResponse, error) { ctx = contextWithApp(ctx, req.GetMeta())