{
  "feature": "payload-auth",
  "version": "1.0.0",
  "description": "Full authentication system with JWT sessions, API keys, account locking, email verification, and custom strategies",
  "category": "auth",
  "tags": [
    "cms",
    "headless",
    "jwt",
    "sessions",
    "api-key",
    "pbkdf2",
    "account-locking",
    "email-verification",
    "payload"
  ],
  "actors": [
    {
      "id": "user",
      "name": "User",
      "type": "human",
      "description": "End user authenticating via email/password, username/password, or API key"
    },
    {
      "id": "auth_service",
      "name": "Auth Service",
      "type": "system",
      "description": "Payload auth engine — handles JWT signing, session management, password hashing"
    },
    {
      "id": "email_service",
      "name": "Email Service",
      "type": "system",
      "description": "Pluggable email adapter for sending verification and password reset emails"
    }
  ],
  "fields": [
    {
      "name": "email",
      "type": "email",
      "required": true,
      "label": "Email",
      "validation": [
        {
          "type": "required",
          "message": "Email is required"
        },
        {
          "type": "email",
          "message": "Must be a valid email address"
        },
        {
          "type": "unique",
          "message": "Email already in use"
        }
      ]
    },
    {
      "name": "username",
      "type": "text",
      "required": false,
      "label": "Username"
    },
    {
      "name": "password",
      "type": "password",
      "required": true,
      "sensitive": true,
      "label": "Password",
      "validation": [
        {
          "type": "minLength",
          "value": 3,
          "message": "Password must be at least 3 characters"
        }
      ]
    },
    {
      "name": "salt",
      "type": "hidden",
      "required": false,
      "sensitive": true,
      "label": "Salt"
    },
    {
      "name": "hash",
      "type": "hidden",
      "required": false,
      "sensitive": true,
      "label": "Hash"
    },
    {
      "name": "reset_password_token",
      "type": "token",
      "required": false,
      "sensitive": true,
      "label": "Reset Password Token"
    },
    {
      "name": "reset_password_expiration",
      "type": "datetime",
      "required": false,
      "label": "Reset Password Expiration"
    },
    {
      "name": "login_attempts",
      "type": "number",
      "required": false,
      "default": 0,
      "label": "Login Attempts"
    },
    {
      "name": "lock_until",
      "type": "datetime",
      "required": false,
      "label": "Lock Until"
    },
    {
      "name": "verification_token",
      "type": "token",
      "required": false,
      "sensitive": true,
      "label": "Verification Token"
    },
    {
      "name": "verified",
      "type": "boolean",
      "required": false,
      "default": false,
      "label": "Verified"
    },
    {
      "name": "sessions",
      "type": "json",
      "required": false,
      "label": "Sessions"
    },
    {
      "name": "enable_api_key",
      "type": "boolean",
      "required": false,
      "default": false,
      "label": "Enable Api Key"
    },
    {
      "name": "api_key",
      "type": "token",
      "required": false,
      "sensitive": true,
      "label": "Api Key"
    },
    {
      "name": "api_key_index",
      "type": "hidden",
      "required": false,
      "sensitive": true,
      "label": "Api Key Index"
    }
  ],
  "rules": {
    "security": {
      "password_hashing": {
        "algorithm": "PBKDF2",
        "digest": "SHA-256",
        "iterations": 25000,
        "key_length": 512,
        "salt_length": 32
      },
      "timing_safe_comparison": true,
      "account_locking": {
        "max_login_attempts": 0,
        "lock_duration": "30m",
        "auto_unlock": true,
        "revoke_recent_sessions_on_lock": true
      },
      "jwt": {
        "algorithm": "HS256",
        "default_expiration": "2h",
        "library": "jose"
      },
      "sessions": {
        "enabled_by_default": true,
        "id_format": "UUID v4",
        "auto_cleanup_expired": true
      },
      "cookies": {
        "http_only": true,
        "secure": "configurable",
        "same_site": "configurable",
        "name_pattern": "{cookiePrefix}-token"
      },
      "csrf": {
        "validates_origin_header": true,
        "validates_sec_fetch_site": true,
        "allows": [
          "same-origin",
          "same-site",
          "none"
        ]
      },
      "api_key_encryption": {
        "algorithm": "AES-256-CTR",
        "iv_length": 16
      },
      "email_verification": {
        "enabled_by_default": false,
        "blocks_login_when_unverified": true,
        "auto_verified_on_password_reset": true
      }
    },
    "access": {
      "auth_fields_hidden": true,
      "sessions_read_own_only": true,
      "api_key_encrypted_at_rest": true
    }
  },
  "sla": {
    "token_expiration": {
      "access_token": "2h",
      "reset_password_token": "1h"
    }
  },
  "outcomes": {
    "login_success": {
      "priority": 10,
      "given": [
        {
          "field": "email",
          "source": "input",
          "operator": "exists",
          "description": "User provides email (or username if loginWithUsername enabled)"
        },
        {
          "field": "password",
          "source": "input",
          "operator": "exists",
          "description": "User provides password"
        },
        "credentials match a user record (timing-safe comparison)",
        "account is not locked",
        "email is verified (if verification enabled)"
      ],
      "then": [
        {
          "action": "set_field",
          "target": "login_attempts",
          "value": 0,
          "description": "Reset failed attempt counter"
        },
        {
          "action": "emit_event",
          "event": "auth.login",
          "payload": [
            "user_id",
            "email",
            "timestamp",
            "strategy"
          ]
        },
        "JWT token signed with user fields marked saveToJWT",
        "Session created with UUID and expiration (if sessions enabled)",
        "Token set as HttpOnly cookie"
      ],
      "result": "User receives JWT token, user object, and expiration timestamp",
      "transaction": true
    },
    "login_invalid_credentials": {
      "priority": 5,
      "error": "AUTH_INVALID_CREDENTIALS",
      "given": [
        {
          "any": [
            {
              "field": "email",
              "source": "db",
              "operator": "not_exists",
              "description": "No user found with provided email"
            },
            "password does not match stored hash"
          ]
        }
      ],
      "then": [
        {
          "action": "set_field",
          "target": "login_attempts",
          "value": "login_attempts + 1",
          "when": "login_attempts >= 0",
          "description": "Increment failed attempt counter"
        }
      ],
      "result": "Generic authentication error — does not reveal whether email exists"
    },
    "login_account_locked": {
      "priority": 2,
      "error": "AUTH_ACCOUNT_LOCKED",
      "given": [
        {
          "field": "lock_until",
          "source": "db",
          "operator": "gt",
          "value": "now",
          "description": "Account lock has not expired"
        }
      ],
      "result": "User informed account is locked due to too many failed attempts"
    },
    "login_unverified_email": {
      "priority": 3,
      "error": "AUTH_EMAIL_UNVERIFIED",
      "given": [
        "email verification is enabled for this collection",
        {
          "field": "verified",
          "source": "db",
          "operator": "eq",
          "value": false
        }
      ],
      "result": "User informed their email is not verified"
    },
    "logout_success": {
      "priority": 10,
      "given": [
        "user is authenticated"
      ],
      "then": [
        {
          "action": "invalidate",
          "target": "session",
          "scope": "current_session",
          "description": "Remove session from user's sessions array"
        },
        {
          "action": "emit_event",
          "event": "auth.logout",
          "payload": [
            "user_id",
            "timestamp"
          ]
        }
      ],
      "result": "Session removed, auth cookie cleared"
    },
    "forgot_password_sent": {
      "priority": 10,
      "given": [
        {
          "field": "email",
          "source": "input",
          "operator": "exists"
        }
      ],
      "then": [
        {
          "action": "set_field",
          "target": "reset_password_token",
          "value": "random 20-byte hex token"
        },
        {
          "action": "set_field",
          "target": "reset_password_expiration",
          "value": "now + 1h"
        },
        {
          "action": "notify",
          "channel": "email",
          "template": "forgot_password",
          "to": "user",
          "description": "Email with reset link (customizable HTML/subject)"
        }
      ],
      "result": "Success response regardless of whether email exists (prevents enumeration)"
    },
    "reset_password_success": {
      "priority": 10,
      "given": [
        {
          "field": "reset_password_token",
          "source": "input",
          "operator": "exists"
        },
        {
          "field": "reset_password_expiration",
          "source": "db",
          "operator": "gt",
          "value": "now",
          "description": "Token has not expired"
        }
      ],
      "then": [
        "New salt and hash generated from provided password",
        {
          "action": "set_field",
          "target": "reset_password_token",
          "value": null
        },
        {
          "action": "set_field",
          "target": "verified",
          "value": true,
          "description": "Auto-verify user on password reset"
        },
        "New JWT token and session created"
      ],
      "result": "Password updated, user logged in with new token",
      "transaction": true
    },
    "reset_password_expired": {
      "priority": 3,
      "error": "AUTH_TOKEN_EXPIRED",
      "given": [
        {
          "field": "reset_password_expiration",
          "source": "db",
          "operator": "lte",
          "value": "now"
        }
      ],
      "result": "User informed the reset token has expired"
    },
    "verify_email_success": {
      "priority": 10,
      "given": [
        {
          "field": "verification_token",
          "source": "input",
          "operator": "exists"
        },
        "token matches stored verification token"
      ],
      "then": [
        {
          "action": "set_field",
          "target": "verified",
          "value": true
        },
        {
          "action": "set_field",
          "target": "verification_token",
          "value": null
        },
        {
          "action": "emit_event",
          "event": "auth.verify",
          "payload": [
            "user_id",
            "email",
            "timestamp"
          ]
        }
      ],
      "result": "User email verified, can now log in"
    },
    "refresh_token_success": {
      "priority": 10,
      "given": [
        "user has valid JWT (not expired or about to expire)",
        "session ID in JWT matches an active session (if sessions enabled)"
      ],
      "then": [
        "New JWT token signed with same user data",
        "Session expiration extended"
      ],
      "result": "New token returned with updated expiration"
    },
    "unlock_account": {
      "priority": 10,
      "given": [
        "requesting user has unlock permission",
        {
          "field": "login_attempts",
          "source": "db",
          "operator": "gt",
          "value": 0
        }
      ],
      "then": [
        {
          "action": "set_field",
          "target": "login_attempts",
          "value": 0
        },
        {
          "action": "set_field",
          "target": "lock_until",
          "value": null
        }
      ],
      "result": "Account unlocked, login attempts reset"
    },
    "register_first_user": {
      "priority": 10,
      "given": [
        "no users exist in the auth collection"
      ],
      "then": [
        "User created with provided data",
        {
          "action": "set_field",
          "target": "verified",
          "value": true,
          "description": "Auto-verify first user"
        },
        "JWT token and session created"
      ],
      "result": "First user registered and logged in automatically"
    },
    "me_current_user": {
      "priority": 10,
      "given": [
        "user is authenticated via any strategy"
      ],
      "result": "Returns current user object, token expiration, and strategy name"
    },
    "api_key_auth": {
      "priority": 10,
      "given": [
        "Authorization header contains '{collectionSlug} API-Key {key}'",
        "HMAC-SHA256 of key matches stored apiKeyIndex",
        "email is verified (if verification enabled)"
      ],
      "result": "User authenticated with _strategy set to 'api-key'",
      "error": "AUTH_UNAUTHORIZED"
    }
  },
  "errors": [
    {
      "code": "AUTH_INVALID_CREDENTIALS",
      "status": 401,
      "message": "The email or password provided is incorrect",
      "retry": true
    },
    {
      "code": "AUTH_ACCOUNT_LOCKED",
      "status": 401,
      "message": "This account has been locked due to too many failed login attempts",
      "retry": false
    },
    {
      "code": "AUTH_EMAIL_UNVERIFIED",
      "status": 401,
      "message": "You must verify your email before logging in",
      "retry": false
    },
    {
      "code": "AUTH_FORBIDDEN",
      "status": 403,
      "message": "You are not allowed to perform this action",
      "retry": false
    },
    {
      "code": "AUTH_TOKEN_EXPIRED",
      "status": 401,
      "message": "The token has expired. Please request a new one",
      "retry": true
    },
    {
      "code": "AUTH_UNAUTHORIZED",
      "status": 401,
      "message": "You must be logged in to perform this action",
      "retry": false
    }
  ],
  "events": [
    {
      "name": "auth.login",
      "payload": [
        "user_id",
        "email",
        "timestamp",
        "strategy",
        "ip_address"
      ],
      "description": "Emitted after successful login — triggers afterLogin hooks"
    },
    {
      "name": "auth.logout",
      "payload": [
        "user_id",
        "timestamp"
      ],
      "description": "Emitted after logout — triggers afterLogout hooks"
    },
    {
      "name": "auth.verify",
      "payload": [
        "user_id",
        "email",
        "timestamp"
      ],
      "description": "Emitted when email verification succeeds"
    },
    {
      "name": "auth.forgot_password",
      "payload": [
        "user_id",
        "email",
        "token",
        "timestamp"
      ],
      "description": "Emitted after forgot password token generated — triggers afterForgotPassword hooks"
    },
    {
      "name": "auth.reset_password",
      "payload": [
        "user_id",
        "email",
        "timestamp"
      ],
      "description": "Emitted after password is successfully reset"
    },
    {
      "name": "auth.refresh",
      "payload": [
        "user_id",
        "token",
        "timestamp"
      ],
      "description": "Emitted after token refresh — triggers afterRefresh hooks"
    },
    {
      "name": "auth.unlock",
      "payload": [
        "user_id",
        "email",
        "timestamp"
      ],
      "description": "Emitted when an account is unlocked"
    }
  ],
  "related": [
    {
      "feature": "payload-access-control",
      "type": "required",
      "reason": "Auth provides the user identity that access control evaluates"
    },
    {
      "feature": "payload-collections",
      "type": "required",
      "reason": "Auth-enabled collections are standard Payload collections with auth fields added"
    },
    {
      "feature": "payload-preferences",
      "type": "optional",
      "reason": "Authenticated users can store UI preferences"
    }
  ],
  "agi": {
    "goals": [
      {
        "id": "reliable_payload_auth",
        "description": "Full authentication system with JWT sessions, API keys, account locking, email verification, and custom strategies",
        "success_metrics": [
          {
            "metric": "unauthorized_access_rate",
            "target": "0%",
            "measurement": "Failed authorization attempts that succeed"
          },
          {
            "metric": "response_time_p95",
            "target": "< 500ms",
            "measurement": "95th percentile response time"
          }
        ],
        "constraints": [
          {
            "type": "security",
            "description": "Follow OWASP security recommendations",
            "negotiable": false
          },
          {
            "type": "security",
            "description": "Sensitive fields must be encrypted at rest and never logged in plaintext",
            "negotiable": false
          }
        ]
      }
    ],
    "autonomy": {
      "level": "supervised",
      "human_checkpoints": [
        "before modifying sensitive data fields"
      ],
      "escalation_triggers": [
        "error_rate > 5",
        "consecutive_failures > 3"
      ]
    },
    "safety": {
      "action_permissions": [
        {
          "action": "login_success",
          "permission": "autonomous"
        },
        {
          "action": "login_invalid_credentials",
          "permission": "autonomous"
        },
        {
          "action": "login_account_locked",
          "permission": "autonomous"
        },
        {
          "action": "login_unverified_email",
          "permission": "autonomous"
        },
        {
          "action": "logout_success",
          "permission": "autonomous"
        },
        {
          "action": "forgot_password_sent",
          "permission": "autonomous"
        },
        {
          "action": "reset_password_success",
          "permission": "autonomous"
        },
        {
          "action": "reset_password_expired",
          "permission": "autonomous"
        },
        {
          "action": "verify_email_success",
          "permission": "autonomous"
        },
        {
          "action": "refresh_token_success",
          "permission": "autonomous"
        },
        {
          "action": "unlock_account",
          "permission": "autonomous"
        },
        {
          "action": "register_first_user",
          "permission": "autonomous"
        },
        {
          "action": "me_current_user",
          "permission": "autonomous"
        },
        {
          "action": "api_key_auth",
          "permission": "autonomous"
        }
      ]
    },
    "tradeoffs": [
      {
        "prefer": "security",
        "over": "performance",
        "reason": "authentication must prioritize preventing unauthorized access"
      }
    ],
    "verification": {
      "invariants": [
        "sensitive fields are never logged in plaintext",
        "all data access is authenticated and authorized",
        "error messages never expose internal system details"
      ]
    },
    "coordination": {
      "protocol": "orchestrated",
      "consumes": [
        {
          "capability": "payload_access_control",
          "from": "payload-access-control",
          "fallback": "fail"
        },
        {
          "capability": "payload_collections",
          "from": "payload-collections",
          "fallback": "fail"
        }
      ]
    }
  },
  "extensions": {
    "tech_stack": {
      "language": "TypeScript",
      "framework": "Payload CMS 3.x (Next.js-based)",
      "database": "Multi-adapter (MongoDB, PostgreSQL, SQLite, D1)",
      "orm": "Drizzle (SQL) / Mongoose (MongoDB)"
    },
    "auth_strategies": [
      {
        "name": "local-jwt",
        "description": "Email/password with PBKDF2 hashing and JWT tokens"
      },
      {
        "name": "api-key",
        "description": "HMAC-indexed API key with AES-256-CTR encryption at rest"
      },
      {
        "name": "custom",
        "description": "Pluggable strategy array for OAuth, SAML, etc."
      }
    ]
  }
}