Как организовать OAuth 2.0 на PHP?

Нужна помощь с OAuth 2.0 на PHP.
Тип authorization code grant.
Мне нужно связать так называемый "навык" яндекс.алисы с моим сервером.
Я немного программирую, но в этом я первый раз и не всё понятно.
Вот тут Яндекс описывает протокол общения: https://yandex.ru/dev/dialogs/alice/doc/auth/about...
Вот тут можно создать свой навык и попробовать: https://dialogs.yandex.ru/developer
Я у себя всё подготовил. Свой VPS/VDS на debian 10, https, php.
Яндекс обращается к моему серверу и я должен произвести авторизацию и выдать токен.

Сделал auth.php:
<?
require_once('../bag_config_mysql.php');

$OpenHabAlice = new OpenHabAlice;
$user = $OpenHabAlice->user;
$password = $OpenHabAlice->password;
$db = $OpenHabAlice->db;


$dbhost = 'localhost';
$dbname = $db;
$dbusername = $user;
$dbpassword = $password;

$link = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbusername, $dbpassword);

$state = @$_GET['state'];
$redirect_uri = @$_GET['redirect_uri'];
$response_type = @$_GET['response_type'];
$client_id = @$_GET['client_id']; // Идентификатор приложения, который мы указывали при настройках навыка https://dialogs.yandex.ru/developer/skills/6408b0e1-4aa3-47bd-a41c-d5d3eb49bc21/draft/settings/main
$scope = @$_GET['scope'];

if (!isset($_POST['email']) and !isset($_POST['password'])){
    if (isset($_GET['state']) and isset($_GET['redirect_uri']) and isset($_GET['response_type']) and isset($_GET['client_id']) and isset($_GET['scope'])){

        $sth = $link->prepare("SELECT * FROM Alice WHERE client_id = :client_id");
        $sth->execute(array('client_id' => $client_id));
        $array = $sth->fetch(PDO::FETCH_ASSOC);
        if (!$array){
            $statement = $link->prepare('INSERT INTO Alice (state, redirect_uri, response_type, client_id, scope)
                VALUES (:fstate, :fredirect_uri, :fresponse_type, :fclient_id, :fscope)');
            $statement->execute([
                'fstate' => $state,
                'fredirect_uri' => $redirect_uri,
                'fresponse_type' => $response_type,
                'fclient_id' => $client_id,
                'fscope' => $scope,
            ]);
            
            $q_state = $statement->errorInfo();

            $count = $statement->rowCount();
            if ($count != 0){
                echo "Мы внесли запись в БД";
            } else {
                echo "Не получилось внести в БД данные.";
                print_r($q_state);
            }
        }
    }
}
?>

<head>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
</head>

<body>
    <?
    if (isset($_POST['email']) and isset($_POST['password'])){
        $username=$_POST['email'];
        $password=$_POST['password'];
        $URL='https://myopenhab.org/rest/';

        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL,$URL);
        curl_setopt($ch, CURLOPT_TIMEOUT, 2);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER,1);
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
        curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
        $result=curl_exec ($ch);
        $status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close ($ch);
        if ($result == "Unauthorized"){
            ?>
            <div class="div_for_alert2 regdiv d-flex justify-content-center">
                <div class="alert alert-warning" role="alert">
                    Логин и пароль для myopenhab.ru не подошли
                </div>
            </div>
            <?
            $OpenHabTest = false;
        } else {
            $json = json_decode($result);
            if (isset($json->runtimeInfo)){
                $OpenHabTest = true;
                $code = md5($client_id . $_POST['email'] . $_POST['password'] . rand());
                
                $sth = $link->prepare("SELECT * FROM Alice WHERE client_id = :client_id");
                $sth->execute(array('client_id' => $client_id));
                $array = $sth->fetch(PDO::FETCH_ASSOC);
                if (!$array){
                    print("Ошибка123.\n");
                } else {
                    $sth = $link->prepare("UPDATE Alice SET oh_login = :flogin, oh_pass = :fpass, code = :code WHERE client_id = :fclient_id");
                    $sth->execute(array('fclient_id' => $client_id, 'flogin' => $_POST['email'], 'fpass' => $_POST['password'], 'code' => $code ));
                    $count = $sth->rowCount();
                    
                    $q_state = $sth->errorInfo();
                    
                    if ($count == 0){
                        print("Затронуто: $count строк.\n");
                    } else {
                        print("Затронуто: $count строк.\n");
                    }
                }
                $url = "$redirect_uri?code=$code&state=$state&client_id=$client_id&scope=$scope";
                header("Location: $url");
            } else {
                $OpenHabTest = false;
            }
        }
    } else {

    }
    ?>
    <div class="div_for_alert regdiv d-flex justify-content-center">
        <div class="alert alert-secondary" role="alert">
            Введите учётные данные с myopenhab.org
        </div>
    </div>
    <div class="regdiv2 d-flex justify-content-center">
        <form method="post">
            <div class="form-group">
                <label for="exampleInputEmail1">Email address</label>
                <input type="email" name="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" placeholder="Enter email">
            </div>
            <div class="form-group">
                <label for="exampleInputPassword1">Password</label>
                <input type="password" name="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
            </div>
            <button type="submit" class="btn btn-primary">Войти</button>
        </form>
    </div>

<style type="text/css">
    .regdiv{
        margin-top: 100px;
    }
</style>
</body>


Яндекс на него присылает мне данные и я в auth.php формирую авторизацию/залогинивание.
И возвращаю яндексу на указанный redirect_uri соответствующие данные:
1. code = md5($client_id . $_POST['email'] . $_POST['password'] . rand());
2. state, client_id, scope - такие же, как и мне яндекс прислал

И яндекс успешно проглатывает эти данные и этот шаг считаю пройденным.

На шаге №2 Яндекс шлёт мне в token.php code, который я же ему сформировал на предыдущем шаге.
А ещё кроме code:
1. client_secret - это строка, которую я указал в настройках навыка.
2. grant_type = "authorization_code"
3. client_id - всё тотже id навыка
4. redirect_uri

И вот на этом шаге я не знаю что нужно сделать.
Пробовал посылать на redirect_uri GET данные:
1. access_token - рэндомно сгенерированный код и записанный в БД.
2. token_type='bearer', expires_in=86400, refresh_token=$8xLOxBtZp8
3. code, client_id, client_secret, grant_type - тоже самое, что прислал яндекс сюда в token.php

Файл token.php:
<html>
    <head>
        <meta charset="utf-8">
        <title>Приём данных с алисы</title>
    </head>
    <body>
    <?

    if (isset($_REQUEST['code']) and isset($_REQUEST['client_secret']) and isset($_REQUEST['grant_type']) and isset($_REQUEST['client_id']) and isset($_REQUEST['redirect_uri'])){
        $code = $_REQUEST['code'];
        $client_secret = $_REQUEST['client_secret'];
        $grant_type = $_REQUEST['grant_type'];
        $client_id = $_REQUEST['client_id'];
        $redirect_uri = $_REQUEST['redirect_uri'];
        
        require_once('../bag_config_mysql.php');

        $OpenHabAlice = new OpenHabAlice;
        $user = $OpenHabAlice->user;
        $password = $OpenHabAlice->password;
        $db = $OpenHabAlice->db;


        $dbhost = 'localhost';
        $dbname = $db;
        $dbusername = $user;
        $dbpassword = $password;

        $link = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbusername, $dbpassword);

        $sth = $link->prepare("SELECT * FROM Alice WHERE client_id = :client_id AND code = :code");
        $sth->execute(array('client_id' => $client_id, 'code' => $code));
        $array = $sth->fetch(PDO::FETCH_ASSOC);
        if (!$array){
            echo "Ошибка! Нет такой связки client_id и code!";
        } else {
            $token = md5($client_id . $code . rand());
            
            $sth = $link->prepare("UPDATE Alice SET token = :token WHERE client_id = :client_id AND code = :code");
            $sth->execute(array('token' => $token, 'client_id' => $client_id, 'code' => $code ));
            $count = $sth->rowCount();
            
            $q_state = $sth->errorInfo();
            if ($q_state[2] <> ""){
                echo "Ошибка при обновлении записи в БД:";
                print_r($q_state);
            }
            
            if ($count == 0){
                print("Обновлено $count строк.\n");
            } else {
                $res_array = array('access_token' => $token, 'token_type' => 'bearer', 'expires_in' => 86400, 'refresh_token' => '8xLOxBtZp8');
                echo json_encode($res_array);

                $url = "$redirect_uri?access_token=$token&token_type='bearer'&expires_in=86400&refresh_token=$8xLOxBtZp8&code=$code&client_id=$client_id&client_secret=$client_secret&grant_type=$grant_type";
                header("Location: $url");
            }
        }
    }
    ?>
    </body>
</html>


В результате Яндекс показывает на страничке "Произошла ошибка в процессе получения данных от сервиса":
6058578ed2bb3190095644.png

Настройки яндекс навыка:
605858520b825779444707.png
6058585b4acf2074431657.png
  • Вопрос задан
  • 404 просмотра
Пригласить эксперта
Ответы на вопрос 1
Bagunda
@Bagunda Автор вопроса
Вопрос решил!
Добавил header("Content-type: application/json; charset=utf-8");

token.php:
<?header("Content-type: application/json; charset=utf-8");
    if (isset($_REQUEST['code']) and isset($_REQUEST['client_secret']) and isset($_REQUEST['grant_type']) and isset($_REQUEST['client_id']) and isset($_REQUEST['redirect_uri'])){
        $code = $_REQUEST['code'];
        $client_secret = $_REQUEST['client_secret'];
        $grant_type = $_REQUEST['grant_type'];
        $client_id = $_REQUEST['client_id'];
        $redirect_uri = $_REQUEST['redirect_uri'];
        
        require_once('../bag_config_mysql.php');

        $OpenHabAlice = new OpenHabAlice;
        $user = $OpenHabAlice->user;
        $password = $OpenHabAlice->password;
        $db = $OpenHabAlice->db;


        $dbhost = 'localhost';
        $dbname = $db;
        $dbusername = $user;
        $dbpassword = $password;

        $link = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbusername, $dbpassword);

        $sth = $link->prepare("SELECT * FROM Alice WHERE client_id = :client_id AND code = :code");
        $sth->execute(array('client_id' => $client_id, 'code' => $code));
        $array = $sth->fetch(PDO::FETCH_ASSOC);
        if (!$array){
            echo "<br>Ошибка! Нет такой связки client_id и code!<br>";
        } else {
            $token = md5($client_id . $code . rand());
            $refresh_token = md5($client_id . $code . rand());
            
            $sth = $link->prepare("UPDATE Alice SET token = :token, refresh_token = :refresh_token WHERE client_id = :client_id AND code = :code");
            $sth->execute(array('token' => $token, 'refresh_token' => $refresh_token, 'client_id' => $client_id, 'code' => $code ));
            $count = $sth->rowCount();
            
            $q_state = $sth->errorInfo();
            if ($q_state[2] <> ""){
                echo "<br>Ошибка при обновлении записи в БД:<br>";
                print_r($q_state);
                echo "<br><br>";
            }
            
            if ($count == 0){
                print("При попытке обновить токен в БД все данные оказались одинаковыми и ничего не было изменено: $count строк.\n");
            } else {
                $res_array = array('access_token' => $token, 'token_type' => 'bearer', 'expires_in' => 86400, 'refresh_token' => $refresh_token);
                echo json_encode($res_array);
            }
        }
    }
    ?>
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
YCLIENTS Москва
от 200 000 до 350 000 ₽
Ведисофт Екатеринбург
от 25 000 ₽
Бюро Цифровых Технологий Санкт-Петербург
от 120 000 до 180 000 ₽
03 мая 2024, в 00:45
1000 руб./за проект
02 мая 2024, в 23:56
2000 руб./за проект
02 мая 2024, в 23:29
1500 руб./в час