Skip to content
← Back to Functions
Code

Legacy Password Migration Check

Verifies modern password hashes and upgrades legacy hashes after successful authentication.

Function signature

ogVerifyAndUpgradePassword(password, stored_hash, legacy_options = array())

Categories

  • Security

Parameters

passwordSubmitted password.stored_hashCurrent stored password hash.legacy_optionsOptional legacy algorithm and salt metadata. Recognized keys: `algorithm`, `salt`.

Return value

Short public-safe status message.

  • verified
  • new_hash
  • needs_upgrade
  • legacy_match

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.
 */

/**
 * Verifies a password and prepares a modern replacement hash when needed.
 *
 * Modern hashes are checked with password_verify(). Legacy md5/sha1 checks are
 * included only for one-time migration after a successful login, never for new
 * password creation.
 *
 * @param string $password Submitted password.
 * @param string $stored_hash Current stored password hash.
 * @param array $legacy_options Optional legacy algorithm and salt metadata.
 * @return array Verification status and replacement hash when needed.
 */
function ogVerifyAndUpgradePassword($password, $stored_hash, $legacy_options = array()) {
	$result = array(
		'success' => false,
		'message' => '',
		'data' => array(
			'verified' => false,
			'new_hash' => '',
			'needs_upgrade' => false,
			'legacy_match' => false
		)
	);

	$password = (string)$password;
	$stored_hash = (string)$stored_hash;

	if (!is_array($legacy_options)) {
		$legacy_options = array();
	}

	if (empty($password) || empty($stored_hash)) {
		$result['message'] = 'Password and stored hash are required.';
		return $result;
	}

	$verified = false;
	$legacy_match = false;
	$new_hash = '';

	if (password_verify($password, $stored_hash)) {
		$verified = true;
		if (password_needs_rehash($stored_hash, PASSWORD_DEFAULT)) {
			$new_hash = password_hash($password, PASSWORD_DEFAULT);
		}
	} else {
		$algorithm = '';
		if (!empty($legacy_options['algorithm'])) {
			$algorithm = strtolower(trim((string)$legacy_options['algorithm']));
		}

		$salt = '';
		if (!empty($legacy_options['salt'])) {
			$salt = (string)$legacy_options['salt'];
		}

		$legacy_candidate = '';
		if ($algorithm == 'md5') {
			$legacy_candidate = md5($password);
		} elseif ($algorithm == 'sha1') {
			$legacy_candidate = sha1($password);
		} elseif ($algorithm == 'md5_salt_password') {
			$legacy_candidate = md5($salt . $password);
		} elseif ($algorithm == 'sha1_salt_password') {
			$legacy_candidate = sha1($salt . $password);
		}

		if (!empty($legacy_candidate) && hash_equals($stored_hash, $legacy_candidate)) {
			$verified = true;
			$legacy_match = true;
			$new_hash = password_hash($password, PASSWORD_DEFAULT);
		}
	}

	$result['success'] = true;
	$result['message'] = 'Password verification completed.';
	$result['data'] = array(
		'verified' => $verified,
		'new_hash' => $new_hash,
		'needs_upgrade' => !empty($new_hash),
		'legacy_match' => $legacy_match
	);

	return $result;
}