У меня есть решение, использующее дух всего в этом вопросе и ответе.
/**
* @param string $provider
* @param \Laravel\Socialite\Contracts\User $sUser
* @return \App\User|false
*/
protected function findOrCreateUser($provider, $sUser)
{
$oauthProvider = OAuthProvider::where('provider', $provider)
->where('provider_user_id', $sUser->id)
->first();
if ($oauthProvider) {
$oauthProvider->update([
'access_token' => $sUser->token,
'refresh_token' => $sUser->refreshToken ?? null,
]);
return $oauthProvider->user;
}
$user = User::firstWhere('email', $sUser->email);
if ($user) {
return $this->createUser($provider, $sUser, $user);
}
return $this->createUser($provider, $sUser);
}
/**
* If a User already exists for the email, skip user creation
* and add this provider to the list of `$user->oauthProviders`.
* @param string $provider
* @param \Laravel\Socialite\Contracts\User $sUser
* @param \App\User $user
* @return \App\User
*/
protected function createUser($provider, $sUser, User $user = null)
{
if (!$user) {
$user = User::create([
'name' => $sUser->name,
'email' => $sUser->email,
'email_verified_at' => now(),
]);
} else if ($user->email_verified_at === null) {
$user->email_verified_at = now();
$user->save();
}
$user->oauthProviders()->create([
'provider' => $provider,
'provider_user_id' => $sUser->id,
'access_token' => $sUser->token,
'refresh_token' => $sUser->refreshToken ?? null,
]);
return $user;
}
Раньше у него была проверка на User::where('email', $sUser->email)
, и если это так, отклоняйте запрос с сообщением «электронная почта уже занята».
С таблицей oauth_providers
и отношением $user->oauthProviders
(User hasMany OAuthProviders) вместо того, чтобы создавать нового пользователя в таблице пользователей каждый раз, когда кто-то использует oauth, он прикрепляет эту запись oauth к существующему пользователю $user = User::firstWhere('email', $sUser->email);
Если кому-то нужно немного больше, я изменил этот репозиторий здесь, чтобы GitHub и Twitter работали с oauth: https://github.com/cretueusebiu/laravel-vue-spa. Основывайтесь на OAuthController.
С помощью приведенного выше кода я могу зарегистрировать пользователя через регистрационную форму, чтобы получить электронное письмо, затем войти в систему как GitHub и Twitter, и мой пользователь плюс два провайдера oauth.
Большая часть волшебства моего решения связана с третьим параметром createUser
. Еще неизвестно, будет ли работать лучше, если оставить createUser всегда создающим, а затем создать новый метод с именем addProviderToUser. Это может быть немного больше кода, но он также может быть проще и удобнее для модульных тестов.
Вот мои методы перенаправления и обратного вызова oauth, по научным причинам:
/**
* Redirect the user to the provider authentication page. Twitter uses OAuth1.0a, and does not support
* Socialite::driver($provider)->stateless(), so library `abraham/twitteroauth` is used to handle everything.
*
* @param string $provider
* @return \Illuminate\Http\RedirectResponse
*/
public function redirectToProvider($provider)
{
if ($provider === 'twitter') {
$tempId = Str::random(40);
$connection = new TwitterOAuth(config('services.twitter.client_id'), config('services.twitter.client_secret'));
$requestToken = $connection->oauth('oauth/request_token', array('oauth_callback' => config('services.twitter.callback_url').'?user='.$tempId));
\Cache::put($tempId, $requestToken['oauth_token_secret'], 86400); // 86400 seconds = 1 day
$url = $connection->url('oauth/authorize', array('oauth_token' => $requestToken['oauth_token']));
} else {
$url = Socialite::driver($provider)->stateless()->redirect()->getTargetUrl();
}
return [
'url' => $url,
];
}
/**
* Obtain the user information from the provider.
*
* @param string $driver
* @return \Illuminate\Http\Response
*/
public function handleProviderCallback(Request $request, $provider)
{
if ($provider === 'twitter') {
$connection = new TwitterOAuth(config('services.twitter.client_id'), config('services.twitter.client_secret'), $request->oauth_token, \Cache::get($request->user));
$access_token = $connection->oauth('oauth/access_token', ['oauth_verifier' => $request->oauth_verifier]);
$connection = new TwitterOAuth(config('services.twitter.client_id'), config('services.twitter.client_secret'), $access_token['oauth_token'], $access_token['oauth_token_secret']);
$user = $connection->get('account/verify_credentials', ['include_email' => 'true']);
$user->token = $access_token['oauth_token'];
} else {
$user = Socialite::driver($provider)->stateless()->user();
}
$user = $this->findOrCreateUser($provider, $user);
$this->guard()->setToken(
$token = $this->guard()->login($user)
);
return view('oauth/callback', [
'token' => $token,
'token_type' => 'bearer',
'expires_in' => $this->guard()->getPayload()->get('exp') - time(),
]);
}
config/services.php
'github' => [
'client_id' => env('GITHUB_CLIENT_ID'),
'client_secret' => env('GITHUB_CLIENT_SECRET'),
'callback_url' => env('GITHUB_CALLBACK_URL'),
'provider_name' => env('GITHUB_PROVIDER_NAME', 'GitHub'),
],
'twitter' => [
'client_id' => env('TWITTER_CLIENT_ID'),
'client_secret' => env('TWITTER_CLIENT_SECRET'),
'callback_url' => env('TWITTER_CALLBACK_URL'),
'provider_name' => env('TWITTER_PROVIDER_NAME', 'Twitter'),
],
.env
# localhost
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GITHUB_CALLBACK_URL=https://valet.test/api/oauth/github
TWITTER_CLIENT_ID=
TWITTER_CLIENT_SECRET=
TWITTER_CALLBACK_URL=https://valet.test/api/oauth/twitter/callback
Вам нужно будет заглянуть в приведенный выше пример репозитория, чтобы выяснить, как используются эти переменные env, но подсказка: посмотрите на spa.blade.php, vuex и api.php.
person
agm1984
schedule
07.05.2020
provider_id
использоватьfacebook_id
,github_id
иtwitter_id
для их индивидуального хранения. - person Joe   schedule 30.11.2018User::where('email', $SocialUser->email)->first()
вместо проверки provider_id - person Joe   schedule 30.11.2018