Hacker DirectoryHacker Directory

WEB / WEB-14.S01

Account lifecycle and password verification

Account records, verified contacts, password verifiers, and sessions are separate state. Track which control changes which state from registration through disablement and closure.

CORE · RED · BLUE · ARTIFACT · PRACTICE

1. Core · Mechanism

Read every scenario without motion

Registration

  1. Registration request

    Contact and password are aliases only; the account does not yet exist.

    Browser / Duru
    Registration alias ready
    Application
    No request
    Account / verifier records
    No account
  2. Registration input arrived

    Input policy is supplied; trusted identifier uniqueness is checked.

    Browser / Duru
    Waiting for registration receipt
    Application
    Received registration claim
    Account / verifier records
    No account
  3. Pending account and verifier

    Local creation: contact pending, verifier alias stored, no session.

    Browser / Duru
    Waiting for registration receipt
    Application
    Pending account created
    Account / verifier records
    subject-duru: pending-contact; verifier current alias
  4. Generic receipt at the Browser

    The Browser received the registration receipt; the account is still pending-contact. This example omits mail and token delivery and assumes the verification alias is now available in the Browser. The Browser can prepare the verify-contact request; no session has been issued.

    Browser / Duru
    Verification alias ready; no session
    Application
    Pending contact verification
    Account / verifier records
    subject-duru: pending-contact; verifier current alias
  5. Verification request arrived

    The verification alias prepared in the Browser reached the Application. The Application will check its subject-duru and purpose=verify-contact binding, unused state and lifetime; arrival alone does not activate the account.

    Browser / Duru
    Waiting for verification result
    Application
    Verification alias received
    Account / verifier records
    subject-duru: pending-contact; token unused
  6. Contact-binding commit

    Supplied match/subject/purpose/lifetime/unused are true; token consumption and activation commit locally.

    Browser / Duru
    Waiting for verification result
    Application
    Contact verified; no session issued
    Account / verifier records
    subject-duru: active; token consumed
  7. Active-account result arrived

    Verification activates account state; normal login remains separate.

    Browser / Duru
    Contact verified; normal login available
    Application
    Verification complete
    Account / verifier records
    subject-duru: active; token consumed

Active verification and rehash

  1. Account and credential start

    Account status and old-verifier metadata are distinct; the candidate is an alias only.

    Browser / Duru
    Login candidate alias ready
    Application
    No request
    Account / verifier records
    active; old verifier alias
  2. Login request arrived

    Trusted account and throttle state will be evaluated locally.

    Browser / Duru
    Waiting for login result
    Application
    Candidate received; no context
    Account / verifier records
    active; old verifier alias
  3. Throttle and verifier gate

    With attemptAllowed=true, verification is supplied as called in this example, with passwordMatches=true. accountStatus=active is checked separately. Account eligibility does not mean the rehash has committed yet.

    Browser / Duru
    Waiting for login result
    Application
    Context eligible; upgrade planned
    Account / verifier records
    active; old verifier alias
  4. Conditional verifier update

    The active branch upgrades only the expected generation; the disabled branch leaves the verifier unchanged.

    Browser / Duru
    Waiting for login result
    Application
    Context eligible
    Account / verifier records
    active; current verifier alias
  5. Login result at the Browser

    The Browser received a successful login result. Verification and account policy passed for this active account, and the expected generation’s verifier upgrade completed. The scene does not show a session cookie in this response or permission for every resource action.

    Browser / Duru
    Authenticated context eligible
    Application
    Accepted context decision
    Account / verifier records
    active; current verifier alias

Account disabled

  1. Account and credential start

    Account status and old-verifier metadata are distinct; the candidate is an alias only.

    Browser / Duru
    Login candidate alias ready
    Application
    No request
    Account / verifier records
    disabled; old verifier alias
  2. Login request arrived

    Trusted account and throttle state will be evaluated locally.

    Browser / Duru
    Waiting for login result
    Application
    Candidate received; no context
    Account / verifier records
    disabled; old verifier alias
  3. Throttle and verifier gate

    Despite attemptAllowed=true and passwordMatches=true, accountStatus=disabled. Failed account eligibility withholds new authenticated context; successful password verification does not activate the account.

    Browser / Duru
    Waiting for login result
    Application
    Denied: account disabled
    Account / verifier records
    disabled; old verifier alias
  4. Verifier retained

    The disabled account cannot receive an authenticated context. Its existing verifier is retained; this branch performs no rehash or upgrade.

    Browser / Duru
    Waiting for login result
    Application
    Context denied
    Account / verifier records
    disabled; old verifier alias
  5. Login result at the Browser

    The Browser received a generic authentication failure. Internally the disabled account caused rejection; contextIssued=false and rehashCommitted=false remain. This response does not prove invalidation of existing sessions at every node.

    Browser / Duru
    Authentication could not be completed
    Application
    Denied: account disabled
    Account / verifier records
    disabled; old verifier alias

Attempt blocked

  1. Account and credential start

    Account status and old-verifier metadata are distinct; the candidate is an alias only.

    Browser / Duru
    Login candidate alias ready
    Application
    No request
    Account / verifier records
    active; old verifier alias
  2. Login request arrived

    Trusted account and throttle state will be evaluated locally.

    Browser / Duru
    Waiting for login result
    Application
    Candidate received; no context
    Account / verifier records
    active; old verifier alias
  3. Throttle and verifier gate

    With attemptAllowed=false, verification is not called: verifyCalled=false and passwordMatches=null. This is not a wrong-password result. This attempt produces neither a new context nor verifier migration.

    Browser / Duru
    Waiting for login result
    Application
    Denied: attempt blocked
    Account / verifier records
    active; old verifier alias
  4. Login result at the Browser

    The Browser received a generic authentication failure. Internally the attempt stopped before verification, leaving the password outcome unknown and account/verifier records unchanged. An active account does not override this throttle decision.

    Browser / Duru
    Authentication could not be completed
    Application
    Denied: attempt blocked
    Account / verifier records
    active; old verifier alias

ACTORS · STATE · DATA FLOW

An account is not one loggedIn boolean

Atlas stores subject-duru with a login identifier, contact verification, account status, credential generation, and authenticator associations. A session is not this account record. A new account can exist in pending-contact state without being eligible for protected login; a disabled account can still have a matching password verifier.

The fixture’s state names are application-owned: pending-contact, active, disabled, and closed. They are not required OWASP or NIST database enums. Administrative disablement, temporary throttling, and closure have different reasons; none automatically specifies another’s duration or recovery path.

Registration separates an account claim from contact control

Duru supplies duru.reader and a contact alias on the registration form. The Application checks uniqueness and product eligibility at the trusted boundary and uses suitable adaptive verification from WEB-14.01 for passwords. CandidateAllowed and identifierAvailable are supplied true; the fixture contains neither real registration data nor a password.

Atlas requires verified contact before activating this example account. A verification token is bound to purpose=verify-contact and subject-duru; tokenMatched, unused, and lifetimeValid jointly allow contactVerified=true and pending-contact→active. Mail transport is outside the scene, which assumes the prepared alias is available.

Contact verification supplies evidence about control of a communication endpoint. It does not prove a real-world identity, MFA assurance, or cross-tenant permission. Public registration and verification messages need not expose every internal account-record detail.

Password policy at enrollment/change; verification at login

An established password library verifies the candidate using the record’s algorithm, salt, and cost metadata without decrypting a hash. Salt is not secret. Even a verifier-only backup is sensitive material; database/backup access, credential logging, and metadata migration need joint assessment.

Selection/change applies a compromised/common-value blocklist, length, and supported-input policy. Requirements in the SP 800-63B-4 scope should not become arbitrary complexity or forced periodic changes. Document the product’s assurance target and the library’s byte/Unicode/truncation behavior; this fixture prescribes no production length or cost value.

Changing credential policy differs from accidentally disabling an existing account. An old verifier can verify successfully and then be rehashed under an updated policy. Password change/reset and rehash are different lifecycle events.

Account gates and safe rehash after verification succeeds

Normal login begins with an active account and an allowed attempt. Supplied passwordMatches=true permits subject resolution. If needsRehash=true, an established library can create a new verifier with a new salt/current cost policy while the candidate is available in the controlled verification path. The fixture changes verifier-old-alias to verifier-current-alias while retaining the same subject.

Rehashing can race with credential-generation changes. A stale login must not overwrite a newly reset verifier with its old candidate. Use account/credential-version checks for conditional updates to the expected generation and reassess conflicts; this is a high-level consistency rule.

The disabled branch has passwordMatches=true but accountAllowed=false, so no new authenticated context is granted. Propagation of existing-session invalidation to each node requires separate design; a verifier match does not establish that sessions were invalidated.

Throttling state and public failures are different surfaces

Throttling governs whether an attempt is allowed; a source-IP-only counter does not fully express account-level behavior. Atlas rejects a blocked attempt before calling the KDF. Distributed limit state, contention, reset conditions, and legitimate-user lockout depend on implementation; no count is presented as a universal retry policy.

Absent accounts, wrong passwords, pending contact, and disabled accounts can share one generic public authentication-failure body. Internal reasons remain separate for troubleshooting/control verification. Matching bodies alone prove neither equal HTTP status, size, timing, redirects, nor registration/reset behavior.

Review the owned public-response matrix against internal state to explain exposed detail. This lesson supplies no request workflow for discovering accounts and makes no constant-time guarantee without actual latency measurements.

Disablement, closure, and recovery are not interchangeable

When account eligibility ends, authenticators and access relationships need controlled invalidation. Retention/deletion after closure depends on the product and its obligations; a closed login gate does not imply deletion of every audit record or backup.

Recovery, clearing a temporary throttle, and changing a password from an active session use different authorization gates. WEB-14.S02 develops recovery and sensitive changes. A contactVerified flag at enrollment does not permit every future factor replacement unconditionally through email.

2. Red · Trust & Impact

Read every scenario without motion

Disabled account

  1. Account and attempt state were separated

    The account is disabled with attemptAllowed=true. A credential match cannot automatically make this lifecycle state active.

    Account/attempt policy
    case=disabled
    Credential verification
    No verify outcome
    Context issuer
    No context issued
  2. The throttle gate was evaluated

    The allowed attempt proceeds with verifyCalled=true; passing the throttle gate is not an account-eligibility decision.

    Account/attempt policy
    case=disabled
    Credential verification
    Verify called
    Context issuer
    No context issued
  3. The password outcome remained in scope

    passwordMatches=true is supplied. This predicate alone is insufficient to issue authenticated context for a disabled account.

    Account/attempt policy
    case=disabled
    Credential verification
    passwordMatches=true
    Context issuer
    No context issued
  4. Eligibility and generation controls were preserved

    The nonactive account has contextIssued=false and rehashCommitted=false. A migration need does not open the lifecycle gate.

    Account/attempt policy
    case=disabled
    Credential verification
    Rehash committed=false
    Context issuer
    Context issued=false
  5. Context issuance was withheld

    No new authenticated context is established for the subject. Resource permission remains a separate downstream condition; no action grant is generated from an unissued context.

    Account/attempt policy
    case=disabled
    Credential verification
    No credential migration
    Context issuer
    No authenticated context
  6. The rejection reason remained accurate

    This case separates a known password match from disabled lifecycle state; a generic public message does not erase that distinction in the internal audit.

    Account/attempt policy
    case=disabled
    Credential verification
    Known: disabled policy denial
    Context issuer
    Context issued=false

Blocked attempt

  1. Account and attempt state were separated

    The account is active with attemptAllowed=false. Being active does not mean every attempt can reach the verifier or session issuer.

    Account/attempt policy
    case=throttled
    Credential verification
    No verify outcome
    Context issuer
    No context issued
  2. The throttle gate was evaluated

    A blocked attempt keeps verifyCalled=false and passwordMatches=null. No call is not relabelled as a wrong-password result.

    Account/attempt policy
    case=throttled
    Credential verification
    Verify not called
    Context issuer
    No context issued
  3. The password outcome remained in scope

    passwordMatches is unknown because the verifier was not called. needsRehash=true metadata is not substituted for successful verification.

    Account/attempt policy
    case=throttled
    Credential verification
    passwordMatches=null
    Context issuer
    No context issued
  4. Eligibility and generation controls were preserved

    The blocked attempt has contextIssued=false and rehashCommitted=false. Metadata updates are not an alternate route to authenticated context.

    Account/attempt policy
    case=throttled
    Credential verification
    Rehash committed=false
    Context issuer
    Context issued=false
  5. Context issuance was withheld

    No new authenticated context is established for the subject. Resource permission remains a separate downstream condition; no action grant is generated from an unissued context.

    Account/attempt policy
    case=throttled
    Credential verification
    No credential migration
    Context issuer
    No authenticated context
  6. The rejection reason remained accurate

    This case separates a blocked attempt from an unknown password outcome. A null predicate is not presented as a credential mismatch or attacker attribution.

    Account/attempt policy
    case=throttled
    Credential verification
    Known: blocked; verify not called
    Context issuer
    Context issued=false

ASSUMPTION · BOUNDARY · CONTROL

When credential matching is confused with account eligibility

Normal login combines password verification and account policy in one trusted decision chain. Passing a match boolean directly to a session issuer can skip pending, disabled, or closed state. The effect is a new authenticated context for an account whose access should end; resource authorization remains another boundary.

Explicit eligibility and credential-generation checks block this failure. Design consistency between concurrent disablement and context issuance, and make cached-status freshness assumptions visible. Neither an active UI label nor client-supplied status provides authority.

What does a public response discrepancy expose?

A registration/login response distinguishing “account absent” from “wrong password” adds account-existence information to the public contract. The prerequisite is an endpoint exposing that distinction; an observation does not supply credentials or access permission.

Generic messages repair one layer. Timing and redirect differences may remain; conversely, body differences can reflect locale or product flow rather than account discovery. Inspect the owned response matrix and internal reasons under controlled conditions; selector output does not establish live endpoint efficacy.

3. Blue · Secure development & defense

Read every scenario without motion

Contact verified

  1. The registration state was selected

    Registration starts with initialStatus=pending-contact. An available identifier and allowed candidate do not establish a new authenticated session.

    Account observation
    Pending contact
    Transition/response review
    Transition not examined
    Scope conclusion
    No session evidence
  2. The contact-proof purpose was read

    The purpose is verify-contact; subjectMatches, tokenMatches, unused and lifetimeValid are supplied as true. The contact alias is neither an actual message address nor a usable token.

    Account observation
    verify-contact scope
    Transition/response review
    Contact predicates true
    Scope conclusion
    No session evidence
  3. The terminal proof state was read

    afterTokenState=consumed records one-time use of contact proof in this transition. Contact verification and password verification are different proof types.

    Account observation
    verify-contact scope
    Transition/response review
    Proof consumed
    Scope conclusion
    No session evidence
  4. Account transition and session were separated

    afterStatus becomes active while sessionIssued=false remains. A contact check changing account eligibility does not mean an ordinary authenticated session was issued.

    Account observation
    Account active
    Transition/response review
    Proof consumed
    Scope conclusion
    Session issued=false
  5. No delivery or KDF measurement was added

    The record contains no mail delivery, user inbox observation or actual Argon2id verification. Alias metadata is not converted into production cost or latency values.

    Account observation
    Account active
    Transition/response review
    Transition known
    Scope conclusion
    Delivery/KDF outcome unknown
  6. The registration conclusion was preserved

    For this fixture, contact proof consumption and an active account are known, with no session issuance. Login and resource authorization are evaluated independently of this limited-purpose transition.

    Account observation
    Known: account active
    Transition/response review
    Known: proof consumed
    Scope conclusion
    No ordinary session

Public message

  1. The failure matrix was selected

    Review uses publicFailures for absent-account, wrong-password, pending-contact and disabled-account. Internal reasons and public responses serve different purposes.

    Account observation
    4 failure rows
    Transition/response review
    Public bodies not compared
    Scope conclusion
    No timing evidence
  2. Body equality was compared

    All four rows use publicBody Authentication could not be completed. This shows equality of body text; equality of HTTP status or redirects is not yet supplied.

    Account observation
    4 failure rows
    Transition/response review
    Public body equal
    Scope conclusion
    Status/redirect unknown
  3. The internal-reason distinction was preserved

    Absent, wrong, pending and disabled remain separate reasons despite their shared public message. Generic text does not require losing the decision source in the audit.

    Account observation
    4 distinct internal reasons
    Transition/response review
    Same public body
    Scope conclusion
    Status/redirect unknown
  4. Context issuance was read separately

    All four failure rows have contextIssued=false. A generic public message is not an assumption concealing internal issuance; the field is checked separately.

    Account observation
    4 failure rows
    Transition/response review
    Context issued=false ×4
    Scope conclusion
    No timing evidence
  5. Timing equality was not inferred

    These records contain no duration measurements, so they establish neither timing equality nor production enumeration resistance. Locale and envelope observations also lie outside the body comparison.

    Account observation
    Body contract known
    Transition/response review
    Internal reasons retained
    Scope conclusion
    UNKNOWN: timing/envelope
  6. The response review was bounded

    One public body contract is supported, with no context issuance. The entire authentication flow is not called uniform without further timing/status/redirect observations.

    Account observation
    Known: public body equal
    Transition/response review
    Known: no context issuance
    Scope conclusion
    UNKNOWN: other response differences

IMPLEMENTATION · SECURITY CONTROL · VERIFICATION

Relate account transitions to verification decisions

Sanitized application audit can include subjectRef, accountStatus, contactVerified, attemptAllowed, passwordVerified, contextIssued, and verifierGeneration. These fields are not standard log events. Candidate passwords, verifier bytes, and verification tokens stay out of telemetry.

The XPath selects an integrity discrepancy where contextIssued=true and accountStatus is not active. Benign active login selects zero; a supplied disabled-context issuance fixture selects one. Missing status means insufficient evidence. Administrative disablement, closure, and verification delay can be normal explanations; a row does not prove a malicious actor.

Separate response review from rate-limit validation

The public-response matrix expresses absent/wrong/pending/disabled conditions under one contract. Compare text, status, and content envelopes separately. Timing equivalence needs controlled measurements and load conditions; the boolean fixture does not perform those measurements.

For the throttle gate, observe that a blocked attempt advances to neither verification nor context issuance. Node distribution, unavailable limit state, and legitimate retry experience require separate checks; one IP blacklist does not constitute the full design.

Durable controls for verifier migration and disablement

Inventory credential generations and cost-policy metadata. Benchmark the capacity effect of migration, and protect successful-verification rehashing against stale-reset overwrites. Salt/cost aliases must not be copied as production parameters.

Document how disablement/closure affects active credentials, session stores/caches, and recovery eligibility. Preserve necessary subject correlation while reducing unnecessary personal data. Permission to rebind a verification contact does not follow automatically from an existing session.

NOW APPLY IT TO THE CASE

Why does a password match not override account lifecycle?

Contact verification, throttling, password verification and context issuance are different transitions. The scope of equality provided by a generic message is separately bounded.

Separate decisions for verified actor, tenant relation and requested action in the case. Do not substitute authentication or proof success for object permission.

Open the related case file ↗

READ THE EXAMPLE TOGETHER

Account-state and verifier-metadata ledger

Select a field and examine what it supports and where the evidence ends.

Read the raw artifact and field explanations
{
  "subjectRef": "subject-duru",
  "contactAlias": "CONTACT_ALIAS_ONLY",
  "accountStates": [
    "pending-contact",
    "active",
    "disabled",
    "closed"
  ],
  "credentialRecord": {
    "algorithm": "Argon2id illustration",
    "salt": "SALT_ALIAS_NOT_BYTES",
    "costProfile": "BENCHMARKED_POLICY_ALIAS_NOT_PARAMETERS",
    "oldVerifier": "VERIFIER_OLD_ALIAS_NOT_A_HASH",
    "currentVerifier": "VERIFIER_CURRENT_ALIAS_NOT_A_HASH",
    "plaintextStored": false,
    "generation": "credential-generation-a"
  },
  "registration": {
    "identifierAvailable": true,
    "candidateAllowed": true,
    "initialStatus": "pending-contact",
    "contactTokenAlias": "VERIFY_CONTACT_ALIAS_ONLY",
    "purpose": "verify-contact",
    "subjectMatches": true,
    "tokenMatches": true,
    "unused": true,
    "lifetimeValid": true,
    "afterTokenState": "consumed",
    "afterStatus": "active",
    "sessionIssued": false
  },
  "loginRows": [
    {
      "case": "normal",
      "accountStatus": "active",
      "attemptAllowed": true,
      "passwordMatches": true,
      "needsRehash": true,
      "verifyCalled": true,
      "contextIssued": true,
      "rehashCommitted": true
    },
    {
      "case": "disabled",
      "accountStatus": "disabled",
      "attemptAllowed": true,
      "passwordMatches": true,
      "needsRehash": true,
      "verifyCalled": true,
      "contextIssued": false,
      "rehashCommitted": false
    },
    {
      "case": "throttled",
      "accountStatus": "active",
      "attemptAllowed": false,
      "passwordMatches": null,
      "needsRehash": true,
      "verifyCalled": false,
      "contextIssued": false,
      "rehashCommitted": false
    }
  ],
  "publicFailures": [
    {
      "internalReason": "absent-account",
      "publicBody": "Authentication could not be completed",
      "contextIssued": false
    },
    {
      "internalReason": "wrong-password",
      "publicBody": "Authentication could not be completed",
      "contextIssued": false
    },
    {
      "internalReason": "pending-contact",
      "publicBody": "Authentication could not be completed",
      "contextIssued": false
    },
    {
      "internalReason": "disabled-account",
      "publicBody": "Authentication could not be completed",
      "contextIssued": false
    }
  ],
  "selector": {
    "language": "XPath",
    "xml": "<audit><login accountStatus=\"active\" contextIssued=\"true\"/></audit>",
    "xpath": "count(/audit/login[@contextIssued=\"true\" and @accountStatus!=\"active\"])",
    "expected": 0,
    "positiveFixtureXml": "<audit><login accountStatus=\"disabled\" contextIssued=\"true\"/></audit>",
    "positiveExpected": 1,
    "scope": "Local account/context integrity; no production log, latency, KDF or mail measurement.",
    "missingFieldBehavior": "Selection requires declared fields. Missing fields mean insufficient evidence; a zero count does not prove control efficacy."
  }
}
accountStatus / contactVerified
Product eligibility and communication-endpoint control, not identity proofing.
credentialRecord
Salt/cost/verifier aliases explain an established-library record; neither real hashes nor production cost settings.
attemptAllowed / verifyCalled
A blocked throttle gate leaves verification unexecuted; passwordMatches=null is unknown.
contextIssued / rehashCommitted
Context issuance and verifier upgrade are separate outcomes; generation races require separate safeguards.
publicFailures / selector
A public contract separated from internal reasons and an owned XML integrity selector.

Account-policy states are authored. Password/hash/token/contact aliases are nonfunctional. Mail, real KDF, session issuance, distributed limiting, and timing measurements are not executed; predicates represent supplied trusted results.

Two reality checks

A matching password must permit a disabled account to log in.

Credential matching and account eligibility are separate gates; disabled state can block new context issuance.

One generic error message proves enumeration risk is eliminated.

Status, timing, size, redirects, and other lifecycle endpoints can differ; text equality is only one piece of evidence.

YOUR TURN

Work through this story.

Predict the outcome, find the evidence boundary, and explain it in your own words.

The account is disabled, but passwordMatches=true. Are a login context and rehash commit produced?

  1. No. contextIssued=false and rehashCommitted=false.
  2. Yes. A matching password automatically reactivates a disabled account.
Read the explained answer

No. contextIssued=false and rehashCommitted=false. — This policy issues no context for a disabled account. A matching password does not remove the account-state condition.

Contact verification activated the account. Was an authenticated browser session also issued?

Selected lesson artifact fields; teaching example

registration.initialStatus
pending-contact
registration.purpose
verify-contact
registration.afterTokenState
consumed
registration.afterStatus
active
registration.sessionIssued
false
  1. Yes. An active account automatically creates all browser sessions.
  2. No. The account is active, but sessionIssued=false.
Read the explained answer

No. The account is active, but sessionIssued=false. — The verify-contact proof is consumed for this lifecycle transition. Account activation is not session issuance.

PublicFailures uses one body for four internal reasons. Why does that not prove identical responses or timing, and what do the KDF aliases not measure?

Read the explained answer

The common public message illustrates limiting account-state disclosure. It alone does not prove equal status, timing, retry behavior or other responses. Salt/verifier/cost aliases explain the Argon2id role but are not real hash bytes or benchmark parameters. Internal audit may retain a limited reason without placing passwords or credential material in the public body.

□ I did not treat one public body as proof of timing equality.

□ I did not treat KDF aliases as real hashes or calibrated parameters.

Open the application console · request / response / browser
Also open request, URL, cookie and DOM examples

Sources and scope