Laravel Socialite란
Laravel Socialite는 OAuth 2.0을 사용한 소셜 로그인을 심플하게 구현할 수 있는 공식 패키지입니다. GitHub, Google, Facebook, X(Twitter), LinkedIn 등 주요한 프로바이더에 대응하고 있어, 복잡한 OAuth의 구현을 몇 줄의 코드로 끝낼 수 있습니다.
조립되어 지원되는 프로바이더는 이하와 같습니다.
프로바이더 키 이름 Bitbucket bitbucketFacebook facebookGitHub githubGitLab gitlabGoogle googleLinkedIn (OpenID) linkedin-openidSlack slack / slack-openidSpotify spotifyTwitch twitchX (Twitter) x
소셜 로그인의 플로우
Composer로 패키지를 추가합니다.
composer require laravel/socialite
Socialite를 메이저 버전 업할 때는 반드시 업그레이드 가이드 를 확인해 주세요.
config/services.php
각 프로바이더의 클라이언트 ID·시크릿·콜백 URL을 config/services.php에 추가합니다.
// config/services.php
'github' => [
'client_id' => env ( 'GITHUB_CLIENT_ID' ),
'client_secret' => env ( 'GITHUB_CLIENT_SECRET' ),
'redirect' => env ( 'GITHUB_REDIRECT_URI' ),
],
'google' => [
'client_id' => env ( 'GOOGLE_CLIENT_ID' ),
'client_secret' => env ( 'GOOGLE_CLIENT_SECRET' ),
'redirect' => env ( 'GOOGLE_REDIRECT_URI' ),
],
'facebook' => [
'client_id' => env ( 'FACEBOOK_CLIENT_ID' ),
'client_secret' => env ( 'FACEBOOK_CLIENT_SECRET' ),
'redirect' => env ( 'FACEBOOK_REDIRECT_URI' ),
],
redirect 옵션에 상대 경로를 지정하면 자동적으로 완전한 URL로 해결됩니다.
.env
환경 변수로 크리덴셜을 관리합니다. GitHub를 예로 나타냅니다.
GITHUB_CLIENT_ID =your-client-id
GITHUB_CLIENT_SECRET =your-client-secret
GITHUB_REDIRECT_URI =https://example.com/auth/github/callback
GitHub의 경우, GitHub Developer Settings 에서 OAuth App을 작성하면 클라이언트 ID와 시크릿을 취득할 수 있습니다.
인증 플로우
라우팅
OAuth 인증에는 2개의 라우트가 필요합니다. 리다이렉트용과 콜백용입니다.
use Laravel\Socialite\Facades\ Socialite ;
// 사용자를 GitHub로 리다이렉트
Route :: get ( '/auth/github' , function () {
return Socialite :: driver ( 'github' ) -> redirect ();
});
// GitHub로부터의 콜백을 처리
Route :: get ( '/auth/github/callback' , function () {
$user = Socialite :: driver ( 'github' ) -> user ();
// $user->token으로 액세스 토큰을 취득
});
사용자의 보존과 로그인
콜백 라우트에서 사용자 정보를 취득하고, 데이터베이스에 보존한 후 로그인합니다.
use App\Models\ User ;
use Illuminate\Support\Facades\ Auth ;
use Laravel\Socialite\Facades\ Socialite ;
Route :: get ( '/auth/github/callback' , function () {
$githubUser = Socialite :: driver ( 'github' ) -> user ();
$user = User :: updateOrCreate (
[ 'github_id' => $githubUser -> id ],
[
'name' => $githubUser -> name ,
'email' => $githubUser -> email ,
'github_token' => $githubUser -> token ,
'github_refresh_token' => $githubUser -> refreshToken ,
]
);
Auth :: login ( $user );
return redirect ( '/dashboard' );
});
updateOrCreate를 사용하는 경우, github_id 컬럼이 users 테이블에 존재하고 있어야 합니다. 후술의 마이그레이션 예를 참고해 주세요.
사용자 정보의 취득
user() 메서드가 반환하는 오브젝트로부터, 이하의 프로퍼티·메서드로 사용자 정보를 취득할 수 있습니다.
Route :: get ( '/auth/github/callback' , function () {
$user = Socialite :: driver ( 'github' ) -> user ();
// OAuth 2.0 프로바이더
$token = $user -> token ;
$refreshToken = $user -> refreshToken ;
$expiresIn = $user -> expiresIn ;
// OAuth 1.0 프로바이더 (X 등)
$token = $user -> token ;
$tokenSecret = $user -> tokenSecret ;
// 전 프로바이더 공통
$user -> getId ();
$user -> getNickname ();
$user -> getName ();
$user -> getEmail ();
$user -> getAvatar ();
});
액세스 토큰으로부터 사용자를 취득
기존의 액세스 토큰으로부터 사용자 정보를 취득하는 경우는 userFromToken()을 사용합니다.
$user = Socialite :: driver ( 'github' ) -> userFromToken ( $token );
스테이트리스 모드
쿠키 세션을 사용하지 않는 API에서는 stateless() 메서드로 세션 상태의 검증을 무효화할 수 있습니다.
return Socialite :: driver ( 'google' ) -> stateless () -> user ();
데이터베이스 연계
마이그레이션
users 테이블에 소셜 로그인용의 컬럼을 추가합니다.
// database/migrations/xxxx_xx_xx_add_github_columns_to_users_table.php
return new class extends Migration
{
public function up () : void
{
Schema :: table ( 'users' , function ( Blueprint $table ) {
$table -> string ( 'github_id' ) -> nullable () -> unique () -> after ( 'id' );
$table -> string ( 'github_token' ) -> nullable () -> after ( 'github_id' );
$table -> string ( 'github_refresh_token' ) -> nullable () -> after ( 'github_token' );
});
}
public function down () : void
{
Schema :: table ( 'users' , function ( Blueprint $table ) {
$table -> dropColumn ([ 'github_id' , 'github_token' , 'github_refresh_token' ]);
});
}
};
provider 컬럼으로 여러 프로바이더에 대응
여러 프로바이더를 한꺼번에 관리하는 경우는 provider / provider_id의 2컬럼 구성이 일반적입니다.
Schema :: table ( 'users' , function ( Blueprint $table ) {
$table -> string ( 'provider' ) -> nullable () -> after ( 'id' );
$table -> string ( 'provider_id' ) -> nullable () -> after ( 'provider' );
$table -> string ( 'provider_token' ) -> nullable () -> after ( 'provider_id' );
$table -> unique ([ 'provider' , 'provider_id' ]);
});
콜백 처리는 프로바이더명을 동적으로 전달하도록 합니다.
Route :: get ( '/auth/{provider}/callback' , function ( string $provider ) {
$socialUser = Socialite :: driver ( $provider ) -> user ();
$user = User :: updateOrCreate (
[
'provider' => $provider ,
'provider_id' => $socialUser -> getId (),
],
[
'name' => $socialUser -> getName (),
'email' => $socialUser -> getEmail (),
'provider_token' => $socialUser -> token ,
]
);
Auth :: login ( $user );
return redirect ( '/dashboard' );
});
기존 사용자와의 연결
같은 메일 주소를 가진 기존 사용자와 계정을 연결하는 경우는, 메일 주소로 검색한 후 컬럼을 업데이트합니다.
$socialUser = Socialite :: driver ( 'github' ) -> user ();
$user = User :: where ( 'email' , $socialUser -> getEmail ()) -> first ();
if ( $user ) {
// 기존 사용자에 GitHub 정보를 연결
$user -> update ([
'github_id' => $socialUser -> getId (),
'github_token' => $socialUser -> token ,
]);
} else {
// 신규 사용자로서 작성
$user = User :: create ([
'name' => $socialUser -> getName (),
'email' => $socialUser -> getEmail (),
'github_id' => $socialUser -> getId (),
'github_token' => $socialUser -> token ,
]);
}
Auth :: login ( $user );
스코프와 옵션
스코프의 추가
scopes() 메서드로 추가의 스코프를 지정할 수 있습니다.
return Socialite :: driver ( 'github' )
-> scopes ([ 'read:user' , 'public_repo' ])
-> redirect ();
setScopes() 메서드를 사용하면, 기존의 스코프를 모두 덮어씁니다.
return Socialite :: driver ( 'github' )
-> setScopes ([ 'read:user' , 'public_repo' ])
-> redirect ();
옵션 파라미터
with() 메서드로 추가 파라미터를 리다이렉트 리퀘스트에 포함합니다.
// Google에서 호스트 제한을 지정
return Socialite :: driver ( 'google' )
-> with ([ 'hd' => 'example.com' ])
-> redirect ();
// Google에서 매번 동의 화면을 표시
return Socialite :: driver ( 'google' )
-> with ([ 'prompt' => 'consent' ])
-> redirect ();
with() 메서드로 state나 response_type 등의 예약된 키워드를 전달하지 않도록 주의해 주세요.
Slack Bot 토큰
Slack Bot 토큰을 생성하는 경우는 asBotUser()를 사용합니다.
// 리다이렉트 시
return Socialite :: driver ( 'slack' )
-> asBotUser ()
-> setScopes ([ 'chat:write' , 'chat:write.public' , 'chat:write.customize' ])
-> redirect ();
// 콜백 시
$user = Socialite :: driver ( 'slack' ) -> asBotUser () -> user ();
테스트
Socialite는 테스트용의 모크 기능을 제공하고 있습니다. 실제의 프로바이더에의 리퀘스트 없이 OAuth 플로우를 테스트할 수 있습니다.
리다이렉트의 테스트
use Laravel\Socialite\Facades\ Socialite ;
test ( '사용자가 GitHub로 리다이렉트된다' , function () {
Socialite :: fake ( 'github' );
$response = $this -> get ( '/auth/github' );
$response -> assertRedirect ();
});
콜백의 테스트
fake() 메서드에 사용자 인스턴스를 전달하여, 프로바이더로부터 돌아오는 사용자 정보를 모크합니다. User::fake()로 페이크 사용자를 생성합니다.
use Laravel\Socialite\Facades\ Socialite ;
use Laravel\Socialite\Two\ User ;
test ( 'GitHub로 로그인할 수 있다' , function () {
Socialite :: fake ( 'github' , User :: fake ([
'id' => 'github-123' ,
'name' => 'Jane Doe' ,
'email' => '[email protected] ' ,
]));
$response = $this -> get ( '/auth/github/callback' );
$response -> assertRedirect ( '/dashboard' );
$this -> assertDatabaseHas ( 'users' , [
'name' => 'Jane Doe' ,
'email' => '[email protected] ' ,
'github_id' => 'github-123' ,
]);
});
기본적으로 페이크의 OAuth 토큰 값이 설정됩니다. 필요에 따라 fake()에 추가 속성을 전달해 오버라이드할 수 있습니다.
$fakeUser = User :: fake ([
'id' => 'github-123' ,
'name' => 'Jane Doe' ,
'email' => '[email protected] ' ,
'token' => 'fake-token' ,
'refreshToken' => 'fake-refresh-token' ,
'expiresIn' => 3600 ,
'approvedScopes' => [ 'read:user' , 'public_repo' ],
]);
OAuth 1의 사용자를 페이크하는 경우는 Laravel\Socialite\One\User 클래스를 사용해 주세요.
커스텀 프로바이더의 작성 방법
조립된 프로바이더 이외를 사용하고 싶은 경우, Socialite::extend()로 커스텀 드라이버를 등록하는 것이 공식의 정규 방법입니다. SocialiteManager는 Illuminate\Support\Manager를 상속하고 있기 때문에, 다른 Laravel 드라이버 시스템과 같은 구조로 확장할 수 있습니다.
1. 프로바이더 클래스를 작성
Laravel\Socialite\Two\AbstractProvider를 상속하고, 4개의 추상 메서드를 구현합니다.
// app/Socialite/ExampleProvider.php
namespace App\Socialite ;
use Laravel\Socialite\Two\ AbstractProvider ;
use Laravel\Socialite\Two\ User ;
class ExampleProvider extends AbstractProvider
{
// 인가 URL (사용자를 리다이렉트할 곳)
public function getAuthUrl ( $state ) : string
{
return $this -> buildAuthUrlFromBase ( 'https://example.com/oauth/authorize' , $state );
}
// 액세스 토큰 취득 URL
protected function getTokenUrl () : string
{
return 'https://example.com/oauth/token' ;
}
// 액세스 토큰으로 사용자 정보를 취득
protected function getUserByToken ( $token ) : array
{
$response = $this -> getHttpClient () -> get ( 'https://example.com/api/user' , [
'headers' => [ 'Authorization' => 'Bearer ' . $token ],
]);
return json_decode ( $response -> getBody (), true );
}
// 취득한 배열을 Socialite의 User 오브젝트에 매핑
protected function mapUserToObject ( array $user ) : User
{
return ( new User ) -> setRaw ( $user ) -> map ([
'id' => $user [ 'id' ],
'nickname' => $user [ 'login' ] ?? null ,
'name' => $user [ 'name' ],
'email' => $user [ 'email' ],
'avatar' => $user [ 'avatar_url' ] ?? null ,
]);
}
}
구현이 필요한 4개의 메서드의 역할은 이하와 같습니다.
메서드 역할 getAuthUrl($state)사용자를 리다이렉트할 OAuth 인가 URL을 반환 getTokenUrl()인가 코드를 액세스 토큰으로 교환할 엔드포인트 getUserByToken($token)액세스 토큰을 사용해 사용자 정보 API를 호출하고, 배열로 반환 mapUserToObject(array $user)취득한 배열을 Socialite의 User 오브젝트로 변환
2. 서비스 프로바이더에서 등록
AppServiceProvider의 boot() 메서드에서 Socialite::extend()를 사용해 드라이버를 등록합니다.
// app/Providers/AppServiceProvider.php
use App\Socialite\ ExampleProvider ;
use Laravel\Socialite\Facades\ Socialite ;
public function boot () : void
{
Socialite :: extend ( 'example' , function ( $app ) {
$config = $app [ 'config' ][ 'services.example' ];
return Socialite :: buildProvider ( ExampleProvider :: class , $config );
});
}
3. 설정을 추가
// config/services.php
'example' => [
'client_id' => env ( 'EXAMPLE_CLIENT_ID' ),
'client_secret' => env ( 'EXAMPLE_CLIENT_SECRET' ),
'redirect' => env ( 'EXAMPLE_REDIRECT_URI' ),
],
4. 통상의 Socialite와 마찬가지로 사용
등록 후는 조립된 프로바이더와 완전히 같은 API로 사용할 수 있습니다.
// 리다이렉트
Route :: get ( '/auth/example' , function () {
return Socialite :: driver ( 'example' ) -> redirect ();
});
// 콜백
Route :: get ( '/auth/example/callback' , function () {
$user = Socialite :: driver ( 'example' ) -> user ();
});
extend()를 사용해 정규 방법으로 Socialite를 확장하고 있는 서드파티 패키지를 이용하는 것도 추천합니다.
관련 패키지
당 사이트의 오너가 공개하고 있는 Socialite 확장 패키지입니다. 모두 Socialite::extend()를 사용한 정규 방법으로 구현되어 있어, config/services.php에 설정을 추가하는 것만으로 사용할 수 있습니다.
LINE LINE SDK for Laravel. Socialite에 의한 OAuth 로그인에 더해, Messaging API도 통합.
Bluesky AT Protocol (Bluesky)와의 연계. OAuth 인증과 포스트 송신을 지원.
Discord Discord OAuth2 로그인.
Threads Meta Threads와의 연계. OAuth 인증과 투고 API를 지원.
Amazon Login with Amazon에 의한 OAuth 로그인.
Mastodon Mastodon 인스턴스에의 OAuth 로그인.
WordPress WordPress.com 및 셀프 호스트 WordPress에의 OAuth 로그인.