Skip to main content
The identity model is the core data structure in Ory Kratos. An identity represents a user account with associated traits, credentials, and addresses.

Identity structure

The Identity struct is defined in identity/identity.go:52:
type Identity struct {
    // Unique identifier (UUID v4)
    ID uuid.UUID `json:"id"`
    
    // Optional external ID for linking to external systems
    ExternalID sqlxx.NullString `json:"external_id,omitempty"`
    
    // Credentials for authentication
    Credentials map[CredentialsType]Credentials `json:"credentials,omitempty"`
    
    // Identity schema ID
    SchemaID string `json:"schema_id"`
    
    // Identity schema URL
    SchemaURL string `json:"schema_url"`
    
    // Account state (active or inactive)
    State State `json:"state"`
    
    // User-defined attributes validated by schema
    Traits Traits `json:"traits"`
    
    // Verifiable addresses (email, phone)
    VerifiableAddresses []VerifiableAddress `json:"verifiable_addresses,omitempty"`
    
    // Recovery addresses
    RecoveryAddresses []RecoveryAddress `json:"recovery_addresses,omitempty"`
    
    // Public metadata (visible to user)
    MetadataPublic sqlxx.NullJSONRawMessage `json:"metadata_public"`
    
    // Admin metadata (only via admin API)
    MetadataAdmin sqlxx.NullJSONRawMessage `json:"metadata_admin,omitempty"`
    
    // Timestamps
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

Identity ID

The identity ID is a UUID v4 that is automatically generated and cannot be changed. This ensures compatibility with distributed databases like CockroachDB.
The identity ID is immutable and serves as the primary identifier for all operations.

External ID

Optional field for linking identities to external systems. When set, it must be unique across all identities in the network. Use cases:
  • Migrating from another identity system
  • Integrating with external databases
  • Mapping to enterprise directory systems

Identity traits

Traits represent user-specific attributes like name, email, or custom fields. Traits are stored as JSON and validated against an identity schema.
// Traits are stored as JSON
type Traits json.RawMessage

Example traits

{
  "email": "user@example.com",
  "name": {
    "first": "Jane",
    "last": "Doe"
  },
  "newsletter": true
}
Traits can be modified by users through the settings flow, subject to schema validation.

Identity schemas

Identity schemas define:
  • Which traits are allowed - Fields users can set
  • Validation rules - Type checking, format validation, required fields
  • Verifiable addresses - Which traits map to email/phone addresses
  • Recovery addresses - Which addresses can be used for account recovery

Schema example

{
  "$id": "https://example.com/user.schema.json",
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "User",
  "type": "object",
  "properties": {
    "traits": {
      "type": "object",
      "properties": {
        "email": {
          "type": "string",
          "format": "email",
          "title": "Email",
          "ory.sh/kratos": {
            "credentials": {
              "password": {
                "identifier": true
              }
            },
            "verification": {
              "via": "email"
            },
            "recovery": {
              "via": "email"
            }
          }
        },
        "name": {
          "type": "string",
          "title": "Name"
        }
      },
      "required": ["email"],
      "additionalProperties": false
    }
  }
}

Multiple schemas

Kratos supports multiple identity schemas for different user types:
identity:
  default_schema_id: customer
  schemas:
    - id: customer
      url: file:///etc/config/identity.customer.schema.json
    - id: employee
      url: file:///etc/config/identity.employee.schema.json

Verifiable addresses

Verifiable addresses are email addresses or phone numbers that can be verified. Defined in identity/identity_verification.go:26:
type VerifiableAddress struct {
    ID         uuid.UUID                 `json:"id"`
    Value      string                    `json:"value"`
    Verified   bool                      `json:"verified"`
    Via        string                    `json:"via"` // "email" or "sms"
    Status     VerifiableAddressStatus   `json:"status"` // pending, sent, completed
    VerifiedAt *sqlxx.NullTime           `json:"verified_at,omitempty"`
}

Address types

  • email - Email addresses that can receive verification links/codes
  • sms - Phone numbers for SMS-based verification
Defined in identity/address.go:6:
const (
    AddressTypeEmail = "email"
    AddressTypeSMS   = "sms"
)

Verification status

  • pending - Address added but verification not initiated
  • sent - Verification message sent to address
  • completed - Address successfully verified

Extraction from traits

Verifiable addresses are automatically extracted from traits based on the identity schema configuration:
"ory.sh/kratos": {
  "verification": {
    "via": "email"
  }
}

Recovery addresses

Recovery addresses are used for account recovery flows. Similar to verifiable addresses but specifically for recovery purposes.
type RecoveryAddress struct {
    ID    uuid.UUID `json:"id"`
    Value string    `json:"value"`
    Via   string    `json:"via"`
}

Identity state

Identities can be in one of two states (defined in identity/identity.go:34):
const (
    StateActive   State = "active"
    StateInactive State = "inactive"
)
  • active - Identity can authenticate and use the system
  • inactive - Identity is disabled and cannot authenticate
Inactive identities cannot create new sessions, but existing sessions remain valid until they expire.

Credentials relationship

Each identity can have multiple credentials for different authentication methods. The credentials are stored in a map indexed by type:
Credentials map[CredentialsType]Credentials
See Credentials for detailed information about credential types and structure.

Metadata fields

Public metadata

Stored in MetadataPublic and accessible to:
  • The identity owner via /sessions/whoami
  • Administrators via admin API
  • Webhooks and hooks
Use cases:
  • UI preferences
  • Non-sensitive user settings
  • Application-specific data
Do not store sensitive data (credit scores, SSNs, etc.) in public metadata.

Admin metadata

Stored in MetadataAdmin and only accessible via admin API:
  • Internal flags
  • Support notes
  • Billing information
  • Sensitive user attributes

Authenticator assurance level

The InternalAvailableAAL field tracks the maximum authentication assurance level for the identity:
InternalAvailableAAL NullableAuthenticatorAssuranceLevel `json:"-"`
  • AAL1 - Single-factor authentication (password, OIDC)
  • AAL2 - Multi-factor authentication (password + TOTP/WebAuthn)
This is calculated based on configured credentials (identity/identity.go:385):
func (i *Identity) SetAvailableAAL(ctx context.Context, m *Manager) error {
    if c, err := m.CountActiveMultiFactorCredentials(ctx, i); err != nil {
        return err
    } else if c > 0 {
        i.InternalAvailableAAL = AuthenticatorAssuranceLevel2
        return nil
    }
    
    if c, err := m.CountActiveFirstFactorCredentials(ctx, i); err != nil {
        return err
    } else if c > 0 {
        i.InternalAvailableAAL = AuthenticatorAssuranceLevel1
        return nil
    }
    
    i.InternalAvailableAAL = NoAuthenticatorAssuranceLevel
    return nil
}

Creating identities

Identities are created through:
  1. Self-service registration - Users register themselves
  2. Admin API - Administrators create identities
  3. Batch import - Bulk import via admin API
New identity initialization (identity/identity.go:340):
func NewIdentity(traitsSchemaID string) *Identity {
    stateChangedAt := sqlxx.NullTime(time.Now().UTC())
    return &Identity{
        ID:                  uuid.Nil,
        Credentials:         map[CredentialsType]Credentials{},
        Traits:              Traits("{}"),
        SchemaID:            traitsSchemaID,
        VerifiableAddresses: []VerifiableAddress{},
        State:               StateActive,
        StateChangedAt:      &stateChangedAt,
    }
}

Best practices

Schema design
  • Keep schemas simple and focused
  • Use semantic field names
  • Mark sensitive fields appropriately
  • Define clear validation rules
Metadata usage
  • Use public metadata for user-visible settings
  • Use admin metadata for internal/sensitive data
  • Keep metadata JSON reasonably sized
  • Structure metadata for easy querying
Address management
  • Always verify email addresses before allowing password reset
  • Support multiple verifiable addresses for user convenience
  • Implement address change flows with re-verification

Build docs developers (and LLMs) love