#php #angularjs #oauth-2.0
#php #angularjs #oauth-2.0
Вопрос:
У меня есть приложение на основе браузера (одностраничный, AngularJS), и я использую hello для использования стороннего входа, такого как Google, FB, Soundcloud и т. Д.
Мое приложение использует сервер PHP API.
Какой хороший способ дать пользователю возможность войти в систему с помощью Google, а также проверить пользователя на стороне сервера?
Я рассматривал:
- Приложение браузера выполняет неявное предоставление с помощью google / fb / etc
- Затем я переношу access_token с клиента на сервер, затем использую, например, google-api-php-client с my
app id
, secret и пользователемaccess_token
? Используя их API, например/me
? (какой тип гранта это будет?) - Получить некоторый ключ от стороннего (facebook_id, email и т. Д.), Сопоставить его с пользователем в моей базе данных, А затем считать пользователя аутентифицированным?
Кроме того, должен ли я выполнять это при каждом запросе API? Или я должен просто немного спрятать access_token
и предположить, что пользователь все еще действителен до истечения срока действия ключа?
Ответ №1:
Одна из проблем заключается в том, что не все эти поставщики поддерживают неявный поток. Но, предполагая, что они это сделают, access_token
вы получите для каждого из них доказательство того, что пользователь прошел аутентификацию в этой системе, не обязательно, что у них есть доступ к вызову вашего API. Вам все еще нужно что-то, что утверждает, что «someone@gmail.com может «прочитать» ресурс X в вашей системе»
Вероятно, вам нужно что-то, что переводит все, что вы получаете от Google, Soundcloud и т. Д., В токен, который понимает ваше приложение. Простой формат (r) — использовать JWT. (веб-токены Json).
App -> Intermmediary -> Soundcloud/Google
<-JWT-- <---whavetever-
и затем:
App - (JWT) -> API
JWT легко манипулировать, проверять и проверять. Смотрите jwt.io
Возможно, вы захотите просмотреть это сообщение в блоге также для получения дополнительной информации (в частности, о интерфейсах AngularJS)
Комментарии:
1. Итак, если я понимаю, я мог бы поместить в JWT достаточно, чтобы доказать, что пользователь аутентифицирован? т.е. Он будет содержать, скажем, электронное письмо с токеном, который мог быть создан только службой X; и если JWT является законным, система будет знать, что он содержит истинную информацию? Тогда моей системе не нужно было бы самостоятельно вызывать Google / FB / etc? (сейчас читаю сообщение в блоге)
2. ДА. JWT — это автономный объект с информацией о пользователе: (например, имя, адрес электронной почты) или пользователь, связанный с вашим API (например, пользователь является «администратором»). Она имеет цифровую подпись, поэтому вы можете проверить целостность. Вы можете проверить подпись в своем бэкэнде без дополнительного запроса к серверу, это будет просто криптографическая функция.
3. Я до сих пор не понимаю, как это может быть абсолютно безопасно. Я последовал совету в вашем сообщении в блоге (действительно полезно!) Я думаю, что единственный способ сделать это безопасным — заставить сервер использовать access_token, который он получает, и запрашивать его у службы (google / fb / …). Самый безопасный способ, о котором я могу думать, — это сформировать JWT (как вы предлагаете), используя один ключ (например, email) в качестве ‘sub’в заявках и идентификатор пользователя как секретный. Но даже это может быть подделано.
Ответ №2:
Сообщение в блоге, упомянутое @eugenio-pace, было действительно полезным для настройки клиентской части.
Однако на стороне сервера access_token
она должна быть проверена.
SDK (в composer) (код ниже):
- Facebook:
"facebook/php-sdk-v4" : "4.0.*"
- Google:
cURL
запрос (не заботился о"google/apiclient"
) - SoundCloud:
"ise/php-soundcloud": "3.*"
(Конечно, есть и другие, только эти три были теми, которые я выбрал, и кажутся приличными.)
В прошлый раз, когда я делал что-то подобное, я допустил ошибку, проверяя access_token
при каждом запросе, что оказало огромное (явно негативное) влияние на производительность. Теперь я просто проверяю его при входе в систему и использую его для получения идентификатора пользователя из этой службы. Итак, браузер отправляет мне access_token A
и сообщает, что он с Facebook, я использую sdk выше access_token
с Facebook, и я получаю обратно их идентификатор, чтобы я знал, что они те, за кого себя выдают.
Я бы предложил сохранить access_token
на сервере с expires_in
помощью .
(Я еще не имел дело с токенами обновления)
Код для проверки токенов с использованием вышеуказанных библиотек:
function validateTokenFacebook($token, $id=null) {
// Performed above
// FacebookSession::setDefaultApplication($config->fb->app_id, $config->fb->secret);
$session = new FacebookSession($token);
// Fetch user info
$request = new FacebookRequest($session, 'GET', '/me');
try {
$response = $request->execute();
} catch (FacebookFacebookServerException $e) {
$this->mlog->err($e . "n" . $e->getTraceAsString());
throw new AuthTokenInvalidException();
}
$graphObject = $response->getGraphObject();
$user_id = $graphObject->getProperty('id');
return array(access_token, $user_id);
}
function validateTokenGoogle($token, $id=null) {
$resp=array();
// This key isn't included in the token from hello.js, but
// google needs it
if (!array_key_exists('created', $token)) $token['created'] = $token['expires'] - $token['expires_in'];
$client = new Google_Client();
$client->setClientId($this->systemConfig->google->app_id);
$client->setClientSecret($this->systemConfig->google->secret);
$client->setRedirectUri($this->systemConfig->google->redirectUri);
$client->setScopes('email');
$client->setAccessToken(json_encode($token));
try {
// Send Client Request
$objOAuthService = new Google_Service_Oauth2($client);
$userData = $objOAuthService->userinfo->get();
return array($token['access_token'], $userData['id']);
} catch (Google_Auth_Exception $e) {
throw new AuthException('Google returned ' . get_class($e));
}
}
function validateTokenSoundcloud($token, $id=null) {
$soundcloud = new SoundcloudService(
$this->systemConfig->soundcloud->app_id,
$this->systemConfig->soundcloud->secret,
$this->systemConfig->soundcloud->redirect);
$soundcloud->setAccessToken($access_token);
try {
$response = json_decode($soundcloud->get('me'), true);
if (array_key_exists('id', $response))
return array($access_token, $response['id']);
} catch (SoundcloudExceptionInvalidHttpResponseCodeException $e) {
$this->mlog->err($e->getMessage());
}
throw new AuthTokenInvalidException();
}
У меня есть несколько пользовательских классов выше, таких как исключения и systemConfig, но я думаю, что это достаточно подробно, чтобы сообщить, что они делают.