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):
Allows signing in without a password.
MFA (AAL2):
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
}
WebAuthn credential filtering
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"`
}
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.