One Time Code Validator
Validates one-time codes with attempt limits, expiry checks, and replay prevention.
Function signature
ogValidateOneTimeCode(submitted_code, stored_hash, expires_at, attempt_count = 0, max_attempts = 5)
Categories
- Security
Parameters
submitted_codeSubmitted one-time code.stored_hashStored password_hash() value for the code.expires_atExpiration timestamp.attempt_countCurrent failed attempt count.max_attemptsMaximum permitted attempts.Return value
Short public-safe status message.
- valid
- invalidate
- attempt_count
Compatibility
Existing function name and call order preserved; metadata signature corrected to source.
Minimum PHP version: 7.4
Security notes
Validate request method, identity, permissions, and caller-owned allowlists before use; keep secrets out of public output.
Code
<?php
/*
* Copyright (c) 2026 Jeffery L. Paris <jparis@phpog.com>.
* Free for personal and internal use. Paid project use requires visible credit
* to Jeffery L. Paris. Corporate use requires a paid license fee unless a
* separate written license states otherwise.
*/
/**
* Validates one-time codes with attempt limits, expiry checks, and replay prevention.
*
* Primary use case: Verify emailed codes or sensitive admin actions.
* Typical inputs: submitted code, stored hash, attempt count.
* Typical output: validation result.
*
* Implementation note: Use constant-time verification behavior and invalidate after success.
*
* @param string $submitted_code Submitted one-time code.
* @param string $stored_hash Stored password_hash() value for the code.
* @param int $expires_at Expiration timestamp.
* @param int $attempt_count Current failed attempt count.
* @param int $max_attempts Maximum permitted attempts.
* @return array Structured result data with success, message, and data keys.
*/
function ogValidateOneTimeCode($submitted_code, $stored_hash, $expires_at, $attempt_count = 0, $max_attempts = 5) {
$result = array(
'success' => false,
'message' => '',
'data' => array()
);
$submitted_code = strtoupper(trim((string)$submitted_code));
$stored_hash = (string)$stored_hash;
$expires_at = (int)$expires_at;
$attempt_count = (int)$attempt_count;
$max_attempts = (int)$max_attempts;
if ($max_attempts < 1) {
$max_attempts = 1;
}
if (empty($submitted_code) || empty($stored_hash)) {
$result['message'] = 'Verification code data is incomplete.';
return $result;
}
if ($attempt_count >= $max_attempts) {
$result['message'] = 'Too many verification attempts.';
$result['data'] = array('valid' => false, 'invalidate' => true);
return $result;
}
if ($expires_at < time()) {
$result['message'] = 'Verification code expired.';
$result['data'] = array('valid' => false, 'invalidate' => true);
return $result;
}
if (!password_verify($submitted_code, $stored_hash)) {
$result['message'] = 'Verification code mismatch.';
$result['data'] = array('valid' => false, 'invalidate' => false, 'attempt_count' => $attempt_count + 1);
return $result;
}
$result['success'] = true;
$result['message'] = 'Verification code accepted.';
$result['data'] = array('valid' => true, 'invalidate' => true);
return $result;
}