Authsome

Audit Trail

Append-only security event log with IP and user agent recording, time-range queries, cursor pagination, and Chronicle bridge integration.

Authsome records every significant authentication and authorization event to an append-only audit trail. The audit trail is the authoritative record for security investigations, compliance reporting, and user activity monitoring. Events are immutable -- there is no update or delete operation on the audit store.

The SecurityEvent model

import "github.com/xraph/authsome/audit"

type SecurityEvent struct {
    ID          id.ID             `json:"id"`           // asec_ prefix
    AppID       string            `json:"app_id"`
    EnvID       string            `json:"env_id"`
    UserID      *id.ID            `json:"user_id,omitempty"`
    SessionID   *id.ID            `json:"session_id,omitempty"`
    OrgID       *id.ID            `json:"org_id,omitempty"`
    Action      string            `json:"action"`       // e.g. "auth.signin"
    Outcome     string            `json:"outcome"`      // "success" or "failure"
    IP          string            `json:"ip,omitempty"`
    UserAgent   string            `json:"user_agent,omitempty"`
    Country     string            `json:"country,omitempty"`
    City        string            `json:"city,omitempty"`
    DeviceType  string            `json:"device_type,omitempty"`
    Reason      string            `json:"reason,omitempty"`     // on failure
    Metadata    map[string]any    `json:"metadata,omitempty"`
    CreatedAt   time.Time         `json:"created_at"`
}
FieldDescription
ActionThe event type string, e.g. "auth.signin", "auth.mfa.verified", "passkey.registered"
Outcome"success" or "failure"
IPClient IP address extracted from the request context
UserAgentFull user agent string from the request
Country / CityGeo-location data if IP geolocation is configured
DeviceTypeParsed device type: "desktop", "mobile", "tablet", "bot"
ReasonHuman-readable failure reason, e.g. "invalid_password", "token_expired"
MetadataAdditional context key-value pairs (e.g. MFA method, SSO provider name)

Event types

Authsome records events across all authentication methods:

ActionCategoryDescription
auth.signinAuthSuccessful sign-in
auth.signin.failedAuthFailed sign-in attempt
auth.signoutAuthUser signed out
auth.password.resetAuthPassword was reset
auth.mfa.enabledMFAMFA was enabled for the account
auth.mfa.disabledMFAMFA was disabled for the account
auth.mfa.enrolledMFAMFA method was enrolled (e.g., TOTP added)
auth.mfa.verifiedMFAMFA challenge was verified successfully
auth.mfa.challengedMFAMFA challenge was initiated
passkey.registeredPasskeyA new passkey credential was registered
passkey.authenticatedPasskeyAuthentication via passkey succeeded
passkey.deletedPasskeyA passkey credential was removed
social.signinSocialOAuth social sign-in
social.signupSocialAccount created via social OAuth
sso.signinSSOEnterprise SSO sign-in
sso.signupSSOAccount created via SSO
session.createdSessionA new session was created
session.revokedSessionA session was explicitly revoked
apikey.createdAPI KeyA new API key was created
apikey.revokedAPI KeyAn API key was revoked
org.member.invitedOrgA user was invited to an organization
org.member.joinedOrgA user accepted an org invitation
org.member.removedOrgA user was removed from an organization

The SecurityEventStore interface

type SecurityEventStore interface {
    // Record appends a new security event. Errors are logged but do not fail the auth operation.
    Record(ctx context.Context, event *SecurityEvent) error

    // List returns events for an app within a time range, with cursor pagination.
    List(ctx context.Context, opts ListEventsOpts) ([]*SecurityEvent, string, error)

    // ListByUser returns events for a specific user.
    ListByUser(ctx context.Context, userID id.ID, opts ListEventsOpts) ([]*SecurityEvent, string, error)

    // ListBySession returns events for a specific session.
    ListBySession(ctx context.Context, sessionID id.ID, opts ListEventsOpts) ([]*SecurityEvent, string, error)
}

ListEventsOpts

type ListEventsOpts struct {
    AppID     string
    EnvID     string
    Limit     int        // default 50, max 500
    Cursor    string     // opaque cursor from previous NextCursor
    Since     *time.Time // return events after this time
    Until     *time.Time // return events before this time
    Actions   []string   // filter by action strings
    Outcomes  []string   // filter by "success" or "failure"
    IPFilter  string     // exact IP match
}

The list methods return (events, nextCursor, error). Pass nextCursor as the Cursor in the next call to get the next page. An empty nextCursor means you have reached the last page.

Querying the audit trail

List recent events for the app

import "github.com/xraph/authsome/audit"

since := time.Now().Add(-24 * time.Hour)
events, nextCursor, err := auth.Audit().List(ctx, audit.ListEventsOpts{
    Since: &since,
    Limit: 100,
})
if err != nil {
    return err
}

for _, e := range events {
    fmt.Printf("  [%s] %s %s ip=%s user=%s\n",
        e.CreatedAt.Format(time.RFC3339),
        e.Action,
        e.Outcome,
        e.IP,
        e.UserID,
    )
}

List events for a specific user

// Last 30 days of events for a user.
since := time.Now().Add(-30 * 24 * time.Hour)
events, nextCursor, err := auth.Audit().ListByUser(ctx, userID, audit.ListEventsOpts{
    Since: &since,
    Limit: 50,
})

Filter by action and outcome

// All failed sign-ins in the last 7 days.
since := time.Now().Add(-7 * 24 * time.Hour)
events, _, err := auth.Audit().List(ctx, audit.ListEventsOpts{
    Since:    &since,
    Actions:  []string{"auth.signin", "auth.signin.failed"},
    Outcomes: []string{"failure"},
    Limit:    200,
})

Paginating through results

opts := audit.ListEventsOpts{
    Since: &since,
    Limit: 100,
}

for {
    events, nextCursor, err := auth.Audit().List(ctx, opts)
    if err != nil {
        return err
    }
    process(events)

    if nextCursor == "" {
        break
    }
    opts.Cursor = nextCursor
}

Chronicle bridge integration

Chronicle is the Forge-ecosystem's dedicated append-only event log service. When Chronicle is configured, Authsome forwards every security event to Chronicle in addition to the local audit store, giving you centralized cross-service audit logging.

import (
    "github.com/xraph/authsome"
    "github.com/xraph/authsome/audit/chronicle"
)

auth := authsome.New(
    authsome.WithAuditBridge(chronicle.NewBridge(
        chronicle.WithEndpoint("https://chronicle.internal:8080"),
        chronicle.WithAPIKey(os.Getenv("CHRONICLE_API_KEY")),
        chronicle.WithAsync(true),        // non-blocking delivery
        chronicle.WithBufferSize(1000),   // in-flight event buffer
    )),
)
OptionDefaultDescription
WithEndpointrequiredChronicle service endpoint
WithAPIKeyrequiredAPI key for Chronicle authentication
WithAsyncfalseWhen true, events are delivered asynchronously -- sign-in is not blocked by delivery failures
WithBufferSize500Size of the async delivery buffer. Events are dropped (and logged) if the buffer is full.
WithActionsallWhen set, only the specified action strings are forwarded to Chronicle

With WithAsync(true), a temporary Chronicle outage will not affect authentication. Events in the buffer are delivered when Chronicle recovers. Events that overflow the buffer are written to the application log with level=warn.

HTTP API endpoints

MethodPathDescription
GET/admin/auditList all security events with filters
GET/admin/audit/users/:user_idList security events for a specific user
GET/admin/audit/sessions/:session_idList security events for a specific session
GET/users/me/security-eventsUser-facing: list the current user's own security events

On this page