Статьи

Аутентификация и авторизация с использованием Auth0 в PHP

В этой статье мы собираемся исследовать сервис Auth0, который обеспечивает аутентификацию и авторизацию как сервис. Auth0 позволяет вам настроить основные функции аутентификации и авторизации для ваших приложений в мгновение ока.

Auth0 — это аутентификация как сервисный инструмент, который делает реализацию функций аутентификации на вашем сайте быстрым. Если вы создали приложение и хотите просто передать функции аутентификации и авторизации на аутсорсинг, вам следует рассмотреть такую ​​услугу, как Auth0.

Позвольте мне кратко изложить, что может предложить Auth0:

  • Единая точка входа
  • многофакторная аутентификация
  • логин без пароля
  • Управление пользователями
  • и многое другое

В этой статье мы рассмотрим несколько методов единого входа, которые вы можете реализовать в своих веб-приложениях, чтобы использовать функции аутентификации, предоставляемые службой Auth0.

В первой половине статьи мы рассмотрим, как настроить базовые функции аутентификации в серверном веб-приложении PHP. Во второй половине я объясню, как можно защитить свои пользовательские API, настроив авторизацию OAuth с помощью службы Auth0.

В этом разделе мы рассмотрим, как можно быстро настроить базовую аутентификацию для серверных веб-приложений с использованием Auth0. Фактически, команда Auth0 уже предоставляет удобный пример GitHub, который демонстрирует основные примеры, поэтому мы будем использовать его вместо изобретения колеса.

Прежде чем двигаться дальше, обязательно установите Composer, так как он будет использоваться для установки актуальных Auth0 SDK с использованием файла composer.json . Кроме того, если вы хотите следовать примерам, приведенным в этой статье, получите бесплатную учетную запись Auth0.

Давайте продолжим и возьмем клон сэмплов проекта.

1
git clone https://github.com/auth0-samples/auth0-php-web-app.git .

Для запуска зависимостей выполните команду composer install .

1
2
cd 00-Starter-Seed
composer install

Согласно файлу composer.json , он должен был установить vlucas/phpdotenv и auth0/auth0-php .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
{
    «name»: «auth0/basic-webapp-sample»,
    «description»: «Basic sample for securing a WebApp with Auth0»,
    «require»: {
         «vlucas/phpdotenv»: «2.4.0»,
         «auth0/auth0-php»: «~5.0»
    },
    «license»: «MIT»,
    «authors»: [
        {
            «name»: «Martin Gontovnikas»,
            «email»: «martin@gon.to»
        },
        {
            «name»: «Germán Lena»,
            «email»: «german.lena@gmail.com»
        }
    ]
}

Библиотека vlucas/phpdotenv используется для инициализации переменных среды из файла .env . Таким образом, он позволяет отделить конфигурацию от кода, который изменяется между средами.

С другой стороны, auth0/auth0-php поможет нам настроить авторизацию в нашем приложении.

Далее, давайте настроим конфигурацию для нашего приложения в файле .env . Создайте файл .env , скопировав его из файла .env.example .

1
cp .env.example .env

Он содержит значения конфигурации, которые будут использоваться библиотекой Auth0.

1
2
3
4
5
AUTH0_CLIENT_ID={CLIENT_ID}
AUTH0_DOMAIN={DOMAIN_NAME}
AUTH0_CLIENT_SECRET={CLIENT_SECRET}
AUTH0_CALLBACK_URL={CALLBACK_URL}
AUTH0_AUDIENCE=

Вы сможете найти большинство настроек в разделе « Приложения»> «Приложение по умолчанию»> «Настройки» на панели мониторинга Auth0. Обратите внимание, что я использую приложение по умолчанию, созданное системой. Конечно, вы можете создать новое приложение, если хотите.

AUTH0_CALLBACK_URL — это URL-адрес вашего приложения, куда Auth0 будет перенаправлять пользователей после входа и выхода из системы. Значение, которое вы задаете в этом поле, должно быть настроено в разделе « Allowed Callback URLs обратные вызовы» в настройках приложения на панели мониторинга Auth0.

Вы найдете три основных файла, реализующих большую часть логики аутентификации.

  • index.php : это главная страница, которая отображает кнопку входа или выхода из системы в зависимости от состояния пользователя.
  • login.php : Этот скрипт будет запущен, когда вы нажмете кнопку входа, и он перенаправит пользователей в интерфейс входа Auth0 для входа. После входа в систему они будут перенаправлены обратно в AUTH0_CALLBACK_URL .
  • logout.php : Этот сценарий будет запущен, когда вы нажмете кнопку выхода из системы, и перенаправит пользователей в Auth0 в фоновом режиме, выйдет из системы и вернет их в AUTH0_CALLBACK_URL .

Давайте быстро пройдемся по каждому файлу в стартовом проекте.

Начнем с файла login.php .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?php
require __DIR__ .
require __DIR__ .
 
use Auth0\SDK\Auth0;
 
$domain = getenv(‘AUTH0_DOMAIN’);
$client_id = getenv(‘AUTH0_CLIENT_ID’);
$client_secret = getenv(‘AUTH0_CLIENT_SECRET’);
$redirect_uri = getenv(‘AUTH0_CALLBACK_URL’);
$audience = getenv(‘AUTH0_AUDIENCE’);
 
if($audience == »){
$audience = ‘https://’ .
}
 
$auth0 = new Auth0([
  ‘domain’ => $domain,
  ‘client_id’ => $client_id,
  ‘client_secret’ => $client_secret,
  ‘redirect_uri’ => $redirect_uri,
  ‘audience’ => $audience,
  ‘scope’ => ‘openid profile’,
  ‘persist_id_token’ => true,
  ‘persist_access_token’ => true,
  ‘persist_refresh_token’ => true,
]);
 
$auth0->login();

В начале мы включили автозагрузчики, которые отвечают за загрузку классов, связанных с переменными среды и Auth0. После этого мы инициализируем переменные конфигурации из файла .env с помощью функции getenv .

Затем мы создаем экземпляр объекта Auth0 и вызываем метод login, который перенаправляет пользователей на Auth0 для входа в систему. После входа пользователи будут перенаправлены обратно на наш сайт.

Вы можете войти в систему, используя свои социальные учетные записи, такие как Facebook, Google и т.п., или создать новую учетную запись во время входа в систему. В любом случае Auth0 создаст записи для новых пользователей на их конце. Вы можете включить различные социальные учетные записи в разделе « Подключения»> «Социальные сети» на панели мониторинга Auth0. Кроме того, вы можете проверить список пользователей, которые вошли в систему с помощью Auth0 на панели мониторинга Auth0 по ссылке « Пользователи» .

Далее давайте кратко рассмотрим файл logout.php .

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?php
require __DIR__ .
require __DIR__ .
use Auth0\SDK\Auth0;
 
$domain = getenv(‘AUTH0_DOMAIN’);
$client_id = getenv(‘AUTH0_CLIENT_ID’);
$client_secret = getenv(‘AUTH0_CLIENT_SECRET’);
$redirect_uri = getenv(‘AUTH0_CALLBACK_URL’);
$audience = getenv(‘AUTH0_AUDIENCE’);
 
if($audience == »){
    $audience = ‘https://’ .
}
 
$auth0 = new Auth0([
  ‘domain’ => $domain,
  ‘client_id’ => $client_id,
  ‘client_secret’ => $client_secret,
  ‘redirect_uri’ => $redirect_uri,
  ‘audience’ => $audience,
  ‘scope’ => ‘openid profile’,
  ‘persist_id_token’ => true,
  ‘persist_refresh_token’ => true,
]);
 
$auth0->logout();
$return_to = ‘http://’ .
$logout_url = sprintf(‘http://%s/v2/logout?client_id=%s&returnTo=%s’, $domain, $client_id, $return_to);
header(‘Location: ‘ . $logout_url);
die();

Это работает почти так же, как файл login.php , за исключением того, что он будет вызываться при выходе пользователя из системы. Метод logout вызывается для истечения сеанса пользователя в вашем приложении. После этого пользователь будет перенаправлен на Auth0, чтобы служба была проинформирована о выходе пользователя из системы. Наконец, пользователь будет перенаправлен обратно в ваше приложение.

Наконец, давайте пройдемся по файлу index.php , который является точкой входа нашего приложения.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
<?php
  // Require composer autoloader
  require __DIR__ .
  require __DIR__ .
  use Auth0\SDK\Auth0;
  $domain = getenv(‘AUTH0_DOMAIN’);
  $client_id = getenv(‘AUTH0_CLIENT_ID’);
  $client_secret = getenv(‘AUTH0_CLIENT_SECRET’);
  $redirect_uri = getenv(‘AUTH0_CALLBACK_URL’);
  $audience = getenv(‘AUTH0_AUDIENCE’);
  if($audience == »){
    $audience = ‘https://’ .
  }
  $auth0 = new Auth0([
    ‘domain’ => $domain,
    ‘client_id’ => $client_id,
    ‘client_secret’ => $client_secret,
    ‘redirect_uri’ => $redirect_uri,
    ‘audience’ => $audience,
    ‘scope’ => ‘openid profile’,
    ‘persist_id_token’ => true,
    ‘persist_access_token’ => true,
    ‘persist_refresh_token’ => true,
  ]);
  $userInfo = $auth0->getUser();
?>
<html>
    <head>
        <script src=»http://code.jquery.com/jquery-3.1.0.min.js» type=»text/javascript»></script>
 
        <meta name=»viewport» content=»width=device-width, initial-scale=1″>
 
        <!— font awesome from BootstrapCDN —>
        <link href=»//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css» rel=»stylesheet»>
        <link href=»//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css» rel=»stylesheet»>
 
        <link href=»public/app.css» rel=»stylesheet»>
    </head>
    <body class=»home»>
        <div class=»container»>
            <div class=»login-page clearfix»>
              <?php if(!$userInfo): ?>
              <div class=»login-box auth0-box before»>
                <img src=»https://i.cloudup.com/StzWWrY34s.png» />
                <h3>Auth0 Example</h3>
                <p>Zero friction identity infrastructure, built for developers</p>
                <a class=»btn btn-primary btn-lg btn-login btn-block» href=»login.php»>Sign In</a>
              </div>
              <?php else: ?>
              <div class=»logged-in-box auth0-box logged-in»>
                <h1 id=»logo»><img src=»//cdn.auth0.com/samples/auth0_logo_final_blue_RGB.png» /></h1>
                <img class=»avatar» src=»<?php echo $userInfo[‘picture’] ?>»/>
                <h2>Welcome <span class=»nickname»><?php echo $userInfo[‘nickname’] ?>
                <a class=»btn btn-warning btn-logout» href=»/logout.php»>Logout</a>
              </div>
              <?php endif ?>
            </div>
        </div>
    </body>
</html>

Здесь мы использовали метод $auth0 объекта $auth0 чтобы посмотреть, есть ли активный сеанс. Если нет активного сеанса, мы отобразим ссылку « Войти» , в которой пользователь заходит на login.php и инициирует поток входа. С другой стороны, мы приветствуем пользователя и отображаем ссылку « Выход из системы», если пользователь уже вошел в систему.

Это была реализация базового потока аутентификации для серверных приложений.

В этом разделе мы увидим, как можно защитить свои пользовательские API-интерфейсы, реализовав поток предоставления кода авторизации OAuth2. Я надеюсь, что вы знакомы со стандартным потоком предоставления кода авторизации, так как мы не будем вдаваться в подробности этого. Посмотрите некоторые другие наши посты здесь на Envato Tuts +, если вы хотите освоиться с OAuth2.

  • OAuth 2.0 — Хороший, плохой и уродливый

  • Как аутентифицировать пользователей с помощью Twitter OAuth 2.0

Вместо этого мы сразу же погрузимся в реальную реализацию. Создайте файл auth_code_grant_example.php со следующим содержимым.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<?php
session_start();
 
if (!isset($_GET[‘code’])) {
    // Check if we need to show the «Sign In» link
    $params = array (
      ‘audience’ => ‘{AUDIENCE}’,
      ‘scope’ => ‘profile’,
      ‘response_type’ => ‘code’,
      ‘client_id’ => ‘{CLIENT_ID}’,
      ‘state’ => ‘SomeRandomString’,
      ‘redirect_uri’ => ‘{CALLBACK_URL}’
    );
 
    $_SESSION[‘oauth2state’]=$params[‘state’];
    $str_params = »;
    foreach($params as $key=>$value) {
      $str_params .= $key .
    }
    ?>
 
    <a href=»https://{AUTH0_DOMAIN}/authorize?<?php echo $str_params;?>»>
      Sign In
    </a>
<?php
} elseif (empty($_GET[‘state’]) || (isset($_SESSION[‘oauth2state’]) && $_GET[‘state’] !== $_SESSION[‘oauth2state’])) {
    // If the «state» var is present in the $_GET, let’s validate it
    if (isset($_SESSION[‘oauth2state’])) {
        unset($_SESSION[‘oauth2state’]);
    }
     
    exit(‘Invalid state’);
 
} elseif(isset($_GET[‘code’]) && !empty($_GET[‘code’])) {
    // If the auth «code» is present in the $_GET
    // let’s exchange it for the access token
    $params = array (
      ‘grant_type’ => ‘authorization_code’,
      ‘client_id’ => ‘{CLIENT_ID}’,
      ‘client_secret’ => ‘{CLIENT_SECRET}’,
      ‘code’ => $_GET[‘code’],
      ‘redirect_uri’ => ‘{CALLBACK_URL}’
    );
 
    $str_params = »;
    foreach($params as $key=>$value) {
      $str_params .= $key .
    }
 
    $curl = curl_init();
 
    curl_setopt_array($curl, array(
      CURLOPT_URL => «https://{AUTH0_DOMAIN}/oauth/token»,
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_CUSTOMREQUEST => «POST»,
      CURLOPT_POSTFIELDS => $str_params
    ));
 
    $curl_response = curl_exec($curl);
    $curl_error = curl_error($curl);
 
    curl_close($curl);
 
    if ($curl_error) {
      echo «Error in the CURL response:» .
    } else {
      $arr_json_data = json_decode($curl_response);
 
      if (isset($arr_json_data->access_token)) {
        $access_token = $arr_json_data->access_token;
        $curl = curl_init();
 
        curl_setopt_array($curl, array(
          CURLOPT_URL => «http://{YOUR_API_DOMAIN}/demo_api_server.php»,
          CURLOPT_RETURNTRANSFER => true,
          CURLOPT_CUSTOMREQUEST => «GET»,
          CURLOPT_HTTPHEADER => array(
            «Authorization: Bearer {$access_token}»
          )
        ));
 
        $curl_response = curl_exec($curl);
        $curl_error = curl_error($curl);
 
        curl_close($curl);
 
        if ($curl_error) {
          echo «Error in the CURL response from DEMO API:» .
        } else {
          echo «Demo API Response:» .
        }
      } else {
        echo ‘Invalid response, no access token was found.’;
      }
    }
}

Давайте посмотрим, как работает этот код!

Сначала мы подготовили ссылку, которая отправляет пользователя на сервер Auth0, чтобы начать процесс авторизации.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
// Check if we need to show the «Sign In» link
$params = array (
  ‘audience’ => ‘{AUDIENCE}’,
  ‘scope’ => ‘profile’,
  ‘response_type’ => ‘code’,
  ‘client_id’ => ‘{CLIENT_ID}’,
  ‘state’ => ‘{SOME_RANDOM_STRING}’,
  ‘redirect_uri’ => ‘{CALLBACK_URL}’
);
 
$_SESSION[‘oauth2state’]=$params[‘state’];
$str_params = »;
foreach($params as $key=>$value) {
  $str_params .= $key .
}
?>
 
<a href=»https://{AUTH0_DOMAIN}/authorize?<?php echo $str_params;?>»>
  Sign In
</a>

Замените {AUDIENCE} , {CLIENT_ID} и {CALLBACK_URL} значениями, соответствующими вашему приложению. Параметр {AUDIENCE} следует заменить значением поля « Идентификатор», которое находится в разделе « APIs»> «YOUR API APPLICATION»> «Настройки» на панели мониторинга Auth0.

{SOME_RANDOM_STRING} следует заменить уникальным значением, которое трудно угадать. Эта строка используется для предотвращения CSRF-атак. Кроме того, обязательно замените {AUTH0_DOMAIN} на свое доменное имя, как мы обсуждали ранее.

Когда пользователь нажимает на ссылку « Войти» , он попадает на сервер Auth0 для аутентификации. После аутентификации их попросят разрешить приложению доступ к вашему профилю. После авторизации пользователь будет перенаправлен обратно в ваше приложение с code в качестве параметра $_GET .

Далее мы можем обменяться этим code чтобы получить токен доступа.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// If the auth «code» is present in the $_GET
// let’s exchange it for the access token
$params = array (
  ‘grant_type’ => ‘authorization_code’,
  ‘client_id’ => ‘{CLIENT_ID}’,
  ‘client_secret’ => ‘{CLIENT_SECRET}’,
  ‘code’ => $_GET[‘code’],
  ‘redirect_uri’ => ‘{CALLBACK_URL}’
);
 
$str_params = »;
foreach($params as $key=>$value) {
  $str_params .= $key .
}
 
$curl = curl_init();
$curl_response = curl_exec($curl);
 
curl_setopt_array($curl, array(
  CURLOPT_URL => «https://{AUTH0_DOMAIN}/oauth/token»,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => «POST»,
  CURLOPT_POSTFIELDS => $str_params
));

Как видите, для получения токена доступа требуется один вызов CURL.

Получив токен доступа, вы можете вызвать свою конечную точку API, включив ее в заголовок.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
$access_token = $arr_json_data->access_token;
$curl = curl_init();
 
curl_setopt_array($curl, array(
  CURLOPT_URL => «http://{YOUR_API_DOMAIN}/demo_api_server.php»,
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_CUSTOMREQUEST => «GET»,
  CURLOPT_HTTPHEADER => array(
    «Authorization: Bearer {$access_token}»
  )
));
 
$curl_response = curl_exec($curl);
$curl_error = curl_error($curl);
 
curl_close($curl);
 
if ($curl_error) {
  echo «Error in the CURL response from DEMO API:» .
} else {
  echo «Demo API Response:» .
}

Вымышленный файл ресурсов API demo_api_server.php может выглядеть примерно так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<?php
// Require composer autoloader
require __DIR__ .
 
use Auth0\SDK\JWTVerifier;
 
try {
  $verifier = new JWTVerifier([
    ‘supported_algs’ => [‘{HASHING_ALGORITHM}’],
    ‘valid_audiences’ => [‘{AUDIENCE}’],
    ‘authorized_iss’ => [‘{DOMAIN}’]
  ]);
 
  $access_token = getBearerToken();
  $token_info = $verifier->verifyAndDecode($access_token);
 
  echo json_encode(array(‘date’=>’API Resource Data!!’));
}
catch(\Auth0\SDK\Exception\CoreException $e) {
  throw $e;
  echo json_encode(array(‘error’=>$e->getMessage()));
}
 
function getAuthorizationHeader() {
    $headers = null;
 
    if (isset($_SERVER[‘Authorization’])) {
        $headers = trim($_SERVER[«Authorization»]);
    }
    else if (isset($_SERVER[‘HTTP_AUTHORIZATION’])) { //Nginx or fast CGI
        $headers = trim($_SERVER[«HTTP_AUTHORIZATION»]);
    } elseif (function_exists(‘apache_request_headers’)) {
        $requestHeaders = apache_request_headers();
        // Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don’t care about capitalization for Authorization)
        $requestHeaders = array_combine(array_map(‘ucwords’, array_keys($requestHeaders)), array_values($requestHeaders));
        //print_r($requestHeaders);
        if (isset($requestHeaders[‘Authorization’])) {
            $headers = trim($requestHeaders[‘Authorization’]);
        }
    }
    return $headers;
}
 
function getBearerToken() {
    $headers = getAuthorizationHeader();
 
    // HEADER: Get the access token from the header
    if (!empty($headers)) {
        if (preg_match(‘/Bearer\s(\S+)/’, $headers, $matches)) {
            return $matches[1];
        }
    }
    return null;
}

Давайте быстро пройдемся по важным частям этого кода.

Вы несете ответственность за проверку токена входящего доступа, прежде чем предоставить доступ к защищенному ресурсу. И это именно то, что мы сделали в следующем фрагменте. Мы использовали служебный класс JWTVerifier для проверки токена доступа.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
try {
  $verifier = new JWTVerifier([
    ‘supported_algs’ => [‘{SIGNING_ALGORITHM}’],
    ‘valid_audiences’ => [‘{AUDIENCE}’],
    ‘authorized_iss’ => [‘{DOMAIN}’]
  ]);
 
  $access_token = getBearerToken();
  $token_info = $verifier->verifyAndDecode($access_token);
 
  echo json_encode(array(‘date’=>’API Resource Data!!’));
}
catch(\Auth0\SDK\Exception\CoreException $e) {
  throw $e;
  echo json_encode(array(‘error’=>$e->getMessage()));
}

{SIGNING_ALGORITHM} следует заменить значением поля Алгоритм {SIGNING_ALGORITHM} находится в разделе API> {ВАША ПРИЛОЖЕНИЕ API}> Настройки .

Таким образом, вы можете защитить свои пользовательские API, если захотите использовать поток OAuth2 в сервисе Auth0.

Сегодня мы прошли через сервис Auth0, который обеспечивает аутентификацию и авторизацию как сервис. После введения службы Auth0 мы рассмотрели несколько практических примеров, чтобы продемонстрировать, как вы можете интегрировать ее с вашими PHP-приложениями.

Пожалуйста, не стесняйтесь размещать ваши предложения и запросы, используя канал ниже!