Authsome

Per-Environment Isolation

How Authsome enforces strict data isolation between environments, including users, sessions, organizations, and per-environment session configuration.

Every entity in Authsome is stamped with both an app_id and an env_id. This means users in development and users in production are completely separate records -- there is no overlap, no accidental cross-contamination, and no way for a query in one environment to accidentally touch another environment's data.

Isolation model

Application (AppID)
  ├── Environment: production  (EnvID: aenv_prod...)
  │     ├── Users (scoped to env)
  │     ├── Sessions (scoped to env)
  │     ├── Organizations (scoped to env)
  │     ├── API Keys (scoped to env)
  │     └── Audit Events (scoped to env)

  ├── Environment: staging     (EnvID: aenv_stg...)
  │     ├── Users (completely separate from production)
  │     ├── Sessions
  │     └── ...

  └── Environment: development (EnvID: aenv_dev...)
        └── ...

The env_id is injected into the context.Context at the HTTP middleware layer and is automatically applied to every database query. Cross-environment access is structurally impossible -- every store method takes an envID parameter and filters by it at the SQL level.

Environment context injection

In HTTP handlers

When a request arrives, Authsome resolves the environment from the request and injects it into the context:

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

// Use X-Authsome-Environment header or env query parameter.
router.Use(middleware.EnvironmentContext(auth))

The middleware resolution order:

  1. X-Authsome-Environment HTTP header (slug or ID)
  2. env query parameter
  3. JWT claim env_id
  4. Default environment for the application

In Go code

import "github.com/xraph/authsome"

// Inject by environment ID.
ctx = authsome.WithEnvID(ctx, envID)

// Inject by slug (resolved at injection time).
ctx, err = authsome.WithEnvSlug(ctx, auth, "production")

// All subsequent calls use this environment.
user, err := auth.Users().GetByEmail(ctx, "alice@example.com")

Isolated user accounts

A user email address can exist in multiple environments simultaneously as separate, independent accounts. Alice can have an account in development and a completely separate account in production. They share nothing: different user IDs, different passwords, different sessions, different MFA configurations.

// In production environment.
prodCtx := authsome.WithEnvID(ctx, productionEnvID)
prodUser, err := auth.Users().GetByEmail(prodCtx, "alice@example.com")
// prodUser.ID = ausr_prod123...

// In development environment.
devCtx := authsome.WithEnvID(ctx, developmentEnvID)
devUser, err := auth.Users().GetByEmail(devCtx, "alice@example.com")
// devUser.ID = ausr_dev456... (completely different user record)

Isolated sessions

Sessions are scoped to an environment. A session token created in staging cannot be used to authenticate in production. Authsome validates the session's env_id against the request's environment context and rejects mismatches.

// Session created in staging.
stagingCtx := authsome.WithEnvID(ctx, stagingEnvID)
session, err := auth.Auth().SignIn(stagingCtx, ...)
// session.EnvID = stagingEnvID

// Attempt to use this session in production.
prodCtx := authsome.WithEnvID(ctx, productionEnvID)
_, err = auth.Sessions().Get(prodCtx, session.ID)
// err = ErrSessionNotFound (the session exists only in staging)

Isolated organizations

Organizations are scoped to an environment. Organizations and their members in staging are separate records from those in production. Inviting someone to an organization in development does not affect production.

Per-environment session configuration

While environments share the same plugin configuration as the application, each environment can override session-related settings independently:

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

// Set a short session TTL for development.
_, err := auth.Environments().Update(ctx, devEnvID, &env.UpdateInput{
    Settings: &env.EnvironmentSettings{
        SessionTTL:          1 * time.Hour,      // very short in dev
        RefreshTokenTTL:     4 * time.Hour,
        MaxSessionsPerUser:  5,
        IdleSessionTimeout:  30 * time.Minute,
    },
})

// Set a longer TTL with MFA required for production.
_, err = auth.Environments().Update(ctx, prodEnvID, &env.UpdateInput{
    Settings: &env.EnvironmentSettings{
        SessionTTL:         7 * 24 * time.Hour,   // 7 days
        RefreshTokenTTL:    30 * 24 * time.Hour,   // 30 days
        MFARequired:        true,
        MaxSessionsPerUser: 10,
        IPBinding:          "subnet",
    },
})

Session config override precedence

Request-time session config
  1. Environment settings (highest priority)
  2. Application-level defaults
  3. Authsome built-in defaults (lowest priority)

Per-environment allowed auth methods

Different environments can enable different authentication methods. For example, in development you might disable SSO to avoid setting up SAML/OIDC connections, while production has SSO required:

// Development: simple password + Google OAuth only.
_, err := auth.Environments().Update(ctx, devEnvID, &env.UpdateInput{
    Settings: &env.EnvironmentSettings{
        AllowedAuthMethods: []string{"password", "google"},
    },
})

// Production: password, Google, and SAML SSO.
_, err = auth.Environments().Update(ctx, prodEnvID, &env.UpdateInput{
    Settings: &env.EnvironmentSettings{
        AllowedAuthMethods: []string{"password", "google", "saml"},
    },
})

When AllowedAuthMethods is nil, all methods provided by registered plugins are enabled.

Preventing accidental production operations

Authsome wraps destructive operations in production environments with an additional confirmation requirement. The HTTP API returns a 424 Failed Dependency with a confirmation_required error when you attempt:

  • Deleting all users
  • Revoking all sessions
  • Deleting the environment
  • Changing the environment type from production to another type

To confirm the operation, include the X-Authsome-Confirm: delete-users (or appropriate action) header in the repeat request.

// The Go SDK handles confirmations transparently when you set the option.
err := auth.Users().DeleteAll(prodCtx,
    authsome.WithConfirmDestructive("delete-all-users"),
)

Environment labels in tokens

JWTs issued by Authsome include the env_id claim so your application can know which environment a token belongs to:

{
  "sub": "ausr_01h455vb...",
  "env_id": "aenv_01h455vb...",
  "app_id": "myapp",
  "iat": 1718000000,
  "exp": 1718604800
}

Validate this claim in your application middleware to ensure users from a development environment cannot access production resources.

On this page