Documentation
¶
Index ¶
- Constants
- type APIKeyAccountAccess
- type APIKeyCreateInput
- type APIKeyGetAccountAccessInput
- type APIKeyListInput
- type APIKeyListRepoInput
- type APIKeyListRepoResult
- type APIKeyMed
- type APIKeyRepo
- type APIKeyRevokeInput
- type APIKeyRotateInput
- type APIKeySvc
- type AccountContext
- type AccountUserAccess
- type AuthAccountRelation
- type AuthBillingClient
- type AuthCoreClient
- type AuthSvc
- type BillingProfileResult
- type CompleteAccountRegistrationInput
- type CompleteRegistrationOutput
- type ConfirmPaymentInput
- type ConfirmPaymentOutput
- type CreateAPIKeyInput
- type CreateAPIKeyResult
- type CreateRegistrationSessionInput
- type CreateRegistrationSessionResult
- type CreateUserForRegistrationInput
- type CreateUserForRegistrationOutput
- type DocAPIKeyMed
- type DocAPIKeyRepo
- type DocAPIKeySvc
- type DocAPIKeySyncInput
- type GetOrCreateDocAPIKeyResult
- type IdempotencyKey
- type IdempotencyKeyRepo
- type IdempotencyMed
- type ListAPIKeysResult
- type ListRegistrationSessionsInput
- type ListRegistrationSessionsResult
- type LoginResult
- type MediatorFactory
- type Mediators
- type NotificationPublisher
- type PasswordMed
- type PasswordSvc
- type PlanInfo
- type RecoveryPoint
- type RefreshToken
- type RefreshTokenMed
- type RefreshTokenRepo
- type RefreshTokenResult
- type RegisterInput
- type RegisterUserInput
- type RegistrationAddress
- type RegistrationMed
- type RegistrationQueueRepo
- type RegistrationSession
- type RegistrationSessionData
- type RegistrationSessionRepo
- type RegistrationSessionSvc
- type RepoFactory
- type RequestIdentity
- type RevokeAPIKeyInput
- type RotateAPIKeyInput
- type SetupBillingInput
- type SetupBillingOutput
- type SetupIntentResult
- type StripeCustomer
- type TokenSvc
- type UpdateRegistrationSessionData
- type UpdateRegistrationSessionInput
- type UserMed
- type UserRepo
- type UserSvc
Constants ¶
const (
ServiceName = "auth-service"
)
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type APIKeyAccountAccess ¶
type APIKeyCreateInput ¶
type APIKeyGetAccountAccessInput ¶
type APIKeyGetAccountAccessInput struct {
AccountMode constants.AccountMode
APIKeyID int64
TargetAccountID string
}
type APIKeyListInput ¶
type APIKeyListRepoInput ¶
type APIKeyListRepoResult ¶
type APIKeyListRepoResult struct {
APIKeys []*apikey.APIKey
PageInfo pagination.PageInfo
}
type APIKeyMed ¶
type APIKeyMed interface {
// FindAndValidate validates a raw API key string and returns the corresponding API key model.
//
// 1. Parse the raw API key string to extract the key ID and secret.
// 2. Look up the API key by its parsed ID.
// 3. Verify the secret HMAC against the stored hash using the pepper.
// 4. Check that the key is not expired or revoked.
FindAndValidate(ctx context.Context, apiKey string) (*apikey.APIKey, *apierror.APIError)
// ParseKey parses a raw API key string into its component parts.
//
// 1. Delegate to apikey.ParseAPIKey to extract the prefix, ID, and secret.
ParseKey(ctx context.Context, apiKey string) (*apikey.ParsedAPIKey, *apierror.APIError)
// TouchIfNotRecent touches a given API key if it has not been used in the last 24 hours.
TouchIfNotRecent(ctx context.Context, apiKeyModel *apikey.APIKey) *apierror.APIError
// Create creates a new API key for the requested account mode and persists it.
//
// 1. Generate a new parsed API key with a random secret for the given account mode.
// 2. Compute the HMAC hash of the secret using the pepper.
// 3. Generate a unique type ID and build the API key model.
// 4. Persist the API key in the repository.
// 5. Re-fetch the key to populate joined fields (role name, type code).
Create(ctx context.Context, input APIKeyCreateInput) (string, *apikey.APIKey, *apierror.APIError)
// Rotate revokes the specified API key and creates a replacement with the same name, owner account, and role.
//
// 1. Look up the existing API key by type ID.
// 2. Revoke the existing key. By default revocation is immediate; a future RevokeAt schedules it (the old key keeps working until then) and is rejected with a validation error if more than 30 days out, while a past/now RevokeAt collapses to immediate.
// 3. Create a new key using the old key's properties, with an optionally overridden expiration.
//
// Scoped to OwnerAccountID: returns a not-found error if the key does not exist for the requested owner.
Rotate(ctx context.Context, input APIKeyRotateInput) (string, *apikey.APIKey, *apierror.APIError)
// Revoke revokes an API key by its type ID.
//
// Scoped to ownerAccountID: returns a not-found error if the key does not exist for the given owner. This enforces tenant boundaries at the persistence layer as a backstop to service-layer ownership checks.
Revoke(ctx context.Context, apiKeyTypeID string, ownerAccountID string) *apierror.APIError
// List returns a paginated list of API keys for the given owner account and filters.
//
// 1. Query the API key repository with the provided filters and pagination parameters.
// 2. Return the list of API keys and page info.
List(ctx context.Context, input APIKeyListInput) (*ListAPIKeysResult, *apierror.APIError)
// GetKeyAccountAccess returns the resolved account access for an API key targeting a specific account.
//
// 1. Look up the API key by its database ID.
// 2. Verify the key's owner account matches the target account.
// 3. Fetch the role permissions from core-service if a role is assigned.
// 4. Return the access record with role and permission details.
GetKeyAccountAccess(ctx context.Context, input APIKeyGetAccountAccessInput) (*APIKeyAccountAccess, *apierror.APIError)
}
type APIKeyRepo ¶
type APIKeyRepo interface {
Find(ctx context.Context, apiKeyID string) (*apikey.APIKey, *apierror.APIError)
FindByDatabaseID(ctx context.Context, id int64, includes []string) (*apikey.APIKey, *apierror.APIError)
FindByTypeID(ctx context.Context, typeID string, includes []string) (*apikey.APIKey, *apierror.APIError)
Touch(ctx context.Context, apiKeyID int64) *apierror.APIError
Create(ctx context.Context, apiKey *apikey.APIKey) (int64, *apierror.APIError)
// CountRoleForOwner returns the number of roles matching roleID that are visible to ownerAccountID (system roles with a NULL account_id, or roles owned by the account). Used to validate a referenced role_id before persisting an API key.
CountRoleForOwner(ctx context.Context, roleID string, ownerAccountID string) (int64, *apierror.APIError)
GetByIDs(ctx context.Context, ownerAccountID string, ids []string) ([]*apikey.APIKey, *apierror.APIError)
// Revoke marks an API key as revoked. A nil revokeAt revokes immediately using the database clock; a non-nil revokeAt schedules a future revocation. Scoped to ownerAccountID; returns a not-found error if the key does not exist for the given owner.
Revoke(ctx context.Context, typeID string, ownerAccountID string, revokeAt *time.Time) *apierror.APIError
List(ctx context.Context, input APIKeyListRepoInput) (*APIKeyListRepoResult, *apierror.APIError)
}
type APIKeyRevokeInput ¶
type APIKeyRotateInput ¶
type APIKeySvc ¶
type APIKeySvc interface {
// GetAPIKey returns a single API key's metadata by its type ID.
//
// Authorization:
// - Requires an internal admin identity with a target account in context.
// - The key must belong to the caller's target account.
GetAPIKey(ctx context.Context, apiKeyID string, includes []string) (*apikey.APIKey, *apierror.APIError)
// CreateAPIKey creates a new API key for the caller's target account.
//
// Authorization:
// - Requires an internal admin identity with a target account in context.
CreateAPIKey(ctx context.Context, input CreateAPIKeyInput) (*CreateAPIKeyResult, *apierror.APIError)
// RotateAPIKey rotates (revokes and replaces) an API key.
//
// Authorization:
// - Requires an internal admin identity with a target account in context.
//
// Side effects:
// - Revokes the prior API key.
RotateAPIKey(ctx context.Context, input RotateAPIKeyInput) (*CreateAPIKeyResult, *apierror.APIError)
// RevokeAPIKey revokes an API key without creating a replacement.
//
// Authorization:
// - Requires an internal admin identity with a target account in context.
RevokeAPIKey(ctx context.Context, input RevokeAPIKeyInput) *apierror.APIError
// ListAPIKeys returns a paginated list of API keys for the caller's target account.
//
// Authorization:
// - Requires an internal admin identity with a target account in context.
//
// Pagination:
// - If cursor is non-nil, results begin after the provided cursor.
// - limit controls the maximum number of results returned.
ListAPIKeys(ctx context.Context, cursor *string, limit int32, query *string, statuses []constants.APIKeyStatus, includes []string) (*ListAPIKeysResult, *apierror.APIError)
BatchGetAPIKeysByIDs(ctx context.Context, ids []string) ([]*apikey.APIKey, *apierror.APIError)
}
type AccountContext ¶
type AccountContext struct {
AccountID string
OwnerAccountID *string
AccountMode constants.AccountMode
SubscriptionStatus *string
}
AccountContext represents the context of an account (sandbox status, mode, etc.)
type AccountUserAccess ¶
type AccountUserAccess struct {
AccountUserID string
AccountID string
RoleID *string
RoleType *string
RoleName *string
Permissions map[string]bool
}
AccountUserAccess represents a user's access to an account
type AuthAccountRelation ¶
type AuthAccountRelation struct {
ID string
CounterpartyAccountID string
AccountRelationRoleCode types.IdentityRelationType
IsOwnerSide bool
}
type AuthBillingClient ¶
type AuthBillingClient interface {
GetPlanByCode(ctx context.Context, planCode string) (*PlanInfo, *apierror.APIError)
CreateCustomer(ctx context.Context, email, name, idempotencyKey string, metadata map[string]string) (*StripeCustomer, *apierror.APIError)
SetupBillingProfile(ctx context.Context, accountID string) (*BillingProfileResult, *apierror.APIError)
SubscribeToPricingPlan(ctx context.Context, stripeCustomerID, planCode string) *apierror.APIError
CreateSetupIntent(ctx context.Context, customerID, idempotencyKey string) (*SetupIntentResult, *apierror.APIError)
GetSetupIntentStatus(ctx context.Context, setupIntentID string) (*SetupIntentResult, *apierror.APIError)
ValidateStripePricingPlan(ctx context.Context, planCode string) *apierror.APIError
Close() error
WaitForReady(ctx context.Context) error
}
AuthBillingClient is the interface for billing-service operations needed by auth-service.
type AuthCoreClient ¶
type AuthCoreClient interface {
// GetAccountContext returns whether an account is a sandbox and its mode
GetAccountContext(ctx context.Context, accountID string) (*AccountContext, *apierror.APIError)
// GetUserAccountAccess returns the user's role/permissions for an account
GetUserAccountAccess(ctx context.Context, userID, accountID string) (*AccountUserAccess, bool, *apierror.APIError)
// GetAccountRelationByUserID returns the relationship between accounts based on user. actorAccountID is required to unlock owner-side matches (the relation's owner_account_id must equal it); pass "" when no actor account has been validated to skip owner-side.
GetAccountRelationByUserID(ctx context.Context, targetAccountID, actorAccountID, userID string) (*AuthAccountRelation, bool, *apierror.APIError)
// GetAccountRelationByAPIKeyID returns the relationship between accounts based on API key
GetAccountRelationByAPIKeyID(ctx context.Context, ownerAccountID string, apiKeyID int64) (*AuthAccountRelation, bool, *apierror.APIError)
// MarkAccountUserUsed marks an account user as recently used
MarkAccountUserUsed(ctx context.Context, accountUserID string) *apierror.APIError
// GetRolePermissions returns the permissions for a role
GetRolePermissions(ctx context.Context, roleID string) (map[string]bool, *apierror.APIError)
// GetSandboxAccountByOwner returns the sandbox account ID for a given owner account
GetSandboxAccountByOwner(ctx context.Context, ownerAccountID string) (string, *apierror.APIError)
// GetAdminRole returns the admin role ID
GetAdminRole(ctx context.Context) (string, *apierror.APIError)
// CompleteRegistration creates the production account, sandbox, roles, and permissions via core-service.
CompleteRegistration(ctx context.Context, input CompleteAccountRegistrationInput) (*CompleteRegistrationOutput, *apierror.APIError)
}
AuthCoreClient is the interface for core-service operations needed by auth-service
type AuthSvc ¶
type AuthSvc interface {
// ValidateCredential validates an auth token and returns the resulting identity.
//
// Behavior:
// - If authToken is empty, returns an unauthenticated identity.
// - If authToken is an API key credential, validates it as an API key.
// - Otherwise, validates it as a user credential.
ValidateCredential(ctx context.Context, authToken string, targetAccountID *string, actorAccountID *string) (*types.Identity, *apierror.APIError)
}
type BillingProfileResult ¶
BillingProfileResult holds the IDs of the created billing profile and cadence.
type CompleteAccountRegistrationInput ¶
type CompleteAccountRegistrationInput struct {
UserID string
PlanCode string
StripeCustomerID string
AccountName string
UserName string
UserEmail string
BusinessAddress *RegistrationAddress
}
CompleteAccountRegistrationInput carries the data sent to core-service to create the account and sandbox.
type CompleteRegistrationOutput ¶
CompleteRegistrationOutput holds the IDs of the newly created accounts.
type ConfirmPaymentInput ¶
type ConfirmPaymentOutput ¶
type CreateAPIKeyInput ¶
type CreateAPIKeyResult ¶
type CreateRegistrationSessionResult ¶
type CreateRegistrationSessionResult struct {
SessionID string
}
type DocAPIKeyMed ¶
type DocAPIKeyMed interface {
// Resolve returns an existing doc API key for the given sandbox account, or creates one if needed.
//
// 1. Look up an existing doc API key for the sandbox account.
// 2. If none exists, create a new doc API key with the system admin role.
// 3. If the existing key is revoked, return an error indicating manual rotation is required.
// 4. If the existing key is expired, rotate it and return the new key.
// 5. Otherwise, decrypt and return the existing key's secret.
//
// Behavior:
// - If a non-revoked, non-expired doc API key exists, it is returned.
// - If the existing key is expired, a new key is created via rotation.
// - If the existing key is revoked, returns an error indicating rotation is required.
Resolve(ctx context.Context, sandboxAccountID string) (*GetOrCreateDocAPIKeyResult, *apierror.APIError)
// SyncRotatedAPIKey updates doc API key state after the underlying API key has been rotated.
//
// 1. Look up the existing doc API key by the old API key ID.
// 2. If no doc API key exists for the old key, return without error (no-op).
// 3. Delete the old doc API key record.
// 4. Encrypt the new secret using AES-GCM.
// 5. Create a new doc API key record pointing to the rotated API key.
//
// Behavior:
// - No-op if no doc API key exists for the old API key.
//
// Side effects:
// - Deletes the old doc API key record (if present).
// - Creates a new doc API key record using the rotated API key and secret.
SyncRotatedAPIKey(ctx context.Context, input DocAPIKeySyncInput) *apierror.APIError
}
type DocAPIKeyRepo ¶
type DocAPIKeyRepo interface {
FindBySandboxAccountID(ctx context.Context, sandboxAccountID string) (*apikey.DocAPIKey, *apierror.APIError)
FindByAPIKeyID(ctx context.Context, apiKeyID string) (*apikey.DocAPIKey, *apierror.APIError)
Create(ctx context.Context, docAPIKey *apikey.DocAPIKey) (int64, *apierror.APIError)
Update(ctx context.Context, docAPIKey *apikey.DocAPIKey) *apierror.APIError
Delete(ctx context.Context, id int64) *apierror.APIError
}
type DocAPIKeySvc ¶
type DocAPIKeySvc interface {
// GetOrCreateDocAPIKey returns a documentation API key for the caller's target account.
//
// Authorization:
// - Requires an internal identity with a target account in context.
//
// Behavior:
// - Reuses an existing valid key.
// - Rotates and replaces an expired key.
//
// Side effects:
// - May rotate (revoke and replace) an existing doc API key.
GetOrCreateDocAPIKey(ctx context.Context) (*GetOrCreateDocAPIKeyResult, *apierror.APIError)
}
type DocAPIKeySyncInput ¶
type IdempotencyKey ¶
type IdempotencyKey struct {
ID int64
TypeID string
ServiceName string
Handler string
IdempotencyKey string
ActorID *string
IdentityType string
ScopeHash string
ResponseCode *int
ResponseBody json.RawMessage
RecoveryPoint RecoveryPoint
}
func (*IdempotencyKey) HasResponse ¶
func (k *IdempotencyKey) HasResponse() bool
func (*IdempotencyKey) IsFinished ¶
func (k *IdempotencyKey) IsFinished() bool
type IdempotencyKeyRepo ¶
type IdempotencyKeyRepo interface {
GetByScopeHash(ctx context.Context, scopeHash string) (*IdempotencyKey, *apierror.APIError)
Create(ctx context.Context, key *IdempotencyKey) (*IdempotencyKey, *apierror.APIError)
AdvanceRecoveryPoint(ctx context.Context, typeID string, recoveryPoint RecoveryPoint) *apierror.APIError
GetRecoveryPoint(ctx context.Context, typeID string) (RecoveryPoint, *apierror.APIError)
SetResponse(ctx context.Context, typeID string, code int, body json.RawMessage, recoveryPoint RecoveryPoint) *apierror.APIError
}
type IdempotencyMed ¶
type IdempotencyMed interface {
// UpsertIdempotencyKey upserts and returns the idempotency key for the request scope.
//
// 1. Resolve the idempotency key from the request context, falling back to the request ID.
// 2. Compute the scope hash from the actor, target account, service, handler, and key.
// 3. Return the existing key for the scope hash when one exists.
// 4. Otherwise persist a new key at the Started recovery point, re-fetching the
// existing row if a concurrent request inserted the same scope hash first.
UpsertIdempotencyKey(ctx context.Context, identity *RequestIdentity) (*IdempotencyKey, *apierror.APIError)
// CacheErrorResponse caches a non-transient error response for the idempotency key.
//
// 1. Return transient errors uncached so the client can retry.
// 2. Persist non-transient errors as the cached response and mark the key finished.
CacheErrorResponse(ctx context.Context, typeID string, apiErr *apierror.APIError) *apierror.APIError
// CacheSuccessResponse caches a successful response for the idempotency key.
//
// 1. Marshal the response data to JSON.
// 2. Persist it as the cached response and mark the key finished.
CacheSuccessResponse(ctx context.Context, typeID string, data any) *apierror.APIError
}
type ListAPIKeysResult ¶
type ListAPIKeysResult struct {
APIKeys []*apikey.APIKey
PageInfo pagination.PageInfo
}
type ListRegistrationSessionsResult ¶
type ListRegistrationSessionsResult struct {
Sessions []*RegistrationSession
PageInfo pagination.PageInfo
}
type LoginResult ¶
type MediatorFactory ¶
type MediatorFactory interface {
Build(RepoFactory) Mediators
}
MediatorFactory builds mediators bound to a given repository factory (e.g., per transaction).
type Mediators ¶
type Mediators struct {
User UserMed
APIKey APIKeyMed
DocAPIKey DocAPIKeyMed
Password PasswordMed
RefreshToken RefreshTokenMed
Idempotency IdempotencyMed
Registration RegistrationMed
}
Mediators groups all mediator dependencies built for a specific repository factory.
type NotificationPublisher ¶
type PasswordMed ¶
type PasswordMed interface {
// RequestReset initiates a password reset flow for the given identifier.
//
// 1. Look up the user by identifier; silently succeed if not found to avoid leaking information about registered identifiers.
// 2. Generate a short-lived password reset JWT (15 minutes).
// 3. Build the reset link, optionally scoped to an account slug.
// 4. Send a password reset email with the link.
//
// Behavior:
// - Only returns an error for internal service failures; unknown identifiers
// succeed silently to prevent enumeration.
//
// Side effects:
// - Sends a password reset email.
RequestReset(ctx context.Context, identifier string, accountSlug, portalBaseURL *string) *apierror.APIError
// ValidatePasswordResetToken validates a password reset token and returns the associated user.
//
// 1. Decode and verify the JWT token as a password-reset type.
// 2. Look up the user by the token's subject (user ID).
// 3. Return an authentication error if the user is not found.
ValidatePasswordResetToken(ctx context.Context, token string) (*types.User, *apierror.APIError)
// Validate validates the identifier/password combination and returns the associated user.
//
// 1. Look up the user by identifier (email or user ID).
// 2. If the user has no stored password hash, silently send a password reset email (when an email is on file) and return the generic invalid-credentials error so that the response is indistinguishable from a missing user or wrong password. This preserves recovery for legitimate passwordless users without leaking account state to unauthenticated callers.
// 3. Compare the provided password against the stored hash.
// 4. Return the user if the password matches; return an authentication error otherwise.
Validate(ctx context.Context, identifier, password string) (*types.User, *apierror.APIError)
// Update updates a user's password.
//
// 1. Hash the new password.
// 2. Persist the updated password hash in the repository.
// 3. Revoke all existing refresh tokens for the user.
// 4. Send a password updated notification email.
//
// Side effects:
// - Updates the stored password hash.
// - Revokes all refresh tokens for the user.
// - Sends a password updated email.
Update(ctx context.Context, user *types.User, newPassword string) *apierror.APIError
}
type PasswordSvc ¶
type PasswordSvc interface {
// UpdatePassword updates a user's password.
//
// Side effects:
// - Revokes existing refresh tokens for the user.
UpdatePassword(ctx context.Context, oldPassword, newPassword string) *apierror.APIError
// ResetPassword completes the password reset flow and returns a token pair plus the user profile.
//
// Side effects:
// - Revokes existing refresh tokens for the user.
ResetPassword(ctx context.Context, token, newPassword string) (*LoginResult, *apierror.APIError)
// RequestPasswordReset initiates a password reset flow for the identifier.
RequestPasswordReset(ctx context.Context, identifier string, accountSlug, portalBaseURL *string) *apierror.APIError
}
type PlanInfo ¶
type PlanInfo struct {
TypeID string
Name string
PlanTypeCode string
PricePerSeat float64
PricePerMonth *float64
SeatMinimum *int
}
PlanInfo holds the pricing plan data returned by the billing service.
type RecoveryPoint ¶
type RecoveryPoint string
const ( RecoveryPointStarted RecoveryPoint = "auth:started" RecoveryPointCustomerCreated RecoveryPoint = "auth:customer_created" RecoveryPointCoreAccountCreated RecoveryPoint = "auth:core_account_created" RecoveryPointAccountsCreated RecoveryPoint = "auth:accounts_created" RecoveryPointFinished RecoveryPoint = "auth:finished" )
func (RecoveryPoint) IsFinished ¶
func (r RecoveryPoint) IsFinished() bool
func (RecoveryPoint) IsStarted ¶
func (r RecoveryPoint) IsStarted() bool
func (RecoveryPoint) IsValid ¶
func (r RecoveryPoint) IsValid() bool
func (RecoveryPoint) String ¶
func (r RecoveryPoint) String() string
type RefreshToken ¶
type RefreshToken struct {
Token string
UserID string `audit:"user_id"`
ExpiresAt time.Time `audit:"expires_at"`
RevokedAt *time.Time `audit:"revoked_at"`
}
func (*RefreshToken) IsExpired ¶
func (m *RefreshToken) IsExpired() bool
IsExpired reports whether the refresh token has passed its expiration time.
func (*RefreshToken) IsRevoked ¶
func (m *RefreshToken) IsRevoked() bool
IsRevoked reports whether the refresh token has been revoked.
type RefreshTokenMed ¶
type RefreshTokenMed interface {
// Create issues a new refresh token for the given user ID.
//
// 1. Generate a cryptographically random opaque token.
// 2. Default expiration to 30 days if expiresInDays is nil.
// 3. Persist the token in the repository with the computed expiration.
Create(ctx context.Context, userID string, expiresInDays *int) (*RefreshToken, *apierror.APIError)
// Validate validates a refresh token and returns the associated user ID.
//
// 1. Look up the refresh token in the repository.
// 2. Verify the token is not revoked.
// 3. Verify the token is not expired.
// 4. Return the associated user ID.
Validate(ctx context.Context, refreshToken string) (string, *apierror.APIError)
// Revoke revokes a single refresh token, preventing it from being used to mint new access tokens.
//
// 1. Look up the refresh token in the repository.
// 2. Verify the token is not already revoked or expired.
// 3. Mark the token as revoked in the repository.
Revoke(ctx context.Context, refreshToken string) *apierror.APIError
// RevokeAll revokes all refresh tokens associated with a user.
//
// 1. Revoke all refresh tokens for the given user ID in the repository.
//
// Behavior:
// - Prevents stale tokens from being used after a password change.
RevokeAll(ctx context.Context, userID string) *apierror.APIError
}
type RefreshTokenRepo ¶
type RefreshTokenRepo interface {
Find(ctx context.Context, token string) (*RefreshToken, *apierror.APIError)
Create(ctx context.Context, userID string, token string, expiresInDays int) (*RefreshToken, *apierror.APIError)
Revoke(ctx context.Context, token string) *apierror.APIError
RevokeAll(ctx context.Context, userID string) *apierror.APIError
}
type RefreshTokenResult ¶
type RefreshTokenResult struct {
AccessToken string // #nosec G117 - Struct field, not a hardcoded credential
}
type RegisterInput ¶
type RegisterInput struct {
Name string
Email string
Password string // #nosec G117 - Struct field, not a hardcoded credential
AccountSlug *string // Portal context for the "already registered" magic login link.
// PortalBaseURL is the base URL of the account's verified custom portal domain, resolved server-side by the gateway. When set, email links use it instead of the slug-prefixed dashboard URL.
PortalBaseURL *string
}
type RegisterUserInput ¶
type RegistrationAddress ¶
type RegistrationAddress struct {
Line1 string
Line2 string
City string
State string
PostalCode string
Country string
}
RegistrationAddress is a structured postal address collected during registration.
type RegistrationMed ¶
type RegistrationMed interface {
// CreateSession creates a new registration session or returns an existing active session for the given email (idempotent).
//
// 1. Check if the user already exists (noted but does not prevent session creation).
// 2. Look for an existing non-expired session for the email; if found, update the plan code if different and resend the verification email.
// 3. Generate a unique type ID and verification token.
// 4. Create a new registration session record.
// 5. Send the verification email.
//
// Side effects:
// - Sends a verification email to the user.
CreateSession(ctx context.Context, input CreateRegistrationSessionInput) (*CreateRegistrationSessionResult, *apierror.APIError)
// ResendVerificationEmail regenerates the verification token and resends the verification email.
//
// 1. Look up the session by type ID.
// 2. Validate the session is not completed and email is not already verified.
// 3. Generate a new verification token and update the session.
// 4. Send the verification email with the new token.
//
// Side effects:
// - Rotates the verification token.
// - Sends a new verification email.
ResendVerificationEmail(ctx context.Context, sessionID string) *apierror.APIError
// VerifyToken verifies the email verification token and marks the session as email-verified.
//
// 1. Look up the session by verification token.
// 2. Reject completed sessions.
// 3. Check token expiry (24-hour TTL from last update).
// 4. If already verified, return the current session without changes (idempotent).
// 5. Check if a user already exists for the session's email.
// 6. Mark the email as verified and advance the step to user_details.
// 7. Re-fetch and return the updated session.
VerifyToken(ctx context.Context, token string) (*RegistrationSession, *apierror.APIError)
// CreateUserForSession creates a new user for the registration session and returns the user ID with auth tokens.
//
// 1. Look up the session by type ID and validate it is not completed and email is verified.
// 2. If the session already has a user, generate tokens for the existing user (idempotent).
// 3. Reject if an account already exists for the session's email — pre-existing accounts must authenticate via login, not by holding a verified session id.
// 4. Hash the password and create a new user record.
// 5. Associate the user with the session and update session data with the user name.
// 6. Advance the session step to account_details.
// 7. Generate and return an access token and refresh token.
//
// Side effects:
// - May create a new user record.
// - Associates the user with the session.
// - Advances the session step to account_details.
CreateUserForSession(ctx context.Context, input CreateUserForRegistrationInput) (*CreateUserForRegistrationOutput, *apierror.APIError)
// UpdateSession updates an in-progress registration session's step and form data.
//
// 1. Look up the session by type ID and validate it is not completed.
// 2. Validate the step transition allows only forward progression.
// 3. Merge the provided session data into the existing data (non-nil fields only).
// 4. Persist the updated step and data.
// 5. Re-fetch and return the refreshed session.
UpdateSession(ctx context.Context, sessionID string, step *constants.RegistrationStep, sessionData *UpdateRegistrationSessionData) (*RegistrationSession, *apierror.APIError)
// GetSession returns the registration session for the given type ID.
//
// 1. Look up and return the session by its type ID.
GetSession(ctx context.Context, sessionID string) (*RegistrationSession, *apierror.APIError)
// CompleteSession marks a registration session as completed and records the account ID.
//
// 1. Look up the session by type ID.
// 2. Mark the session as completed with the provided account ID in the repository.
CompleteSession(ctx context.Context, sessionID, accountID string) *apierror.APIError
}
type RegistrationQueueRepo ¶
type RegistrationQueueRepo interface {
// Create inserts a registration queue entry for the session, deduplicating on registration_session_id. The returned bool is true only when this call actually inserted a new row (so callers can suppress follow-on side effects like alert emails on retries).
Create(ctx context.Context, email, name, planCode, registrationSessionID string) (bool, *apierror.APIError)
}
type RegistrationSession ¶
type RegistrationSession struct {
ID int64
TypeID string
Email string `audit:"email"`
PlanCode string `audit:"plan_code"`
Step constants.RegistrationStep `audit:"step"`
VerificationToken string
IsEmailVerified bool `audit:"is_email_verified"`
IsExistingUser *bool `audit:"is_existing_user"`
UserID *string `audit:"user_id"`
AccountID *string `audit:"account_id"`
StripeCustomerID *string
StripeCheckoutSessionID *string
StripeSubscriptionID *string
PaymentCompleted bool `audit:"payment_completed"`
SessionData RegistrationSessionData `audit:"session_data"`
CompletedAt *time.Time `audit:"completed_at"`
CreatedAt time.Time
UpdatedAt time.Time
}
func (*RegistrationSession) ToProto ¶
func (s *RegistrationSession) ToProto() *pb.RegistrationSessionInfo
type RegistrationSessionData ¶
type RegistrationSessionRepo ¶
type RegistrationSessionRepo interface {
// GetByEmail returns the most recent active (uncompleted) registration session for the given email. Returns a not-found error if none exists.
GetByEmail(ctx context.Context, email string) (*RegistrationSession, *apierror.APIError)
// GetByTypeID returns the registration session with the given type ID. Returns a not-found error if none exists.
GetByTypeID(ctx context.Context, typeID string) (*RegistrationSession, *apierror.APIError)
// GetByToken returns the registration session matching the verification token. Returns a not-found error if no session has the given token.
GetByToken(ctx context.Context, token string) (*RegistrationSession, *apierror.APIError)
// GetByID returns the registration session with the given database ID. Returns a not-found error if none exists.
GetByID(ctx context.Context, id int64) (*RegistrationSession, *apierror.APIError)
// GetIncompleteByUserID returns the most recent incomplete registration session for the given user, or (nil, nil) if none exists.
GetIncompleteByUserID(ctx context.Context, userID string) (*RegistrationSession, *apierror.APIError)
// Create persists a new registration session and returns the database-assigned ID.
Create(ctx context.Context, session *RegistrationSession) (int64, *apierror.APIError)
// UpdatePlanCode changes the plan code on a registration session.
UpdatePlanCode(ctx context.Context, id int64, planCode string) *apierror.APIError
// UpdateToken replaces the verification token for the given session.
UpdateToken(ctx context.Context, id int64, verificationToken string) *apierror.APIError
// UpdateEmailVerified marks the session's email as verified and sets the is_existing_user flag.
UpdateEmailVerified(ctx context.Context, id int64, isExistingUser *bool) *apierror.APIError
// UpdateStep advances the session to the given step and persists the session data.
UpdateStep(ctx context.Context, id int64, step constants.RegistrationStep, sessionData RegistrationSessionData) *apierror.APIError
// UpdateUser sets the user ID on the session and persists updated session data.
UpdateUser(ctx context.Context, id int64, userID string, sessionData RegistrationSessionData) *apierror.APIError
// UpdateStripeCustomer sets the Stripe customer ID and checkout session ID on the registration session.
UpdateStripeCustomer(ctx context.Context, id int64, stripeCustomerID *string, stripeCheckoutSessionID *string) *apierror.APIError
// UpdatePaymentCompleted sets the payment completed flag and Stripe subscription ID on the registration session.
UpdatePaymentCompleted(ctx context.Context, id int64, paymentCompleted bool, stripeSubscriptionID *string) *apierror.APIError
// ListByUserID returns open (uncompleted) registration sessions for the given user with cursor-based pagination.
ListByUserID(ctx context.Context, userID string, cursor *string, limit int32) ([]*RegistrationSession, pagination.PageInfo, *apierror.APIError)
// UpdateAccountID sets the account ID on the registration session without marking it as completed.
UpdateAccountID(ctx context.Context, id int64, accountID string) *apierror.APIError
// Complete marks the registration session as completed and records the account ID.
Complete(ctx context.Context, id int64, accountID *string) *apierror.APIError
}
type RegistrationSessionSvc ¶
type RegistrationSessionSvc interface {
// CreateSession creates a new registration session or returns an existing active (uncompleted) session for the given email.
//
// Side effects:
// - Sends a verification email to the user.
CreateSession(ctx context.Context, input CreateRegistrationSessionInput) (*CreateRegistrationSessionResult, *apierror.APIError)
// ResendVerificationEmail regenerates the verification token for an existing registration session and resends the verification email.
//
// Side effects:
// - Rotates the verification token.
// - Sends a new verification email to the user.
ResendVerificationEmail(ctx context.Context, sessionID string) *apierror.APIError
// VerifyToken verifies the email token from the registration verification link. Marks the session's email as verified and advances the step to user_details. Idempotent: repeated calls return the same session.
VerifyToken(ctx context.Context, token string) (*RegistrationSession, *apierror.APIError)
// GetSession returns the current state of a registration session by its type ID. Returns a not-found error if the session does not exist.
GetSession(ctx context.Context, sessionID string) (*RegistrationSession, *apierror.APIError)
// CreateUserForSession creates or resolves a user for a registration session and returns the user ID with auth tokens.
//
// Side effects:
// - Creates a new user if one does not already exist for the session email.
// - Associates the user with the registration session.
// - Advances the session step to account_details.
CreateUserForSession(ctx context.Context, input CreateUserForRegistrationInput) (*CreateUserForRegistrationOutput, *apierror.APIError)
// UpdateSession updates an in-progress registration session's step, form data, and/or Stripe-related fields. Returns the updated session.
//
// Authorization:
// - Requires a user identity in context.
UpdateSession(ctx context.Context, input UpdateRegistrationSessionInput) (*RegistrationSession, *apierror.APIError)
// ListSessions returns a paginated list of open (uncompleted) registration sessions for the authenticated user.
//
// Authorization:
// - Requires a user identity in context.
ListSessions(ctx context.Context, input ListRegistrationSessionsInput) (*ListRegistrationSessionsResult, *apierror.APIError)
// SetupBilling creates a Stripe customer and Setup Intent for a registration session. Uses recovery points for crash safety.
//
// Authorization:
// - Requires a user identity in context.
SetupBilling(ctx context.Context, input SetupBillingInput) (*SetupBillingOutput, *apierror.APIError)
// ConfirmPayment verifies that a Setup Intent succeeded and marks the registration session's payment as completed.
//
// Authorization:
// - Requires a user identity in context matching the session's user.
ConfirmPayment(ctx context.Context, input ConfirmPaymentInput) (*ConfirmPaymentOutput, *apierror.APIError)
// CompleteRegistration finalizes a registration session by calling core-service to create the production account, sandbox, roles, and permissions, then marks the session as completed.
//
// Authorization:
// - Requires a user identity in context matching the session's user.
CompleteRegistration(ctx context.Context, sessionID string) (*CompleteRegistrationOutput, *apierror.APIError)
// GetIncompleteByUserID returns the most recent incomplete registration session for the given user, or (nil, nil) if none exists.
GetIncompleteByUserID(ctx context.Context, userID string) (*RegistrationSession, *apierror.APIError)
}
type RepoFactory ¶
type RepoFactory interface {
NewUserRepo() UserRepo
NewRefreshTokenRepo() RefreshTokenRepo
NewAPIKeyRepo() APIKeyRepo
NewDocAPIKeyRepo() DocAPIKeyRepo
NewRegistrationSessionRepo() RegistrationSessionRepo
NewRegistrationQueueRepo() RegistrationQueueRepo
NewIdempotencyKeyRepo() IdempotencyKeyRepo
NewOutboxRepo() messaging.OutboxRepo
}
RepoFactory provides repository instances that share the same underlying sqlc query executor (plain DB access or a transaction).
type RequestIdentity ¶
type RequestIdentity struct {
ActorID string
IdentityType types.IdentityActorType
TargetAccountID *string
}
type RevokeAPIKeyInput ¶
type RevokeAPIKeyInput struct {
APIKeyID string
}
type RotateAPIKeyInput ¶
type SetupBillingInput ¶
type SetupBillingInput struct {
SessionID string
}
type SetupBillingOutput ¶
type SetupIntentResult ¶
type SetupIntentResult struct {
SetupIntentID string
ClientSecret string // #nosec G117 -- Stripe ephemeral client secret
Status string
PaymentMethodID *string
PublishableKey string
}
SetupIntentResult holds the result of a Setup Intent operation from billing-service.
type StripeCustomer ¶
type StripeCustomer struct {
ID string
}
StripeCustomer represents a Stripe customer created during registration.
type TokenSvc ¶
type TokenSvc interface {
// RefreshToken exchanges a valid refresh token for a new short-lived access token.
RefreshToken(ctx context.Context, refreshToken string) (*RefreshTokenResult, *apierror.APIError)
// RevokeRefreshToken invalidates a refresh token so it can no longer be used to mint access tokens.
RevokeRefreshToken(ctx context.Context, refreshToken string) *apierror.APIError
}
type UpdateRegistrationSessionData ¶
type UpdateRegistrationSessionData struct {
UserName *string
AccountName *string
BillingAddressLine1 *string
BillingAddressLine2 *string
BillingAddressCity *string
BillingAddressState *string
BillingAddressPostalCode *string
BillingAddressCountry *string
}
func (*UpdateRegistrationSessionData) MergeInto ¶
func (u *UpdateRegistrationSessionData) MergeInto(target *RegistrationSessionData)
MergeInto applies non-nil fields from the update into the target, leaving fields that were not provided in the PATCH request unchanged.
type UpdateRegistrationSessionInput ¶
type UpdateRegistrationSessionInput struct {
SessionID string
Step *constants.RegistrationStep
SessionData *UpdateRegistrationSessionData
}
type UserMed ¶
type UserMed interface {
// GenAuthAccessToken mints an access token that can be used to authenticate requests to the API.
GenAuthAccessToken(ctx context.Context, userID string) (string, *apierror.APIError)
// Register registers a new user with the given input.
//
// 1. Check if a user with the given email already exists; return a validation error if so.
// 2. Generate a unique user ID.
// 3. Create the user record in the repository.
// 4. Send a welcome email if the user has an email and name.
//
// Side effects:
// - Sends a welcome email.
Register(ctx context.Context, input RegisterUserInput) (*types.User, *apierror.APIError)
// ValidateMagicLoginToken validates a magic-login token and returns the associated user.
ValidateMagicLoginToken(ctx context.Context, token string) (*types.User, *apierror.APIError)
// SendAlreadyRegisteredEmail generates a magic login token and sends the "already registered" email so the user can log in with one click. This must be called outside a transaction so the outbox message is not rolled back.
SendAlreadyRegisteredEmail(ctx context.Context, user *types.User, accountSlug, portalBaseURL *string)
// ValidateCredential validates credentials provided by a request and returns an identity.
//
// 1. If authToken is empty, resolve the account mode for the target account (if provided) and return an unauthenticated identity.
// 2. If authToken has the API key prefix, delegate to validateAPIKeyCredential.
// 3. Otherwise, delegate to validateUserCredential for JWT-based validation.
//
// Behavior:
// - If authToken is empty, returns an unauthenticated identity.
// - If authToken is an API key credential, validates it as an API key.
// - Otherwise, validates it as a user credential (JWT).
ValidateCredential(ctx context.Context, authToken string, targetAccountID *string, actorAccountID *string) (*types.Identity, *apierror.APIError)
}
type UserRepo ¶
type UserRepo interface {
Find(ctx context.Context, identifier string) (*types.User, *apierror.APIError)
Create(ctx context.Context, userID, email, name, hashedPassword string) (*types.User, *apierror.APIError)
UpdatePassword(ctx context.Context, userID string, hashedPassword string) *apierror.APIError
}
type UserSvc ¶
type UserSvc interface {
// Login authenticates a user and returns a token pair (access + refresh) plus the user profile.
Login(ctx context.Context, identifier, password string) (*LoginResult, *apierror.APIError)
// Register creates a new user and returns a token pair so the user is immediately logged in.
Register(ctx context.Context, input RegisterInput) (*LoginResult, *apierror.APIError)
// MagicLogin exchanges a magic-login token for a token pair, logging the user in without a password.
MagicLogin(ctx context.Context, token string) (*LoginResult, *apierror.APIError)
}
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
mock
|
|
|
client
Package clientmock is a generated GoMock package.
|
Package clientmock is a generated GoMock package. |
|
factory
Package factorymock is a generated GoMock package.
|
Package factorymock is a generated GoMock package. |
|
mediator
Package mediatormock is a generated GoMock package.
|
Package mediatormock is a generated GoMock package. |
|
publisher
Package publishermock is a generated GoMock package.
|
Package publishermock is a generated GoMock package. |
|
repository
Package repositorymock is a generated GoMock package.
|
Package repositorymock is a generated GoMock package. |
|
service
Package servicemock is a generated GoMock package.
|
Package servicemock is a generated GoMock package. |