Documentation
¶
Index ¶
- Constants
- Variables
- type AuditLog
- type Authenticator
- type Client
- type CollectionList
- type EmailTemplate
- type Env
- type FederatedIdentity
- type MFASession
- type OAuthState
- type OTP
- type OrgDomain
- type OrgMembership
- type Organization
- type Paging
- type ScimEndpoint
- type Session
- type SessionToken
- type TrustedIssuer
- type User
- type VerificationRequest
- type WebauthnCredential
- type Webhook
- type WebhookLog
Constants ¶
const ( // FieldName email is the field name for email FieldNameEmail = "email" // FieldNamePhoneNumber is the field name for phone number FieldNamePhoneNumber = "phone_number" )
Variables ¶
var ( // Prefix for table name / collection names Prefix = "authorizer_" // Collections / Tables available for authorizer in the database (used for dbs other than gorm) Collections = CollectionList{ User: Prefix + "users", VerificationRequest: Prefix + "verification_requests", Session: Prefix + "sessions", Env: Prefix + "env", Webhook: Prefix + "webhooks", WebhookLog: Prefix + "webhook_logs", EmailTemplate: Prefix + "email_templates", OTP: Prefix + "otps", SMSVerificationRequest: Prefix + "sms_verification_requests", Authenticators: Prefix + "authenticators", SessionToken: Prefix + "session_tokens", MFASession: Prefix + "mfa_sessions", OAuthState: Prefix + "oauth_states", AuditLog: Prefix + "audit_logs", Client: Prefix + "clients", TrustedIssuer: Prefix + "trusted_issuers", Organization: Prefix + "organizations", OrgMembership: Prefix + "org_memberships", FederatedIdentity: Prefix + "federated_identities", ScimEndpoint: Prefix + "scim_endpoints", WebauthnCredential: Prefix + "webauthn_credentials", OrgDomain: Prefix + "org_domains", } )
var ErrOrgDomainConflict = errors.New("domain already verified by another organization")
ErrOrgDomainConflict is returned by AddOrgDomain when the normalized domain is already verified by a DIFFERENT organization. It is the atomic first-writer-wins signal — the service layer maps it to "domain_already_verified_by_another_org". A same-org re-verify is NOT a conflict (AddOrgDomain returns the existing row, idempotently).
Functions ¶
This section is empty.
Types ¶
type AuditLog ¶
type AuditLog struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
ActorID string `gorm:"type:char(36)" json:"actor_id" bson:"actor_id" cql:"actor_id" dynamo:"actor_id,omitempty" index:"actor_id,hash"`
ActorType string `gorm:"type:varchar(30)" json:"actor_type" bson:"actor_type" cql:"actor_type" dynamo:"actor_type"`
ActorEmail string `gorm:"type:varchar(256)" json:"actor_email" bson:"actor_email" cql:"actor_email" dynamo:"actor_email"`
Action string `gorm:"type:varchar(100)" json:"action" bson:"action" cql:"action" dynamo:"action,omitempty" index:"action,hash"`
ResourceType string `gorm:"type:varchar(50)" json:"resource_type" bson:"resource_type" cql:"resource_type" dynamo:"resource_type"`
ResourceID string `gorm:"type:char(36)" json:"resource_id" bson:"resource_id" cql:"resource_id" dynamo:"resource_id"`
IPAddress string `gorm:"type:varchar(45)" json:"ip_address" bson:"ip_address" cql:"ip_address" dynamo:"ip_address"`
UserAgent string `gorm:"type:text" json:"user_agent" bson:"user_agent" cql:"user_agent" dynamo:"user_agent"`
Metadata string `gorm:"type:text" json:"metadata" bson:"metadata" cql:"metadata" dynamo:"metadata"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
}
AuditLog model for db
func (*AuditLog) AsAPIAuditLog ¶
AsAPIAuditLog converts the database audit log to a GraphQL response object.
type Authenticator ¶
type Authenticator struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
UserID string `` /* 147-byte string literal not displayed */
Method string `gorm:"type:varchar(64);uniqueIndex:idx_authenticator_user_id_method" json:"method" bson:"method" cql:"method" dynamo:"method"`
Secret string `json:"secret" bson:"secret" cql:"secret" dynamo:"secret"`
RecoveryCodes *string `json:"recovery_codes" bson:"recovery_codes" cql:"recovery_codes" dynamo:"recovery_codes"`
VerifiedAt *int64 `json:"verified_at" bson:"verified_at" cql:"verified_at" dynamo:"verified_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
Authenticator model for db
type Client ¶
type Client struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // ArangoDB document key
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
// ClientID is the public OAuth client_id presented at the authorize/token
// endpoints. It is distinct from the surrogate primary key ID: ID is an
// internal UUID, ClientID is the externally-referenced identifier and is
// UNIQUE across the registry. ClientID is IMMUTABLE after creation.
//
// For the reserved interactive client seeded at boot, ClientID equals the
// deployment's Config.ClientID verbatim (BC1) — every token aud, JWKS kid,
// and introspection client_id continues to key on that exact string.
//
// When left empty on create, every provider defaults ClientID to ID so the
// unique index always holds and lookups keep working for admin-created
// clients that do not (yet) supply an explicit client_id — ID has
// historically doubled as the client_id.
ClientID string `gorm:"uniqueIndex" json:"client_id" bson:"client_id" cql:"client_id" dynamo:"client_id" index:"client_id,hash"`
// Kind distinguishes the client type. Values: "interactive" (human-facing
// login clients) | "service_account" (machine/workload clients). It is
// immutable after creation. This rename step defaults existing and newly
// created rows to "service_account"; the interactive kind and its
// additional fields (redirect_uris, grant_types, etc.) land in a later step.
Kind string `json:"kind" bson:"kind" cql:"kind" dynamo:"kind"`
// Name is a human-readable label for this service account (e.g. "payments-worker").
Name string `json:"name" bson:"name" cql:"name" dynamo:"name"`
// Description is an optional free-text note.
Description *string `json:"description" bson:"description" cql:"description" dynamo:"description"`
// ClientSecret holds the bcrypt hash of the credential.
// Never expose this value in API responses.
// json:"-" keeps it out of any json.Marshal of this struct (structured
// logging, webhook payloads, error dumps), matching User.Password.
ClientSecret string `json:"-" bson:"client_secret" cql:"client_secret" dynamo:"client_secret"`
// AllowedScopes is a comma-separated list of OAuth2 scopes this service
// account may request. Scopes in a client_credentials request MUST be a
// strict subset of this list.
//
// SECURITY: the service layer MUST trim whitespace and drop empty segments
// when parsing this field (e.g. "read, write" → ["read","write"]).
// Empty string MUST be treated as DENY-ALL, not grant-all, in the token
// endpoint — an unparseable or empty AllowedScopes must reject the request.
AllowedScopes string `json:"allowed_scopes" bson:"allowed_scopes" cql:"allowed_scopes" dynamo:"allowed_scopes"`
// RedirectURIs is a comma-separated allow-list of exact redirect URIs for
// interactive clients. Empty for service_account clients. The service layer
// MUST exact-match a presented redirect_uri against a parsed segment of this
// list — never a prefix or substring match.
RedirectURIs string `json:"redirect_uris" bson:"redirect_uris" cql:"redirect_uris" dynamo:"redirect_uris"`
// GrantTypes is a comma-separated list of OAuth2 grant types this client is
// allowed to use (e.g. "authorization_code,refresh_token" for interactive
// clients, "client_credentials" for service accounts).
GrantTypes string `json:"grant_types" bson:"grant_types" cql:"grant_types" dynamo:"grant_types"`
// TokenEndpointAuthMethod is how the client authenticates at the token
// endpoint: "client_secret_basic" | "client_secret_post" | "none" (public
// client authenticating via PKCE only).
TokenEndpointAuthMethod string `` /* 136-byte string literal not displayed */
// IsActive controls whether this service account may authenticate.
// Flipping to false blocks new token issuance immediately; existing
// tokens remain valid until their exp.
//
// GORM NOTE: gorm:"default:true" means db.Create with IsActive=false will
// persist as true (Go zero-value triggers the column default). The service
// layer must always set IsActive explicitly and use Save (not Create-only)
// when creating a disabled account.
IsActive bool `json:"is_active" bson:"is_active" cql:"is_active" dynamo:"is_active" gorm:"default:true"`
// OrgID optionally scopes this client to an organization. A nil value means
// the client is global (not org-scoped). This is a plain nullable column,
// NOT a cross-table foreign key — five of the six storage providers are
// NoSQL and cannot enforce an FK constraint, so referential integrity is the
// service layer's responsibility.
//
// No `omitempty` on the json/bson tags: a nil pointer must serialize to
// null so an update clears a previously-set org (see
// docs/storage-optional-null-fields.md).
OrgID *string `json:"org_id" bson:"org_id" cql:"org_id" dynamo:"org_id" gorm:"index"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
Client is a registered OAuth client in the unified client registry.
The Kind discriminator distinguishes interactive, human-facing clients (browser/app login flows) from machine service accounts (the OAuth2 client_credentials grant and workload identity federation). Both kinds share this single registry so client authentication has one authoritative source of truth.
Authentication:
- client_id = ID (UUID)
- client_secret = bcrypt hash (cost 12); plaintext returned ONCE at creation and ONCE at rotation — never again.
This struct intentionally omits email, phone, MFA, social-login, and session fields — those belong to User. Mixing human and machine identity in the same table is an anti-pattern explicitly avoided here. See: authorizer-docs repo, specs/WORKLOAD_IDENTITY_PROGRAM.md §3.
Note: any field addition must also be reflected in the cassandradb provider; it does not use GORM AutoMigrate for collection creation.
func (*Client) AsAPIClient ¶
AsAPIClient converts the storage record into the GraphQL model. It never exposes ClientSecret — there is no client_secret field on model.Client by design; the plaintext is surfaced only once via CreateClientResponse at creation/rotation.
func (*Client) ParsedAllowedScopes ¶
ParsedAllowedScopes returns AllowedScopes as a slice: comma-separated, whitespace trimmed, empty segments dropped. This is the single source of truth for interpreting the stored scope string — the token endpoint uses it to enforce that a client_credentials request stays within the authorized set. An empty or whitespace-only AllowedScopes yields an empty slice, which the token endpoint MUST treat as DENY-ALL (schema § AllowedScopes comment).
type CollectionList ¶
type CollectionList struct {
User string
VerificationRequest string
Session string
Env string
Webhook string
WebhookLog string
EmailTemplate string
OTP string
SMSVerificationRequest string
Authenticators string
SessionToken string
MFASession string
OAuthState string
AuditLog string
Client string
TrustedIssuer string
Organization string
OrgMembership string
FederatedIdentity string
ScimEndpoint string
WebauthnCredential string
OrgDomain string
}
CollectionList / Tables available for authorizer in the database
type EmailTemplate ¶
type EmailTemplate struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name" dynamo:"event_name" index:"event_name,hash"`
Subject string `json:"subject" bson:"subject" cql:"subject" dynamo:"subject"`
Template string `json:"template" bson:"template" cql:"template" dynamo:"template"`
Design string `json:"design" bson:"design" cql:"design" dynamo:"design"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
EmailTemplate model for database
func (*EmailTemplate) AsAPIEmailTemplate ¶
func (e *EmailTemplate) AsAPIEmailTemplate() *model.EmailTemplate
AsAPIEmailTemplate to return email template as graphql response object
type Env ¶
type Env struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
EnvData string `json:"env" bson:"env" cql:"env" dynamo:"env"`
Hash string `json:"hash" bson:"hash" cql:"hash" dynamo:"hash"`
EncryptionKey string `json:"encryption_key" bson:"encryption_key" cql:"encryption_key" dynamo:"encryption_key"` // couchbase has "hash" as reserved keyword so we cannot use it. This will be empty for other dbs.
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
}
Env model for db
type FederatedIdentity ¶
type FederatedIdentity struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // ArangoDB document key
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
// OrgID is the organization whose SSO connection provisioned the identity.
// Part of the unique (org_id, issuer, subject) constraint.
OrgID string `` /* 130-byte string literal not displayed */
// Issuer is the upstream IdP's `iss` claim value.
// Part of the unique (org_id, issuer, subject) constraint.
Issuer string `gorm:"uniqueIndex:idx_federated_identity_org_iss_sub" json:"issuer" bson:"issuer" cql:"issuer" dynamo:"issuer"`
// Subject is the upstream `sub` claim value (stable per-IdP user identifier).
// Part of the unique (org_id, issuer, subject) constraint.
Subject string `gorm:"uniqueIndex:idx_federated_identity_org_iss_sub" json:"subject" bson:"subject" cql:"subject" dynamo:"subject"`
// UserID is the Authorizer user this federated identity maps to.
UserID string `json:"user_id" bson:"user_id" cql:"user_id" dynamo:"user_id" gorm:"index" index:"user_id,hash"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
FederatedIdentity records that an end user was provisioned (JIT) from a specific upstream identity at a specific organization's SSO connection.
SECURITY (design §4.4, account-takeover defense): a federated login is keyed by the tuple (OrgID, Issuer, Subject) — NEVER by email alone. On callback the broker looks up this row to find the already-provisioned Authorizer user for a returning federated principal; an email that merely collides with some other account is NOT sufficient to link. The (org_id, issuer, subject) triple is UNIQUE: the SQL providers enforce it with a composite unique index; the NoSQL providers enforce it with a compound unique index (Mongo/Arango) or a check-then-insert guard (Cassandra/Couchbase/DynamoDB). The service layer also looks up before inserting so behaviour is uniform across every backend.
Note: any field addition must also be reflected in the cassandradb provider; it does not use GORM AutoMigrate for collection creation.
type MFASession ¶
type MFASession struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
UserID string `` /* 140-byte string literal not displayed */
KeyName string `gorm:"type:varchar(255);index:idx_mfa_session_user_id_key" json:"key_name" bson:"key_name" cql:"key_name" dynamo:"key_name"`
ExpiresAt int64 `gorm:"index" json:"expires_at" bson:"expires_at" cql:"expires_at" dynamo:"expires_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
MFASession model for storing MFA sessions in database This replaces the in-memory storage for MFA sessions when Redis is not configured
type OAuthState ¶
type OAuthState struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
StateKey string `` /* 128-byte string literal not displayed */
State string `gorm:"type:text" json:"state" bson:"state" cql:"state" dynamo:"state"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
OAuthState model for storing OAuth state in database This replaces the in-memory storage for OAuth state when Redis is not configured
type OTP ¶
type OTP struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
Email string `gorm:"index" json:"email" bson:"email" cql:"email" dynamo:"email,omitempty" index:"email,hash"`
PhoneNumber string `gorm:"index" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number"`
Otp string `json:"otp" bson:"otp" cql:"otp" dynamo:"otp"`
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at" dynamo:"expires_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
OTP model for database
type OrgDomain ¶
type OrgDomain struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // ArangoDB document key
// ID is the primary key: the normalized domain (punycode, lowercased). Not a
// uuid — the domain IS the key so uniqueness is enforced by the DB atomically.
ID string `gorm:"primaryKey;type:varchar(255)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
// OrgID is the organization this verified domain routes to. Indexed for
// ListOrgDomainsByOrg; NOT unique (an org may verify many domains).
OrgID string `gorm:"index" json:"org_id" bson:"org_id" cql:"org_id" dynamo:"org_id" index:"org_id,hash"`
// Domain is the normalized domain as a readable column (== ID).
Domain string `json:"domain" bson:"domain" cql:"domain" dynamo:"domain"`
// VerifiedAt is when the domain became verified (unix seconds).
VerifiedAt int64 `json:"verified_at" bson:"verified_at" cql:"verified_at" dynamo:"verified_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
OrgDomain is a VERIFIED mapping from a DNS domain to exactly one organization, used for home-realm discovery (routing a login to the right tenant IdP). A row exists ONLY once a domain is verified; a pending verification challenge is ephemeral state in the memory store, never a row here.
Uniqueness is the load-bearing security invariant (one verified domain → one org). It is enforced by the PRIMARY/partition key being the normalized domain itself, NOT a secondary unique index: DynamoDB, Cassandra and Couchbase cannot enforce a unique constraint on a non-key attribute, so a check-then-insert guard would race. With the domain as the key, first-writer-wins is atomic on every backend (unique PK / conditional put / INSERT ... IF NOT EXISTS).
ID holds the normalized domain and is the primary key. Domain holds the same value as a human-readable column (never expose ID directly — on ArangoDB a read populates it with the "collection/key" handle; use Domain / AsAPIOrgDomain).
Note: any field addition must also be reflected in the cassandradb provider; it does not use GORM AutoMigrate for collection creation.
func (*OrgDomain) AsAPIOrgDomain ¶
AsAPIOrgDomain converts the storage record into the GraphQL model. It exposes Domain (never ID — on ArangoDB a read populates ID with the "collection/key" handle).
type OrgMembership ¶
type OrgMembership struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // ArangoDB document key
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
// OrgID references the Organization this membership belongs to.
// Part of the unique (org_id, user_id) constraint.
OrgID string `gorm:"uniqueIndex:idx_org_membership_org_user" json:"org_id" bson:"org_id" cql:"org_id" dynamo:"org_id" index:"org_id,hash"`
// UserID references the User who is a member.
// Part of the unique (org_id, user_id) constraint; also indexed on its own
// for the "list an org membership by user" query.
UserID string `` /* 134-byte string literal not displayed */
// Roles is a comma-separated list of per-organization roles.
Roles string `json:"roles" bson:"roles" cql:"roles" dynamo:"roles"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
OrgMembership links a User to an Organization with a set of per-org roles.
A user may belong to many organizations, each with independent roles; an organization has many members. The (OrgID, UserID) pair is UNIQUE — a user can be a member of a given organization at most once. The SQL providers enforce this with a composite unique index; the NoSQL providers enforce it with a compound unique index (Mongo/Arango) or a check-then-insert guard (Cassandra/Couchbase/DynamoDB). The service layer additionally rejects duplicates up-front so behaviour is uniform across every backend.
Roles is a comma-separated list of per-organization role names, mirroring the storage convention used by Client.AllowedScopes. Use ParsedRoles to interpret it; the service layer normalizes on write.
Note: any field addition must also be reflected in the cassandradb provider; it does not use GORM AutoMigrate for collection creation.
func (*OrgMembership) AsAPIOrgMember ¶
func (m *OrgMembership) AsAPIOrgMember() *model.OrgMember
AsAPIOrgMember converts the storage record into the GraphQL model.
func (*OrgMembership) ParsedRoles ¶
func (m *OrgMembership) ParsedRoles() []string
ParsedRoles returns Roles as a slice: comma-separated, whitespace trimmed, empty segments dropped. An empty or whitespace-only Roles yields an empty slice.
type Organization ¶
type Organization struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // ArangoDB document key
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
// Name is the unique organization slug (e.g. "acme-corp").
Name string `gorm:"uniqueIndex" json:"name" bson:"name" cql:"name" dynamo:"name" index:"name,hash"`
// DisplayName is the human-readable organization name.
DisplayName *string `json:"display_name" bson:"display_name" cql:"display_name" dynamo:"display_name"`
// Enabled controls whether the organization is active.
//
// GORM NOTE: gorm:"default:true" means db.Create with Enabled=false will
// persist as true (Go zero-value triggers the column default). The service
// layer must always set Enabled explicitly and use Save (not Create-only)
// when creating a disabled organization.
Enabled bool `json:"enabled" bson:"enabled" cql:"enabled" dynamo:"enabled" gorm:"default:true"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
Organization is a tenant grouping that owns per-org memberships (and, in later phases, SSO/SCIM connections and (org, client) grants). It is managed by platform admins through the `_organization`/`_organizations` admin API.
Name is a unique, URL-safe slug (the stable external identifier); DisplayName is the human-readable label. Enabled gates whether the organization is active; disabling it does not delete its memberships.
Note: any field addition must also be reflected in the cassandradb provider; it does not use GORM AutoMigrate for collection creation.
func (*Organization) AsAPIOrganization ¶
func (o *Organization) AsAPIOrganization() *model.Organization
AsAPIOrganization converts the storage record into the GraphQL model.
type ScimEndpoint ¶
type ScimEndpoint struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // ArangoDB document key
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
// OrgID is the organization this SCIM endpoint provisions into. Unique:
// one SCIM endpoint per org.
OrgID string `gorm:"uniqueIndex" json:"org_id" bson:"org_id" cql:"org_id" dynamo:"org_id" index:"org_id,hash"`
// TokenHash is the bcrypt hash of the endpoint's bearer-token secret.
// json:"-" keeps it out of any json.Marshal (structured logging, webhook
// payloads, error dumps), matching User.Password / Client.ClientSecret.
TokenHash string `json:"-" bson:"token_hash" cql:"token_hash" dynamo:"token_hash"`
// Enabled gates whether the endpoint accepts requests. Disabling it stops
// provisioning without deleting the row (rotate/re-enable later).
//
// GORM NOTE: gorm:"default:true" means Create with Enabled=false persists as
// true (Go zero-value triggers the column default). The service layer always
// sets Enabled explicitly and uses Save when creating a disabled endpoint.
Enabled bool `gorm:"default:true" json:"enabled" bson:"enabled" cql:"enabled" dynamo:"enabled"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
ScimEndpoint is the per-organization inbound SCIM 2.0 connection credential. A customer's IdP (Okta, Entra, …) authenticates to /scim/v2/ with a bearer token; the org it may provision is derived ONLY from the matched endpoint (design §4.4 H6 — never from the URL or payload).
Authentication:
- The presented bearer token is "<endpointID>.<hexSecret>". The endpointID selects this row; the secret is verified (constant-time bcrypt) against TokenHash. The plaintext is revealed ONCE at create and ONCE at rotate.
One endpoint per org: OrgID is unique.
Note: any field addition must also be reflected in the cassandradb provider; it does not use GORM AutoMigrate for collection creation.
func (*ScimEndpoint) AsAPIScimEndpoint ¶
func (s *ScimEndpoint) AsAPIScimEndpoint() *model.ScimEndpoint
AsAPIScimEndpoint converts the storage record into the GraphQL model. The token secret is never included — it is revealed only at create/rotate.
type Session ¶
type Session struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
UserID string `gorm:"type:char(36)" json:"user_id" bson:"user_id" cql:"user_id" dynamo:"user_id" index:"user_id,hash"`
UserAgent string `json:"user_agent" bson:"user_agent" cql:"user_agent" dynamo:"user_agent"`
IP string `json:"ip" bson:"ip" cql:"ip" dynamo:"ip"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
Session model for db
type SessionToken ¶
type SessionToken struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
UserID string `` /* 142-byte string literal not displayed */
KeyName string `gorm:"type:varchar(255);index:idx_session_token_user_id_key" json:"key_name" bson:"key_name" cql:"key_name" dynamo:"key_name"`
Token string `gorm:"type:text" json:"token" bson:"token" cql:"token" dynamo:"token"`
ExpiresAt int64 `gorm:"index" json:"expires_at" bson:"expires_at" cql:"expires_at" dynamo:"expires_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
SessionToken model for storing user session tokens in database This replaces the in-memory storage for session tokens when Redis is not configured
type TrustedIssuer ¶
type TrustedIssuer struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // ArangoDB document key
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
// Kind discriminates the trust relationship this row represents (design §4.3 /
// §5 K1): constants.TrustKindClientAssertion (default), TrustKindSSOOIDC, or
// TrustKindSSOSAML (reserved). Immutable after creation.
//
// SECURITY (design §5.2 CR1): the client_assertion resolver accepts ONLY
// client_assertion_trust rows (with an empty OrgID). An sso_oidc row — which
// has NO subject pin — must never be reachable on the client-authentication
// path. A pre-existing row written before this column existed reads back as ""
// and is interpreted as client_assertion_trust by EffectiveKind, so upgrades
// don't break existing trust rows.
Kind string `json:"kind" bson:"kind" cql:"kind" dynamo:"kind" gorm:"default:'client_assertion_trust'"`
// OrgID scopes an SSO connection (sso_oidc/sso_saml) to one Organization.
// EMPTY for client_assertion_trust rows (which are instance-global). Immutable.
// Part of the (kind, org_id) lookup that finds an org's SSO connection.
OrgID string `json:"org_id" bson:"org_id" cql:"org_id" dynamo:"org_id" gorm:"index" index:"org_id,hash"`
// ClientID links this issuer to the Client it authenticates. Empty on
// sso_oidc/sso_saml rows (which federate users, not clients). client_id is a
// DynamoDB GSI key and DynamoDB rejects an empty-string key value, so
// dynamo:"...,omitempty" omits it when empty — the row simply doesn't appear
// in the client_id GSI (sparse index), which is correct for SSO rows.
ClientID string `json:"client_id" bson:"client_id" cql:"client_id" dynamo:"client_id,omitempty" gorm:"index" index:"client_id,hash"`
// Name is a human-readable label (e.g. "prod-k8s-cluster").
Name string `json:"name" bson:"name" cql:"name" dynamo:"name"`
// IssuerURL is the `iss` claim value expected in presented JWTs.
// For SPIFFE issuers this is the trust domain URI (e.g. "spiffe://example.org").
// Unique per Authorizer instance — one URL maps to one TrustedIssuer.
IssuerURL string `gorm:"uniqueIndex" json:"issuer_url" bson:"issuer_url" cql:"issuer_url" dynamo:"issuer_url" index:"issuer_url,hash"`
// KeySourceType determines how the JWKS key set is fetched. See type-level docs.
KeySourceType string `json:"key_source_type" bson:"key_source_type" cql:"key_source_type" dynamo:"key_source_type"`
// JWKSUrl is required when KeySourceType is "static_jwks_url".
// Strongly preferred over "oidc_discovery" for private K8s clusters (S10).
JWKSUrl *string `json:"jwks_url" bson:"jwks_url" cql:"jwks_url" dynamo:"jwks_url"`
// ExpectedAud is the audience value the presented token MUST contain (S1).
// For K8s SA tokens this MUST be set to Authorizer's own issuer URL so that
// tokens minted for other services cannot be replayed here.
ExpectedAud string `json:"expected_aud" bson:"expected_aud" cql:"expected_aud" dynamo:"expected_aud"`
// SubjectClaim is the JWT claim used to identify the workload. Defaults to "sub".
SubjectClaim string `json:"subject_claim" bson:"subject_claim" cql:"subject_claim" dynamo:"subject_claim"`
// AllowedSubjects is a comma-separated allow-list of the exact subject values
// (the value of the SubjectClaim claim) permitted to authenticate as this
// issuer's Client — e.g. "system:serviceaccount:prod:payments".
//
// SECURITY (design §5.2 C1): an empty AllowedSubjects is DENY-ALL, mirroring
// Client.AllowedScopes. A row with no configured subjects authenticates
// nobody; the client_assertion resolver MUST reject when the parsed list is
// empty. Matching is EXACT (never prefix/substring) to defeat subject
// confusion (H3): "prod-evil" must not satisfy a pin of "prod".
AllowedSubjects string `json:"allowed_subjects" bson:"allowed_subjects" cql:"allowed_subjects" dynamo:"allowed_subjects"`
// IssuerType categorises the issuer. See type-level docs for valid values.
IssuerType string `json:"issuer_type" bson:"issuer_type" cql:"issuer_type" dynamo:"issuer_type"`
// AuthMethod selects the client-authentication mechanism. See type-level docs.
// Defaults to "jwt_assertion".
AuthMethod string `json:"auth_method" bson:"auth_method" cql:"auth_method" dynamo:"auth_method" gorm:"default:'jwt_assertion'"`
// IsActive controls whether this issuer is accepted for authentication.
IsActive bool `json:"is_active" bson:"is_active" cql:"is_active" dynamo:"is_active" gorm:"default:true"`
// EnableTokenReview activates online K8s TokenReview validation (kubernetes_sa only).
// When true, Authorizer calls the K8s API server after offline JWT verification to
// confirm the bound Pod/ServiceAccount still exists.
// Default false — offline JWKS validation only (same as Keycloak default).
// SECURITY NOTE (S7): offline-only validation accepts tokens for deleted objects
// until exp. Enable this flag for high-security workloads.
EnableTokenReview bool `json:"enable_token_review" bson:"enable_token_review" cql:"enable_token_review" dynamo:"enable_token_review"`
// KubernetesAPIServerURL is required when EnableTokenReview is true.
// Example: "https://kubernetes.default.svc:443"
KubernetesAPIServerURL *string `` /* 132-byte string literal not displayed */
// SpiffeRefreshHintSeconds controls the JWKS refresh cadence for
// "spiffe_bundle_endpoint" key sources. Default 300 (5 min) when 0.
// HARD RUNTIME REQUIREMENT: failure to refresh at this cadence will reject
// valid SVIDs after a trust-bundle key rotation.
SpiffeRefreshHintSeconds int64 `` /* 140-byte string literal not displayed */
// TrustedProxyHeader is the HTTP header name from which to read a
// forwarded client certificate (PEM or DER base64) in Model B deployments.
// Empty means direct TLS only (Model A).
TrustedProxyHeader *string `json:"trusted_proxy_header" bson:"trusted_proxy_header" cql:"trusted_proxy_header" dynamo:"trusted_proxy_header"`
// TrustedProxyCIDRs is a comma-separated list of CIDR ranges whose requests
// are permitted to supply TrustedProxyHeader.
// MANDATORY when TrustedProxyHeader is set — requests from outside this list
// that carry the header MUST be rejected to prevent certificate spoofing.
TrustedProxyCIDRs *string `json:"trusted_proxy_cidrs" bson:"trusted_proxy_cidrs" cql:"trusted_proxy_cidrs" dynamo:"trusted_proxy_cidrs"`
// SSOClientID is the client_id Authorizer was issued AT the upstream IdP.
SSOClientID string `json:"sso_client_id" bson:"sso_client_id" cql:"sso_client_id" dynamo:"sso_client_id"`
// SSOClientSecretEnc is the upstream client_secret, AES-encrypted at rest
// (crypto.EncryptAES keyed on Config.ClientSecret — reversible because the
// value is replayed to the upstream token endpoint; a bcrypt hash could not be
// used). json:"-" so it NEVER serializes into an API/webhook/log projection.
SSOClientSecretEnc string `json:"-" bson:"sso_client_secret_enc" cql:"sso_client_secret_enc" dynamo:"sso_client_secret_enc"`
// SSOScopes is the space-separated scope set requested at the upstream IdP.
// Defaults to "openid profile email" when empty.
SSOScopes string `json:"sso_scopes" bson:"sso_scopes" cql:"sso_scopes" dynamo:"sso_scopes"`
// SSORedirectURI is the redirect_uri registered at the upstream IdP that points
// back at Authorizer's broker callback. When empty the broker derives
// {scheme}://{host}/oauth/sso/{org}/callback from the request host.
SSORedirectURI string `json:"sso_redirect_uri" bson:"sso_redirect_uri" cql:"sso_redirect_uri" dynamo:"sso_redirect_uri"`
// SAMLSSOURL is the upstream IdP Single Sign-On endpoint the signed
// AuthnRequest is sent to (HTTP-Redirect binding).
SAMLSSOURL *string `json:"saml_sso_url" bson:"saml_sso_url" cql:"saml_sso_url" dynamo:"saml_sso_url"`
// SAMLIDPCertPEM is the IdP's X.509 signing certificate (PEM). Assertion
// signatures are validated ONLY against this certificate — the sole trust
// anchor for this org's connection.
SAMLIDPCertPEM *string `json:"saml_idp_cert_pem" bson:"saml_idp_cert_pem" cql:"saml_idp_cert_pem" dynamo:"saml_idp_cert_pem"`
// SAMLSPEntityID is the SP entity ID Authorizer advertises for this org (the
// assertion Audience an incoming assertion MUST target). When empty the SP
// derives {scheme}://{host}/oauth/saml/{org}/metadata from the request host.
SAMLSPEntityID *string `json:"saml_sp_entity_id" bson:"saml_sp_entity_id" cql:"saml_sp_entity_id" dynamo:"saml_sp_entity_id"`
// SAMLACSURL is the Assertion Consumer Service URL for this org (the
// Recipient/Destination an incoming assertion MUST target). When empty the SP
// derives {scheme}://{host}/oauth/saml/{org}/acs from the request host.
SAMLACSURL *string `json:"saml_acs_url" bson:"saml_acs_url" cql:"saml_acs_url" dynamo:"saml_acs_url"`
// SAMLAttributeMapping is a JSON object mapping Authorizer profile fields to
// upstream SAML attribute names, e.g.
// {"email":"email","given_name":"firstName","family_name":"lastName"}.
// The NameID is ALWAYS the federated-identity subject; this map only governs
// optional profile-attribute extraction. Empty means "use default names".
SAMLAttributeMapping *string `json:"saml_attribute_mapping" bson:"saml_attribute_mapping" cql:"saml_attribute_mapping" dynamo:"saml_attribute_mapping"`
// SAMLAllowIDPInitiated permits IdP-initiated SSO (a POST to ACS with no
// matching pending AuthnRequest). DEFAULT FALSE — SP-initiated only, with
// InResponseTo bound to a pending request. Enable only if the org's IdP does
// not support SP-initiated flows and the operator accepts the reduced CSRF
// protection. NOTE: enabling this disables InResponseTo validation for ALL
// responses on this connection (crewjam limitation), including SP-initiated
// ones — the assertion then relies solely on the single-use AssertionID cache
// for replay defence.
SAMLAllowIDPInitiated bool `` /* 128-byte string literal not displayed */
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
TrustedIssuer registers an external JWT issuer whose tokens are accepted as client credentials for a Client (RFC 7523 client_assertion flow).
One Client may have multiple TrustedIssuers (e.g. K8s SA tokens AND a SPIFFE JWT-SVID from the same workload). Each TrustedIssuer maps to exactly one Client.
Supported issuer types (IssuerType field):
- "kubernetes_sa" — Kubernetes projected ServiceAccount tokens (Phase 4)
- "spiffe_jwt" — SPIFFE JWT-SVIDs (Phase 5, preview)
- "oidc" — Generic OIDC / cloud-provider tokens (Phase 3)
- "cloud_oidc" — AWS/GCP/Azure workload identity tokens (Phase 3)
Key-source types (KeySourceType field):
- "oidc_discovery" — fetch JWKS via <IssuerURL>/.well-known/openid-configuration
- "static_jwks_url" — fetch JWKS directly from JWKSUrl (required for private clusters)
- "spiffe_bundle_endpoint" — fetch from a SPIFFE bundle endpoint (Phase 5)
Authentication methods (AuthMethod field):
- "jwt_assertion" — RFC 7523 client_assertion JWT (Phases 3–5, default)
- "x509_mtls" — SPIFFE X.509-SVID via mTLS (Phase 6)
Security invariants enforced at the service layer (not here):
S1 — aud claim in presented token MUST equal ExpectedAud.
S2 — alg allow-list: RS/ES/PS 256/384/512 only; none and HS* rejected.
S3 — exp claim MUST be present and valid.
S7 — offline JWKS validation does not confirm the bound K8s object still
exists; EnableTokenReview provides online hardening for Phase 4.
S10 — static_jwks_url is always available; OIDC discovery is never forced.
Note: any field addition must also be reflected in the cassandradb provider.
func (*TrustedIssuer) AsAPITrustedIssuer ¶
func (t *TrustedIssuer) AsAPITrustedIssuer() *model.TrustedIssuer
AsAPITrustedIssuer converts the storage record into the GraphQL model. The Phase 4 Kubernetes TokenReview fields (EnableTokenReview, KubernetesAPIServerURL) are surfaced in the admin API; the remaining Phase 5/6 fields (SPIFFE bundle refresh, mTLS proxy) are intentionally not surfaced.
func (*TrustedIssuer) EffectiveKind ¶
func (t *TrustedIssuer) EffectiveKind() string
EffectiveKind returns the row's Kind, treating an empty value as TrustKindClientAssertion. A row written before the kind column existed reads back with an empty Kind; interpreting that as client_assertion_trust keeps existing trust rows working across an upgrade AND keeps the CR1 guard correct (an sso_oidc row always carries an explicit non-empty Kind, so it can never be mistaken for a client_assertion row).
func (*TrustedIssuer) ParsedAllowedSubjects ¶
func (t *TrustedIssuer) ParsedAllowedSubjects() []string
ParsedAllowedSubjects returns AllowedSubjects as a slice: comma-separated, whitespace trimmed, empty segments dropped. This is the single source of truth for interpreting the stored subject allow-list — the client_assertion resolver uses it to exact-match the presented token's subject claim. An empty or whitespace-only AllowedSubjects yields an empty slice, which the resolver MUST treat as DENY-ALL (schema § AllowedSubjects comment).
type User ¶
type User struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
Email *string `gorm:"index" json:"email" bson:"email" cql:"email" dynamo:"email" index:"email,hash"`
EmailVerifiedAt *int64 `json:"email_verified_at" bson:"email_verified_at" cql:"email_verified_at" dynamo:"email_verified_at"`
Password *string `json:"-" bson:"password" cql:"password" dynamo:"password"`
SignupMethods string `json:"signup_methods" bson:"signup_methods" cql:"signup_methods" dynamo:"signup_methods"`
GivenName *string `json:"given_name" bson:"given_name" cql:"given_name" dynamo:"given_name"`
FamilyName *string `json:"family_name" bson:"family_name" cql:"family_name" dynamo:"family_name"`
MiddleName *string `json:"middle_name" bson:"middle_name" cql:"middle_name" dynamo:"middle_name"`
Nickname *string `json:"nickname" bson:"nickname" cql:"nickname" dynamo:"nickname"`
Gender *string `json:"gender" bson:"gender" cql:"gender" dynamo:"gender"`
Birthdate *string `json:"birthdate" bson:"birthdate" cql:"birthdate" dynamo:"birthdate"`
PhoneNumber *string `gorm:"index" json:"phone_number" bson:"phone_number" cql:"phone_number" dynamo:"phone_number"`
PhoneNumberVerifiedAt *int64 `` /* 128-byte string literal not displayed */
Picture *string `json:"picture" bson:"picture" cql:"picture" dynamo:"picture"`
Roles string `json:"roles" bson:"roles" cql:"roles" dynamo:"roles"`
RevokedTimestamp *int64 `json:"revoked_timestamp" bson:"revoked_timestamp" cql:"revoked_timestamp" dynamo:"revoked_timestamp"`
IsMultiFactorAuthEnabled *bool `` /* 144-byte string literal not displayed */
// HasSkippedMFASetupAt is set the moment a user explicitly skips the
// optional MFA setup prompt shown at login (never set when EnforceMFA is
// on — skip is not offered in that mode). Nil means "never skipped."
HasSkippedMFASetupAt *int64 `` /* 128-byte string literal not displayed */
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
AppData *string `json:"app_data" bson:"app_data" cql:"app_data" dynamo:"app_data"`
// ExternalID is the stable key an external IdP (SCIM/SSO) assigns to this
// user. It is nullable — only IdP-provisioned users carry one. For SCIM it
// is namespaced per org as "<orgID>:<idpExternalId>" (see
// Provider.GetUserByExternalID) so one org's IdP can never resolve another
// org's user by external id (design §4.4 H6).
ExternalID *string `gorm:"index" json:"external_id" bson:"external_id" cql:"external_id" dynamo:"external_id" index:"external_id,hash"`
// IsActive controls whether the user is provisioned/active. SCIM
// deprovisioning (active:false / DELETE) sets this false and revokes the
// user's sessions. Existing rows default to true (gorm column default);
// the service layer always sets it explicitly so the GORM zero-value
// default:true quirk never silently re-activates a deprovisioned user.
IsActive bool `gorm:"default:true" json:"is_active" bson:"is_active" cql:"is_active" dynamo:"is_active"`
}
User model for db
func (*User) MatchesSearch ¶
MatchesSearch reports whether the user matches a case-insensitive substring query across id, email, given_name, family_name and nickname. An empty query matches everything. Used by storage backends without a native substring index (DynamoDB, Cassandra/ScyllaDB) to filter in application code.
type VerificationRequest ¶
type VerificationRequest struct {
Key string `json:"_key,omitempty" bson:"_key" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
Token string `json:"token" bson:"token" cql:"jwt_token" dynamo:"token" index:"token,hash"`
Identifier string `` /* 129-byte string literal not displayed */
ExpiresAt int64 `json:"expires_at" bson:"expires_at" cql:"expires_at" dynamo:"expires_at"`
Email string `gorm:"uniqueIndex:idx_email_identifier;type:varchar(256)" json:"email" bson:"email" cql:"email" dynamo:"email"`
Nonce string `json:"nonce" bson:"nonce" cql:"nonce" dynamo:"nonce"`
RedirectURI string `json:"redirect_uri" bson:"redirect_uri" cql:"redirect_uri" dynamo:"redirect_uri"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
VerificationRequest model for db
func (*VerificationRequest) AsAPIVerificationRequest ¶
func (v *VerificationRequest) AsAPIVerificationRequest() *model.VerificationRequest
type WebauthnCredential ¶
type WebauthnCredential struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // ArangoDB document key
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
// UserID links the credential to its owning User. Indexed to list a user's
// passkeys and to authorize deletion against the authenticated caller.
UserID string `json:"user_id" bson:"user_id" cql:"user_id" dynamo:"user_id" gorm:"index" index:"user_id,hash"`
// CredentialID is the base64-standard-encoded WebAuthn credential ID. It is
// globally UNIQUE and is the only key that resolves a user during a
// usernameless (discoverable) login, so it must be indexed and unique.
CredentialID string `` /* 130-byte string literal not displayed */
// PublicKey is the base64-standard-encoded COSE public key used to verify
// assertions.
PublicKey string `json:"public_key" bson:"public_key" cql:"public_key" dynamo:"public_key"`
// SignCount is the authenticator signature counter. go-webauthn rejects a
// login whose counter regresses below this value (cloned-authenticator
// detection); it is written back on every successful login.
SignCount int64 `json:"sign_count" bson:"sign_count" cql:"sign_count" dynamo:"sign_count"`
// Flags is the raw single-byte go-webauthn CredentialFlags (UP/UV/BE/BS).
// NOT in the original design spec but REQUIRED for correctness: go-webauthn's
// login validation rejects a credential whose stored BackupEligible flag
// disagrees with the assertion, and synced passkeys report BE=1 — so the flag
// captured at registration must be persisted and restored, or every synced
// passkey login fails.
Flags int64 `json:"flags" bson:"flags" cql:"flags" dynamo:"flags"`
// Transports is the comma-separated list of authenticator transports the
// credential supports (e.g. "internal,hybrid").
Transports string `json:"transports" bson:"transports" cql:"transports" dynamo:"transports"`
// AAGUID is the base64-standard-encoded authenticator model identifier.
AAGUID string `json:"aaguid" bson:"aaguid" cql:"aaguid" dynamo:"aaguid"`
// Name is a user-supplied label (e.g. "MacBook Touch ID").
Name string `json:"name" bson:"name" cql:"name" dynamo:"name"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
LastUsedAt *int64 `json:"last_used_at" bson:"last_used_at" cql:"last_used_at" dynamo:"last_used_at"`
}
WebauthnCredential is a single WebAuthn/passkey credential registered to a User. One User may own many credentials (multiple devices/passkeys).
Binary WebAuthn values (credential id, COSE public key, AAGUID) are stored as base64-standard strings and Transports as a comma-separated string so the row persists uniformly across all backends (SQL text, Cassandra text, DynamoDB S) — the same []byte/[]string-avoidance convention used by every other schema.
Note: any field addition must also be reflected in the cassandradb provider.
func (*WebauthnCredential) AsAPIWebauthnCredential ¶
func (w *WebauthnCredential) AsAPIWebauthnCredential() *model.WebauthnCredentialInfo
AsAPIWebauthnCredential converts the storage record into the GraphQL model. It never exposes the public key, sign count or AAGUID — only the metadata a user needs to manage their passkeys.
func (*WebauthnCredential) ParsedTransports ¶
func (w *WebauthnCredential) ParsedTransports() []string
ParsedTransports returns Transports as a slice: comma-separated, trimmed, empty segments dropped.
type Webhook ¶
type Webhook struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
EventName string `gorm:"unique" json:"event_name" bson:"event_name" cql:"event_name" dynamo:"event_name" index:"event_name,hash"`
EventDescription string `json:"event_description" bson:"event_description" cql:"event_description" dynamo:"event_description"`
EndPoint string `json:"endpoint" bson:"endpoint" cql:"endpoint" dynamo:"endpoint"`
Headers string `json:"headers" bson:"headers" cql:"headers" dynamo:"headers"`
Enabled bool `json:"enabled" bson:"enabled" cql:"enabled" dynamo:"enabled"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
Webhook model for db
func (*Webhook) AsAPIWebhook ¶
AsAPIWebhook to return webhook as graphql response object
type WebhookLog ¶
type WebhookLog struct {
Key string `json:"_key,omitempty" bson:"_key,omitempty" cql:"_key,omitempty" dynamo:"key,omitempty"` // for arangodb
ID string `gorm:"primaryKey;type:char(36)" json:"_id" bson:"_id" cql:"id" dynamo:"id,hash"`
HttpStatus int64 `json:"http_status" bson:"http_status" cql:"http_status" dynamo:"http_status"`
Response string `json:"response" bson:"response" cql:"response" dynamo:"response"`
Request string `json:"request" bson:"request" cql:"request" dynamo:"request"`
WebhookID string `gorm:"type:char(36)" json:"webhook_id" bson:"webhook_id" cql:"webhook_id" dynamo:"webhook_id" index:"webhook_id,hash"`
CreatedAt int64 `json:"created_at" bson:"created_at" cql:"created_at" dynamo:"created_at"`
UpdatedAt int64 `json:"updated_at" bson:"updated_at" cql:"updated_at" dynamo:"updated_at"`
}
WebhookLog model for db
func (*WebhookLog) AsAPIWebhookLog ¶
func (w *WebhookLog) AsAPIWebhookLog() *model.WebhookLog
AsAPIWebhookLog to return webhook log as graphql response object
Source Files
¶
- audit_log.go
- authenticators.go
- client.go
- email_templates.go
- env.go
- federated_identity.go
- mfa_session.go
- model.go
- oauth_state.go
- org_domain.go
- org_membership.go
- organization.go
- otp.go
- scim_endpoint.go
- session.go
- session_token.go
- trusted_issuer.go
- user.go
- verification_requests.go
- webauthn_credential.go
- webhook.go
- webhook_log.go