Documentation
¶
Overview ¶
Package service implements the business logic for the identity service.
The AuthService sits between the Connect-Go handler layer and the EntDB persistence layer. It contains all authentication, token management, and 2FA logic. It does NOT import Connect/protobuf types -- it uses plain Go structs and returns errors that the handler translates to gRPC codes.
Security invariants:
- Passwords, secrets, and tokens are NEVER logged.
- Failed login lockout: 5 attempts, 15-min lock (configurable).
- TOTP challenges: 5-min expiry, single-use.
- QR login sessions: 5-min expiry, consumed after token issuance.
- Refresh tokens: rotated on every use (old token deleted, new one issued).
- All security events are audit-logged via the audit.Logger.
Package service — stub implementations for Repository and DB.
These stubs return ErrServiceUnavailable for every operation. They exist so the identity service binary can start and serve health checks / JWKS even when the EntDB persistence adapter has not yet been wired up. Any RPC that touches persistence will receive a clean "service unavailable" error instead of a nil-pointer panic.
Index ¶
- Constants
- Variables
- type AdminService
- func (s *AdminService) DeactivateUser(ctx context.Context, actorID, targetUserID, reason string) error
- func (s *AdminService) GetUser(ctx context.Context, actorID, userID string) (*User, error)
- func (s *AdminService) InviteUser(ctx context.Context, actorID, email, name, role, recoveryEmail string, ...) (*InviteResult, error)
- func (s *AdminService) ListUsers(ctx context.Context, actorID, statusFilter, search, cursor string, limit int) ([]*User, string, int, error)
- func (s *AdminService) ReactivateUser(ctx context.Context, actorID, targetUserID string) error
- func (s *AdminService) ResetUserPassword(ctx context.Context, actorID, targetUserID string, generateTemp bool) (*ResetPasswordResult, error)
- func (s *AdminService) SetUserQuota(ctx context.Context, actorID, targetUserID string, quotaBytes int64) error
- func (s *AdminService) UpdateUser(ctx context.Context, actorID, userID, name, role, avatarURL string) (*User, error)
- type AuditEvent
- type AuthService
- func (s *AuthService) AcceptInvitation(ctx context.Context, invitationToken, password, name, ipAddr, userAgent string) (*LoginResult, error)
- func (s *AuthService) ApproveQrLogin(ctx context.Context, sessionID string, approve bool, userID, userAgent string) (string, error)
- func (s *AuthService) BeginOAuthLogin(ctx context.Context, provider, redirectURI string) (*OAuthBeginResult, error)
- func (s *AuthService) BeginPasskeyLogin(ctx context.Context, email string) (string, string, error)
- func (s *AuthService) BeginPasskeyRegistration(ctx context.Context, userID, deviceName string) (string, string, error)
- func (s *AuthService) BeginTotpSetup(ctx context.Context, userID string) (string, string, []string, error)
- func (s *AuthService) CompletePasskeyLogin(ctx context.Context, challengeID, credentialJSON, ipAddr, userAgent string) (*LoginResult, error)
- func (s *AuthService) CompletePasskeyRegistration(ctx context.Context, userID, challengeID, credentialJSON, deviceName string) (*PasskeyInfo, error)
- func (s *AuthService) ConfirmEmailChange(ctx context.Context, token string) (*User, error)
- func (s *AuthService) ConfirmPasswordReset(ctx context.Context, token, newPassword string) error
- func (s *AuthService) DisableTotp(ctx context.Context, userID, password string) error
- func (s *AuthService) GetCurrentUser(ctx context.Context, userID string) (*User, error)
- func (s *AuthService) GetQrLoginSession(ctx context.Context, sessionID string) (*QrSessionInfo, error)
- func (s *AuthService) InitiateQrLogin(ctx context.Context, deviceInfo, userAgent, ipAddr string) (string, string, int32, error)
- func (s *AuthService) Logout(ctx context.Context, rawRefreshToken string) error
- func (s *AuthService) OAuthLogin(ctx context.Context, ...) (*LoginResult, error)
- func (s *AuthService) PasswordLogin(ctx context.Context, email, password, ipAddr, userAgent string) (*LoginResult, error)
- func (s *AuthService) PasswordSignup(ctx context.Context, email, password, name, recoveryEmail string) (*LoginResult, error)
- func (s *AuthService) PollQrLogin(ctx context.Context, sessionID, ipAddr, userAgent string) (*PollQrResult, error)
- func (s *AuthService) RefreshToken(ctx context.Context, rawRefreshToken, ipAddr, userAgent string) (*User, string, string, error)
- func (s *AuthService) RegenerateRecoveryCodes(ctx context.Context, userID, password string) ([]string, error)
- func (s *AuthService) RequestEmailChange(ctx context.Context, userID, newEmail, currentPassword string) error
- func (s *AuthService) RequestPasswordReset(ctx context.Context, emailAddr string) error
- func (s *AuthService) SendEmailVerification(ctx context.Context, userID string) error
- func (s *AuthService) VerifyEmail(ctx context.Context, token string) (*User, error)
- func (s *AuthService) VerifyTotp(ctx context.Context, challengeID, code, ipAddr, userAgent string) (*LoginResult, error)
- func (s *AuthService) VerifyTotpSetup(ctx context.Context, userID, code string) (bool, error)
- type BeginIdentityVerificationResult
- type DB
- type EmailChangeToken
- type EmailVerificationToken
- type Group
- type GroupService
- func (s *GroupService) AddGroupMember(ctx context.Context, actorID, groupID, userID string) error
- func (s *GroupService) CreateGroup(ctx context.Context, actorID, name, description string) (*Group, error)
- func (s *GroupService) DeleteGroup(ctx context.Context, actorID, groupID string) error
- func (s *GroupService) ListGroupMembers(ctx context.Context, actorID, groupID string) ([]*User, error)
- func (s *GroupService) ListGroups(ctx context.Context, actorID, cursor string, limit int) ([]*Group, string, error)
- func (s *GroupService) RemoveGroupMember(ctx context.Context, actorID, groupID, userID string) error
- func (s *GroupService) UpdateGroup(ctx context.Context, actorID, groupID, name, description string) (*Group, error)
- type HelpRequest
- type HelpService
- func (s *HelpService) ListHelpRequests(ctx context.Context, actorID, statusFilter, cursor string, limit int) ([]*HelpRequest, string, int, error)
- func (s *HelpService) RequestAdminHelp(ctx context.Context, email, reason, sourceIP, userAgent string) error
- func (s *HelpService) ResolveHelpRequest(ctx context.Context, actorID, requestID string, reject bool, notes string) (*HelpRequest, error)
- type IdentityVerificationRecord
- type IdentityVerificationService
- func (s *IdentityVerificationService) BeginIdentityVerification(ctx context.Context, userID string) (*BeginIdentityVerificationResult, error)
- func (s *IdentityVerificationService) GetIdentityVerificationStatus(ctx context.Context, callerUserID, verificationID string) (*IdentityVerificationRecord, error)
- type InvitationRecord
- type InviteResult
- type LoginChallengeRecord
- type LoginResult
- type OAuthBeginResult
- type OAuthIdentity
- type PasskeyChallengeRecord
- type PasskeyCredRecord
- type PasskeyInfo
- type PasswordResetToken
- type PollQrResult
- type ProfileService
- func (s *ProfileService) ChangePassword(ctx context.Context, userID, currentPassword, newPassword string) error
- func (s *ProfileService) DeletePasskey(ctx context.Context, userID, credentialID string) error
- func (s *ProfileService) ListAuditEvents(ctx context.Context, actorID, targetID, eventType string, ...) ([]*AuditEvent, string, error)
- func (s *ProfileService) ListMyPasskeys(ctx context.Context, userID string) ([]*PasskeyInfo, error)
- func (s *ProfileService) ListMySessions(ctx context.Context, userID string) ([]*Session, error)
- func (s *ProfileService) RevokeAllSessions(ctx context.Context, userID, password string) (int, error)
- func (s *ProfileService) RevokeSession(ctx context.Context, userID, sessionID string) error
- func (s *ProfileService) UpdateProfile(ctx context.Context, userID, name, avatarURL string) (*User, error)
- type QrLoginSessionRecord
- type QrSessionInfo
- type RecoveryCodeRecord
- type RefreshTokenRecord
- type Repository
- type ResetPasswordResult
- type Session
- type StubDB
- func (StubDB) ExecuteAtomic(context.Context, string, string, string, []entdb.Operation) (*entdb.CommitResult, error)
- func (StubDB) GetEdgesFrom(context.Context, string, string, string, int) ([]*entdb.Edge, error)
- func (StubDB) GetEdgesTo(context.Context, string, string, string, int) ([]*entdb.Edge, error)
- func (StubDB) GetNode(context.Context, string, string, int, string) (*entdb.Node, error)
- func (StubDB) QueryNodes(context.Context, string, string, int, map[string]any) ([]*entdb.Node, error)
- func (StubDB) SearchNodes(context.Context, string, string, int, string) ([]*entdb.Node, error)
- type StubRepository
- func (StubRepository) ConsumeRefreshTokenByHash(context.Context, string, int64) error
- func (StubRepository) CreateEmailChangeToken(context.Context, *EmailChangeToken) error
- func (StubRepository) CreateEmailVerificationToken(context.Context, *EmailVerificationToken) error
- func (StubRepository) CreateIdentityVerification(context.Context, *IdentityVerificationRecord) error
- func (StubRepository) CreateLoginChallenge(context.Context, *LoginChallengeRecord) (string, error)
- func (StubRepository) CreateOAuthIdentity(context.Context, *OAuthIdentity) error
- func (StubRepository) CreatePasskeyChallenge(context.Context, *PasskeyChallengeRecord) (string, error)
- func (StubRepository) CreatePasskeyCredential(context.Context, *PasskeyCredRecord) (string, error)
- func (StubRepository) CreatePasswordResetToken(context.Context, *PasswordResetToken) error
- func (StubRepository) CreateQrLoginSession(context.Context, *QrLoginSessionRecord) (string, error)
- func (StubRepository) CreateRecoveryCode(context.Context, *RecoveryCodeRecord) (string, error)
- func (StubRepository) CreateRefreshToken(context.Context, *RefreshTokenRecord) (string, error)
- func (StubRepository) CreateTotpCredential(context.Context, *TotpCredRecord) (string, error)
- func (StubRepository) CreateUser(context.Context, *User) (string, error)
- func (StubRepository) DeleteLoginChallenge(context.Context, string) error
- func (StubRepository) DeletePasskeyChallenge(context.Context, string) error
- func (StubRepository) DeleteRecoveryCodesForUser(context.Context, string) error
- func (StubRepository) DeleteRefreshToken(context.Context, string) error
- func (StubRepository) DeleteRefreshTokensForUser(context.Context, string) error
- func (StubRepository) DeleteTotpCredential(context.Context, string) error
- func (StubRepository) DeleteTotpCredentialsForUser(context.Context, string) error
- func (StubRepository) FindEmailChangeTokenByHash(context.Context, string) (*EmailChangeToken, error)
- func (StubRepository) FindEmailVerificationTokenByHash(context.Context, string) (*EmailVerificationToken, error)
- func (StubRepository) FindInvitationByHash(context.Context, string) (*InvitationRecord, error)
- func (StubRepository) FindPasswordResetTokenByHash(context.Context, string) (*PasswordResetToken, error)
- func (StubRepository) FindQrLoginSession(context.Context, string) (*QrLoginSessionRecord, error)
- func (StubRepository) FindRecoveryCodeByHash(context.Context, string, string) (*RecoveryCodeRecord, error)
- func (StubRepository) FindRefreshTokenByHash(context.Context, string) (*RefreshTokenRecord, error)
- func (StubRepository) FindRefreshTokenByHashIncludingConsumed(context.Context, string) (*RefreshTokenRecord, error)
- func (StubRepository) FindUserByEmail(context.Context, string) (*User, error)
- func (StubRepository) FindUserByProviderID(context.Context, string, string) (*User, error)
- func (StubRepository) GetIdentityVerification(context.Context, string) (*IdentityVerificationRecord, error)
- func (StubRepository) GetLatestIdentityVerificationForUser(context.Context, string) (*IdentityVerificationRecord, error)
- func (StubRepository) GetLoginChallengeByChallengeID(context.Context, string) (*LoginChallengeRecord, error)
- func (StubRepository) GetPasskeyChallenge(context.Context, string) (*PasskeyChallengeRecord, error)
- func (StubRepository) GetPasskeyCredentialByCredID(context.Context, string) (*PasskeyCredRecord, error)
- func (StubRepository) GetTotpCredential(context.Context, string) (*TotpCredRecord, error)
- func (StubRepository) GetUser(context.Context, string) (*User, error)
- func (StubRepository) IncrementFailedLoginCount(context.Context, string) (int32, error)
- func (StubRepository) ListOAuthIdentitiesForUser(context.Context, string) ([]*OAuthIdentity, error)
- func (StubRepository) ListPasskeyCredentials(context.Context, string) ([]*PasskeyCredRecord, error)
- func (StubRepository) MarkEmailChangeTokenConsumed(context.Context, string, int64) error
- func (StubRepository) MarkEmailVerificationTokenConsumed(context.Context, string, int64) error
- func (StubRepository) MarkPasswordResetTokenConsumed(context.Context, string, int64) error
- func (StubRepository) ResetFailedLoginCount(context.Context, string) error
- func (StubRepository) SetUserEmailVerified(context.Context, string, int64) error
- func (StubRepository) SetUserIDVVerified(context.Context, string, int64) error
- func (StubRepository) SetUserLockedUntil(context.Context, string, int64) error
- func (StubRepository) UpdateIdentityVerificationStatus(context.Context, string, string, string, int64, int64) error
- func (StubRepository) UpdateInvitation(context.Context, string, map[string]any) error
- func (StubRepository) UpdatePasskeyCredential(context.Context, string, map[string]any) error
- func (StubRepository) UpdateQrLoginSession(context.Context, string, map[string]any) error
- func (StubRepository) UpdateRecoveryCode(context.Context, string, map[string]any) error
- func (StubRepository) UpdateTotpCredential(context.Context, string, map[string]any) error
- func (StubRepository) UpdateUser(context.Context, string, map[string]any) error
- func (StubRepository) UpdateUserEmail(context.Context, string, string, int64) error
- type TotpCredRecord
- type User
Constants ¶
const ( IDVStatusPending = "pending" IDVStatusInReview = "in_review" IDVStatusApproved = "approved" IDVStatusRejected = "rejected" IDVStatusExpired = "expired" )
Identity-verification status string constants. Mirrored as proto enum values in IdentityVerificationStatus.
Variables ¶
var ( ErrUnauthenticated = errors.New("unauthenticated") ErrPermissionDenied = errors.New("permission denied") ErrInvalidArgument = errors.New("invalid argument") ErrNotFound = errors.New("not found") ErrAlreadyExists = errors.New("already exists") ErrAccountLocked = errors.New("account locked") ErrNoPasswordSet = errors.New("no password set for this account") ErrAccountNotActive = errors.New("account is not active") ErrInvitationPending = errors.New("account has not completed invitation") ErrIDVRequired = errors.New("identity verification required") ErrWeakPassword = errors.New("password does not meet strength requirements") ErrTotpRequired = errors.New("totp required") ErrTokenExpired = errors.New("token expired") ErrInvalidTotpCode = errors.New("invalid totp code") ErrQrLoginExpired = errors.New("qr login session expired") ErrQrLoginNotPending = errors.New("qr login session is not pending") ErrInvitationUsed = errors.New("invitation has already been accepted") ErrInvitationExpired = errors.New("invitation has expired") ErrLocalAuthDisabled = errors.New("local auth disabled") ErrOAuthDisabled = errors.New("oauth login is not configured") ErrSignupDisabled = errors.New("signup is disabled for this deployment") )
ErrServiceUnavailable is returned by stub implementations.
Functions ¶
This section is empty.
Types ¶
type AdminService ¶
type AdminService struct {
// contains filtered or unexported fields
}
AdminService implements admin user-management operations. All methods verify that the acting user has role=admin.
func NewAdminService ¶
func NewAdminService(db DB, tenantID string, auditLog *audit.Logger, cfg *config.Config, mailer email.Transport, logger *zap.Logger) *AdminService
NewAdminService creates an AdminService.
mailer may be nil; if nil, a log-only transport is substituted so invitation emails are at least visible in the logs during local dev. Email-side-effect failures never block the surrounding RPC.
func (*AdminService) DeactivateUser ¶
func (s *AdminService) DeactivateUser(ctx context.Context, actorID, targetUserID, reason string) error
DeactivateUser marks a user as deactivated and revokes sessions.
func (*AdminService) InviteUser ¶
func (s *AdminService) InviteUser( ctx context.Context, actorID, email, name, role, recoveryEmail string, quotaBytes int64, createImmediately bool, ) (*InviteResult, error)
InviteUser creates a new user (invited or immediately active) and returns the user, an invitation token, setup URL, and optional temporary password.
func (*AdminService) ListUsers ¶
func (s *AdminService) ListUsers( ctx context.Context, actorID, statusFilter, search, cursor string, limit int, ) ([]*User, string, int, error)
ListUsers returns a paginated list of users, optionally filtered by status and/or search substring.
func (*AdminService) ReactivateUser ¶
func (s *AdminService) ReactivateUser(ctx context.Context, actorID, targetUserID string) error
ReactivateUser sets a deactivated user back to active.
func (*AdminService) ResetUserPassword ¶
func (s *AdminService) ResetUserPassword( ctx context.Context, actorID, targetUserID string, generateTemp bool, ) (*ResetPasswordResult, error)
ResetUserPassword generates a temp password or reset token for a user.
func (*AdminService) SetUserQuota ¶
func (s *AdminService) SetUserQuota(ctx context.Context, actorID, targetUserID string, quotaBytes int64) error
SetUserQuota updates the storage quota for a user.
func (*AdminService) UpdateUser ¶
func (s *AdminService) UpdateUser(ctx context.Context, actorID, userID, name, role, avatarURL string) (*User, error)
UpdateUser patches name, role, and/or avatar_url for a user.
type AuditEvent ¶
type AuditEvent struct {
ID string
EventType string
ActorUserID string
TargetUserID string
IPAddress string
UserAgent string
Success bool
Details map[string]any
CreatedAt int64
}
AuditEvent is the domain representation of an audit event (type_id 26).
type AuthService ¶
type AuthService struct {
// contains filtered or unexported fields
}
AuthService implements authentication and token management business logic.
func NewAuthService ¶
func NewAuthService( repo Repository, cfg *config.Config, keyRing *jwt.KeyRing, passkeysSvc *passkeys.WebAuthnService, auditLogger *audit.Logger, totpKey []byte, mailer email.Transport, logger *zap.Logger, ) *AuthService
NewAuthService creates an AuthService with all required dependencies.
mailer may be nil; if nil, a non-delivering log-only transport is substituted so service code can always call s.mailer.Send without a nil check. Email-side-effect failures are logged and do NOT fail the surrounding RPC.
func NewAuthServiceWithOAuth ¶
func NewAuthServiceWithOAuth( repo Repository, cfg *config.Config, keyRing *jwt.KeyRing, passkeysSvc *passkeys.WebAuthnService, auditLogger *audit.Logger, totpKey []byte, mailer email.Transport, logger *zap.Logger, oauthRegistry *oauth.Registry, ) *AuthService
NewAuthServiceWithOAuth is the extended constructor that injects an oauth.Registry. Pass nil to disable OAuth login.
func (*AuthService) AcceptInvitation ¶
func (s *AuthService) AcceptInvitation(ctx context.Context, invitationToken, password, name, ipAddr, userAgent string) (*LoginResult, error)
AcceptInvitation completes an admin-issued invitation.
func (*AuthService) ApproveQrLogin ¶
func (s *AuthService) ApproveQrLogin(ctx context.Context, sessionID string, approve bool, userID, userAgent string) (string, error)
ApproveQrLogin approves or rejects a QR login session. Returns the new status.
func (*AuthService) BeginOAuthLogin ¶
func (s *AuthService) BeginOAuthLogin( ctx context.Context, provider, redirectURI string, ) (*OAuthBeginResult, error)
BeginOAuthLogin returns a provider authorization URL plus the server-minted state artifacts needed to complete the callback safely.
func (*AuthService) BeginPasskeyLogin ¶
BeginPasskeyLogin generates WebAuthn authentication options. If email is provided, scopes to that user's credentials. Otherwise allows discoverable credentials (usernameless flow).
func (*AuthService) BeginPasskeyRegistration ¶
func (s *AuthService) BeginPasskeyRegistration(ctx context.Context, userID, deviceName string) (string, string, error)
BeginPasskeyRegistration generates WebAuthn registration options for the authenticated user. Returns (optionsJSON, challengeID, error).
func (*AuthService) BeginTotpSetup ¶
func (s *AuthService) BeginTotpSetup(ctx context.Context, userID string) (string, string, []string, error)
BeginTotpSetup starts TOTP enrollment for the authenticated user. Returns (secret, qrURI, recoveryCodes, error).
func (*AuthService) CompletePasskeyLogin ¶
func (s *AuthService) CompletePasskeyLogin(ctx context.Context, challengeID, credentialJSON, ipAddr, userAgent string) (*LoginResult, error)
CompletePasskeyLogin verifies the assertion response and issues tokens.
func (*AuthService) CompletePasskeyRegistration ¶
func (s *AuthService) CompletePasskeyRegistration(ctx context.Context, userID, challengeID, credentialJSON, deviceName string) (*PasskeyInfo, error)
CompletePasskeyRegistration verifies the attestation response and stores the new credential. Returns (credentialInfo, error).
func (*AuthService) ConfirmEmailChange ¶
ConfirmEmailChange consumes a pending EmailChangeToken and swaps the user's primary email to the verified new address. Token must be unconsumed and unexpired. On success:
- user.email is updated to new_email
- user.email_verified is set to true (verified by clicking the link)
- user.email_verified_at is updated
- the token is marked consumed
- all of the user's refresh tokens are revoked (OAuth 2.1 §4.13: credential changes force re-auth on every device)
If the new address has been claimed by another user since the request, returns ErrAlreadyExists — the token is NOT consumed in that case so the user can call ConfirmEmailChange again if the conflict resolves before the token expires.
func (*AuthService) ConfirmPasswordReset ¶
func (s *AuthService) ConfirmPasswordReset(ctx context.Context, token, newPassword string) error
ConfirmPasswordReset consumes a password-reset token and sets the user's new password.
Token must be unconsumed and unexpired. On success, every refresh token belonging to the user is revoked — OAuth 2.1 §4.13 best practice for any credential change forces re-login on all devices.
func (*AuthService) DisableTotp ¶
func (s *AuthService) DisableTotp(ctx context.Context, userID, password string) error
DisableTotp disables 2FA for the authenticated user. Requires password confirmation.
func (*AuthService) GetCurrentUser ¶
GetCurrentUser returns the user record for the given user ID.
func (*AuthService) GetQrLoginSession ¶
func (s *AuthService) GetQrLoginSession(ctx context.Context, sessionID string) (*QrSessionInfo, error)
GetQrLoginSession returns display-safe details of a QR login session.
func (*AuthService) InitiateQrLogin ¶
func (s *AuthService) InitiateQrLogin(ctx context.Context, deviceInfo, userAgent, ipAddr string) (string, string, int32, error)
InitiateQrLogin creates a new QR login session for an unauthenticated device. Returns (sessionID, qrURL, expiresIn, error).
func (*AuthService) Logout ¶
func (s *AuthService) Logout(ctx context.Context, rawRefreshToken string) error
Logout deletes the refresh token identified by the raw token value.
func (*AuthService) OAuthLogin ¶
func (s *AuthService) OAuthLogin( ctx context.Context, code, provider, redirectURI, codeVerifier, state, stateToken, ipAddr, userAgent string, ) (*LoginResult, error)
OAuthLogin performs the full OAuth code-exchange flow: it looks up the registered Exchanger for the provider, swaps the code for a verified Identity, then upserts the local user and issues tokens.
The frontend / gateway is NOT trusted to validate the user's identity; identity does the exchange itself. Provider access / refresh tokens are discarded — they are not persisted.
func (*AuthService) PasswordLogin ¶
func (s *AuthService) PasswordLogin(ctx context.Context, email, password, ipAddr, userAgent string) (*LoginResult, error)
PasswordLogin authenticates a user with email + password. If TOTP is enabled, returns TotpRequired=true with a LoginChallengeID.
func (*AuthService) PasswordSignup ¶
func (s *AuthService) PasswordSignup(ctx context.Context, email, password, name, recoveryEmail string) (*LoginResult, error)
PasswordSignup creates a new user with email + password and issues tokens.
func (*AuthService) PollQrLogin ¶
func (s *AuthService) PollQrLogin(ctx context.Context, sessionID, ipAddr, userAgent string) (*PollQrResult, error)
PollQrLogin polls a QR login session. When approved, issues tokens and marks the session consumed. Returns (status, user, accessToken, refreshToken, error).
func (*AuthService) RefreshToken ¶
func (s *AuthService) RefreshToken(ctx context.Context, rawRefreshToken, ipAddr, userAgent string) (*User, string, string, error)
RefreshToken validates a refresh token, rotates it, and returns new tokens.
Replay detection is durable: rotated tokens are kept in the repository with consumed_at != 0 instead of being deleted, so any instance — and any process restart — can detect a stolen-token replay (OAuth 2.1 §4.13). When replay is detected, ALL of the user's refresh tokens are hard-deleted to bound the blast radius.
Concurrency: the repository's ConsumeRefreshTokenByHash is the serialization point. Two goroutines presenting the same refresh token race to consume it; the loser sees ErrUnauthenticated and the row stays consumed exactly once.
Background sweep: rows whose ConsumedAtMs is older than the desired retention window (e.g. 90 days) may be hard-deleted by a periodic job. That sweep is intentionally NOT implemented here.
func (*AuthService) RegenerateRecoveryCodes ¶
func (s *AuthService) RegenerateRecoveryCodes(ctx context.Context, userID, password string) ([]string, error)
RegenerateRecoveryCodes issues a fresh batch of recovery codes. Requires password confirmation and TOTP to be enabled.
func (*AuthService) RequestEmailChange ¶
func (s *AuthService) RequestEmailChange(ctx context.Context, userID, newEmail, currentPassword string) error
RequestEmailChange initiates a primary-email rotation for the user.
The caller must re-authenticate with their current password (OAuth 2.1 best practice for any high-value credential change). The new address is validated and checked for uniqueness; an EmailChangeToken is created and two emails are dispatched:
- to the NEW address: a verification link the user must click to complete the change.
- to the OLD address: a security notice informing the user that a change has been requested.
The email swap does NOT take effect until ConfirmEmailChange consumes the token. If another user claims the new_email between this call and the confirm call, the confirm call will fail with ErrAlreadyExists (last-write-wins is rejected — the contended new_email is still owned by the other user, so we cannot reassign it).
func (*AuthService) RequestPasswordReset ¶
func (s *AuthService) RequestPasswordReset(ctx context.Context, emailAddr string) error
RequestPasswordReset creates a password-reset token for the user matching the supplied email and dispatches a reset email.
Per OWASP guidance and the proto contract, this method always returns nil even when the email is unknown — the response time is also kept roughly equivalent so the endpoint cannot be used as an email-enumeration oracle. Errors during token persistence or email dispatch are logged internally; the caller is told nothing.
func (*AuthService) SendEmailVerification ¶
func (s *AuthService) SendEmailVerification(ctx context.Context, userID string) error
SendEmailVerification creates a verification token for the user and dispatches a verification email. Idempotent — calling it repeatedly just creates additional valid tokens (older tokens remain valid until their own expiry, on the principle that we should never invalidate a token a user might have already clicked).
func (*AuthService) VerifyEmail ¶
VerifyEmail consumes a verification token and marks the user's email as verified. Idempotent — re-verifying an already-verified user still consumes the supplied token but does not change state. Returns the updated user.
func (*AuthService) VerifyTotp ¶
func (s *AuthService) VerifyTotp(ctx context.Context, challengeID, code, ipAddr, userAgent string) (*LoginResult, error)
VerifyTotp completes a pending login by verifying a TOTP or recovery code. Returns (user, accessToken, refreshToken, error).
func (*AuthService) VerifyTotpSetup ¶
VerifyTotpSetup completes TOTP enrollment by verifying a code. Returns (verified, error).
type BeginIdentityVerificationResult ¶ added in v0.4.0
type BeginIdentityVerificationResult struct {
VerificationID string
Provider string
SessionToken string
ExpiresAt time.Time
}
BeginIdentityVerificationResult carries everything the connect handler needs to build a BeginIdentityVerificationResponse. The service layer keeps proto types out of its surface so the wiring is unit-testable without spinning up a Connect server.
type DB ¶
type DB interface {
GetNode(ctx context.Context, tenantID, actor string, typeID int, nodeID string) (*entdb.Node, error)
QueryNodes(ctx context.Context, tenantID, actor string, typeID int, filter map[string]any) ([]*entdb.Node, error)
ExecuteAtomic(ctx context.Context, tenantID, actor, idempotencyKey string, ops []entdb.Operation) (*entdb.CommitResult, error)
GetEdgesFrom(ctx context.Context, tenantID, actor, fromNodeID string, edgeTypeID int) ([]*entdb.Edge, error)
GetEdgesTo(ctx context.Context, tenantID, actor, toNodeID string, edgeTypeID int) ([]*entdb.Edge, error)
SearchNodes(ctx context.Context, tenantID, actor string, typeID int, query string) ([]*entdb.Node, error)
}
DB is the subset of the EntDB Transport used by identity services.
type EmailChangeToken ¶
type EmailChangeToken struct {
NodeID string
TokenHash string
UserID string
OldEmail string
NewEmail string
ExpiresAt int64 // epoch ms
CreatedAt int64 // epoch ms
ConsumedAt int64 // epoch ms; 0 = unconsumed
}
EmailChangeToken represents a pending primary-email rotation. The token is created at request time (after re-auth) and consumed when the user clicks the verification link sent to the *new* address.
type EmailVerificationToken ¶
type EmailVerificationToken struct {
NodeID string
TokenHash string
UserID string
Email string
ExpiresAt int64 // epoch ms
CreatedAt int64 // epoch ms
ConsumedAt int64 // epoch ms; 0 = unconsumed
}
EmailVerificationToken represents a stored email-verification token.
type GroupService ¶
type GroupService struct {
// contains filtered or unexported fields
}
GroupService implements working-group CRUD and membership operations. The underlying EntDB node type is WorkingGroup (type_id 2).
func NewGroupService ¶
func NewGroupService(db DB, tenantID string, auditLog *audit.Logger, logger *zap.Logger) *GroupService
NewGroupService creates a GroupService.
func (*GroupService) AddGroupMember ¶
func (s *GroupService) AddGroupMember(ctx context.Context, actorID, groupID, userID string) error
AddGroupMember creates a MEMBER_OF edge from user to group.
func (*GroupService) CreateGroup ¶
func (s *GroupService) CreateGroup(ctx context.Context, actorID, name, description string) (*Group, error)
CreateGroup creates a new working group.
func (*GroupService) DeleteGroup ¶
func (s *GroupService) DeleteGroup(ctx context.Context, actorID, groupID string) error
DeleteGroup deletes a working group.
func (*GroupService) ListGroupMembers ¶
func (s *GroupService) ListGroupMembers(ctx context.Context, actorID, groupID string) ([]*User, error)
ListGroupMembers returns all users that belong to a group via MEMBER_OF edges.
func (*GroupService) ListGroups ¶
func (s *GroupService) ListGroups(ctx context.Context, actorID, cursor string, limit int) ([]*Group, string, error)
ListGroups returns a paginated list of groups.
func (*GroupService) RemoveGroupMember ¶
func (s *GroupService) RemoveGroupMember(ctx context.Context, actorID, groupID, userID string) error
RemoveGroupMember deletes the MEMBER_OF edge from user to group.
func (*GroupService) UpdateGroup ¶
func (s *GroupService) UpdateGroup(ctx context.Context, actorID, groupID, name, description string) (*Group, error)
UpdateGroup patches name and/or description of a group.
type HelpRequest ¶
type HelpRequest struct {
ID string
Email string
Reason string
SourceIP string
UserAgent string
Status string
ResolvedBy string
ResolutionNotes string
ResolvedAt int64
CreatedAt int64
}
HelpRequest is the domain representation of an admin help request (type_id 28).
type HelpService ¶
type HelpService struct {
// contains filtered or unexported fields
}
HelpService implements the admin-help-request flow. End users who cannot log in raise a help request; admins resolve or reject them from the dashboard.
func NewHelpService ¶
func NewHelpService(db DB, tenantID string, auditLog *audit.Logger, logger *zap.Logger) *HelpService
NewHelpService creates a HelpService.
func (*HelpService) ListHelpRequests ¶
func (s *HelpService) ListHelpRequests( ctx context.Context, actorID, statusFilter, cursor string, limit int, ) ([]*HelpRequest, string, int, error)
ListHelpRequests returns a paginated list of help requests. Admin-only. Returns pendingCount (all pending, regardless of filter).
func (*HelpService) RequestAdminHelp ¶
func (s *HelpService) RequestAdminHelp( ctx context.Context, email, reason, sourceIP, userAgent string, ) error
RequestAdminHelp creates a help request. Always returns nil error to prevent email enumeration — internal failures are logged and swallowed. Rate limited to 3 requests per email per 24 hours.
func (*HelpService) ResolveHelpRequest ¶
func (s *HelpService) ResolveHelpRequest( ctx context.Context, actorID, requestID string, reject bool, notes string, ) (*HelpRequest, error)
ResolveHelpRequest marks a help request as resolved or rejected. Admin-only.
type IdentityVerificationRecord ¶ added in v0.4.0
type IdentityVerificationRecord struct {
NodeID string
VerificationID string // public identifier returned to clients
UserID string
TenantID string
Provider string
ProviderSessionID string
Status string // one of IDVStatus* constants
CreatedAt int64 // epoch ms
UpdatedAt int64 // epoch ms
CompletedAt int64 // epoch ms; 0 if not yet completed
RejectionReason string // empty unless Status == IDVStatusRejected
}
IdentityVerificationRecord represents a single verification session (document + selfie) tracked by the service. The provider field names the backing implementation (e.g. "azure", "stub"); ProviderSessionID is the provider's own identifier for the check.
type IdentityVerificationService ¶ added in v0.4.0
type IdentityVerificationService struct {
// contains filtered or unexported fields
}
IdentityVerificationService orchestrates document + selfie verification through a pluggable idv.Provider, persisting one IdentityVerificationRecord per session against the user.
The caller-facing identifier (VerificationID) is server-issued; the provider's own session id is stored alongside but never returned to the client.
func NewIdentityVerificationService ¶ added in v0.4.0
func NewIdentityVerificationService( repo Repository, provider idv.Provider, tenantID string, logger *zap.Logger, ) *IdentityVerificationService
NewIdentityVerificationService constructs the service. clock and newID default to time.Now / a hex random id when nil.
func (*IdentityVerificationService) BeginIdentityVerification ¶ added in v0.4.0
func (s *IdentityVerificationService) BeginIdentityVerification( ctx context.Context, userID string, ) (*BeginIdentityVerificationResult, error)
BeginIdentityVerification creates a new verification session for the given user. It allocates a server-side VerificationID, asks the provider to mint a client session token, and persists a PENDING record so subsequent status polls have something to read.
Returns ErrNotFound when userID does not resolve to a user.
func (*IdentityVerificationService) GetIdentityVerificationStatus ¶ added in v0.4.0
func (s *IdentityVerificationService) GetIdentityVerificationStatus( ctx context.Context, callerUserID, verificationID string, ) (*IdentityVerificationRecord, error)
GetIdentityVerificationStatus returns the current state of a verification. When verificationID is empty the caller's latest session is returned; this matches the "what's my status?" query the client SDK makes most often.
For sessions whose persisted state is non-terminal, the provider is consulted and the local record is updated if a verdict has arrived.
Returns ErrNotFound when no matching session exists, and ErrPermissionDenied when the caller is not the owner.
type InvitationRecord ¶
type InvitationRecord struct {
NodeID string
TokenHash string
Email string
UserID string
InvitedBy string
Role string
ExpiresAt int64
AcceptedAt int64
CreatedAt int64
}
InvitationRecord represents a user invitation.
type InviteResult ¶
type InviteResult struct {
User *User
InvitationToken string
SetupURL string
TemporaryPassword string
}
InviteResult is returned by AdminService.InviteUser.
type LoginChallengeRecord ¶
type LoginChallengeRecord struct {
NodeID string
ChallengeID string
UserID string
ExpiresAt int64
CreatedAt int64
}
LoginChallengeRecord represents a pending 2FA login challenge.
type LoginResult ¶
type LoginResult struct {
User *User
AccessToken string
RefreshToken string
ExpiresIn int32
TotpRequired bool
LoginChallengeID string
}
LoginResult is returned by password/OAuth login when successful.
type OAuthBeginResult ¶
type OAuthBeginResult struct {
AuthorizationURL string
State string
StateToken string
CodeVerifier string
ExpiresIn int32
}
OAuthBeginResult carries the provider authorization URL and the server-minted state artifacts needed to complete the OAuth flow.
type OAuthIdentity ¶
type OAuthIdentity struct {
NodeID string
UserID string
Provider string
ProviderUserID string
EmailAtLinkTime string
CreatedAt int64 // epoch ms
}
OAuthIdentity is the persisted link between a local User and a provider-side stable identity (provider, provider_user_id).
Lookups by (provider, provider_user_id) are what make OAuth login resilient to a provider-side email change: the user keeps the same local account even if their gmail address changes.
Composite uniqueness on (provider, provider_user_id) is enforced at the application layer — EntDB does not currently expose composite unique constraints. CreateOAuthIdentity callers must lookup first.
type PasskeyChallengeRecord ¶
type PasskeyChallengeRecord struct {
NodeID string
Challenge string // base64url
UserID string
ChallengeType string // "registration" or "authentication"
ExpiresAt int64
CreatedAt int64
}
PasskeyChallengeRecord represents a stored passkey challenge.
type PasskeyCredRecord ¶
type PasskeyCredRecord struct {
NodeID string
CredentialID string
UserID string
PublicKey string
SignCount int64
DeviceName string
AAGUID string
Transports string
CreatedAt int64
LastUsedAt int64
}
PasskeyCredRecord represents a stored passkey credential.
type PasskeyInfo ¶
type PasskeyInfo struct {
CredentialID string
DeviceName string
CreatedAt time.Time
LastUsedAt time.Time
}
PasskeyInfo holds display-safe passkey credential metadata.
type PasswordResetToken ¶
type PasswordResetToken struct {
NodeID string
TokenHash string
UserID string
ExpiresAt int64 // epoch ms
CreatedAt int64 // epoch ms
ConsumedAt int64 // epoch ms; 0 = unconsumed
}
PasswordResetToken represents a stored password-reset token.
type PollQrResult ¶
type PollQrResult struct {
Status string
User *User
AccessToken string
RefreshToken string
ExpiresIn int32
}
PollQrResult holds the result of polling a QR login session.
type ProfileService ¶
type ProfileService struct {
// contains filtered or unexported fields
}
ProfileService implements self-service profile, session, passkey, password, and audit log operations.
func NewProfileService ¶
func NewProfileService(db DB, tenantID string, auditLog *audit.Logger, logger *zap.Logger) *ProfileService
NewProfileService creates a ProfileService.
func (*ProfileService) ChangePassword ¶
func (s *ProfileService) ChangePassword(ctx context.Context, userID, currentPassword, newPassword string) error
ChangePassword changes the user's password. Requires current password verification. Invalidates all refresh tokens.
func (*ProfileService) DeletePasskey ¶
func (s *ProfileService) DeletePasskey(ctx context.Context, userID, credentialID string) error
DeletePasskey deletes a passkey belonging to the user.
func (*ProfileService) ListAuditEvents ¶
func (s *ProfileService) ListAuditEvents( ctx context.Context, actorID, targetID, eventType string, startTime, endTime int64, cursor string, limit int, ) ([]*AuditEvent, string, error)
ListAuditEvents queries audit events. Admin-only.
func (*ProfileService) ListMyPasskeys ¶
func (s *ProfileService) ListMyPasskeys(ctx context.Context, userID string) ([]*PasskeyInfo, error)
ListMyPasskeys returns all passkey credentials for the user.
func (*ProfileService) ListMySessions ¶
ListMySessions returns all active sessions for the user.
func (*ProfileService) RevokeAllSessions ¶
func (s *ProfileService) RevokeAllSessions(ctx context.Context, userID, password string) (int, error)
RevokeAllSessions revokes every session for the user. Requires password confirmation.
func (*ProfileService) RevokeSession ¶
func (s *ProfileService) RevokeSession(ctx context.Context, userID, sessionID string) error
RevokeSession revokes a specific session. The session must belong to the calling user.
func (*ProfileService) UpdateProfile ¶
func (s *ProfileService) UpdateProfile(ctx context.Context, userID, name, avatarURL string) (*User, error)
UpdateProfile updates the authenticated user's name and/or avatar.
type QrLoginSessionRecord ¶
type QrLoginSessionRecord struct {
NodeID string
SessionID string
Status string
UserID string
NewDeviceInfo string
NewDeviceIP string
NewDeviceUserAgent string
ApprovedDeviceInfo string
ExpiresAt int64
CreatedAt int64
UpdatedAt int64
}
QrLoginSessionRecord represents a stored QR login session.
type QrSessionInfo ¶
type QrSessionInfo struct {
Status string
NewDeviceInfo string
NewDeviceIP string
ExpiresAt time.Time
}
QrSessionInfo holds the public details of a QR login session.
type RecoveryCodeRecord ¶
type RecoveryCodeRecord struct {
NodeID string
UserID string
CodeHash string
Used bool
CreatedAt int64
UsedAt int64
}
RecoveryCodeRecord represents a stored recovery code.
type RefreshTokenRecord ¶
type RefreshTokenRecord struct {
NodeID string
TokenHash string
UserID string
DeviceInfo string
DeviceName string
IPAddress string
UserAgent string
ExpiresAt int64 // epoch ms
CreatedAt int64
LastUsedAt int64
ConsumedAtMs int64 // epoch ms; 0 = unconsumed (still valid for refresh)
}
RefreshTokenRecord represents a stored refresh token.
type Repository ¶
type Repository interface {
// Users
FindUserByEmail(ctx context.Context, email string) (*User, error)
GetUser(ctx context.Context, userID string) (*User, error)
CreateUser(ctx context.Context, u *User) (string, error) // returns node ID
UpdateUser(ctx context.Context, userID string, fields map[string]any) error
// Lockout state. These are dedicated methods (rather than UpdateUser
// patches) so the persistence layer can implement them as single
// atomic writes — important for racing concurrent failed-login
// attempts on the same account.
IncrementFailedLoginCount(ctx context.Context, userID string) (newCount int32, err error)
ResetFailedLoginCount(ctx context.Context, userID string) error
SetUserLockedUntil(ctx context.Context, userID string, lockedUntilMs int64) error
// Refresh tokens
FindRefreshTokenByHash(ctx context.Context, hash string) (*RefreshTokenRecord, error)
// FindRefreshTokenByHashIncludingConsumed returns the row even when
// consumed_at != 0, so the replay-detection branch can identify the
// user_id whose sessions must be invalidated.
FindRefreshTokenByHashIncludingConsumed(ctx context.Context, hash string) (*RefreshTokenRecord, error)
CreateRefreshToken(ctx context.Context, r *RefreshTokenRecord) (string, error)
DeleteRefreshToken(ctx context.Context, nodeID string) error
DeleteRefreshTokensForUser(ctx context.Context, userID string) error
// ConsumeRefreshTokenByHash marks the refresh-token row as rotated by
// setting consumed_at = atMs. The row is NOT deleted: replay attempts
// must still find it so they can be detected. The implementation must
// only mark a row consumed if it is currently unconsumed (consumed_at
// == 0); concurrent rotations of the same token must result in
// exactly one caller succeeding. Returns ErrUnauthenticated when the
// row is already consumed or does not exist.
//
// Background sweep: rows with consumed_at older than the desired
// retention window (e.g. 90 days, comfortably exceeding the longest
// refresh-token lifetime) may be hard-deleted by a periodic job. Such
// a sweep is not implemented in this package.
ConsumeRefreshTokenByHash(ctx context.Context, hash string, atMs int64) error
// Passkey credentials
ListPasskeyCredentials(ctx context.Context, userID string) ([]*PasskeyCredRecord, error)
GetPasskeyCredentialByCredID(ctx context.Context, credentialID string) (*PasskeyCredRecord, error)
CreatePasskeyCredential(ctx context.Context, r *PasskeyCredRecord) (string, error)
UpdatePasskeyCredential(ctx context.Context, nodeID string, fields map[string]any) error
// Passkey challenges
GetPasskeyChallenge(ctx context.Context, nodeID string) (*PasskeyChallengeRecord, error)
CreatePasskeyChallenge(ctx context.Context, r *PasskeyChallengeRecord) (string, error)
DeletePasskeyChallenge(ctx context.Context, nodeID string) error
// QR login sessions
FindQrLoginSession(ctx context.Context, sessionID string) (*QrLoginSessionRecord, error)
CreateQrLoginSession(ctx context.Context, r *QrLoginSessionRecord) (string, error)
UpdateQrLoginSession(ctx context.Context, nodeID string, fields map[string]any) error
// TOTP credentials
GetTotpCredential(ctx context.Context, userID string) (*TotpCredRecord, error)
CreateTotpCredential(ctx context.Context, r *TotpCredRecord) (string, error)
UpdateTotpCredential(ctx context.Context, nodeID string, fields map[string]any) error
DeleteTotpCredential(ctx context.Context, nodeID string) error
DeleteTotpCredentialsForUser(ctx context.Context, userID string) error
// Recovery codes
CreateRecoveryCode(ctx context.Context, r *RecoveryCodeRecord) (string, error)
FindRecoveryCodeByHash(ctx context.Context, userID, hash string) (*RecoveryCodeRecord, error)
UpdateRecoveryCode(ctx context.Context, nodeID string, fields map[string]any) error
DeleteRecoveryCodesForUser(ctx context.Context, userID string) error
// Login challenges (TOTP 2FA step)
CreateLoginChallenge(ctx context.Context, r *LoginChallengeRecord) (string, error)
GetLoginChallengeByChallengeID(ctx context.Context, challengeID string) (*LoginChallengeRecord, error)
DeleteLoginChallenge(ctx context.Context, nodeID string) error
// User invitations
FindInvitationByHash(ctx context.Context, tokenHash string) (*InvitationRecord, error)
UpdateInvitation(ctx context.Context, nodeID string, fields map[string]any) error
// Password-reset tokens
CreatePasswordResetToken(ctx context.Context, t *PasswordResetToken) error
FindPasswordResetTokenByHash(ctx context.Context, tokenHash string) (*PasswordResetToken, error)
MarkPasswordResetTokenConsumed(ctx context.Context, tokenID string, atMs int64) error
// Email-verification tokens
CreateEmailVerificationToken(ctx context.Context, t *EmailVerificationToken) error
FindEmailVerificationTokenByHash(ctx context.Context, tokenHash string) (*EmailVerificationToken, error)
MarkEmailVerificationTokenConsumed(ctx context.Context, tokenID string, atMs int64) error
// User email-verified update
SetUserEmailVerified(ctx context.Context, userID string, atMs int64) error
// User idv-verified update; called by IdentityVerificationService
// when a verification reaches APPROVED.
SetUserIDVVerified(ctx context.Context, userID string, atMs int64) error
// Identity-verification records (document + selfie verification).
// Latest ordering is by CreatedAt descending.
CreateIdentityVerification(ctx context.Context, r *IdentityVerificationRecord) error
GetIdentityVerification(ctx context.Context, verificationID string) (*IdentityVerificationRecord, error)
GetLatestIdentityVerificationForUser(ctx context.Context, userID string) (*IdentityVerificationRecord, error)
UpdateIdentityVerificationStatus(ctx context.Context, verificationID, status, rejectionReason string, completedAtMs, updatedAtMs int64) error
// Email-change tokens (primary email rotation, double-opt-in)
CreateEmailChangeToken(ctx context.Context, t *EmailChangeToken) error
FindEmailChangeTokenByHash(ctx context.Context, tokenHash string) (*EmailChangeToken, error)
MarkEmailChangeTokenConsumed(ctx context.Context, tokenID string, atMs int64) error
// UpdateUserEmail sets the user's primary email and marks it verified
// (since the new address has just proven control via the consumed
// token). Implementations must also set updated_at = atMs.
UpdateUserEmail(ctx context.Context, userID, newEmail string, atMs int64) error
// OAuth identities — links a (provider, provider_user_id) pair to a
// local User so OAuth login can survive provider-side email changes.
FindUserByProviderID(ctx context.Context, provider, providerUserID string) (*User, error)
CreateOAuthIdentity(ctx context.Context, oi *OAuthIdentity) error
ListOAuthIdentitiesForUser(ctx context.Context, userID string) ([]*OAuthIdentity, error)
}
Repository abstracts all persistence operations for the auth service.
type ResetPasswordResult ¶
ResetPasswordResult is returned by AdminService.ResetUserPassword.
type Session ¶
type Session struct {
ID string
DeviceName string
IPAddress string
UserAgent string
CreatedAt int64
LastUsedAt int64
ExpiresAt int64
Current bool
}
Session represents an active refresh-token-backed session.
type StubDB ¶
type StubDB struct{}
StubDB implements DB (and audit.NodeWriter) but returns ErrServiceUnavailable for every method.
func (StubDB) ExecuteAtomic ¶
func (StubDB) GetEdgesFrom ¶
func (StubDB) GetEdgesTo ¶
func (StubDB) QueryNodes ¶
type StubRepository ¶
type StubRepository struct{}
StubRepository implements Repository but returns ErrServiceUnavailable for every method. Use it as a placeholder until the EntDB-backed repository is implemented.
func (StubRepository) ConsumeRefreshTokenByHash ¶
func (StubRepository) CreateEmailChangeToken ¶
func (StubRepository) CreateEmailChangeToken(context.Context, *EmailChangeToken) error
func (StubRepository) CreateEmailVerificationToken ¶
func (StubRepository) CreateEmailVerificationToken(context.Context, *EmailVerificationToken) error
func (StubRepository) CreateIdentityVerification ¶ added in v0.4.0
func (StubRepository) CreateIdentityVerification(context.Context, *IdentityVerificationRecord) error
func (StubRepository) CreateLoginChallenge ¶
func (StubRepository) CreateLoginChallenge(context.Context, *LoginChallengeRecord) (string, error)
func (StubRepository) CreateOAuthIdentity ¶
func (StubRepository) CreateOAuthIdentity(context.Context, *OAuthIdentity) error
func (StubRepository) CreatePasskeyChallenge ¶
func (StubRepository) CreatePasskeyChallenge(context.Context, *PasskeyChallengeRecord) (string, error)
func (StubRepository) CreatePasskeyCredential ¶
func (StubRepository) CreatePasskeyCredential(context.Context, *PasskeyCredRecord) (string, error)
func (StubRepository) CreatePasswordResetToken ¶
func (StubRepository) CreatePasswordResetToken(context.Context, *PasswordResetToken) error
func (StubRepository) CreateQrLoginSession ¶
func (StubRepository) CreateQrLoginSession(context.Context, *QrLoginSessionRecord) (string, error)
func (StubRepository) CreateRecoveryCode ¶
func (StubRepository) CreateRecoveryCode(context.Context, *RecoveryCodeRecord) (string, error)
func (StubRepository) CreateRefreshToken ¶
func (StubRepository) CreateRefreshToken(context.Context, *RefreshTokenRecord) (string, error)
func (StubRepository) CreateTotpCredential ¶
func (StubRepository) CreateTotpCredential(context.Context, *TotpCredRecord) (string, error)
func (StubRepository) CreateUser ¶
func (StubRepository) DeleteLoginChallenge ¶
func (StubRepository) DeleteLoginChallenge(context.Context, string) error
func (StubRepository) DeletePasskeyChallenge ¶
func (StubRepository) DeletePasskeyChallenge(context.Context, string) error
func (StubRepository) DeleteRecoveryCodesForUser ¶
func (StubRepository) DeleteRecoveryCodesForUser(context.Context, string) error
func (StubRepository) DeleteRefreshToken ¶
func (StubRepository) DeleteRefreshToken(context.Context, string) error
func (StubRepository) DeleteRefreshTokensForUser ¶
func (StubRepository) DeleteRefreshTokensForUser(context.Context, string) error
func (StubRepository) DeleteTotpCredential ¶
func (StubRepository) DeleteTotpCredential(context.Context, string) error
func (StubRepository) DeleteTotpCredentialsForUser ¶
func (StubRepository) DeleteTotpCredentialsForUser(context.Context, string) error
func (StubRepository) FindEmailChangeTokenByHash ¶
func (StubRepository) FindEmailChangeTokenByHash(context.Context, string) (*EmailChangeToken, error)
func (StubRepository) FindEmailVerificationTokenByHash ¶
func (StubRepository) FindEmailVerificationTokenByHash(context.Context, string) (*EmailVerificationToken, error)
func (StubRepository) FindInvitationByHash ¶
func (StubRepository) FindInvitationByHash(context.Context, string) (*InvitationRecord, error)
func (StubRepository) FindPasswordResetTokenByHash ¶
func (StubRepository) FindPasswordResetTokenByHash(context.Context, string) (*PasswordResetToken, error)
func (StubRepository) FindQrLoginSession ¶
func (StubRepository) FindQrLoginSession(context.Context, string) (*QrLoginSessionRecord, error)
func (StubRepository) FindRecoveryCodeByHash ¶
func (StubRepository) FindRecoveryCodeByHash(context.Context, string, string) (*RecoveryCodeRecord, error)
func (StubRepository) FindRefreshTokenByHash ¶
func (StubRepository) FindRefreshTokenByHash(context.Context, string) (*RefreshTokenRecord, error)
func (StubRepository) FindRefreshTokenByHashIncludingConsumed ¶
func (StubRepository) FindRefreshTokenByHashIncludingConsumed(context.Context, string) (*RefreshTokenRecord, error)
func (StubRepository) FindUserByEmail ¶
func (StubRepository) FindUserByProviderID ¶
func (StubRepository) GetIdentityVerification ¶ added in v0.4.0
func (StubRepository) GetIdentityVerification(context.Context, string) (*IdentityVerificationRecord, error)
func (StubRepository) GetLatestIdentityVerificationForUser ¶ added in v0.4.0
func (StubRepository) GetLatestIdentityVerificationForUser(context.Context, string) (*IdentityVerificationRecord, error)
func (StubRepository) GetLoginChallengeByChallengeID ¶
func (StubRepository) GetLoginChallengeByChallengeID(context.Context, string) (*LoginChallengeRecord, error)
func (StubRepository) GetPasskeyChallenge ¶
func (StubRepository) GetPasskeyChallenge(context.Context, string) (*PasskeyChallengeRecord, error)
func (StubRepository) GetPasskeyCredentialByCredID ¶
func (StubRepository) GetPasskeyCredentialByCredID(context.Context, string) (*PasskeyCredRecord, error)
func (StubRepository) GetTotpCredential ¶
func (StubRepository) GetTotpCredential(context.Context, string) (*TotpCredRecord, error)
func (StubRepository) IncrementFailedLoginCount ¶
func (StubRepository) ListOAuthIdentitiesForUser ¶
func (StubRepository) ListOAuthIdentitiesForUser(context.Context, string) ([]*OAuthIdentity, error)
func (StubRepository) ListPasskeyCredentials ¶
func (StubRepository) ListPasskeyCredentials(context.Context, string) ([]*PasskeyCredRecord, error)
func (StubRepository) MarkEmailChangeTokenConsumed ¶
func (StubRepository) MarkEmailVerificationTokenConsumed ¶
func (StubRepository) MarkPasswordResetTokenConsumed ¶
func (StubRepository) ResetFailedLoginCount ¶
func (StubRepository) ResetFailedLoginCount(context.Context, string) error
func (StubRepository) SetUserEmailVerified ¶
func (StubRepository) SetUserIDVVerified ¶ added in v0.4.2
func (StubRepository) SetUserLockedUntil ¶
func (StubRepository) UpdateIdentityVerificationStatus ¶ added in v0.4.0
func (StubRepository) UpdateInvitation ¶
func (StubRepository) UpdatePasskeyCredential ¶
func (StubRepository) UpdateQrLoginSession ¶
func (StubRepository) UpdateRecoveryCode ¶
func (StubRepository) UpdateTotpCredential ¶
func (StubRepository) UpdateUser ¶
func (StubRepository) UpdateUserEmail ¶
type TotpCredRecord ¶
type TotpCredRecord struct {
NodeID string
UserID string
SecretEncrypted string
Verified bool
CreatedAt int64
LastUsedAt int64
}
TotpCredRecord represents a stored TOTP credential.
type User ¶
type User struct {
ID string
Email string
Name string
AvatarURL string
Role string
CreatedAt time.Time
UpdatedAt time.Time
TotpRequired bool
Status string // "active", "invited", "deactivated", "suspended"
RecoveryEmail string
QuotaBytes int64
LastLoginAtMs int64
PasswordHash string // never exposed via RPC
FailedLoginCount int
LockedUntil int64 // epoch ms; 0 = not locked
EmailVerified bool
EmailVerifiedAt int64 // epoch ms
IDVVerified bool // latest identity verification reached APPROVED
IDVVerifiedAt int64 // epoch ms; 0 = never verified
}
User represents a user in the identity system.