From 1d9f03092d409a03b9f5aaf3eedd3892436d9332 Mon Sep 17 00:00:00 2001 From: AndrewSC208 Date: Tue, 7 Oct 2025 07:24:12 -0500 Subject: [PATCH 1/2] Basic auth api models --- api/core/authentication/README.md | 40 ++ .../authentication/basic/v1/service.pb.go | 399 ++++++++++++++++++ .../basic/v1/service.pb.validate.go | 364 ++++++++++++++++ .../authentication/basic/v1/service.proto | 42 ++ .../authentication/basic/v1/service_pb.js | 61 +++ .../authentication/basic/v1/service_pb.ts | 185 ++++++++ 6 files changed, 1091 insertions(+) create mode 100644 api/core/authentication/README.md create mode 100644 api/core/authentication/basic/v1/service.pb.go create mode 100644 api/core/authentication/basic/v1/service.pb.validate.go create mode 100644 api/core/authentication/basic/v1/service.proto create mode 100644 api/core/authentication/basic/v1/service_pb.js create mode 100644 api/core/authentication/basic/v1/service_pb.ts diff --git a/api/core/authentication/README.md b/api/core/authentication/README.md new file mode 100644 index 0000000..c993bef --- /dev/null +++ b/api/core/authentication/README.md @@ -0,0 +1,40 @@ +# Authentication +This is a loaded topic and most likely wont be implemented by hand for a development team. In most companies each developer is given the tools to use +auth when working on the their project. Since draft is aiming to be platform framework its should have some type of auth. Now as far as having implemented services with customer interfaces that may not be draft b/c I normally like to just pickup something off the shelf and then integrate my system with that Authentik [] and Authealia [] come to mind. + +For my imediate case though I'll be implementing basic authentication. It's probably a good idea to document how the following can be implemented or used with Draft[]. + +## OAuth + +## API Keys + +## Basic Authentication +Basic will first exist as a set of globally defined types like `user`, and `session`. an a generic package which will contain an interface, and a default implementation using blueprint as it's data store. In the future integrations with postgres or other datastores would really be helpful. + +## JWT Authentication + +## OpenID Connect Authentication + +### Research on Auth +Below are the most common forms of authentication implemented in most web applications and software systems. Draft must support a path forward for each of the below to be a viable option for enterprise grade systems. + +Username and Password (Basic Authentication): +Users log in with a username/email and password. Often combined with session cookies. + +Token-Based Authentication (e.g., JWT): +Users receive a signed token (like a JWT) after login, which is sent with each request for stateless authentication. + +OAuth 2.0: +Delegated authentication using third-party providers (Google, Facebook, GitHub, etc.). Common for "Login with X" buttons. + +API Keys: +Used for authenticating API requests, especially for service-to-service communication. + +Multi-Factor Authentication (MFA/2FA): +Requires a second factor (e.g., SMS code, authenticator app) in addition to password. + +OpenID Connect: +An identity layer on top of OAuth 2.0, often used for single sign-on (SSO). + +SAML: +Used for enterprise SSO, especially in corporate environments. \ No newline at end of file diff --git a/api/core/authentication/basic/v1/service.pb.go b/api/core/authentication/basic/v1/service.pb.go new file mode 100644 index 0000000..c38239d --- /dev/null +++ b/api/core/authentication/basic/v1/service.pb.go @@ -0,0 +1,399 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.2 +// protoc (unknown) +// source: core/authentication/basic/v1/service.proto + +package v1 + +import ( + _ "github.com/envoyproxy/protoc-gen-validate/validate" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + _ "google.golang.org/protobuf/types/known/anypb" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" + reflect "reflect" + sync "sync" +) + +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) +) + +// Valid keys for looking up an entity +type LookupEntityKeys int32 + +const ( + LookupEntityKeys_LOOKUP_ENTITY_KEY_UNSPECIFIED LookupEntityKeys = 0 + LookupEntityKeys_LOOKUP_ENTITY_KEY_ID LookupEntityKeys = 1 + LookupEntityKeys_LOOKUP_ENTITY_KEY_USERNAME LookupEntityKeys = 2 + LookupEntityKeys_LOOKUP_ENTITY_KEY_EMAIL LookupEntityKeys = 3 +) + +// Enum value maps for LookupEntityKeys. +var ( + LookupEntityKeys_name = map[int32]string{ + 0: "LOOKUP_ENTITY_KEY_UNSPECIFIED", + 1: "LOOKUP_ENTITY_KEY_ID", + 2: "LOOKUP_ENTITY_KEY_USERNAME", + 3: "LOOKUP_ENTITY_KEY_EMAIL", + } + LookupEntityKeys_value = map[string]int32{ + "LOOKUP_ENTITY_KEY_UNSPECIFIED": 0, + "LOOKUP_ENTITY_KEY_ID": 1, + "LOOKUP_ENTITY_KEY_USERNAME": 2, + "LOOKUP_ENTITY_KEY_EMAIL": 3, + } +) + +func (x LookupEntityKeys) Enum() *LookupEntityKeys { + p := new(LookupEntityKeys) + *p = x + return p +} + +func (x LookupEntityKeys) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (LookupEntityKeys) Descriptor() protoreflect.EnumDescriptor { + return file_core_authentication_basic_v1_service_proto_enumTypes[0].Descriptor() +} + +func (LookupEntityKeys) Type() protoreflect.EnumType { + return &file_core_authentication_basic_v1_service_proto_enumTypes[0] +} + +func (x LookupEntityKeys) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use LookupEntityKeys.Descriptor instead. +func (LookupEntityKeys) EnumDescriptor() ([]byte, []int) { + return file_core_authentication_basic_v1_service_proto_rawDescGZIP(), []int{0} +} + +// Entity the basic representation of a user or actor operating in the system with fields for +// ID, username, password, email, and timestamps for creation and updates of the Entity values. +// A combination of these fields uniquely identifies an entity within the system and allows for authentication. +type Entity struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id" bun:"id" csv:"id" pg:"id" yaml:"id"` + Username string `protobuf:"bytes,2,opt,name=username,proto3" json:"username" bun:"username" csv:"username" pg:"username" yaml:"username"` + Password string `protobuf:"bytes,3,opt,name=password,proto3" json:"password" bun:"password" csv:"password" pg:"password" yaml:"password"` + Email string `protobuf:"bytes,4,opt,name=email,proto3" json:"email" bun:"email" csv:"email" pg:"email" yaml:"email"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at" bun:"created_at" csv:"created_at" pg:"created_at" yaml:"created_at"` + UpdatedAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at" bun:"updated_at" csv:"updated_at" pg:"updated_at" yaml:"updated_at"` +} + +func (x *Entity) Reset() { + *x = Entity{} + if protoimpl.UnsafeEnabled { + mi := &file_core_authentication_basic_v1_service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Entity) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Entity) ProtoMessage() {} + +func (x *Entity) ProtoReflect() protoreflect.Message { + mi := &file_core_authentication_basic_v1_service_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Entity.ProtoReflect.Descriptor instead. +func (*Entity) Descriptor() ([]byte, []int) { + return file_core_authentication_basic_v1_service_proto_rawDescGZIP(), []int{0} +} + +func (x *Entity) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Entity) GetUsername() string { + if x != nil { + return x.Username + } + return "" +} + +func (x *Entity) GetPassword() string { + if x != nil { + return x.Password + } + return "" +} + +func (x *Entity) GetEmail() string { + if x != nil { + return x.Email + } + return "" +} + +func (x *Entity) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *Entity) GetUpdatedAt() *timestamppb.Timestamp { + if x != nil { + return x.UpdatedAt + } + return nil +} + +// Session is time bound authentication token that is issued to a user upon successful login. It contains +// the user ID, a unique token, and timestamps for when the session was created and when it expires. +// The token is used to authenticate the user in subsequent requests, allowing them to access protected resources +// without needing to re-enter their credentials. +type Session struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id" bun:"id" csv:"id" pg:"id" yaml:"id"` + UserId string `protobuf:"bytes,2,opt,name=user_id,json=userId,proto3" json:"user_id" bun:"user_id" csv:"user_id" pg:"user_id" yaml:"user_id"` + Token string `protobuf:"bytes,3,opt,name=token,proto3" json:"token" bun:"token" csv:"token" pg:"token" yaml:"token"` + RefreshToken string `protobuf:"bytes,4,opt,name=refresh_token,json=refreshToken,proto3" json:"refresh_token" bun:"refresh_token" csv:"refresh_token" pg:"refresh_token" yaml:"refresh_token"` + CreatedAt *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=created_at,json=createdAt,proto3" json:"created_at" bun:"created_at" csv:"created_at" pg:"created_at" yaml:"created_at"` + ExpiresAt *timestamppb.Timestamp `protobuf:"bytes,6,opt,name=expires_at,json=expiresAt,proto3" json:"expires_at" bun:"expires_at" csv:"expires_at" pg:"expires_at" yaml:"expires_at"` +} + +func (x *Session) Reset() { + *x = Session{} + if protoimpl.UnsafeEnabled { + mi := &file_core_authentication_basic_v1_service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Session) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Session) ProtoMessage() {} + +func (x *Session) ProtoReflect() protoreflect.Message { + mi := &file_core_authentication_basic_v1_service_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Session.ProtoReflect.Descriptor instead. +func (*Session) Descriptor() ([]byte, []int) { + return file_core_authentication_basic_v1_service_proto_rawDescGZIP(), []int{1} +} + +func (x *Session) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +func (x *Session) GetUserId() string { + if x != nil { + return x.UserId + } + return "" +} + +func (x *Session) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + +func (x *Session) GetRefreshToken() string { + if x != nil { + return x.RefreshToken + } + return "" +} + +func (x *Session) GetCreatedAt() *timestamppb.Timestamp { + if x != nil { + return x.CreatedAt + } + return nil +} + +func (x *Session) GetExpiresAt() *timestamppb.Timestamp { + if x != nil { + return x.ExpiresAt + } + return nil +} + +var File_core_authentication_basic_v1_service_proto protoreflect.FileDescriptor + +var file_core_authentication_basic_v1_service_proto_rawDesc = []byte{ + 0x0a, 0x2a, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x61, 0x73, 0x69, 0x63, 0x2f, 0x76, 0x31, 0x2f, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1c, 0x63, 0x6f, + 0x72, 0x65, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x2e, 0x62, 0x61, 0x73, 0x69, 0x63, 0x2e, 0x76, 0x31, 0x1a, 0x19, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61, 0x6e, 0x79, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, + 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0xdc, 0x01, 0x0a, 0x06, 0x45, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x75, 0x73, + 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x75, 0x73, + 0x65, 0x72, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, + 0x72, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x05, 0x65, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x39, 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, + 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x22, 0xe3, + 0x01, 0x0a, 0x07, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, + 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x75, 0x73, 0x65, + 0x72, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x66, + 0x72, 0x65, 0x73, 0x68, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0c, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x39, + 0x0a, 0x0a, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, + 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x65, 0x78, 0x70, + 0x69, 0x72, 0x65, 0x73, 0x5f, 0x61, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x65, 0x78, 0x70, 0x69, 0x72, + 0x65, 0x73, 0x41, 0x74, 0x2a, 0x8c, 0x01, 0x0a, 0x10, 0x4c, 0x6f, 0x6f, 0x6b, 0x75, 0x70, 0x45, + 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x21, 0x0a, 0x1d, 0x4c, 0x4f, 0x4f, + 0x4b, 0x55, 0x50, 0x5f, 0x45, 0x4e, 0x54, 0x49, 0x54, 0x59, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x18, 0x0a, 0x14, + 0x4c, 0x4f, 0x4f, 0x4b, 0x55, 0x50, 0x5f, 0x45, 0x4e, 0x54, 0x49, 0x54, 0x59, 0x5f, 0x4b, 0x45, + 0x59, 0x5f, 0x49, 0x44, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x4c, 0x4f, 0x4f, 0x4b, 0x55, 0x50, + 0x5f, 0x45, 0x4e, 0x54, 0x49, 0x54, 0x59, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x55, 0x53, 0x45, 0x52, + 0x4e, 0x41, 0x4d, 0x45, 0x10, 0x02, 0x12, 0x1b, 0x0a, 0x17, 0x4c, 0x4f, 0x4f, 0x4b, 0x55, 0x50, + 0x5f, 0x45, 0x4e, 0x54, 0x49, 0x54, 0x59, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x45, 0x4d, 0x41, 0x49, + 0x4c, 0x10, 0x03, 0x42, 0x40, 0x5a, 0x3e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x73, 0x74, 0x65, 0x61, 0x64, 0x79, 0x2d, 0x62, 0x79, 0x74, 0x65, 0x73, 0x2f, 0x64, + 0x72, 0x61, 0x66, 0x74, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x61, 0x75, + 0x74, 0x68, 0x65, 0x6e, 0x74, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x62, 0x61, 0x73, + 0x69, 0x63, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_core_authentication_basic_v1_service_proto_rawDescOnce sync.Once + file_core_authentication_basic_v1_service_proto_rawDescData = file_core_authentication_basic_v1_service_proto_rawDesc +) + +func file_core_authentication_basic_v1_service_proto_rawDescGZIP() []byte { + file_core_authentication_basic_v1_service_proto_rawDescOnce.Do(func() { + file_core_authentication_basic_v1_service_proto_rawDescData = protoimpl.X.CompressGZIP(file_core_authentication_basic_v1_service_proto_rawDescData) + }) + return file_core_authentication_basic_v1_service_proto_rawDescData +} + +var file_core_authentication_basic_v1_service_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_core_authentication_basic_v1_service_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_core_authentication_basic_v1_service_proto_goTypes = []any{ + (LookupEntityKeys)(0), // 0: core.authentication.basic.v1.LookupEntityKeys + (*Entity)(nil), // 1: core.authentication.basic.v1.Entity + (*Session)(nil), // 2: core.authentication.basic.v1.Session + (*timestamppb.Timestamp)(nil), // 3: google.protobuf.Timestamp +} +var file_core_authentication_basic_v1_service_proto_depIdxs = []int32{ + 3, // 0: core.authentication.basic.v1.Entity.created_at:type_name -> google.protobuf.Timestamp + 3, // 1: core.authentication.basic.v1.Entity.updated_at:type_name -> google.protobuf.Timestamp + 3, // 2: core.authentication.basic.v1.Session.created_at:type_name -> google.protobuf.Timestamp + 3, // 3: core.authentication.basic.v1.Session.expires_at:type_name -> google.protobuf.Timestamp + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] 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 +} + +func init() { file_core_authentication_basic_v1_service_proto_init() } +func file_core_authentication_basic_v1_service_proto_init() { + if File_core_authentication_basic_v1_service_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_core_authentication_basic_v1_service_proto_msgTypes[0].Exporter = func(v any, i int) any { + switch v := v.(*Entity); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_core_authentication_basic_v1_service_proto_msgTypes[1].Exporter = func(v any, i int) any { + switch v := v.(*Session); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_core_authentication_basic_v1_service_proto_rawDesc, + NumEnums: 1, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_core_authentication_basic_v1_service_proto_goTypes, + DependencyIndexes: file_core_authentication_basic_v1_service_proto_depIdxs, + EnumInfos: file_core_authentication_basic_v1_service_proto_enumTypes, + MessageInfos: file_core_authentication_basic_v1_service_proto_msgTypes, + }.Build() + File_core_authentication_basic_v1_service_proto = out.File + file_core_authentication_basic_v1_service_proto_rawDesc = nil + file_core_authentication_basic_v1_service_proto_goTypes = nil + file_core_authentication_basic_v1_service_proto_depIdxs = nil +} diff --git a/api/core/authentication/basic/v1/service.pb.validate.go b/api/core/authentication/basic/v1/service.pb.validate.go new file mode 100644 index 0000000..fdc7d46 --- /dev/null +++ b/api/core/authentication/basic/v1/service.pb.validate.go @@ -0,0 +1,364 @@ +// Code generated by protoc-gen-validate. DO NOT EDIT. +// source: core/authentication/basic/v1/service.proto + +package v1 + +import ( + "bytes" + "errors" + "fmt" + "net" + "net/mail" + "net/url" + "regexp" + "sort" + "strings" + "time" + "unicode/utf8" + + "google.golang.org/protobuf/types/known/anypb" +) + +// ensure the imports are used +var ( + _ = bytes.MinRead + _ = errors.New("") + _ = fmt.Print + _ = utf8.UTFMax + _ = (*regexp.Regexp)(nil) + _ = (*strings.Reader)(nil) + _ = net.IPv4len + _ = time.Duration(0) + _ = (*url.URL)(nil) + _ = (*mail.Address)(nil) + _ = anypb.Any{} + _ = sort.Sort +) + +// Validate checks the field values on Entity with the rules defined in the +// proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *Entity) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on Entity with the rules defined in the +// proto definition for this message. If any rules are violated, the result is +// a list of violation errors wrapped in EntityMultiError, or nil if none found. +func (m *Entity) ValidateAll() error { + return m.validate(true) +} + +func (m *Entity) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Id + + // no validation rules for Username + + // no validation rules for Password + + // no validation rules for Email + + if all { + switch v := interface{}(m.GetCreatedAt()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, EntityValidationError{ + field: "CreatedAt", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, EntityValidationError{ + field: "CreatedAt", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetCreatedAt()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return EntityValidationError{ + field: "CreatedAt", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetUpdatedAt()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, EntityValidationError{ + field: "UpdatedAt", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, EntityValidationError{ + field: "UpdatedAt", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetUpdatedAt()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return EntityValidationError{ + field: "UpdatedAt", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if len(errors) > 0 { + return EntityMultiError(errors) + } + + return nil +} + +// EntityMultiError is an error wrapping multiple validation errors returned by +// Entity.ValidateAll() if the designated constraints aren't met. +type EntityMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m EntityMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m EntityMultiError) AllErrors() []error { return m } + +// EntityValidationError is the validation error returned by Entity.Validate if +// the designated constraints aren't met. +type EntityValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e EntityValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e EntityValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e EntityValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e EntityValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e EntityValidationError) ErrorName() string { return "EntityValidationError" } + +// Error satisfies the builtin error interface +func (e EntityValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sEntity.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = EntityValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = EntityValidationError{} + +// Validate checks the field values on Session with the rules defined in the +// proto definition for this message. If any rules are violated, the first +// error encountered is returned, or nil if there are no violations. +func (m *Session) Validate() error { + return m.validate(false) +} + +// ValidateAll checks the field values on Session with the rules defined in the +// proto definition for this message. If any rules are violated, the result is +// a list of violation errors wrapped in SessionMultiError, or nil if none found. +func (m *Session) ValidateAll() error { + return m.validate(true) +} + +func (m *Session) validate(all bool) error { + if m == nil { + return nil + } + + var errors []error + + // no validation rules for Id + + // no validation rules for UserId + + // no validation rules for Token + + // no validation rules for RefreshToken + + if all { + switch v := interface{}(m.GetCreatedAt()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, SessionValidationError{ + field: "CreatedAt", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, SessionValidationError{ + field: "CreatedAt", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetCreatedAt()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return SessionValidationError{ + field: "CreatedAt", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if all { + switch v := interface{}(m.GetExpiresAt()).(type) { + case interface{ ValidateAll() error }: + if err := v.ValidateAll(); err != nil { + errors = append(errors, SessionValidationError{ + field: "ExpiresAt", + reason: "embedded message failed validation", + cause: err, + }) + } + case interface{ Validate() error }: + if err := v.Validate(); err != nil { + errors = append(errors, SessionValidationError{ + field: "ExpiresAt", + reason: "embedded message failed validation", + cause: err, + }) + } + } + } else if v, ok := interface{}(m.GetExpiresAt()).(interface{ Validate() error }); ok { + if err := v.Validate(); err != nil { + return SessionValidationError{ + field: "ExpiresAt", + reason: "embedded message failed validation", + cause: err, + } + } + } + + if len(errors) > 0 { + return SessionMultiError(errors) + } + + return nil +} + +// SessionMultiError is an error wrapping multiple validation errors returned +// by Session.ValidateAll() if the designated constraints aren't met. +type SessionMultiError []error + +// Error returns a concatenation of all the error messages it wraps. +func (m SessionMultiError) Error() string { + var msgs []string + for _, err := range m { + msgs = append(msgs, err.Error()) + } + return strings.Join(msgs, "; ") +} + +// AllErrors returns a list of validation violation errors. +func (m SessionMultiError) AllErrors() []error { return m } + +// SessionValidationError is the validation error returned by Session.Validate +// if the designated constraints aren't met. +type SessionValidationError struct { + field string + reason string + cause error + key bool +} + +// Field function returns field value. +func (e SessionValidationError) Field() string { return e.field } + +// Reason function returns reason value. +func (e SessionValidationError) Reason() string { return e.reason } + +// Cause function returns cause value. +func (e SessionValidationError) Cause() error { return e.cause } + +// Key function returns key value. +func (e SessionValidationError) Key() bool { return e.key } + +// ErrorName returns error name. +func (e SessionValidationError) ErrorName() string { return "SessionValidationError" } + +// Error satisfies the builtin error interface +func (e SessionValidationError) Error() string { + cause := "" + if e.cause != nil { + cause = fmt.Sprintf(" | caused by: %v", e.cause) + } + + key := "" + if e.key { + key = "key for " + } + + return fmt.Sprintf( + "invalid %sSession.%s: %s%s", + key, + e.field, + e.reason, + cause) +} + +var _ error = SessionValidationError{} + +var _ interface { + Field() string + Reason() string + Key() bool + Cause() error + ErrorName() string +} = SessionValidationError{} diff --git a/api/core/authentication/basic/v1/service.proto b/api/core/authentication/basic/v1/service.proto new file mode 100644 index 0000000..b77d149 --- /dev/null +++ b/api/core/authentication/basic/v1/service.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; + +package core.authentication.basic.v1; + +option go_package = "github.com/steady-bytes/draft/api/core/authentication/basic/v1"; + +import "google/protobuf/any.proto"; +import "google/protobuf/timestamp.proto"; +import "validate/validate.proto"; + +// Entity the basic representation of a user or actor operating in the system with fields for +// ID, username, password, email, and timestamps for creation and updates of the Entity values. +// A combination of these fields uniquely identifies an entity within the system and allows for authentication. +message Entity { + string id = 1; + string username = 2; + string password = 3; + string email = 4; + google.protobuf.Timestamp created_at = 5; + google.protobuf.Timestamp updated_at = 6; +} + +// Valid keys for looking up an entity +enum LookupEntityKeys { + LOOKUP_ENTITY_KEY_UNSPECIFIED = 0; + LOOKUP_ENTITY_KEY_ID = 1; + LOOKUP_ENTITY_KEY_USERNAME = 2; + LOOKUP_ENTITY_KEY_EMAIL = 3; +} + +// Session is time bound authentication token that is issued to a user upon successful login. It contains +// the user ID, a unique token, and timestamps for when the session was created and when it expires. +// The token is used to authenticate the user in subsequent requests, allowing them to access protected resources +// without needing to re-enter their credentials. +message Session { + string id = 1; + string user_id = 2; + string token = 3; + string refresh_token = 4; + google.protobuf.Timestamp created_at = 5; + google.protobuf.Timestamp expires_at = 6; +} \ No newline at end of file diff --git a/api/core/authentication/basic/v1/service_pb.js b/api/core/authentication/basic/v1/service_pb.js new file mode 100644 index 0000000..f0be891 --- /dev/null +++ b/api/core/authentication/basic/v1/service_pb.js @@ -0,0 +1,61 @@ +// @generated by protoc-gen-es v1.6.0 with parameter "target=js" +// @generated from file core/authentication/basic/v1/service.proto (package core.authentication.basic.v1, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import { proto3, Timestamp } from "@bufbuild/protobuf"; + +/** + * Valid keys for looking up an entity + * + * @generated from enum core.authentication.basic.v1.LookupEntityKeys + */ +export const LookupEntityKeys = proto3.makeEnum( + "core.authentication.basic.v1.LookupEntityKeys", + [ + {no: 0, name: "LOOKUP_ENTITY_KEY_UNSPECIFIED"}, + {no: 1, name: "LOOKUP_ENTITY_KEY_ID"}, + {no: 2, name: "LOOKUP_ENTITY_KEY_USERNAME"}, + {no: 3, name: "LOOKUP_ENTITY_KEY_EMAIL"}, + ], +); + +/** + * Entity the basic representation of a user or actor operating in the system with fields for + * ID, username, password, email, and timestamps for creation and updates of the Entity values. + * A combination of these fields uniquely identifies an entity within the system and allows for authentication. + * + * @generated from message core.authentication.basic.v1.Entity + */ +export const Entity = proto3.makeMessageType( + "core.authentication.basic.v1.Entity", + () => [ + { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "username", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 3, name: "password", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 4, name: "email", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 5, name: "created_at", kind: "message", T: Timestamp }, + { no: 6, name: "updated_at", kind: "message", T: Timestamp }, + ], +); + +/** + * Session is time bound authentication token that is issued to a user upon successful login. It contains + * the user ID, a unique token, and timestamps for when the session was created and when it expires. + * The token is used to authenticate the user in subsequent requests, allowing them to access protected resources + * without needing to re-enter their credentials. + * + * @generated from message core.authentication.basic.v1.Session + */ +export const Session = proto3.makeMessageType( + "core.authentication.basic.v1.Session", + () => [ + { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 3, name: "token", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 4, name: "refresh_token", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 5, name: "created_at", kind: "message", T: Timestamp }, + { no: 6, name: "expires_at", kind: "message", T: Timestamp }, + ], +); + diff --git a/api/core/authentication/basic/v1/service_pb.ts b/api/core/authentication/basic/v1/service_pb.ts new file mode 100644 index 0000000..2c091f5 --- /dev/null +++ b/api/core/authentication/basic/v1/service_pb.ts @@ -0,0 +1,185 @@ +// @generated by protoc-gen-es v1.6.0 with parameter "target=ts" +// @generated from file core/authentication/basic/v1/service.proto (package core.authentication.basic.v1, syntax proto3) +/* eslint-disable */ +// @ts-nocheck + +import type { BinaryReadOptions, FieldList, JsonReadOptions, JsonValue, PartialMessage, PlainMessage } from "@bufbuild/protobuf"; +import { Message, proto3, Timestamp } from "@bufbuild/protobuf"; + +/** + * Valid keys for looking up an entity + * + * @generated from enum core.authentication.basic.v1.LookupEntityKeys + */ +export enum LookupEntityKeys { + /** + * @generated from enum value: LOOKUP_ENTITY_KEY_UNSPECIFIED = 0; + */ + LOOKUP_ENTITY_KEY_UNSPECIFIED = 0, + + /** + * @generated from enum value: LOOKUP_ENTITY_KEY_ID = 1; + */ + LOOKUP_ENTITY_KEY_ID = 1, + + /** + * @generated from enum value: LOOKUP_ENTITY_KEY_USERNAME = 2; + */ + LOOKUP_ENTITY_KEY_USERNAME = 2, + + /** + * @generated from enum value: LOOKUP_ENTITY_KEY_EMAIL = 3; + */ + LOOKUP_ENTITY_KEY_EMAIL = 3, +} +// Retrieve enum metadata with: proto3.getEnumType(LookupEntityKeys) +proto3.util.setEnumType(LookupEntityKeys, "core.authentication.basic.v1.LookupEntityKeys", [ + { no: 0, name: "LOOKUP_ENTITY_KEY_UNSPECIFIED" }, + { no: 1, name: "LOOKUP_ENTITY_KEY_ID" }, + { no: 2, name: "LOOKUP_ENTITY_KEY_USERNAME" }, + { no: 3, name: "LOOKUP_ENTITY_KEY_EMAIL" }, +]); + +/** + * Entity the basic representation of a user or actor operating in the system with fields for + * ID, username, password, email, and timestamps for creation and updates of the Entity values. + * A combination of these fields uniquely identifies an entity within the system and allows for authentication. + * + * @generated from message core.authentication.basic.v1.Entity + */ +export class Entity extends Message { + /** + * @generated from field: string id = 1; + */ + id = ""; + + /** + * @generated from field: string username = 2; + */ + username = ""; + + /** + * @generated from field: string password = 3; + */ + password = ""; + + /** + * @generated from field: string email = 4; + */ + email = ""; + + /** + * @generated from field: google.protobuf.Timestamp created_at = 5; + */ + createdAt?: Timestamp; + + /** + * @generated from field: google.protobuf.Timestamp updated_at = 6; + */ + updatedAt?: Timestamp; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "core.authentication.basic.v1.Entity"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "username", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 3, name: "password", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 4, name: "email", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 5, name: "created_at", kind: "message", T: Timestamp }, + { no: 6, name: "updated_at", kind: "message", T: Timestamp }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): Entity { + return new Entity().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): Entity { + return new Entity().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): Entity { + return new Entity().fromJsonString(jsonString, options); + } + + static equals(a: Entity | PlainMessage | undefined, b: Entity | PlainMessage | undefined): boolean { + return proto3.util.equals(Entity, a, b); + } +} + +/** + * Session is time bound authentication token that is issued to a user upon successful login. It contains + * the user ID, a unique token, and timestamps for when the session was created and when it expires. + * The token is used to authenticate the user in subsequent requests, allowing them to access protected resources + * without needing to re-enter their credentials. + * + * @generated from message core.authentication.basic.v1.Session + */ +export class Session extends Message { + /** + * @generated from field: string id = 1; + */ + id = ""; + + /** + * @generated from field: string user_id = 2; + */ + userId = ""; + + /** + * @generated from field: string token = 3; + */ + token = ""; + + /** + * @generated from field: string refresh_token = 4; + */ + refreshToken = ""; + + /** + * @generated from field: google.protobuf.Timestamp created_at = 5; + */ + createdAt?: Timestamp; + + /** + * @generated from field: google.protobuf.Timestamp expires_at = 6; + */ + expiresAt?: Timestamp; + + constructor(data?: PartialMessage) { + super(); + proto3.util.initPartial(data, this); + } + + static readonly runtime: typeof proto3 = proto3; + static readonly typeName = "core.authentication.basic.v1.Session"; + static readonly fields: FieldList = proto3.util.newFieldList(() => [ + { no: 1, name: "id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 2, name: "user_id", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 3, name: "token", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 4, name: "refresh_token", kind: "scalar", T: 9 /* ScalarType.STRING */ }, + { no: 5, name: "created_at", kind: "message", T: Timestamp }, + { no: 6, name: "expires_at", kind: "message", T: Timestamp }, + ]); + + static fromBinary(bytes: Uint8Array, options?: Partial): Session { + return new Session().fromBinary(bytes, options); + } + + static fromJson(jsonValue: JsonValue, options?: Partial): Session { + return new Session().fromJson(jsonValue, options); + } + + static fromJsonString(jsonString: string, options?: Partial): Session { + return new Session().fromJsonString(jsonString, options); + } + + static equals(a: Session | PlainMessage | undefined, b: Session | PlainMessage | undefined): boolean { + return proto3.util.equals(Session, a, b); + } +} + From 2b6859d19b95b00650a323155300a1b56a117386 Mon Sep 17 00:00:00 2001 From: AndrewSC208 Date: Tue, 7 Oct 2025 07:24:56 -0500 Subject: [PATCH 2/2] remove readme --- api/core/authentication/README.md | 40 ------------------------------- 1 file changed, 40 deletions(-) delete mode 100644 api/core/authentication/README.md diff --git a/api/core/authentication/README.md b/api/core/authentication/README.md deleted file mode 100644 index c993bef..0000000 --- a/api/core/authentication/README.md +++ /dev/null @@ -1,40 +0,0 @@ -# Authentication -This is a loaded topic and most likely wont be implemented by hand for a development team. In most companies each developer is given the tools to use -auth when working on the their project. Since draft is aiming to be platform framework its should have some type of auth. Now as far as having implemented services with customer interfaces that may not be draft b/c I normally like to just pickup something off the shelf and then integrate my system with that Authentik [] and Authealia [] come to mind. - -For my imediate case though I'll be implementing basic authentication. It's probably a good idea to document how the following can be implemented or used with Draft[]. - -## OAuth - -## API Keys - -## Basic Authentication -Basic will first exist as a set of globally defined types like `user`, and `session`. an a generic package which will contain an interface, and a default implementation using blueprint as it's data store. In the future integrations with postgres or other datastores would really be helpful. - -## JWT Authentication - -## OpenID Connect Authentication - -### Research on Auth -Below are the most common forms of authentication implemented in most web applications and software systems. Draft must support a path forward for each of the below to be a viable option for enterprise grade systems. - -Username and Password (Basic Authentication): -Users log in with a username/email and password. Often combined with session cookies. - -Token-Based Authentication (e.g., JWT): -Users receive a signed token (like a JWT) after login, which is sent with each request for stateless authentication. - -OAuth 2.0: -Delegated authentication using third-party providers (Google, Facebook, GitHub, etc.). Common for "Login with X" buttons. - -API Keys: -Used for authenticating API requests, especially for service-to-service communication. - -Multi-Factor Authentication (MFA/2FA): -Requires a second factor (e.g., SMS code, authenticator app) in addition to password. - -OpenID Connect: -An identity layer on top of OAuth 2.0, often used for single sign-on (SSO). - -SAML: -Used for enterprise SSO, especially in corporate environments. \ No newline at end of file