简体   繁体   中英

How to generate unique QR codes with Google Authenticator in PHP?

want for each of the user on our PHP system to have a unique QR code. I have implemented the Google Authenticator system but it only generates the same QR code for each user and one user can use their code to log-in to another user's account.

How do I get Google Authenticator to generate a unique code for each user? What value/variable do I need to pass onto Google Authenticator for it to come up with a unique code for each user account on our system?

We have tried passing the email address or username of the user but the Authenticator sends back the same QR code despite the email addresses being unique.

[1]using username , username variable name is $name

public function getQR($name, $secret, $title = null, $params = array())
    {
        $width = !empty($params['width']) && (int) $params['width'] > 0 ? (int) $params['width'] : 200;
        $height = !empty($params['height']) && (int) $params['height'] > 0 ? (int) $params['height'] : 200;
        $level = !empty($params['level']) && array_search($params['level'], array('L', 'M', 'Q', 'H')) !== false ? $params['level'] : 'M';


        $urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.'');
        if (isset($title)) {
            $urlencoded .= urlencode('&issuer='.urlencode($title));
        }

        return 'https://chart.googleapis.com/chart?chs='.$width.'x'.$height.'&chld='.$level.'|0&cht=qr&chl='.$urlencoded.'';
    }

    public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null)
    {
        if ($currentTimeSlice === null) {
            $currentTimeSlice = floor(time() / 30);
        }

        if (strlen($code) != 6) {
            return false;
        }

        for ($i = -$discrepancy; $i <= $discrepancy; ++$i) {
            $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i);
            if ($this->timingSafeEquals($calculatedCode, $code)) {
                return true;
            }
        }

        return false;
    }

[2]using email , email variable name is $email

public function getQR($email, $secret, $title = null, $params = array())
    {
        $width = !empty($params['width']) && (int) $params['width'] > 0 ? (int) $params['width'] : 200;
        $height = !empty($params['height']) && (int) $params['height'] > 0 ? (int) $params['height'] : 200;
        $level = !empty($params['level']) && array_search($params['level'], array('L', 'M', 'Q', 'H')) !== false ? $params['level'] : 'M';


        $urlencoded = urlencode('otpauth://totp/'.$email.'?secret='.$secret.'');
        if (isset($title)) {
            $urlencoded .= urlencode('&issuer='.urlencode($title));
        }

        return 'https://chart.googleapis.com/chart?chs='.$width.'x'.$height.'&chld='.$level.'|0&cht=qr&chl='.$urlencoded.'';
    }

    public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null)
    {
        if ($currentTimeSlice === null) {
            $currentTimeSlice = floor(time() / 30);
        }

        if (strlen($code) != 6) {
            return false;
        }

        for ($i = -$discrepancy; $i <= $discrepancy; ++$i) {
            $calculatedCode = $this->getCode($secret, $currentTimeSlice + $i);
            if ($this->timingSafeEquals($calculatedCode, $code)) {
                return true;
            }
        }

        return false;
    }

I want for the code to generate a unique QR code for each user so each user have their own authentication codes in the Google Authenticator app on their phones.

The secret is nothing more than a random base32 string. So can be done as simple as

 substr( str_shuffle( "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" ), 0, 32 );

Where "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" is the base32 alphabet

Your code looks pretty similar to https://github.com/PHPGangsta/GoogleAuthenticator and problem that leads to similar QR codes for different users is that you are using same secret for they.

Every user must have unique secret!

There is function by PHPGangsta that generates pseudorandom secret:

public function createSecret($secretLength = 16)
{
    $validChars = $this->_getBase32LookupTable();

    // Valid secret lengths are 80 to 640 bits
    if ($secretLength < 16 || $secretLength > 128) {
        throw new Exception('Bad secret length');
    }
    $secret = '';
    $rnd = false;
    if (function_exists('random_bytes')) {
        $rnd = random_bytes($secretLength);
    } elseif (function_exists('mcrypt_create_iv')) {
        $rnd = mcrypt_create_iv($secretLength, MCRYPT_DEV_URANDOM);
    } elseif (function_exists('openssl_random_pseudo_bytes')) {
        $rnd = openssl_random_pseudo_bytes($secretLength, $cryptoStrong);
        if (!$cryptoStrong) {
            $rnd = false;
        }
    }
    if ($rnd !== false) {
        for ($i = 0; $i < $secretLength; ++$i) {
            $secret .= $validChars[ord($rnd[$i]) & 31];
        }
    } else {
        throw new Exception('No source of secure random');
    }

    return $secret;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM