Skip to main content
Credentials are authentication factors that prove a user’s identity. Ory Kratos supports multiple credential types, and each identity can have multiple credentials for different authentication methods.

Credential structure

The Credentials struct is defined in identity/credentials.go:163:
type Credentials struct {
    ID   uuid.UUID       `json:"-"`
    Type CredentialsType `json:"type"`
    
    // Unique identifiers for this credential (e.g., email, username)
    Identifiers []string `json:"identifiers"`
    
    // Credential-specific configuration (encrypted passwords, OIDC tokens, etc.)
    Config sqlxx.JSONRawMessage `json:"config,omitempty"`
    
    // Version for schema migrations
    Version int `json:"version"`
    
    // Identity this credential belongs to
    IdentityID uuid.UUID `json:"-"`
    
    // Timestamps
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

Credential types

Kratos supports multiple credential types defined in identity/credentials.go:87:
const (
    CredentialsTypePassword CredentialsType = "password"
    CredentialsTypeOIDC     CredentialsType = "oidc"
    CredentialsTypeTOTP     CredentialsType = "totp"
    CredentialsTypeLookup   CredentialsType = "lookup_secret"
    CredentialsTypeWebAuthn CredentialsType = "webauthn"
    CredentialsTypeCodeAuth CredentialsType = "code"
    CredentialsTypePasskey  CredentialsType = "passkey"
    CredentialsTypeProfile  CredentialsType = "profile"
    CredentialsTypeSAML     CredentialsType = "saml"
)

Credential classification

First-factor credentials (AAL1):
  • Password
  • OIDC
  • Code (passwordless)
  • Passkey (passwordless WebAuthn)
Second-factor credentials (AAL2):
  • TOTP
  • WebAuthn (MFA)
  • Lookup secrets

Password credentials

Password credentials store hashed passwords for traditional authentication.

Configuration structure

From identity/credentials_password.go:9:
type CredentialsPassword struct {
    // Argon2id hashed password
    HashedPassword string `json:"hashed_password"`
    
    // Enable password migration hook
    UsePasswordMigrationHook bool `json:"use_password_migration_hook,omitempty"`
}

Password hashing

Passwords are hashed using Argon2id with configurable parameters:
hashers:
  argon2:
    memory: 128MB
    iterations: 3
    parallelism: 4
    salt_length: 16
    key_length: 32

Identifiers

Password credentials use the identifier field from the identity schema (typically email or username):
{
  "identifiers": ["user@example.com"]
}
Identifiers must be unique across all identities in the network. Kratos enforces this at the database level.

Password migration hook

For migrating from external systems, enable the password migration hook:
func (cp *CredentialsPassword) ShouldUsePasswordMigrationHook() bool {
    return cp != nil && cp.HashedPassword == "" && cp.UsePasswordMigrationHook
}
When enabled, Kratos calls a webhook during login to verify passwords against the legacy system.

OIDC credentials

OIDC credentials link identities to external OAuth2/OpenID Connect providers.

Configuration structure

From identity/credentials_oidc.go:19:
type CredentialsOIDC struct {
    Providers []CredentialsOIDCProvider `json:"providers"`
}

type CredentialsOIDCProvider struct {
    Subject             string `json:"subject"`
    Provider            string `json:"provider"`
    InitialIDToken      string `json:"initial_id_token"`      // Encrypted
    InitialAccessToken  string `json:"initial_access_token"`  // Encrypted
    InitialRefreshToken string `json:"initial_refresh_token"` // Encrypted
    Organization        string `json:"organization,omitempty"`
}

Identifiers

OIDC credentials use provider-specific identifiers (identity/credentials_oidc.go:111):
func OIDCUniqueID(provider, subject string) string {
    return fmt.Sprintf("%s:%s", provider, subject)
}
Example:
{
  "identifiers": ["google:108123456789"]
}

Token encryption

OAuth tokens are encrypted at rest to protect sensitive data. The cipher provider handles encryption/decryption.

Multiple providers

Users can link multiple OIDC providers to a single identity:
{
  "providers": [
    {
      "subject": "108123456789",
      "provider": "google"
    },
    {
      "subject": "567890123456",
      "provider": "github"
    }
  ]
}
Each OIDC provider connection is stored in the same credential with multiple provider entries.

WebAuthn credentials

WebAuthn credentials support hardware security keys, biometrics, and passkeys.

Configuration structure

From identity/credentials_webauthn.go:17:
type CredentialsWebAuthnConfig struct {
    Credentials CredentialsWebAuthn `json:"credentials"`
    UserHandle  []byte              `json:"user_handle"`
}

type CredentialWebAuthn struct {
    ID              []byte                            `json:"id"`
    PublicKey       []byte                            `json:"public_key"`
    AttestationType string                            `json:"attestation_type"`
    Authenticator   *AuthenticatorWebAuthn            `json:"authenticator,omitempty"`
    DisplayName     string                            `json:"display_name"`
    AddedAt         time.Time                         `json:"added_at"`
    IsPasswordless  bool                              `json:"is_passwordless"`
    Flags           *CredentialWebAuthnFlags          `json:"flags,omitempty"`
}

Passwordless vs MFA

WebAuthn credentials can serve different purposes based on IsPasswordless: Passwordless (AAL1):
IsPasswordless: true
Allows signing in without a password. MFA (AAL2):
IsPasswordless: false
Requires another first factor (password/OIDC) before use.

Authenticator details

type AuthenticatorWebAuthn struct {
    AAGUID       []byte `json:"aaguid"`        // Authenticator model identifier
    SignCount    uint32 `json:"sign_count"`    // Anti-cloning protection
    CloneWarning bool   `json:"clone_warning"` // Detected credential cloning
}

Flags

type CredentialWebAuthnFlags struct {
    UserPresent    bool `json:"user_present"`    // User was present
    UserVerified   bool `json:"user_verified"`   // Biometric/PIN verified
    BackupEligible bool `json:"backup_eligible"` // Credential can be backed up
    BackupState    bool `json:"backup_state"`    // Credential is backed up
}
Kratos filters credentials based on requested AAL (identity/credentials_webauthn.go:83):
func (c CredentialsWebAuthn) ToWebAuthnFiltered(
    aal AuthenticatorAssuranceLevel,
    authenticatorResponseFlags *protocol.AuthenticatorFlags,
) (result []webauthn.Credential) {
    for k, cc := range c {
        if (aal == AuthenticatorAssuranceLevel1 && cc.IsPasswordless) ||
           (aal == AuthenticatorAssuranceLevel2 && !cc.IsPasswordless) {
            result = append(result, *c[k].ToWebAuthn())
        }
    }
    return result
}

TOTP credentials

Time-based one-time password credentials for authenticator apps.

Configuration structure

From identity/credentials_totp.go:7:
type CredentialsTOTPConfig struct {
    // TOTP URL for QR code generation
    // Format: otpauth://totp/...
    TOTPURL string `json:"totp_url"`
}

TOTP URL format

Follows the Key URI Format:
otpauth://totp/Kratos:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Kratos

Usage

TOTP is always a second factor (AAL2). Users must have a first factor before enabling TOTP.
TOTP secrets are generated during setup and stored in the TOTP URL. The URL should be kept secure.

Lookup secrets

Backup recovery codes for account access when primary MFA is unavailable.

Configuration structure

From identity/credentials_lookup.go:16:
type CredentialsLookupConfig struct {
    RecoveryCodes []RecoveryCode `json:"recovery_codes"`
}

type RecoveryCode struct {
    Code   string           `json:"code"`
    UsedAt sqlxx.NullTime   `json:"used_at,omitempty"`
}

Code generation

Lookup secrets are typically:
  • 12 codes generated at setup
  • 8-12 characters each
  • Single-use (marked with UsedAt timestamp)

Example configuration

{
  "recovery_codes": [
    {
      "code": "ABCD-1234-EFGH",
      "used_at": null
    },
    {
      "code": "IJKL-5678-MNOP",
      "used_at": "2024-01-01T12:00:00Z"
    }
  ]
}
Lookup secrets should be displayed only once during setup. Users must save them securely.

Code credentials

One-time codes sent via email or SMS for passwordless authentication.

Configuration structure

From identity/credentials_code.go:60:
type CredentialsCode struct {
    Addresses []CredentialsCodeAddress `json:"addresses"`
}

type CredentialsCodeAddress struct {
    Channel CodeChannel `json:"channel"` // "email" or "sms"
    Address string      `json:"address"`
}

Code channels

const (
    CodeChannelEmail CodeChannel = "email"
    CodeChannelSMS   CodeChannel = "sms"
)

Example configuration

{
  "addresses": [
    {
      "channel": "email",
      "address": "user@example.com"
    },
    {
      "channel": "sms",
      "address": "+1234567890"
    }
  ]
}

Use cases

  • Passwordless login
  • Registration without password
  • Account recovery
  • Email/phone verification

Credential identifiers

Identifiers are unique strings that map to credentials and enable login.

Identifier structure

From identity/credentials.go:217:
type CredentialIdentifier struct {
    ID                        uuid.UUID  `db:"id"`
    Identifier                string     `db:"identifier"`
    IdentityID                *uuid.UUID `db:"identity_id"`
    IdentityCredentialsID     uuid.UUID  `db:"identity_credential_id"`
    IdentityCredentialsTypeID uuid.UUID  `db:"identity_credential_type_id"`
}

Uniqueness

Identifiers must be unique within a credential type and network:
  • Email user@example.com can exist once for password credentials
  • Same email can exist for code credentials (different type)
  • OIDC identifiers like google:123 are unique per provider

Multiple identifiers

Credentials can have multiple identifiers:
{
  "type": "password",
  "identifiers": [
    "user@example.com",
    "username123"
  ]
}
Both identifiers can be used to log in.

Credential versioning

The Version field enables credential schema migrations:
Version int `json:"version"`
When credential structure changes, Kratos can migrate old versions:
  • Version 0: Legacy format
  • Version 1: Current format
  • Future versions: Migration path for breaking changes

Credential operations

Setting credentials

From identity/identity.go:196:
func (i *Identity) SetCredentials(t CredentialsType, c Credentials) {
    if i.Credentials == nil {
        i.Credentials = make(map[CredentialsType]Credentials)
    }
    c.Type = t
    c.IdentityID = i.ID
    i.Credentials[t] = c
}

Getting credentials

func (i *Identity) GetCredentials(t CredentialsType) (*Credentials, bool) {
    if c, ok := i.Credentials[t]; ok {
        return &c, true
    }
    return nil, false
}

Deleting credentials

func (i *Identity) DeleteCredentialsType(t CredentialsType) {
    if i.Credentials == nil {
        return
    }
    delete(i.Credentials, t)
}

Credential configuration

Enable/disable credential types in configuration:
selfservice:
  methods:
    password:
      enabled: true
    oidc:
      enabled: true
      config:
        providers:
          - id: google
            provider: google
            client_id: ...
            client_secret: ...
            mapper_url: file:///etc/config/oidc.google.jsonnet
    webauthn:
      enabled: true
      config:
        rp:
          display_name: My App
          id: example.com
          origins:
            - https://example.com
    totp:
      enabled: true
      config:
        issuer: My App
    lookup_secret:
      enabled: true
    code:
      enabled: true
      config:
        lifespan: 15m

Multi-factor authentication

Credentials contribute to authenticator assurance levels:

First factors (AAL1)

  • Password
  • OIDC
  • Code (email/SMS)
  • Passkey (passwordless WebAuthn)

Second factors (AAL2)

  • TOTP
  • WebAuthn (MFA mode)
  • Lookup secrets

AAL enforcement

Applications can require AAL2 for sensitive operations:
GET /sessions/whoami?aal=aal2
If session is only AAL1, Kratos redirects to login flow with AAL2 requirement.

Best practices

Credential security
  • Never expose credential configs in public APIs
  • Encrypt sensitive token data at rest
  • Rotate OIDC tokens when possible
  • Hash passwords with strong parameters
Identifier management
  • Validate identifier uniqueness before creation
  • Handle identifier conflicts gracefully
  • Support identifier updates through settings flow
  • Re-verify addresses after changes
Multi-factor setup
  • Require first factor before enabling second factors
  • Always generate lookup secrets with TOTP/WebAuthn
  • Show recovery codes only once
  • Allow multiple second factors for redundancy
Deleting all credentials locks users out of their accounts. Always verify at least one credential remains functional.

Build docs developers (and LLMs) love