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

@@ -0,0 +1,568 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.10
// protoc v6.30.2
// source: auth.proto
package authv1
import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
unsafe "unsafe"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// LoginRequest contains login credentials.
type LoginRequest 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 *LoginRequest) Reset() {
*x = LoginRequest{}
mi := &file_auth_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *LoginRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*LoginRequest) ProtoMessage() {}
func (x *LoginRequest) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[0]
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 LoginRequest.ProtoReflect.Descriptor instead.
func (*LoginRequest) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{0}
}
func (x *LoginRequest) GetEmail() string {
if x != nil {
return x.Email
}
return ""
}
func (x *LoginRequest) GetPassword() string {
if x != nil {
return x.Password
}
return ""
}
// LoginResponse contains authentication tokens.
type LoginResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
RefreshToken string `protobuf:"bytes,2,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"`
ExpiresIn int64 `protobuf:"varint,3,opt,name=expires_in,json=expiresIn,proto3" json:"expires_in,omitempty"` // seconds
TokenType string `protobuf:"bytes,4,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` // "Bearer"
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *LoginResponse) Reset() {
*x = LoginResponse{}
mi := &file_auth_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *LoginResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*LoginResponse) ProtoMessage() {}
func (x *LoginResponse) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[1]
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 LoginResponse.ProtoReflect.Descriptor instead.
func (*LoginResponse) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{1}
}
func (x *LoginResponse) GetAccessToken() string {
if x != nil {
return x.AccessToken
}
return ""
}
func (x *LoginResponse) GetRefreshToken() string {
if x != nil {
return x.RefreshToken
}
return ""
}
func (x *LoginResponse) GetExpiresIn() int64 {
if x != nil {
return x.ExpiresIn
}
return 0
}
func (x *LoginResponse) GetTokenType() string {
if x != nil {
return x.TokenType
}
return ""
}
// RefreshTokenRequest contains a refresh token.
type RefreshTokenRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
RefreshToken string `protobuf:"bytes,1,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RefreshTokenRequest) Reset() {
*x = RefreshTokenRequest{}
mi := &file_auth_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RefreshTokenRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RefreshTokenRequest) ProtoMessage() {}
func (x *RefreshTokenRequest) ProtoReflect() protoreflect.Message {
mi := &file_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 RefreshTokenRequest.ProtoReflect.Descriptor instead.
func (*RefreshTokenRequest) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{2}
}
func (x *RefreshTokenRequest) GetRefreshToken() string {
if x != nil {
return x.RefreshToken
}
return ""
}
// RefreshTokenResponse contains new authentication tokens.
type RefreshTokenResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
AccessToken string `protobuf:"bytes,1,opt,name=access_token,json=accessToken,proto3" json:"access_token,omitempty"`
RefreshToken string `protobuf:"bytes,2,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"`
ExpiresIn int64 `protobuf:"varint,3,opt,name=expires_in,json=expiresIn,proto3" json:"expires_in,omitempty"` // seconds
TokenType string `protobuf:"bytes,4,opt,name=token_type,json=tokenType,proto3" json:"token_type,omitempty"` // "Bearer"
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *RefreshTokenResponse) Reset() {
*x = RefreshTokenResponse{}
mi := &file_auth_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *RefreshTokenResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*RefreshTokenResponse) ProtoMessage() {}
func (x *RefreshTokenResponse) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[3]
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 RefreshTokenResponse.ProtoReflect.Descriptor instead.
func (*RefreshTokenResponse) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{3}
}
func (x *RefreshTokenResponse) GetAccessToken() string {
if x != nil {
return x.AccessToken
}
return ""
}
func (x *RefreshTokenResponse) GetRefreshToken() string {
if x != nil {
return x.RefreshToken
}
return ""
}
func (x *RefreshTokenResponse) GetExpiresIn() int64 {
if x != nil {
return x.ExpiresIn
}
return 0
}
func (x *RefreshTokenResponse) GetTokenType() string {
if x != nil {
return x.TokenType
}
return ""
}
// ValidateTokenRequest contains a JWT token to validate.
type ValidateTokenRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ValidateTokenRequest) Reset() {
*x = ValidateTokenRequest{}
mi := &file_auth_proto_msgTypes[4]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ValidateTokenRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ValidateTokenRequest) ProtoMessage() {}
func (x *ValidateTokenRequest) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[4]
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 ValidateTokenRequest.ProtoReflect.Descriptor instead.
func (*ValidateTokenRequest) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{4}
}
func (x *ValidateTokenRequest) GetToken() string {
if x != nil {
return x.Token
}
return ""
}
// ValidateTokenResponse contains token claims.
type ValidateTokenResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
UserId string `protobuf:"bytes,1,opt,name=user_id,json=userId,proto3" json:"user_id,omitempty"`
Email string `protobuf:"bytes,2,opt,name=email,proto3" json:"email,omitempty"`
Roles []string `protobuf:"bytes,3,rep,name=roles,proto3" json:"roles,omitempty"`
ExpiresAt int64 `protobuf:"varint,4,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ValidateTokenResponse) Reset() {
*x = ValidateTokenResponse{}
mi := &file_auth_proto_msgTypes[5]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ValidateTokenResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ValidateTokenResponse) ProtoMessage() {}
func (x *ValidateTokenResponse) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[5]
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 ValidateTokenResponse.ProtoReflect.Descriptor instead.
func (*ValidateTokenResponse) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{5}
}
func (x *ValidateTokenResponse) GetUserId() string {
if x != nil {
return x.UserId
}
return ""
}
func (x *ValidateTokenResponse) GetEmail() string {
if x != nil {
return x.Email
}
return ""
}
func (x *ValidateTokenResponse) GetRoles() []string {
if x != nil {
return x.Roles
}
return nil
}
func (x *ValidateTokenResponse) GetExpiresAt() int64 {
if x != nil {
return x.ExpiresAt
}
return 0
}
// LogoutRequest contains a refresh token to invalidate.
type LogoutRequest struct {
state protoimpl.MessageState `protogen:"open.v1"`
RefreshToken string `protobuf:"bytes,1,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *LogoutRequest) Reset() {
*x = LogoutRequest{}
mi := &file_auth_proto_msgTypes[6]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *LogoutRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*LogoutRequest) ProtoMessage() {}
func (x *LogoutRequest) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[6]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use LogoutRequest.ProtoReflect.Descriptor instead.
func (*LogoutRequest) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{6}
}
func (x *LogoutRequest) GetRefreshToken() string {
if x != nil {
return x.RefreshToken
}
return ""
}
// LogoutResponse indicates success.
type LogoutResponse struct {
state protoimpl.MessageState `protogen:"open.v1"`
Success bool `protobuf:"varint,1,opt,name=success,proto3" json:"success,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *LogoutResponse) Reset() {
*x = LogoutResponse{}
mi := &file_auth_proto_msgTypes[7]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *LogoutResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*LogoutResponse) ProtoMessage() {}
func (x *LogoutResponse) ProtoReflect() protoreflect.Message {
mi := &file_auth_proto_msgTypes[7]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use LogoutResponse.ProtoReflect.Descriptor instead.
func (*LogoutResponse) Descriptor() ([]byte, []int) {
return file_auth_proto_rawDescGZIP(), []int{7}
}
func (x *LogoutResponse) GetSuccess() bool {
if x != nil {
return x.Success
}
return false
}
var File_auth_proto protoreflect.FileDescriptor
const file_auth_proto_rawDesc = "" +
"\n" +
"\n" +
"auth.proto\x12\aauth.v1\"@\n" +
"\fLoginRequest\x12\x14\n" +
"\x05email\x18\x01 \x01(\tR\x05email\x12\x1a\n" +
"\bpassword\x18\x02 \x01(\tR\bpassword\"\x95\x01\n" +
"\rLoginResponse\x12!\n" +
"\faccess_token\x18\x01 \x01(\tR\vaccessToken\x12#\n" +
"\rrefresh_token\x18\x02 \x01(\tR\frefreshToken\x12\x1d\n" +
"\n" +
"expires_in\x18\x03 \x01(\x03R\texpiresIn\x12\x1d\n" +
"\n" +
"token_type\x18\x04 \x01(\tR\ttokenType\":\n" +
"\x13RefreshTokenRequest\x12#\n" +
"\rrefresh_token\x18\x01 \x01(\tR\frefreshToken\"\x9c\x01\n" +
"\x14RefreshTokenResponse\x12!\n" +
"\faccess_token\x18\x01 \x01(\tR\vaccessToken\x12#\n" +
"\rrefresh_token\x18\x02 \x01(\tR\frefreshToken\x12\x1d\n" +
"\n" +
"expires_in\x18\x03 \x01(\x03R\texpiresIn\x12\x1d\n" +
"\n" +
"token_type\x18\x04 \x01(\tR\ttokenType\",\n" +
"\x14ValidateTokenRequest\x12\x14\n" +
"\x05token\x18\x01 \x01(\tR\x05token\"{\n" +
"\x15ValidateTokenResponse\x12\x17\n" +
"\auser_id\x18\x01 \x01(\tR\x06userId\x12\x14\n" +
"\x05email\x18\x02 \x01(\tR\x05email\x12\x14\n" +
"\x05roles\x18\x03 \x03(\tR\x05roles\x12\x1d\n" +
"\n" +
"expires_at\x18\x04 \x01(\x03R\texpiresAt\"4\n" +
"\rLogoutRequest\x12#\n" +
"\rrefresh_token\x18\x01 \x01(\tR\frefreshToken\"*\n" +
"\x0eLogoutResponse\x12\x18\n" +
"\asuccess\x18\x01 \x01(\bR\asuccess2\x9d\x02\n" +
"\vAuthService\x126\n" +
"\x05Login\x12\x15.auth.v1.LoginRequest\x1a\x16.auth.v1.LoginResponse\x12K\n" +
"\fRefreshToken\x12\x1c.auth.v1.RefreshTokenRequest\x1a\x1d.auth.v1.RefreshTokenResponse\x12N\n" +
"\rValidateToken\x12\x1d.auth.v1.ValidateTokenRequest\x1a\x1e.auth.v1.ValidateTokenResponse\x129\n" +
"\x06Logout\x12\x16.auth.v1.LogoutRequest\x1a\x17.auth.v1.LogoutResponseBEZCgit.dcentral.systems/toolz/goplt/api/proto/generated/auth/v1;authv1b\x06proto3"
var (
file_auth_proto_rawDescOnce sync.Once
file_auth_proto_rawDescData []byte
)
func file_auth_proto_rawDescGZIP() []byte {
file_auth_proto_rawDescOnce.Do(func() {
file_auth_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_auth_proto_rawDesc), len(file_auth_proto_rawDesc)))
})
return file_auth_proto_rawDescData
}
var file_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 8)
var file_auth_proto_goTypes = []any{
(*LoginRequest)(nil), // 0: auth.v1.LoginRequest
(*LoginResponse)(nil), // 1: auth.v1.LoginResponse
(*RefreshTokenRequest)(nil), // 2: auth.v1.RefreshTokenRequest
(*RefreshTokenResponse)(nil), // 3: auth.v1.RefreshTokenResponse
(*ValidateTokenRequest)(nil), // 4: auth.v1.ValidateTokenRequest
(*ValidateTokenResponse)(nil), // 5: auth.v1.ValidateTokenResponse
(*LogoutRequest)(nil), // 6: auth.v1.LogoutRequest
(*LogoutResponse)(nil), // 7: auth.v1.LogoutResponse
}
var file_auth_proto_depIdxs = []int32{
0, // 0: auth.v1.AuthService.Login:input_type -> auth.v1.LoginRequest
2, // 1: auth.v1.AuthService.RefreshToken:input_type -> auth.v1.RefreshTokenRequest
4, // 2: auth.v1.AuthService.ValidateToken:input_type -> auth.v1.ValidateTokenRequest
6, // 3: auth.v1.AuthService.Logout:input_type -> auth.v1.LogoutRequest
1, // 4: auth.v1.AuthService.Login:output_type -> auth.v1.LoginResponse
3, // 5: auth.v1.AuthService.RefreshToken:output_type -> auth.v1.RefreshTokenResponse
5, // 6: auth.v1.AuthService.ValidateToken:output_type -> auth.v1.ValidateTokenResponse
7, // 7: auth.v1.AuthService.Logout:output_type -> auth.v1.LogoutResponse
4, // [4:8] is the sub-list for method output_type
0, // [0:4] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_auth_proto_init() }
func file_auth_proto_init() {
if File_auth_proto != nil {
return
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: unsafe.Slice(unsafe.StringData(file_auth_proto_rawDesc), len(file_auth_proto_rawDesc)),
NumEnums: 0,
NumMessages: 8,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_auth_proto_goTypes,
DependencyIndexes: file_auth_proto_depIdxs,
MessageInfos: file_auth_proto_msgTypes,
}.Build()
File_auth_proto = out.File
file_auth_proto_goTypes = nil
file_auth_proto_depIdxs = nil
}