| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293 |
- <?php
- namespace App\Services;
- class GoogleAuthenticatorService
- {
- const BASE32_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';
- public function generateSecret($length = 32)
- {
- $alphabet = self::BASE32_ALPHABET;
- $max = strlen($alphabet) - 1;
- $secret = '';
- for ($i = 0; $i < $length; $i++) {
- $secret .= $alphabet[random_int(0, $max)];
- }
- return $secret;
- }
- public function verifyCode($secret, $code, $window = 1, $period = 30, $digits = 6)
- {
- $code = trim((string) $code);
- if ($code === '' || !preg_match('/^\d{6}$/', $code)) {
- return false;
- }
- $timeSlice = (int) floor(time() / $period);
- for ($i = -$window; $i <= $window; $i++) {
- $calc = $this->getCode($secret, $timeSlice + $i, $digits);
- if (hash_equals($calc, $code)) {
- return true;
- }
- }
- return false;
- }
- public function getOtpAuthUrl($issuer, $account, $secret)
- {
- $label = rawurlencode($issuer . ':' . $account);
- $issuerParam = rawurlencode($issuer);
- return "otpauth://totp/{$label}?secret={$secret}&issuer={$issuerParam}&algorithm=SHA1&digits=6&period=30";
- }
- public function getQrCodeUrl($otpAuthUrl, $size = 200)
- {
- $encoded = rawurlencode($otpAuthUrl);
- return "https://api.qrserver.com/v1/create-qr-code/?size={$size}x{$size}&data={$encoded}";
- }
- protected function getCode($secret, $timeSlice, $digits)
- {
- $secretKey = $this->base32Decode($secret);
- if ($secretKey === '') {
- return '';
- }
- $time = pack('N*', 0) . pack('N*', $timeSlice);
- $hash = hash_hmac('sha1', $time, $secretKey, true);
- $offset = ord(substr($hash, -1)) & 0x0F;
- $truncatedHash = substr($hash, $offset, 4);
- $value = unpack('N', $truncatedHash)[1] & 0x7FFFFFFF;
- $modulo = pow(10, $digits);
- return str_pad((string) ($value % $modulo), $digits, '0', STR_PAD_LEFT);
- }
- protected function base32Decode($secret)
- {
- $secret = strtoupper($secret);
- $secret = preg_replace('/[^A-Z2-7]/', '', $secret);
- if ($secret === '') {
- return '';
- }
- $alphabet = array_flip(str_split(self::BASE32_ALPHABET));
- $binary = '';
- $buffer = 0;
- $bitsLeft = 0;
- foreach (str_split($secret) as $char) {
- if (!isset($alphabet[$char])) {
- continue;
- }
- $buffer = ($buffer << 5) | $alphabet[$char];
- $bitsLeft += 5;
- while ($bitsLeft >= 8) {
- $bitsLeft -= 8;
- $binary .= chr(($buffer >> $bitsLeft) & 0xFF);
- }
- }
- return $binary;
- }
- }
|