feat(auth): Complete Auth Service implementation and fix Consul health checks

- Add VerifyPassword RPC to Identity Service
  - Added to proto file and generated code
  - Implemented in Identity Service gRPC server
  - Added to Identity Service client interface and gRPC client

- Complete RefreshToken implementation
  - Store refresh tokens in database using RefreshToken entity
  - Validate refresh tokens with expiration checking
  - Revoke refresh tokens on logout and token rotation

- Integrate Authz Service for role retrieval
  - Added AuthzServiceClient to Auth Service
  - Get user roles during login and token refresh
  - Gracefully handle Authz Service failures

- Require JWT secret in configuration
  - Removed default secret fallback
  - Service fails to start if JWT secret is not configured

- Fix Consul health checks for Docker
  - Services now register with Docker service names (e.g., audit-service)
  - Allows Consul (in Docker) to reach services via Docker DNS
  - Health checks use gRPC service names instead of localhost

This completes all TODOs in auth_service_fx.go and fixes the Consul
health check failures in Docker environments.
This commit is contained in:
2025-11-06 21:26:34 +01:00
parent b02c1d44c8
commit 04022b835e
34 changed files with 6775 additions and 90 deletions

View File

@@ -914,6 +914,104 @@ func (x *ResetPasswordResponse) GetSuccess() bool {
return false
}
// VerifyPasswordRequest contains email and password.
type VerifyPasswordRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"`
Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *VerifyPasswordRequest) Reset() {
*x = VerifyPasswordRequest{}
mi := &file_identity_proto_msgTypes[17]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *VerifyPasswordRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*VerifyPasswordRequest) ProtoMessage() {}
func (x *VerifyPasswordRequest) ProtoReflect() protoreflect.Message {
mi := &file_identity_proto_msgTypes[17]
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 VerifyPasswordRequest.ProtoReflect.Descriptor instead.
func (*VerifyPasswordRequest) Descriptor() ([]byte, []int) {
return file_identity_proto_rawDescGZIP(), []int{17}
}
func (x *VerifyPasswordRequest) GetEmail() string {
if x != nil {
return x.Email
}
return ""
}
func (x *VerifyPasswordRequest) GetPassword() string {
if x != nil {
return x.Password
}
return ""
}
// VerifyPasswordResponse contains the user if password is valid.
type VerifyPasswordResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *VerifyPasswordResponse) Reset() {
*x = VerifyPasswordResponse{}
mi := &file_identity_proto_msgTypes[18]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *VerifyPasswordResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*VerifyPasswordResponse) ProtoMessage() {}
func (x *VerifyPasswordResponse) ProtoReflect() protoreflect.Message {
mi := &file_identity_proto_msgTypes[18]
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 VerifyPasswordResponse.ProtoReflect.Descriptor instead.
func (*VerifyPasswordResponse) Descriptor() ([]byte, []int) {
return file_identity_proto_rawDescGZIP(), []int{18}
}
func (x *VerifyPasswordResponse) GetUser() *User {
if x != nil {
return x.User
}
return nil
}
var File_identity_proto protoreflect.FileDescriptor
const file_identity_proto_rawDesc = "" +
@@ -978,7 +1076,12 @@ const file_identity_proto_rawDesc = "" +
"\x05token\x18\x01 \x01(\tR\x05token\x12!\n" +
"\fnew_password\x18\x02 \x01(\tR\vnewPassword\"1\n" +
"\x15ResetPasswordResponse\x12\x18\n" +
"\asuccess\x18\x01 \x01(\bR\asuccess2\xb6\x05\n" +
"\asuccess\x18\x01 \x01(\bR\asuccess\"I\n" +
"\x15VerifyPasswordRequest\x12\x14\n" +
"\x05email\x18\x01 \x01(\tR\x05email\x12\x1a\n" +
"\bpassword\x18\x02 \x01(\tR\bpassword\"?\n" +
"\x16VerifyPasswordResponse\x12%\n" +
"\x04user\x18\x01 \x01(\v2\x11.identity.v1.UserR\x04user2\x91\x06\n" +
"\x0fIdentityService\x12D\n" +
"\aGetUser\x12\x1b.identity.v1.GetUserRequest\x1a\x1c.identity.v1.GetUserResponse\x12Y\n" +
"\x0eGetUserByEmail\x12\".identity.v1.GetUserByEmailRequest\x1a#.identity.v1.GetUserByEmailResponse\x12M\n" +
@@ -990,7 +1093,8 @@ const file_identity_proto_rawDesc = "" +
"DeleteUser\x12\x1e.identity.v1.DeleteUserRequest\x1a\x1f.identity.v1.DeleteUserResponse\x12P\n" +
"\vVerifyEmail\x12\x1f.identity.v1.VerifyEmailRequest\x1a .identity.v1.VerifyEmailResponse\x12k\n" +
"\x14RequestPasswordReset\x12(.identity.v1.RequestPasswordResetRequest\x1a).identity.v1.RequestPasswordResetResponse\x12V\n" +
"\rResetPassword\x12!.identity.v1.ResetPasswordRequest\x1a\".identity.v1.ResetPasswordResponseBMZKgit.dcentral.systems/toolz/goplt/api/proto/generated/identity/v1;identityv1b\x06proto3"
"\rResetPassword\x12!.identity.v1.ResetPasswordRequest\x1a\".identity.v1.ResetPasswordResponse\x12Y\n" +
"\x0eVerifyPassword\x12\".identity.v1.VerifyPasswordRequest\x1a#.identity.v1.VerifyPasswordResponseBMZKgit.dcentral.systems/toolz/goplt/api/proto/generated/identity/v1;identityv1b\x06proto3"
var (
file_identity_proto_rawDescOnce sync.Once
@@ -1004,7 +1108,7 @@ func file_identity_proto_rawDescGZIP() []byte {
return file_identity_proto_rawDescData
}
var file_identity_proto_msgTypes = make([]protoimpl.MessageInfo, 17)
var file_identity_proto_msgTypes = make([]protoimpl.MessageInfo, 19)
var file_identity_proto_goTypes = []any{
(*User)(nil), // 0: identity.v1.User
(*GetUserRequest)(nil), // 1: identity.v1.GetUserRequest
@@ -1023,33 +1127,38 @@ var file_identity_proto_goTypes = []any{
(*RequestPasswordResetResponse)(nil), // 14: identity.v1.RequestPasswordResetResponse
(*ResetPasswordRequest)(nil), // 15: identity.v1.ResetPasswordRequest
(*ResetPasswordResponse)(nil), // 16: identity.v1.ResetPasswordResponse
(*VerifyPasswordRequest)(nil), // 17: identity.v1.VerifyPasswordRequest
(*VerifyPasswordResponse)(nil), // 18: identity.v1.VerifyPasswordResponse
}
var file_identity_proto_depIdxs = []int32{
0, // 0: identity.v1.GetUserResponse.user:type_name -> identity.v1.User
0, // 1: identity.v1.GetUserByEmailResponse.user:type_name -> identity.v1.User
0, // 2: identity.v1.CreateUserResponse.user:type_name -> identity.v1.User
0, // 3: identity.v1.UpdateUserResponse.user:type_name -> identity.v1.User
1, // 4: identity.v1.IdentityService.GetUser:input_type -> identity.v1.GetUserRequest
3, // 5: identity.v1.IdentityService.GetUserByEmail:input_type -> identity.v1.GetUserByEmailRequest
5, // 6: identity.v1.IdentityService.CreateUser:input_type -> identity.v1.CreateUserRequest
7, // 7: identity.v1.IdentityService.UpdateUser:input_type -> identity.v1.UpdateUserRequest
9, // 8: identity.v1.IdentityService.DeleteUser:input_type -> identity.v1.DeleteUserRequest
11, // 9: identity.v1.IdentityService.VerifyEmail:input_type -> identity.v1.VerifyEmailRequest
13, // 10: identity.v1.IdentityService.RequestPasswordReset:input_type -> identity.v1.RequestPasswordResetRequest
15, // 11: identity.v1.IdentityService.ResetPassword:input_type -> identity.v1.ResetPasswordRequest
2, // 12: identity.v1.IdentityService.GetUser:output_type -> identity.v1.GetUserResponse
4, // 13: identity.v1.IdentityService.GetUserByEmail:output_type -> identity.v1.GetUserByEmailResponse
6, // 14: identity.v1.IdentityService.CreateUser:output_type -> identity.v1.CreateUserResponse
8, // 15: identity.v1.IdentityService.UpdateUser:output_type -> identity.v1.UpdateUserResponse
10, // 16: identity.v1.IdentityService.DeleteUser:output_type -> identity.v1.DeleteUserResponse
12, // 17: identity.v1.IdentityService.VerifyEmail:output_type -> identity.v1.VerifyEmailResponse
14, // 18: identity.v1.IdentityService.RequestPasswordReset:output_type -> identity.v1.RequestPasswordResetResponse
16, // 19: identity.v1.IdentityService.ResetPassword:output_type -> identity.v1.ResetPasswordResponse
12, // [12:20] is the sub-list for method output_type
4, // [4:12] is the sub-list for method input_type
4, // [4:4] is the sub-list for extension type_name
4, // [4:4] is the sub-list for extension extendee
0, // [0:4] is the sub-list for field type_name
0, // 4: identity.v1.VerifyPasswordResponse.user:type_name -> identity.v1.User
1, // 5: identity.v1.IdentityService.GetUser:input_type -> identity.v1.GetUserRequest
3, // 6: identity.v1.IdentityService.GetUserByEmail:input_type -> identity.v1.GetUserByEmailRequest
5, // 7: identity.v1.IdentityService.CreateUser:input_type -> identity.v1.CreateUserRequest
7, // 8: identity.v1.IdentityService.UpdateUser:input_type -> identity.v1.UpdateUserRequest
9, // 9: identity.v1.IdentityService.DeleteUser:input_type -> identity.v1.DeleteUserRequest
11, // 10: identity.v1.IdentityService.VerifyEmail:input_type -> identity.v1.VerifyEmailRequest
13, // 11: identity.v1.IdentityService.RequestPasswordReset:input_type -> identity.v1.RequestPasswordResetRequest
15, // 12: identity.v1.IdentityService.ResetPassword:input_type -> identity.v1.ResetPasswordRequest
17, // 13: identity.v1.IdentityService.VerifyPassword:input_type -> identity.v1.VerifyPasswordRequest
2, // 14: identity.v1.IdentityService.GetUser:output_type -> identity.v1.GetUserResponse
4, // 15: identity.v1.IdentityService.GetUserByEmail:output_type -> identity.v1.GetUserByEmailResponse
6, // 16: identity.v1.IdentityService.CreateUser:output_type -> identity.v1.CreateUserResponse
8, // 17: identity.v1.IdentityService.UpdateUser:output_type -> identity.v1.UpdateUserResponse
10, // 18: identity.v1.IdentityService.DeleteUser:output_type -> identity.v1.DeleteUserResponse
12, // 19: identity.v1.IdentityService.VerifyEmail:output_type -> identity.v1.VerifyEmailResponse
14, // 20: identity.v1.IdentityService.RequestPasswordReset:output_type -> identity.v1.RequestPasswordResetResponse
16, // 21: identity.v1.IdentityService.ResetPassword:output_type -> identity.v1.ResetPasswordResponse
18, // 22: identity.v1.IdentityService.VerifyPassword:output_type -> identity.v1.VerifyPasswordResponse
14, // [14:23] is the sub-list for method output_type
5, // [5:14] is the sub-list for method input_type
5, // [5:5] is the sub-list for extension type_name
5, // [5:5] is the sub-list for extension extendee
0, // [0:5] is the sub-list for field type_name
}
func init() { file_identity_proto_init() }
@@ -1064,7 +1173,7 @@ func file_identity_proto_init() {
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_identity_proto_rawDesc), len(file_identity_proto_rawDesc)),
NumEnums: 0,
NumMessages: 17,
NumMessages: 19,
NumExtensions: 0,
NumServices: 1,
},

View File

@@ -27,6 +27,7 @@ const (
IdentityService_VerifyEmail_FullMethodName = "/identity.v1.IdentityService/VerifyEmail"
IdentityService_RequestPasswordReset_FullMethodName = "/identity.v1.IdentityService/RequestPasswordReset"
IdentityService_ResetPassword_FullMethodName = "/identity.v1.IdentityService/ResetPassword"
IdentityService_VerifyPassword_FullMethodName = "/identity.v1.IdentityService/VerifyPassword"
)
// IdentityServiceClient is the client API for IdentityService service.
@@ -51,6 +52,8 @@ type IdentityServiceClient interface {
RequestPasswordReset(ctx context.Context, in *RequestPasswordResetRequest, opts ...grpc.CallOption) (*RequestPasswordResetResponse, error)
// ResetPassword resets a user's password using a reset token.
ResetPassword(ctx context.Context, in *ResetPasswordRequest, opts ...grpc.CallOption) (*ResetPasswordResponse, error)
// VerifyPassword verifies a user's password.
VerifyPassword(ctx context.Context, in *VerifyPasswordRequest, opts ...grpc.CallOption) (*VerifyPasswordResponse, error)
}
type identityServiceClient struct {
@@ -141,6 +144,16 @@ func (c *identityServiceClient) ResetPassword(ctx context.Context, in *ResetPass
return out, nil
}
func (c *identityServiceClient) VerifyPassword(ctx context.Context, in *VerifyPasswordRequest, opts ...grpc.CallOption) (*VerifyPasswordResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(VerifyPasswordResponse)
err := c.cc.Invoke(ctx, IdentityService_VerifyPassword_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// IdentityServiceServer is the server API for IdentityService service.
// All implementations must embed UnimplementedIdentityServiceServer
// for forward compatibility.
@@ -163,6 +176,8 @@ type IdentityServiceServer interface {
RequestPasswordReset(context.Context, *RequestPasswordResetRequest) (*RequestPasswordResetResponse, error)
// ResetPassword resets a user's password using a reset token.
ResetPassword(context.Context, *ResetPasswordRequest) (*ResetPasswordResponse, error)
// VerifyPassword verifies a user's password.
VerifyPassword(context.Context, *VerifyPasswordRequest) (*VerifyPasswordResponse, error)
mustEmbedUnimplementedIdentityServiceServer()
}
@@ -197,6 +212,9 @@ func (UnimplementedIdentityServiceServer) RequestPasswordReset(context.Context,
func (UnimplementedIdentityServiceServer) ResetPassword(context.Context, *ResetPasswordRequest) (*ResetPasswordResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method ResetPassword not implemented")
}
func (UnimplementedIdentityServiceServer) VerifyPassword(context.Context, *VerifyPasswordRequest) (*VerifyPasswordResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method VerifyPassword not implemented")
}
func (UnimplementedIdentityServiceServer) mustEmbedUnimplementedIdentityServiceServer() {}
func (UnimplementedIdentityServiceServer) testEmbeddedByValue() {}
@@ -362,6 +380,24 @@ func _IdentityService_ResetPassword_Handler(srv interface{}, ctx context.Context
return interceptor(ctx, in, info, handler)
}
func _IdentityService_VerifyPassword_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(VerifyPasswordRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(IdentityServiceServer).VerifyPassword(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: IdentityService_VerifyPassword_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(IdentityServiceServer).VerifyPassword(ctx, req.(*VerifyPasswordRequest))
}
return interceptor(ctx, in, info, handler)
}
// IdentityService_ServiceDesc is the grpc.ServiceDesc for IdentityService service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
@@ -401,6 +437,10 @@ var IdentityService_ServiceDesc = grpc.ServiceDesc{
MethodName: "ResetPassword",
Handler: _IdentityService_ResetPassword_Handler,
},
{
MethodName: "VerifyPassword",
Handler: _IdentityService_VerifyPassword_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "identity.proto",