> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel Socialite（社群認證）

> 說明如何使用 Laravel Socialite 以簡潔的方式實作透過 OAuth 2.0 的社群登入（GitHub、Google、Facebook 等）。

## 什麼是 Laravel Socialite

Laravel Socialite 是可簡潔實作以 OAuth 2.0 進行社群登入的官方套件。支援 GitHub、Google、Facebook、X（Twitter）、LinkedIn 等主要 provider，能將複雜的 OAuth 實作以少數幾行程式碼完成。

內建支援的 provider 如下：

| Provider          | 鍵名                       |
| ----------------- | ------------------------ |
| Bitbucket         | `bitbucket`              |
| Facebook          | `facebook`               |
| GitHub            | `github`                 |
| GitLab            | `gitlab`                 |
| Google            | `google`                 |
| LinkedIn (OpenID) | `linkedin-openid`        |
| Slack             | `slack` / `slack-openid` |
| Spotify           | `spotify`                |
| Twitch            | `twitch`                 |
| X (Twitter)       | `x`                      |

### 社群登入的流程

```mermaid theme={null}
sequenceDiagram
    participant User as 使用者
    participant App as Laravel 應用
    participant OAuth as GitHub (OAuth)
    participant DB as 資料庫

    User->>App: 點擊登入按鈕
    App->>OAuth: 重新導向 (Socialite::driver('github')->redirect())
    OAuth->>User: 認證、權限確認畫面
    User->>OAuth: 同意
    OAuth->>App: 回呼（含 code 參數）
    App->>OAuth: 取得 access token
    OAuth-->>App: 回傳使用者資訊
    App->>DB: 建立／更新使用者 (updateOrCreate)
    DB-->>App: 使用者記錄
    App->>User: 完成登入、重新導向到儀表板
```

***

## 安裝

以 Composer 加入套件。

```shell theme={null}
composer require laravel/socialite
```

<Info>
  將 Socialite 進行主要版本升級時，務必確認 [升級指南](https://github.com/laravel/socialite/blob/master/UPGRADE.md)。
</Info>

***

## 設定

### config/services.php

在 `config/services.php` 加入每個 provider 的 client ID、secret、回呼 URL。

```php theme={null}
// 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'),
],
```

<Info>
  `redirect` 選項若指定相對路徑，會自動解析為完整 URL。
</Info>

### .env

以環境變數管理認證資訊。以 GitHub 為例：

```ini theme={null}
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](https://github.com/settings/developers) 建立 OAuth App 以取得 client ID 與 secret。

***

## 認證流程

### 路由

OAuth 認證需要兩條路由：重新導向與回呼。

```php theme={null}
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 取得 access token
});
```

### 儲存並登入使用者

在回呼路由中取得使用者資訊，儲存到資料庫後登入。

```php theme={null}
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');
});
```

<Warning>
  使用 `updateOrCreate` 時，`users` 資料表需存在 `github_id` 欄位。可參考後述的 migration 範例。
</Warning>

***

## 取得使用者資訊

從 `user()` 方法回傳的物件，可透過下列屬性／方法取得使用者資訊。

```php theme={null}
Route::get('/auth/github/callback', function () {
    $user = Socialite::driver('github')->user();

    // OAuth 2.0 provider
    $token = $user->token;
    $refreshToken = $user->refreshToken;
    $expiresIn = $user->expiresIn;

    // OAuth 1.0 provider（如 X）
    $token = $user->token;
    $tokenSecret = $user->tokenSecret;

    // 所有 provider 共通
    $user->getId();
    $user->getNickname();
    $user->getName();
    $user->getEmail();
    $user->getAvatar();
});
```

### 以 access token 取得使用者

從既有 access token 取得使用者資訊，使用 `userFromToken()`。

```php theme={null}
$user = Socialite::driver('github')->userFromToken($token);
```

### 無狀態模式

在不使用 Cookie session 的 API 中，可用 `stateless()` 方法停用 session 狀態驗證。

```php theme={null}
return Socialite::driver('google')->stateless()->user();
```

***

## 資料庫整合

### Migration

於 `users` 資料表加上社群登入用欄位。

```php theme={null}
// 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，通常會採用 `provider` / `provider_id` 兩欄結構。

```php theme={null}
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']);
});
```

回呼處理將 provider 名稱動態傳入。

```php theme={null}
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');
});
```

### 與既有使用者連結

若要將帳號連結到 email 相同的既有使用者，可以 email 搜尋後更新欄位。

```php theme={null}
$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);
```

***

## Scope 與選項

### 加入 scope

以 `scopes()` 方法指定額外 scope。

```php theme={null}
return Socialite::driver('github')
    ->scopes(['read:user', 'public_repo'])
    ->redirect();
```

使用 `setScopes()` 方法會覆寫既有 scope。

```php theme={null}
return Socialite::driver('github')
    ->setScopes(['read:user', 'public_repo'])
    ->redirect();
```

### 選項參數

以 `with()` 方法在重新導向請求中包含額外參數。

```php theme={null}
// 在 Google 指定 host 限制
return Socialite::driver('google')
    ->with(['hd' => 'example.com'])
    ->redirect();

// 讓 Google 每次都顯示同意畫面
return Socialite::driver('google')
    ->with(['prompt' => 'consent'])
    ->redirect();
```

<Warning>
  請注意，不要在 `with()` 傳入 `state`、`response_type` 等保留關鍵字。
</Warning>

### Slack Bot Token

要產生 Slack Bot token，使用 `asBotUser()`。

```php theme={null}
// 重新導向時
return Socialite::driver('slack')
    ->asBotUser()
    ->setScopes(['chat:write', 'chat:write.public', 'chat:write.customize'])
    ->redirect();

// 回呼時
$user = Socialite::driver('slack')->asBotUser()->user();
```

***

## 測試

Socialite 提供測試用的 mock 機制。可在不對實際 provider 發出請求的情況下測試 OAuth 流程。

### 重新導向的測試

```php theme={null}
use Laravel\Socialite\Facades\Socialite;

test('使用者會被重新導向到 GitHub', function () {
    Socialite::fake('github');

    $response = $this->get('/auth/github');

    $response->assertRedirect();
});
```

### 回呼的測試

將 user 實例傳給 `fake()` 方法，模擬 provider 回傳的使用者資訊。用 `User::fake()` 產生假的使用者。

```php theme={null}
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' => 'jane@example.com',
    ]));

    $response = $this->get('/auth/github/callback');

    $response->assertRedirect('/dashboard');

    $this->assertDatabaseHas('users', [
        'name' => 'Jane Doe',
        'email' => 'jane@example.com',
        'github_id' => 'github-123',
    ]);
});
```

預設會設定假的 OAuth token 值。可視需要在 `fake()` 傳入額外屬性覆寫。

```php theme={null}
$fakeUser = User::fake([
    'id' => 'github-123',
    'name' => 'Jane Doe',
    'email' => 'jane@example.com',
    'token' => 'fake-token',
    'refreshToken' => 'fake-refresh-token',
    'expiresIn' => 3600,
    'approvedScopes' => ['read:user', 'public_repo'],
]);
```

若要模擬 OAuth 1 的使用者，請使用 `Laravel\Socialite\One\User` 類別。

***

## 建立自訂 Provider

若想使用內建 provider 以外的服務，官方正規做法是以 `Socialite::extend()` 註冊自訂 driver。`SocialiteManager` 繼承自 `Illuminate\Support\Manager`，因此與其他 Laravel driver 系統的擴充機制相同。

<Info>
  關於 Manager 模式與 `extend()` 的機制，也可參閱 [Manager 類別解說](/zh-TW/advanced/manager)。
</Info>

### 1. 建立 provider 類別

繼承 `Laravel\Socialite\Two\AbstractProvider`，實作 4 個 abstract 方法。

```php theme={null}
// 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);
    }

    // Access token 取得 URL
    protected function getTokenUrl(): string
    {
        return 'https://example.com/oauth/token';
    }

    // 以 access 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()`                | 將授權碼換取 access token 的端點            |
| `getUserByToken($token)`       | 使用 access token 呼叫使用者資訊 API，並以陣列回傳 |
| `mapUserToObject(array $user)` | 將取得的陣列轉為 Socialite 的 `User` 物件     |

### 2. 於 Service Provider 註冊

在 `AppServiceProvider` 的 `boot()` 方法中以 `Socialite::extend()` 註冊 driver。

```php theme={null}
// 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. 加入設定

```php theme={null}
// config/services.php
'example' => [
    'client_id'     => env('EXAMPLE_CLIENT_ID'),
    'client_secret' => env('EXAMPLE_CLIENT_SECRET'),
    'redirect'      => env('EXAMPLE_REDIRECT_URI'),
],
```

### 4. 與一般 Socialite 相同方式使用

註冊後，即可用與內建 provider 完全相同的 API 使用。

```php theme={null}
// 重新導向
Route::get('/auth/example', function () {
    return Socialite::driver('example')->redirect();
});

// 回呼
Route::get('/auth/example/callback', function () {
    $user = Socialite::driver('example')->user();
});
```

<Tip>
  也建議使用以正規方式 (`extend()`) 擴充 Socialite 的第三方套件。
</Tip>

***

## 相關套件

以下是本站作者公開的 Socialite 擴充套件。皆採用 `Socialite::extend()` 的正規做法實作，只需在 `config/services.php` 加入設定即可使用。

<CardGroup cols={2}>
  <Card title="LINE" icon="comment" href="https://github.com/invokable/laravel-line-sdk">
    LINE SDK for Laravel。除了透過 Socialite 的 OAuth 登入外，還整合 Messaging API。
  </Card>

  <Card title="Bluesky" icon="cloud" href="https://github.com/invokable/laravel-bluesky">
    與 AT Protocol（Bluesky）的整合。支援 OAuth 認證與貼文送出。
  </Card>

  <Card title="Discord" icon="discord" href="https://github.com/invokable/socialite-discord">
    Discord OAuth2 登入。
  </Card>

  <Card title="Threads" icon="instagram" href="https://github.com/invokable/laravel-threads">
    與 Meta Threads 的整合。支援 OAuth 認證與貼文 API。
  </Card>

  <Card title="Amazon" icon="amazon" href="https://github.com/invokable/socialite-amazon">
    透過 Login with Amazon 進行 OAuth 登入。
  </Card>

  <Card title="Mastodon" icon="mastodon" href="https://github.com/invokable/socialite-mastodon">
    對 Mastodon instance 的 OAuth 登入。
  </Card>

  <Card title="WordPress" icon="wordpress" href="https://github.com/invokable/socialite-wordpress">
    對 WordPress.com 與自架 WordPress 的 OAuth 登入。
  </Card>
</CardGroup>


## Related topics

- [起始套件（Starter Kit）](/zh-TW/starter-kits.md)
- [Socialite for Discord](/zh-TW/packages/socialite-discord.md)
- [Socialite - Laravel Bluesky](/zh-TW/packages/laravel-bluesky/socialite.md)
- [認證方式比較 - Laravel Bluesky](/zh-TW/packages/laravel-bluesky/authentication.md)
- [Bot 教學 - Laravel Bluesky](/zh-TW/packages/laravel-bluesky/bot-tutorial.md)
