478 lines
22 KiB
PHP
478 lines
22 KiB
PHP
<?php
|
|
/**
|
|
* Shared public authentication REST operations.
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
namespace AKSRT\Admin;
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
trait UserControllerAuthPhoneOperations {
|
|
|
|
private function delete_oauth_registration_user(int $user_id): void {
|
|
if ($user_id <= 0) {
|
|
return;
|
|
}
|
|
require_once ABSPATH . 'wp-admin/includes/user.php';
|
|
wp_delete_user($user_id);
|
|
}
|
|
|
|
private function auth_payload(\WP_REST_Request $request): array {
|
|
$params = $this->get_json_params($request);
|
|
return is_array($params) ? $params : [];
|
|
}
|
|
|
|
private function auth_verify_rest_nonce(\WP_REST_Request $request, array $payload): bool {
|
|
$nonce = sanitize_text_field((string) ($request->get_header('X-WP-Nonce') ?: ($payload['_wpnonce'] ?? '')));
|
|
return $nonce !== '' && (bool) wp_verify_nonce($nonce, 'wp_rest');
|
|
}
|
|
|
|
private function auth_require_captcha(string $scene, array $payload): ?\WP_REST_Response {
|
|
$result = aksrt_require_captcha($scene, $payload);
|
|
if ($result === true) {
|
|
return null;
|
|
}
|
|
|
|
if (is_wp_error($result)) {
|
|
$data = $result->get_error_data();
|
|
$status = is_array($data) ? (int) ($data['status'] ?? 400) : 400;
|
|
$message = $result->get_error_message() ?: __('人机验证失败', 'aksrt');
|
|
return new \WP_REST_Response([
|
|
'success' => false,
|
|
'code' => $result->get_error_code() ?: 'aksrt_captcha_failed',
|
|
'message' => $message,
|
|
], $status > 0 ? $status : 400);
|
|
}
|
|
|
|
return new \WP_REST_Response([
|
|
'success' => false,
|
|
'code' => 'aksrt_captcha_failed',
|
|
'message' => __('人机验证失败', 'aksrt'),
|
|
], 400);
|
|
}
|
|
|
|
private function auth_rate_key(string $action, string $subject = ''): string {
|
|
$ip = sanitize_text_field((string) ($_SERVER['REMOTE_ADDR'] ?? '0.0.0.0'));
|
|
return 'aksrt_auth_rate_' . md5($action . '|' . strtolower($subject) . '|' . $ip);
|
|
}
|
|
|
|
private function auth_rate_limited(string $action, string $subject = '', int $limit = 8, int $ttl = 600): bool {
|
|
$key = $this->auth_rate_key($action, $subject);
|
|
$count = (int) get_transient($key);
|
|
if ($count >= $limit) {
|
|
return true;
|
|
}
|
|
set_transient($key, $count + 1, $ttl);
|
|
return false;
|
|
}
|
|
|
|
private function auth_code_key(string $scene, string $target): string {
|
|
return 'aksrt_auth_code_' . md5($scene . '|' . strtolower(trim($target)));
|
|
}
|
|
|
|
private function auth_store_email_code(string $scene, string $target, string $code): void {
|
|
set_transient($this->auth_code_key($scene, $target), [
|
|
'hash' => wp_hash_password($code),
|
|
'attempts' => 0,
|
|
], 10 * MINUTE_IN_SECONDS);
|
|
}
|
|
|
|
private function auth_consume_code(string $scene, string $target, string $code, string $channel): bool {
|
|
if ($channel === 'phone') {
|
|
if (!class_exists('\AKSRT\Sms\SMS')) {
|
|
return false;
|
|
}
|
|
$result = \AKSRT\Sms\SMS::get_instance()->verify_code($target, $code);
|
|
return !empty($result['success']);
|
|
}
|
|
|
|
$key = $this->auth_code_key($scene, $target);
|
|
$stored = get_transient($key);
|
|
if (!is_array($stored) || empty($stored['hash'])) {
|
|
return false;
|
|
}
|
|
$attempts = (int) ($stored['attempts'] ?? 0) + 1;
|
|
if ($attempts > 6 || !wp_check_password($code, (string) $stored['hash'])) {
|
|
$stored['attempts'] = $attempts;
|
|
set_transient($key, $stored, 10 * MINUTE_IN_SECONDS);
|
|
return false;
|
|
}
|
|
delete_transient($key);
|
|
return true;
|
|
}
|
|
|
|
private function auth_channel(string $target, string $requested = ''): string {
|
|
if ($requested === 'phone' || preg_match('/^1[3-9]\d{9}$/', $target)) {
|
|
return 'phone';
|
|
}
|
|
return 'email';
|
|
}
|
|
|
|
private function auth_channel_allowed(string $configured, string $channel): bool {
|
|
return $configured === 'email_phone' || $configured === $channel;
|
|
}
|
|
|
|
private function auth_safe_redirect(string $requested, string $fallback = ''): string {
|
|
$fallback = $fallback !== '' ? $fallback : home_url('/');
|
|
return wp_validate_redirect($requested, $fallback);
|
|
}
|
|
|
|
private function auth_registration_error(string $error): string {
|
|
if (str_contains($error, '已被') || str_contains($error, '已注册') || str_contains($error, '已使用')) {
|
|
return __('注册信息不可用,请检查后重试', 'aksrt');
|
|
}
|
|
return $error;
|
|
}
|
|
|
|
private function auth_find_user(string $target, string $channel) {
|
|
if ($channel === 'phone') {
|
|
$users = get_users([
|
|
'meta_key' => 'aksrt_phone',
|
|
'meta_value' => $target,
|
|
'number' => 1,
|
|
]);
|
|
return $users[0] ?? null;
|
|
}
|
|
return is_email($target)
|
|
? get_user_by('email', sanitize_email($target))
|
|
: get_user_by('login', sanitize_user($target, true));
|
|
}
|
|
|
|
public function auth_code(\WP_REST_Request $request): \WP_REST_Response {
|
|
$payload = $this->auth_payload($request);
|
|
if (!$this->auth_verify_rest_nonce($request, $payload)) {
|
|
return $this->error(__('安全验证失败,请刷新页面重试', 'aksrt'), 403);
|
|
}
|
|
|
|
$scene = sanitize_key((string) ($payload['scene'] ?? ''));
|
|
$target = trim(sanitize_text_field((string) ($payload['target'] ?? '')));
|
|
$channel = $this->auth_channel($target, sanitize_key((string) ($payload['channel'] ?? '')));
|
|
$obk = sanitize_text_field((string) ($payload['obk'] ?? ''));
|
|
$oauth_bind = $obk !== '' && class_exists('\AKSRT\Auth\Service') && is_array(\AKSRT\Auth\Service::get_oauth_bind_context($obk));
|
|
if (!in_array($scene, ['login', 'register', 'resetpassword'], true)) {
|
|
return $this->error(__('请求类型无效', 'aksrt'));
|
|
}
|
|
if (class_exists('\AKSRT\Settings\Settings')) {
|
|
if (\AKSRT\Settings\Settings::close_sign()) return $this->error(__('登录注册已关闭', 'aksrt'), 403);
|
|
if (($scene === 'login' || $scene === 'resetpassword') && \AKSRT\Settings\Settings::close_signin() && !$oauth_bind) return $this->error(__('当前登录方式已关闭', 'aksrt'), 403);
|
|
if ($scene === 'register' && !\AKSRT\Settings\Settings::registration_enabled() && !$oauth_bind) return $this->error(__('当前已关闭新用户注册', 'aksrt'), 403);
|
|
$type = $scene === 'login' ? \AKSRT\Settings\Settings::passwordless_login_type() : ($scene === 'register' ? \AKSRT\Settings\Settings::signup_verification_type() : \AKSRT\Settings\Settings::reset_password_verification_type());
|
|
$enabled = $scene === 'login' ? \AKSRT\Settings\Settings::passwordless_login_enabled() : ($scene === 'register' ? \AKSRT\Settings\Settings::signup_verification_enabled() : true);
|
|
if (!$enabled || ($type !== 'email_phone' && $type !== $channel)) return $this->error(__('当前验证方式不可用', 'aksrt'), 403);
|
|
}
|
|
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !preg_match('/^1[3-9]\d{9}$/', $target))) {
|
|
return $this->error(__('请输入有效的邮箱或手机号', 'aksrt'));
|
|
}
|
|
if ($this->auth_rate_limited('code:' . $scene, $target, 5, HOUR_IN_SECONDS)) {
|
|
return $this->error(__('请求过于频繁,请稍后再试', 'aksrt'), 429);
|
|
}
|
|
$captcha_scene = $scene === 'resetpassword' ? 'lostpassword' : $scene;
|
|
$captcha_error = $this->auth_require_captcha($captcha_scene, $payload);
|
|
if ($captcha_error) {
|
|
return $captcha_error;
|
|
}
|
|
|
|
$generic = __('如果信息有效,验证码将发送到该账号', 'aksrt');
|
|
if ($scene !== 'register' && !$this->auth_find_user($target, $channel)) {
|
|
return $this->success(['message' => $generic, 'countdown' => 60]);
|
|
}
|
|
|
|
if ($channel === 'phone') {
|
|
if (!class_exists('\AKSRT\Sms\SMS')) {
|
|
return $this->error(__('短信服务不可用', 'aksrt'), 503);
|
|
}
|
|
$result = \AKSRT\Sms\SMS::get_instance()->send_code($target);
|
|
if (empty($result['success'])) {
|
|
return $this->error((string) ($result['message'] ?? __('验证码发送失败', 'aksrt')));
|
|
}
|
|
return $this->success(['message' => $generic, 'countdown' => (int) ($result['remaining_time'] ?? 60)]);
|
|
}
|
|
|
|
$code = (string) random_int(100000, 999999);
|
|
$scene_event = [
|
|
'login' => 'login_code',
|
|
'register' => 'reg_verify_code',
|
|
'resetpassword' => 'password_reset',
|
|
][$scene] ?? '';
|
|
$user = $this->auth_find_user($target, $channel);
|
|
$display_name = $user instanceof \WP_User ? $user->display_name : $target;
|
|
$site_name = get_bloginfo('name');
|
|
$placeholders = [
|
|
'{username}' => $display_name,
|
|
'{site_name}' => $site_name,
|
|
'{code}' => $code,
|
|
];
|
|
$sent = $scene_event !== '' && class_exists('\\AKSRT\\Settings\\Settings')
|
|
? \AKSRT\Settings\Settings::send_event_email($scene_event, $target, $placeholders)
|
|
: false;
|
|
|
|
if (!$sent) {
|
|
delete_transient($this->auth_code_key($scene, $target));
|
|
if ($scene !== 'register') {
|
|
return $this->success(['message' => $generic, 'countdown' => 60]);
|
|
}
|
|
return $this->error(__('验证码发送失败,请稍后再试', 'aksrt'), 500);
|
|
}
|
|
$this->auth_store_email_code($scene, $target, $code);
|
|
return $this->success(['message' => $generic, 'countdown' => 60]);
|
|
|
|
}
|
|
|
|
public function auth_login(\WP_REST_Request $request): \WP_REST_Response {
|
|
$payload = $this->auth_payload($request);
|
|
if (!$this->auth_verify_rest_nonce($request, $payload)) {
|
|
return $this->error(__('安全验证失败,请刷新页面重试', 'aksrt'), 403);
|
|
}
|
|
$target = trim(sanitize_text_field((string) ($payload['target'] ?? $payload['log'] ?? '')));
|
|
$obk = sanitize_text_field((string) ($payload['obk'] ?? ''));
|
|
$oauth_data = $obk !== '' && class_exists('\AKSRT\Auth\Service')
|
|
? \AKSRT\Auth\Service::get_oauth_bind_context($obk)
|
|
: null;
|
|
if ($obk !== '' && !$oauth_data) {
|
|
return $this->error(__('绑定会话已过期,请重新使用第三方登录', 'aksrt'), 410);
|
|
}
|
|
if (
|
|
class_exists('\AKSRT\Settings\Settings')
|
|
&& (\AKSRT\Settings\Settings::close_sign() || (!$oauth_data && \AKSRT\Settings\Settings::close_signin()))
|
|
) {
|
|
return $this->error(__('当前登录方式已关闭', 'aksrt'), 403);
|
|
}
|
|
if ($target === '' || (preg_match('/^1[3-9]\d{9}$/', $target) && empty($payload['code']) && class_exists('\AKSRT\Settings\Settings') && !\AKSRT\Settings\Settings::signin_phone_enabled())) {
|
|
return $this->error(__('账号或密码错误', 'aksrt'), 401);
|
|
}
|
|
if ($this->auth_rate_limited('login', $target)) {
|
|
return $this->error(__('请求过于频繁,请稍后再试', 'aksrt'), 429);
|
|
}
|
|
$captcha_error = $this->auth_require_captcha($oauth_data ? 'bind' : 'login', $payload);
|
|
if ($captcha_error) {
|
|
return $captcha_error;
|
|
}
|
|
$redirect = $this->auth_safe_redirect((string) ($payload['redirect_to'] ?? ''), home_url('/'));
|
|
|
|
if (!empty($payload['code'])) {
|
|
if (!class_exists('\AKSRT\Settings\Settings') || !\AKSRT\Settings\Settings::passwordless_login_enabled()) {
|
|
return $this->error(__('当前未开启免密登录', 'aksrt'));
|
|
}
|
|
$channel = $this->auth_channel($target, sanitize_key((string) ($payload['channel'] ?? '')));
|
|
$configured = \AKSRT\Settings\Settings::passwordless_login_type();
|
|
if (!$this->auth_channel_allowed($configured, $channel)) {
|
|
return $this->error(__('当前验证方式不可用', 'aksrt'), 403);
|
|
}
|
|
$user = $this->auth_find_user($target, $channel);
|
|
if (!$user || !$this->auth_consume_code('login', $target, sanitize_text_field((string) $payload['code']), $channel)) {
|
|
return $this->error(__('账号或验证码错误', 'aksrt'), 401);
|
|
}
|
|
if ($oauth_data) {
|
|
$oauth_data = \AKSRT\Auth\Service::claim_oauth_bind_context($obk);
|
|
if (!$oauth_data) {
|
|
return $this->error(__('绑定会话已被使用,请重新授权', 'aksrt'), 410);
|
|
}
|
|
$binding = \AKSRT\Auth\Service::finalize_oauth_binding((int) $user->ID, $oauth_data, $obk);
|
|
if (is_wp_error($binding)) {
|
|
return $this->error($binding->get_error_message(), 409);
|
|
}
|
|
\AKSRT\Auth\OAuthAccountService::complete_login($user, !empty($payload['rememberme']));
|
|
$redirect = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : $redirect;
|
|
return $this->success(['message' => __('账号绑定成功', 'aksrt'), 'redirect' => $redirect]);
|
|
}
|
|
wp_set_current_user((int) $user->ID);
|
|
wp_set_auth_cookie((int) $user->ID, !empty($payload['rememberme']));
|
|
do_action('wp_login', $user->user_login, $user);
|
|
return $this->success(['message' => __('登录成功', 'aksrt'), 'redirect' => $redirect]);
|
|
}
|
|
|
|
if (!class_exists('\AKSRT\Auth\Service')) {
|
|
return $this->error(__('认证服务不可用', 'aksrt'), 500);
|
|
}
|
|
$payload['log'] = $target;
|
|
$payload['pwd'] = (string) ($payload['password'] ?? $payload['pwd'] ?? '');
|
|
$payload['redirect_to'] = $redirect;
|
|
if ($oauth_data) {
|
|
$resolved_login = \AKSRT\Auth\Service::resolve_email_to_username($target);
|
|
$user = wp_authenticate($resolved_login, (string) ($payload['password'] ?? $payload['pwd'] ?? ''));
|
|
if (is_wp_error($user) || !$user instanceof \WP_User) {
|
|
return $this->error(__('账号或密码错误', 'aksrt'), 401);
|
|
}
|
|
$oauth_data = \AKSRT\Auth\Service::claim_oauth_bind_context($obk);
|
|
if (!$oauth_data) {
|
|
return $this->error(__('绑定会话已被使用,请重新授权', 'aksrt'), 410);
|
|
}
|
|
$binding = \AKSRT\Auth\Service::finalize_oauth_binding((int) $user->ID, $oauth_data, $obk);
|
|
if (is_wp_error($binding)) {
|
|
return $this->error($binding->get_error_message(), 409);
|
|
}
|
|
\AKSRT\Auth\OAuthAccountService::complete_login($user, !empty($payload['rememberme']));
|
|
$redirect = function_exists('aksrt_get_user_center_url') ? aksrt_get_user_center_url() : $redirect;
|
|
return $this->success(['message' => __('账号绑定成功', 'aksrt'), 'redirect' => $redirect]);
|
|
}
|
|
$result = \AKSRT\Auth\Service::login_user($payload);
|
|
if (empty($result['success'])) {
|
|
return $this->error(__('账号或密码错误', 'aksrt'), 401);
|
|
}
|
|
return $this->success(['message' => $oauth_data ? __('账号绑定成功', 'aksrt') : __('登录成功', 'aksrt'), 'redirect' => $redirect]);
|
|
}
|
|
|
|
public function auth_register(\WP_REST_Request $request): \WP_REST_Response {
|
|
$payload = $this->auth_payload($request);
|
|
if (!$this->auth_verify_rest_nonce($request, $payload)) {
|
|
return $this->error(__('安全验证失败,请刷新页面重试', 'aksrt'), 403);
|
|
}
|
|
$obk = sanitize_text_field((string) ($payload['obk'] ?? ''));
|
|
$oauth_data = $obk !== '' && class_exists('\AKSRT\Auth\Service')
|
|
? \AKSRT\Auth\Service::get_oauth_bind_context($obk)
|
|
: null;
|
|
if ($obk !== '' && !$oauth_data) {
|
|
return $this->error(__('绑定会话已过期,请重新使用第三方登录', 'aksrt'), 410);
|
|
}
|
|
if (class_exists('\AKSRT\Settings\Settings') && (
|
|
\AKSRT\Settings\Settings::close_sign()
|
|
|| ($oauth_data ? !\AKSRT\Settings\Settings::oauth_registration_enabled() : !\AKSRT\Settings\Settings::registration_enabled())
|
|
)) {
|
|
return $this->error(__('当前已关闭新用户注册', 'aksrt'), 403);
|
|
}
|
|
if ($this->auth_rate_limited('register')) {
|
|
return $this->error(__('请求过于频繁,请稍后再试', 'aksrt'), 429);
|
|
}
|
|
$target = trim(sanitize_text_field((string) ($payload['target'] ?? $payload['user_email'] ?? $payload['phone'] ?? '')));
|
|
$channel = $this->auth_channel($target, sanitize_key((string) ($payload['channel'] ?? '')));
|
|
$signup_verification = class_exists('\AKSRT\Settings\Settings') && \AKSRT\Settings\Settings::signup_verification_enabled();
|
|
$configured = $signup_verification ? \AKSRT\Settings\Settings::signup_verification_type() : 'email';
|
|
if (!$this->auth_channel_allowed($configured, $channel)) {
|
|
return $this->error(__('当前注册方式不可用', 'aksrt'), 403);
|
|
}
|
|
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !preg_match('/^1[3-9]\d{9}$/', $target))) {
|
|
return $this->error(__('请输入有效的邮箱或手机号', 'aksrt'));
|
|
}
|
|
$captcha_error = $this->auth_require_captcha('register', $payload);
|
|
if ($captcha_error) {
|
|
return $captcha_error;
|
|
}
|
|
$verification_completed = false;
|
|
if ($signup_verification) {
|
|
if (!$this->auth_consume_code('register', $target, sanitize_text_field((string) ($payload['code'] ?? '')), $channel)) {
|
|
return $this->error(__('验证码错误或已过期', 'aksrt'));
|
|
}
|
|
$verification_completed = true;
|
|
}
|
|
if (!class_exists('\AKSRT\Auth\Service')) {
|
|
return $this->error(__('认证服务不可用', 'aksrt'), 500);
|
|
}
|
|
if ($channel === 'phone') {
|
|
$payload['phone'] = $target;
|
|
$result = \AKSRT\Auth\Service::register_by_phone($payload, $verification_completed, !empty($oauth_data));
|
|
if (empty($result['success'])) {
|
|
return $this->error($this->auth_registration_error((string) ($result['error'] ?? __('注册失败', 'aksrt'))));
|
|
}
|
|
$user_id = (int) ($result['user_id'] ?? 0);
|
|
if ($oauth_data && $user_id > 0) {
|
|
$binding = \AKSRT\Auth\Service::finalize_oauth_binding($user_id, $oauth_data, $obk);
|
|
if (is_wp_error($binding)) {
|
|
$this->delete_oauth_registration_user($user_id);
|
|
return $this->error($binding->get_error_message(), 409);
|
|
}
|
|
$user = get_user_by('id', $user_id);
|
|
if ($user instanceof \WP_User) {
|
|
\AKSRT\Auth\OAuthAccountService::complete_login($user, true);
|
|
}
|
|
}
|
|
return $this->success(['message' => $oauth_data ? __('注册并绑定成功', 'aksrt') : __('注册成功', 'aksrt'), 'redirect' => $result['redirect'] ?? home_url('/'), 'notice' => (string) ($result['notice'] ?? '')]);
|
|
}
|
|
$payload['user_email'] = sanitize_email($target);
|
|
$payload['user_pass'] = (string) ($payload['password'] ?? '');
|
|
$payload['user_pass_confirm'] = (string) ($payload['password_confirm'] ?? $payload['password'] ?? '');
|
|
$result = \AKSRT\Auth\Service::register_user($payload, $verification_completed, !empty($oauth_data));
|
|
if (empty($result['success'])) {
|
|
return $this->error($this->auth_registration_error((string) ($result['error'] ?? __('注册失败', 'aksrt'))));
|
|
}
|
|
$user_id = (int) ($result['user_id'] ?? 0);
|
|
if ($oauth_data && $user_id > 0) {
|
|
$binding = \AKSRT\Auth\Service::finalize_oauth_binding($user_id, $oauth_data, $obk);
|
|
if (is_wp_error($binding)) {
|
|
$this->delete_oauth_registration_user($user_id);
|
|
return $this->error($binding->get_error_message(), 409);
|
|
}
|
|
$user = get_user_by('id', $user_id);
|
|
if ($user instanceof \WP_User) {
|
|
if ($channel === 'email' && $verification_completed) {
|
|
\AKSRT\Auth\Service::mark_verified_email($user_id);
|
|
}
|
|
\AKSRT\Auth\OAuthAccountService::complete_login($user, true);
|
|
}
|
|
}
|
|
return $this->success([
|
|
'message' => $oauth_data ? __('注册并绑定成功', 'aksrt') : __('注册成功', 'aksrt'),
|
|
'redirect' => $result['redirect'] ?? home_url('/'),
|
|
'notice' => (string) ($result['notice'] ?? ''),
|
|
]);
|
|
}
|
|
|
|
public function auth_reset_password(\WP_REST_Request $request): \WP_REST_Response {
|
|
$payload = $this->auth_payload($request);
|
|
if (!$this->auth_verify_rest_nonce($request, $payload)) {
|
|
return $this->error(__('安全验证失败,请刷新页面重试', 'aksrt'), 403);
|
|
}
|
|
$target = trim(sanitize_text_field((string) ($payload['target'] ?? '')));
|
|
$channel = $this->auth_channel($target, sanitize_key((string) ($payload['channel'] ?? '')));
|
|
if (class_exists('\AKSRT\Settings\Settings')) {
|
|
if (\AKSRT\Settings\Settings::close_sign() || \AKSRT\Settings\Settings::close_signin() || !$this->auth_channel_allowed(\AKSRT\Settings\Settings::reset_password_verification_type(), $channel)) {
|
|
return $this->error(__('当前验证方式不可用', 'aksrt'), 403);
|
|
}
|
|
}
|
|
if (($channel === 'email' && !is_email($target)) || ($channel === 'phone' && !preg_match('/^1[3-9]\d{9}$/', $target))) {
|
|
return $this->error(__('请输入有效的邮箱或手机号', 'aksrt'));
|
|
}
|
|
if ($this->auth_rate_limited('reset-password', $target, 8, HOUR_IN_SECONDS)) {
|
|
return $this->error(__('请求过于频繁,请稍后再试', 'aksrt'), 429);
|
|
}
|
|
$captcha_error = $this->auth_require_captcha('lostpassword', $payload);
|
|
if ($captcha_error) {
|
|
return $captcha_error;
|
|
}
|
|
$user = $this->auth_find_user($target, $channel);
|
|
$password = (string) ($payload['password'] ?? '');
|
|
$confirm = (string) ($payload['password_confirm'] ?? '');
|
|
if (!class_exists('\AKSRT\Auth\Service')) {
|
|
return $this->error(__('认证服务不可用', 'aksrt'), 500);
|
|
}
|
|
$password_check = \AKSRT\Auth\Service::validate_password($password, $confirm);
|
|
if ($password_check !== true) {
|
|
return $this->error((string) $password_check);
|
|
}
|
|
if (!$user || !$this->auth_consume_code('resetpassword', $target, sanitize_text_field((string) ($payload['code'] ?? '')), $channel)) {
|
|
return $this->error(__('账号或验证码错误', 'aksrt'));
|
|
}
|
|
wp_set_password($password, (int) $user->ID);
|
|
return $this->success(['message' => __('密码已重置,请重新登录', 'aksrt'), 'redirect' => function_exists('aksrt_get_sign_page_url') ? aksrt_get_sign_page_url('signin') : wp_login_url()]);
|
|
}
|
|
|
|
public function user_phone_code(\WP_REST_Request $request): \WP_REST_Response {
|
|
$payload = $this->auth_payload($request);
|
|
if (!$this->auth_verify_rest_nonce($request, $payload)) {
|
|
return $this->error(__('安全验证失败', 'aksrt'), 403);
|
|
}
|
|
$phone = sanitize_text_field((string) ($payload['phone'] ?? ''));
|
|
if (!preg_match('/^1[3-9]\d{9}$/', $phone)) {
|
|
return $this->error(__('手机号格式不正确', 'aksrt'));
|
|
}
|
|
$captcha_error = $this->auth_require_captcha('bind_phone', $payload);
|
|
if ($captcha_error) return $captcha_error;
|
|
if (!class_exists('\AKSRT\Sms\SMS')) return $this->error(__('短信服务不可用', 'aksrt'), 503);
|
|
$result = \AKSRT\Sms\SMS::get_instance()->send_code($phone);
|
|
return !empty($result['success'])
|
|
? $this->success(['message' => __('验证码已发送', 'aksrt'), 'countdown' => (int) ($result['remaining_time'] ?? 60)])
|
|
: $this->error((string) ($result['message'] ?? __('发送失败', 'aksrt')));
|
|
}
|
|
|
|
public function user_bind_phone(\WP_REST_Request $request): \WP_REST_Response {
|
|
$payload = $this->auth_payload($request);
|
|
if (!$this->auth_verify_rest_nonce($request, $payload)) return $this->error(__('安全验证失败', 'aksrt'), 403);
|
|
$captcha_error = $this->auth_require_captcha('bind_phone', $payload);
|
|
if ($captcha_error) return $captcha_error;
|
|
if (!class_exists('\AKSRT\Auth\Service')) return $this->error(__('认证服务不可用', 'aksrt'), 500);
|
|
$result = \AKSRT\Auth\Service::bind_phone($payload);
|
|
return !empty($result['success']) ? $this->success(['message' => $result['message'] ?? __('绑定成功', 'aksrt')]) : $this->error((string) ($result['error'] ?? __('绑定失败', 'aksrt')));
|
|
}
|
|
}
|