> ## 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 Bluesky

> Laravel Bluesky의 App Password(LegacyAgent)와 OAuth(OAuthAgent)의 차이, 내부 아키텍처, 코드 예시를 설명합니다.

## 개요

Laravel Bluesky는 두 가지 인증 방식을 지원합니다. 인증 이후의 API 호출은 두 방식 모두 공통입니다.

| 항목         | App Password                    | OAuth                              |
| ---------- | ------------------------------- | ---------------------------------- |
| 내부 클래스     | `LegacyAgent` / `LegacySession` | `OAuthAgent` / `OAuthSession`      |
| 진입점        | `Bluesky::login()`              | `Bluesky::withToken(OAuthSession)` |
| 비밀 키       | 불필요                             | `BLUESKY_OAUTH_PRIVATE_KEY`가 필요    |
| 사용자의 인가 조작 | 불필요                             | 필요(브라우저에서 승인)                      |
| 백그라운드 실행   | ✅ 적합                            | ✅ 가능(refresh\_token을 저장)           |
| 세션 키       | `accessJwt` / `refreshJwt`      | `access_token` / `refresh_token`   |
| 폐지 예정      | **없음**                          | —                                  |
| 주 용도       | 자동 게시·알림·배치 처리                  | 사용자 대리 조작·Socialite 연동             |

<Info>
  `LegacyAgent`라는 명칭은 "OAuth 이전의 인증 방식"을 의미하지만, App Password 자체는 폐지 예정이 아닙니다. 알림이나 자동 게시 등 사용자 조작이 필요 없는 상황에서는 App Password가 더 단순하고 적합합니다.
</Info>

## 아키텍처

```mermaid theme={null}
classDiagram
    class BlueskyManager {
        +login(identifier, password) Factory
        +withToken(session) Factory
        +agent() Agent
        +check() bool
        +refreshSession() Factory
    }
    class LegacyAgent {
        +session LegacySession
        +http() PendingRequest
        +refreshSession() self
    }
    class OAuthAgent {
        +session OAuthSession
        +http() PendingRequest
        +refreshSession() self
    }
    class LegacySession {
        +accessJwt
        +refreshJwt
        +did
        +handle
        +token() string
        +refresh() string
    }
    class OAuthSession {
        +access_token
        +refresh_token
        +did
        +iss
        +token() string
        +refresh() string
        +issuer() string
    }

    BlueskyManager --> LegacyAgent : login()
    BlueskyManager --> OAuthAgent : withToken(OAuthSession)
    BlueskyManager --> LegacyAgent : withToken(LegacySession)
    LegacyAgent --> LegacySession : holds
    OAuthAgent --> OAuthSession : holds
```

`BlueskyManager`는 Facade `Bluesky`의 실체입니다. `login()` 또는 `withToken()`으로 에이전트를 설정하고, 이후의 API 호출은 두 인증 방식 모두 동일한 메서드를 사용합니다.

## App Password (LegacyAgent)

### 인증 흐름

```mermaid theme={null}
sequenceDiagram
    participant App as Laravel 앱
    participant Bluesky as Bluesky 서버

    App->>Bluesky: createSession(identifier, password)
    Bluesky-->>App: LegacySession (accessJwt + refreshJwt)
    App->>App: LegacyAgent::create(session)
    App->>Bluesky: API 호출 (Bearer accessJwt)
    Bluesky-->>App: 응답
    Note over App: accessJwt가 만료된 경우
    App->>Bluesky: refreshSession (refreshJwt)
    Bluesky-->>App: 새로운 accessJwt + refreshJwt
```

### Bluesky::login()

`.env`에 App Password를 설정하고 `login()`을 호출하기만 하면 됩니다.

```dotenv theme={null}
BLUESKY_IDENTIFIER=your-handle.bsky.social
BLUESKY_APP_PASSWORD=xxxx-xxxx-xxxx-xxxx
```

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;

$response = Bluesky::login(
    identifier: config('bluesky.identifier'),
    password: config('bluesky.password'),
)->post('Hello Bluesky');
```

### LegacySession의 재사용

매번 `login()`을 호출하면 매번 API 요청이 발생합니다. 세션을 캐시하여 재사용하면 더 효율적입니다.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Session\LegacySession;

// 최초 로그인 후 세션을 저장
Bluesky::login(
    identifier: config('bluesky.identifier'),
    password: config('bluesky.password'),
);
cache()->put('bluesky_session', Bluesky::agent()->session()->toArray(), now()->addDay());

// 다음 실행부터는 캐시에서 복원
$session = LegacySession::create(cache('bluesky_session', []));
Bluesky::withToken($session);

// 액세스 토큰이 만료되었다면 갱신
if (! Bluesky::check()) {
    Bluesky::refreshSession();
}

$response = Bluesky::post('Hello from cached session');
```

### LegacySession의 주요 키

| 키            | 내용          | 메서드         |
| ------------ | ----------- | ----------- |
| `accessJwt`  | 액세스 토큰      | `token()`   |
| `refreshJwt` | 리프레시 토큰     | `refresh()` |
| `did`        | Bluesky DID | `did()`     |
| `handle`     | 핸들          | `handle()`  |
| `email`      | 이메일 주소      | `email()`   |
| `active`     | 계정이 활성 상태인지 | `active()`  |

### 적합한 사용 사례

* 백그라운드 잡·큐 처리에서의 자동 게시
* Laravel Notification 채널을 사용한 알림
* 배치 처리·스케줄링
* 애플리케이션 자신의 계정으로 게시하는 경우

## OAuth (OAuthAgent)

### 인증 흐름

```mermaid theme={null}
sequenceDiagram
    participant User as 사용자
    participant App as Laravel 앱
    participant Bluesky as Bluesky 서버

    User->>App: 로그인 폼 전송(핸들 입력)
    App->>Bluesky: PAR 요청(login_hint 포함)
    Bluesky-->>App: request_uri
    App->>Bluesky: 인가 엔드포인트로 리디렉트
    User->>Bluesky: Bluesky에서 로그인 승인
    Bluesky-->>App: 콜백(code + iss)
    App->>Bluesky: DPoP를 사용한 토큰 교환
    Bluesky-->>App: OAuthSession (access_token + refresh_token)
    App->>User: 로그인 완료
    App->>Bluesky: API 호출 (DPoP access_token)
    Bluesky-->>App: 응답
```

### Bluesky::withToken()

Socialite로 취득한 `OAuthSession`을 `withToken()`에 전달합니다.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Session\OAuthSession;

// Laravel 세션에서 복원(Web 요청)
$session = OAuthSession::create(session('bluesky_session'));
$timeline = Bluesky::withToken($session)->getTimeline();
```

백그라운드 잡이나 Console에서도 DB에 저장한 값으로 `OAuthSession`을 구성할 수 있습니다.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Session\OAuthSession;

$session = OAuthSession::create([
    'did'           => $user->did,
    'refresh_token' => $user->refresh_token,
    // bsky.social 이외의 계정은 iss도 지정
    // 'iss'        => $user->iss,
]);

$response = Bluesky::withToken($session)
                   ->refreshSession()
                   ->post('Hello from OAuth');
```

### OAuthSession의 주요 키

| 키                     | 내용                 | 메서드             |
| --------------------- | ------------------ | --------------- |
| `access_token`        | 액세스 토큰             | `token()`       |
| `refresh_token`       | 리프레시 토큰(1회만 사용 가능) | `refresh()`     |
| `did` / `sub`         | Bluesky DID        | `did()`         |
| `iss`                 | 인가 서버 URL          | `issuer()`      |
| `profile.handle`      | 핸들                 | `handle()`      |
| `profile.displayName` | 표시 이름              | `displayName()` |

<Warning>
  OAuth의 refresh\_token은 한 번밖에 사용할 수 없습니다. `OAuthSessionUpdated` 이벤트를 사용하여 토큰 갱신 후에는 반드시 DB를 업데이트하세요. 자세한 내용은 [Socialite](/ko/packages/laravel-bluesky/socialite)를 참조하세요.
</Warning>

### 적합한 사용 사례

* Socialite를 사용한 사용자 로그인
* 사용자를 대신하여 API를 호출하는 조작
* 사용자별로 다른 계정으로 조작해야 하는 경우

## 인증 이후의 API 호출은 공통

두 인증 방식 모두 `withToken()` 이후에는 동일한 API 메서드를 사용합니다.

```php theme={null}
use Revolution\Bluesky\Facades\Bluesky;
use Revolution\Bluesky\Session\LegacySession;
use Revolution\Bluesky\Session\OAuthSession;

// App Password
Bluesky::login(config('bluesky.identifier'), config('bluesky.password'));

// 또는 OAuth
$session = OAuthSession::create(session('bluesky_session'));
Bluesky::withToken($session);

// ↓ 이후의 API 호출은 완전히 공통 ↓

Bluesky::post('Hello Bluesky');
Bluesky::getTimeline();
Bluesky::getProfile();
Bluesky::searchPosts(q: '#laravel');
```

`BlueskyManager`는 내부에서 `LegacyAgent` 또는 `OAuthAgent`를 나눠 사용하지만, 호출 측 코드에는 영향을 주지 않습니다.

## 어느 쪽을 선택해야 하는가

```mermaid theme={null}
flowchart TD
    A["어느 인증 방식을 사용할까?"] --> B{"사용자가 Bluesky 계정으로<br>로그인해야 하는가?"}
    B -->|"예"| C["OAuth (OAuthAgent)<br>Socialite 연동"]
    B -->|"아니오"| D{"앱 자신의 계정으로<br>조작하는가?"}
    D -->|"예"| E["App Password (LegacyAgent)<br>단순하고 운용하기 쉬움"]
    D -->|"여러 사용자의 대리 조작"| C
```

| 상황            | 권장           |
| ------------- | ------------ |
| 자동 게시·알림·배치   | App Password |
| 사용자 로그인 기능    | OAuth        |
| 백그라운드 잡만 사용   | App Password |
| 사용자 대리 조작이 필요 | OAuth        |
| 운용의 단순함을 우선   | App Password |
| 보안·권한 제어 중시   | OAuth        |

<Tip>
  망설여질 때는 목적별로 나누는 것이 가장 알기 쉽습니다. "앱이 스스로 동작"한다면 App Password, "사용자가 조작"한다면 OAuth입니다. 두 가지를 조합하는 것도 가능하며, 알림에는 App Password를 사용하고 사용자 로그인에는 OAuth를 사용하는 구성도 자주 볼 수 있습니다.
</Tip>

## 참고 링크

* [Basic client](/ko/packages/laravel-bluesky/basic-client) — 인증 후의 API 조작
* [Socialite](/ko/packages/laravel-bluesky/socialite) — OAuth 흐름의 상세
* [알림 채널](/ko/packages/laravel-bluesky/notification) — App Password / OAuth를 사용한 알림
* Source: [invokable/laravel-bluesky](https://github.com/invokable/laravel-bluesky)


## Related topics

- [BlueskyManager와 HasShortHand](/ko/packages/laravel-bluesky/bluesky-manager.md)
- [프런트엔드](/ko/frontend.md)
- [Laravel Sentinel — 라우트 보호 미들웨어 조사](/ko/blog/sentinel-introduction.md)
- [Socialite - Laravel Bluesky](/ko/packages/laravel-bluesky/socialite.md)
- [알림 채널 - Laravel Bluesky](/ko/packages/laravel-bluesky/notification.md)
