> This page is for 플랫폼, version V4 (default).
> For other versions, use one of these documentation indexes:
> - V4 (default): https://next.developer.frame.io/platform/v4/llms.txt
> - V4 실험적: https://next.developer.frame.io/platform/v4-experimental/llms.txt
> - 레거시: https://next.developer.frame.io/platform/v2/llms.txt

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://next.developer.frame.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://next.developer.frame.io/_mcp/server.

# Frame.io TypeScript SDK — 인증 가이드

이 가이드는 **Frame.io TypeScript SDK**(`frameio`)를 사용하여 Frame.io API로 인증하는 방법을 설명합니다. Frame.io V4 API는 Adobe의 OAuth 2.0 ID 플랫폼인 [Adobe IMS(Identity Management Service)](https://developer.adobe.com/developer-console/docs/guides/authentication/)를 사용합니다. 이는 TypeScript/JavaScript 개발자를 위한 독립형 참조 문서입니다. 아래의 모든 코드 예제와 흐름은 `frameio` 패키지에만 해당됩니다.

---

## TypeScript SDK의 인증 유형

TypeScript SDK는 4가지 OAuth 인증 클래스와 직접 토큰 사용을 지원합니다.

| 방법                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          | 사용 사례                                   | 사용자 상호 작용? | 클라이언트 시크릿 필요? |
| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- | ---------- | ------------- |
| **정적 토큰**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   | 빠른 스크립트, 테스트 또는 이미 토큰이 있는 경우            | 아니요        | 아니요           |
| **서버 간 인증**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 | 백엔드 서비스, cron 작업, 자동화                   | 아니요        | 예             |
| **웹 앱**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     | 서버측 앱(Express, Fastify, Next.js)        | 예          | 예             |
| **SPA(PKCE)**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               | 시크릿을 저장할 수 없는 브라우저 앱                    | 예          | 아니요           |
| **Native App(PKCE)**                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        | 사용자 지정 URI 스키마 리디렉션이 있는 데스크탑/모바일 애플리케이션 | 예          | 아니요           |
| **서버 간 인증**을 사용하면 앱이 사용자 상호 작용 없이 서비스 계정으로 작동할 수 있습니다. [Adobe Admin Console](https://adminconsole.adobe.com/)을 통해 관리되는 Frame.io V4 계정에서만 사용할 수 있습니다. **웹 앱** 및 **SPA**를 사용하면 앱이 특정 사용자 역할을 수행할 수 있습니다. 둘 다 내부적으로 Adobe IMS를 사용합니다. 사용자가 앱을 인증하면 SDK가 결과 코드를 토큰으로 교환합니다. TypeScript SDK는 IMS `/authorize/v2` 및 `/token/v3` 흐름을 처리합니다. 웹 앱의 경우 클라이언트 시크릿이 필요하고, SPA의 경우 [PKCE](https://datatracker.ietf.org/doc/html/rfc7636)를 대신 사용합니다. **Native App**은 SPA와 동일한 PKCE 흐름을 따르지만 Adobe가 Native App 자격 증명에 할당하는 `adobe+<hash>://callback</hash>` 리디렉션 URI를 사용합니다. 이를 통해 애플리케이션이 인증 후 OS 레벨에서 리디렉션을 가로막을 수 있습니다. |                                         |            |               |

---

## 서비스 계정 사용자

서버 간 인증을 사용할 때 애플리케이션은 **서비스 계정 사용자** 역할을 합니다. 이는 서비스를 대신하여 작업을 수행할 수 있는 고유한 계정 유형입니다. 이 계정은 Frame.io의 다른 사용자에게 표시됩니다. 서비스 계정이 작업을 수행하면 UI에 해당 이름이 표시됩니다. [Adobe Admin Console](https://adminconsole.adobe.com/) 및 [Developer Console](https://developer.adobe.com/console)을 통해 서비스 계정 액세스 권한을 부여하고 취소할 수 있습니다. 서비스 계정 이름은 Frame.io UI에서 관리됩니다. 기본적으로 첫 번째 S2S 연결의 이름은 **Service Account User**, 두 번째는 **Service Account User 2** 등으로 지정됩니다.

> **Info**
>
> 자세한 내용은 [Frame.io 서버 간 지원을 사용하여 설정 자동화하기](https://helpx.adobe.com/enterprise/using/automate-using-frame-io.html)를 참조하세요.

---

## 빠른 시작

### 사전 요구 사항

1. [Adobe Developer Console](https://developer.adobe.com/console)의 **자격 증명**:

* **클라이언트 ID** — 모든 OAuth 흐름에 필요 - **클라이언트 시크릿** — 서버 간 및 웹 앱 흐름에 필요 - **리디렉션 URI** — 웹 앱, SPA, 네이티브 앱 흐름에 필요하며, Adobe 프로젝트에 등록되어야 함

2. **SDK 설치:**

```bash
npm install frameio
```

### 방법 선택

* **사용자가 개입하지 않습니까?** **서버 간**(`ServerToServerAuth`) 방식을 사용하세요.
* **사용자가 개입하며 시크릿을 저장할 수 있습니까?** **웹 앱**(`WebAppAuth`) 방식을 사용하세요.
* **사용자가 개입하지만 시크릿을 저장할 수 없습니까?** 브라우저 앱의 경우 **SPA**(`SPAAuth`)를 사용하고, 데스크톱/모바일 앱의 경우 **네이티브 앱**(`NativeAppAuth`)을 사용하세요.

---

## 액세스 토큰

(다른 OAuth 시스템 또는 이전 교환(예: [API 탐색기](/platform/api-reference/accounts/index?explorer=true))에서 발급받은) 액세스 토큰이 이미 있는 경우 이를 직접 전달할 수 있습니다.

```typescript
import { FrameioClient } from "frameio";

const client = new FrameioClient({ token: "YOUR_ACCESS_TOKEN" });
```

이것이 가장 간단한 방식이지만, 토큰은 결국 만료되며 SDK가 이를 대신 새로 고쳐주지 않습니다.

### 이전 개발자 토큰

아직 [Adobe Admin Console](https://adminconsole.adobe.com/)을 통해 관리되지 않는 V4 마이그레이션 계정의 경우, [Frame.io 개발자 사이트](https://developer.frame.io/app/tokens)에서 발급받은 이전 개발자 토큰을 계속 사용할 수 있습니다. 반드시 `x-frameio-legacy-token-auth` 헤더를 포함하고 이를 `true`로 설정해야 합니다.

```typescript
import { FrameioClient } from "frameio";

const client = new FrameioClient({
    token: "YOUR_LEGACY_DEVELOPER_TOKEN",
    headers: { "x-frameio-legacy-token-auth": "true" },
});
```

이전 개발자 토큰은 만료되지 않지만 이는 임시적인 전환 메커니즘입니다. 새로운 통합 및 프로덕션 워크로드의 경우 아래의 OAuth 2.0 흐름 중 하나를 사용하는 것이 좋습니다. 자세한 내용은 [마이그레이션 가이드](/platform/docs/resources/migration#authentication)를 참조하세요.

---

## 서버 간 방식(클라이언트 자격 증명)

사용자 상호 작용 없이 Frame.io 액세스가 필요한 백엔드 서비스 및 스크립트에 이 방식을 사용하세요. 이 흐름은 [Adobe Admin Console](https://adminconsole.adobe.com/)을 통해 관리되는 Frame.io V4 계정에서만 사용할 수 있습니다. 애플리케이션은 사용자 개입 없이 [서비스 계정 사용자](#service-account-users)로 인증됩니다.

```typescript
import { FrameioClient, ServerToServerAuth } from "frameio";

const auth = new ServerToServerAuth({
    clientId: "YOUR_CLIENT_ID",
    clientSecret: "YOUR_CLIENT_SECRET",
});

const client = new FrameioClient({ token: () => auth.getToken() });
```

이것이 전부입니다. `auth.getToken()`은 모든 요청에서 SDK가 호출하는 비동기 함수입니다. 현재 토큰이 여전히 유효하면 즉시 반환됩니다. 토큰이 만료될 예정이면 먼저 완벽하게 투명한 방식으로 새 토큰을 가져옵니다.

### 작동 방식

클라이언트 자격 증명(클라이언트 ID + 시크릿)은 **절대 만료되지 않습니다**. 보안 관리를 위해 수동으로만 순환시킵니다. S2S는 수동 개입 없이 사실상 영구적이고 중단 없는 API 액세스를 제공합니다.

내부 동작 방식:

1. 첫 번째 API 호출 시, `getToken()`은 `client_credentials` 권한 부여를 사용하여 Adobe IMS에 새 액세스 토큰을 요청합니다.
2. 토큰은 메모리에 캐시됩니다. 개별 액세스 토큰은 만료(일반적으로 24시간)되지만 이는 자동으로 처리됩니다.
3. 캐시된 토큰이 새로 고침 버퍼(기본값: 만료 60초 전) 내에 있으면, SDK는 동일한 클라이언트 자격 증명을 사용하여 새 토큰을 자동으로 가져옵니다.
4. 새로 고침 토큰은 관여하지 않습니다. 클라이언트 자격 증명 자체가 수명이 긴 시크릿이며, 이를 통해 언제든 새 액세스 토큰을 발행할 수 있습니다.

### 명시적 인증

(예를 들어 시작 시 잘못된 자격 증명에 대해 빠르게 실패 처리를 하기 위해) 즉시 토큰을 가져오려는 경우는 다음과 같습니다.

```typescript
const auth = new ServerToServerAuth({ clientId: "...", clientSecret: "..." });
await auth.authenticate(); // throws AuthenticationError if credentials are invalid
const client = new FrameioClient({ token: () => auth.getToken() });
```

---

## 웹 앱(권한 부여 코드)

사용자가 자신의 Adobe ID로 로그인하는 서버 측 애플리케이션에 이 방식을 사용하세요. 이 흐름에는 클라이언트 시크릿이 필요하며, 이는 서버에 안전하게 저장되어야 합니다.

#### 사용자를 Adobe IMS로 리디렉션

```typescript
    import { WebAppAuth } from "frameio";
    import crypto from "crypto";

    const auth = new WebAppAuth({
        clientId: "YOUR_CLIENT_ID",
        clientSecret: "YOUR_CLIENT_SECRET",
        redirectUri: "https://yourapp.com/callback",
    });

    // Generate a cryptographically random state value to prevent CSRF attacks
    const state = crypto.randomBytes(32).toString("hex");

    const authorizationUrl = auth.getAuthorizationUrl({ state });
    // Store `state` in the user's session, then redirect them to `authorizationUrl`
```

#### 콜백 처리

Adobe IMS가 사용자를 `redirectUri`로 리디렉션하면 `code` 및 `state` 매개변수를 추출합니다. 저장한 내용과 state가 일치하는지 확인한 다음, 코드를 토큰으로 교환합니다.

```typescript
    // In your callback handler (e.g. an Express route):
    await auth.exchangeCode(req.query.code as string);
```

이는 권한 부여 코드를 액세스 토큰 및 새로 고침 토큰으로 교환하고 두 가지를 내부적으로 저장합니다.

#### 클라이언트 사용

```typescript
    import { FrameioClient } from "frameio";

    const client = new FrameioClient({ token: () => auth.getToken() });
```

이것이 전부입니다. 이 시점부터 `getToken()`이 토큰 수명 주기를 자동으로 관리합니다. 액세스 토큰의 만료가 임박하면 SDK는 새로 고침 토큰을 사용하여 새 토큰을 가져옵니다. 사용자 상호 작용이 필요하지 않습니다.

### 전체 Express 예제

```typescript
import crypto from "crypto";
import express from "express";
import session from "express-session";
import { FrameioClient, WebAppAuth } from "frameio";

const auth = new WebAppAuth({
    clientId: "YOUR_CLIENT_ID",
    clientSecret: "YOUR_CLIENT_SECRET",
    redirectUri: "http://localhost:3000/callback",
});

const app = express();
app.use(session({ secret: crypto.randomBytes(32).toString("hex"), resave: false, saveUninitialized: false }));

app.get("/login", (req, res) => {
    const state = crypto.randomBytes(32).toString("hex");
    (req.session as any).oauthState = state;
    res.redirect(auth.getAuthorizationUrl({ state }));
});

app.get("/callback", async (req, res) => {
    if (req.query.state !== (req.session as any).oauthState) {
        return res.status(403).send("Invalid state parameter");
    }

    await auth.exchangeCode(req.query.code as string);

    const client = new FrameioClient({ token: () => auth.getToken() });
    const accounts = await client.accounts.index();
    res.json(accounts);
});

app.listen(3000);
```

---

## 단일 페이지 앱 / PKCE(권한 부여 코드 + PKCE)

클라이언트 시크릿을 안전하게 저장할 수 없는 브라우저 기반 애플리케이션, 데스크톱 앱, 또는 CLI 도구에 이 방식을 사용하세요. 이 흐름은 [PKCE(RFC 7636)](https://datatracker.ietf.org/doc/html/rfc7636)를 사용하여 권한 부여 코드 교환을 보호합니다.

#### 권한 부여 URL 생성

```typescript
    import { SPAAuth } from "frameio";

    const auth = new SPAAuth({
        clientId: "YOUR_CLIENT_ID",
        redirectUri: "https://yourapp.com/callback",
    });

    const state = crypto.randomUUID();
    const result = await auth.getAuthorizationUrl({ state });
    // result.url            -> redirect the user here
    // result.codeVerifier   -> store this securely until the callback
```

`getAuthorizationUrl`은 (PKCE `code_challenge`가 임베드된) 전체 URL 및 다음 단계에서 필요한 `codeVerifier`가 포함된 `AuthorizationUrlResult`를 반환합니다.

#### 검증자를 사용하여 코드 교환

사용자가 다시 리디렉션된 경우:

```typescript
    await auth.exchangeCode({
        code: "CODE_FROM_CALLBACK",
        codeVerifier: result.codeVerifier,
    });
```

#### 클라이언트 사용

```typescript
    import { FrameioClient } from "frameio";

    const client = new FrameioClient({ token: () => auth.getToken() });
```

이것이 전부입니다. 새로 고침은 웹 앱 방식과 동일하게 작동하며, SDK가 새로 고침 토큰을 자동으로 사용합니다. 차이점은 SPA 흐름이 공개 클라이언트용으로 설계되었기 때문에 새로 고침 중에 클라이언트 시크릿이 전송되지 않는다는 점입니다.

> **Warning**
>
> `codeVerifier`는 권한 부여 요청과 코드 교환 사이에 클라이언트 측에 안전하게 저장되어야 합니다. 브라우저 앱에서는 `sessionStorage` 또는 동등한 기능을 사용하세요.

---

## 네이티브 앱(권한 부여 코드 + PKCE)

데스크톱 및 모바일 애플리케이션에 이 방식을 사용하세요. [Adobe Developer Console](https://developer.adobe.com/console)에서 네이티브 앱 자격 증명을 생성하면 Adobe에서 `adobe+<hash>://callback</hash>` 형태의 리디렉션 URI를 할당합니다. 귀하는 OS 수준에서 해당 사용자 지정 URI 스킴(scheme)을 처리하도록 애플리케이션을 등록하게 됩니다. 루프백 리디렉션(`http://127.0.0.1:<port>/callback</port>`) 역시 로컬 개발을 위해 지원됩니다. 이 흐름은 SPA와 동일하며, 클라이언트 시크릿 없이 PKCE를 사용합니다.

```typescript
import { NativeAppAuth } from "frameio";

const auth = new NativeAppAuth({
    clientId: "YOUR_CLIENT_ID",
    redirectUri: "adobe+abc123def456://callback", // from your Adobe Developer Console Native App credential
    // Also supports loopback: "http://127.0.0.1:8080/callback"
});

const { url, codeVerifier } = await auth.getAuthorizationUrl({
    state: crypto.randomUUID(),
});

// Open system browser to `url`
// Listen for redirect on your custom URI scheme or loopback server

await auth.exchangeCode({ code: "CODE_FROM_REDIRECT", codeVerifier });
const client = new FrameioClient({ token: () => auth.getToken() });
```

### 리디렉션 URI 규칙

Adobe는 [Adobe Developer Console](https://developer.adobe.com/console)에서 자격 증명을 등록할 때와 `redirect_uri` 매개변수가 `/authorize/v2` 엔드포인트에 도달할 때, 총 두 번에 걸쳐 리디렉션 URI 규칙을 시행합니다. 이 SDK에서 `redirectUri`에 전달하는 값은 귀하가 자격 증명에 등록한 "리디렉션 URI 패턴" 중 하나와 일치해야 합니다. 그렇지 않으면 Adobe는 이를 무시하고 자격 증명에 등록된 기본 리디렉션 URI로 대신 리디렉션합니다.

* **웹 앱** 및 **SPA** 자격 증명에는 HTTPS가 필요합니다.
* **네이티브 앱** 자격 증명은 비-HTTPS 리디렉션을 사용합니다 — 일반적으로 `adobe+<hash>://callback</hash>`과 같이 자격 증명을 위해 개발자 콘솔에 표시된 URI를 사용합니다.

귀하의 자격 증명에 허용되는 정확한 패턴은 [Adobe Developer Console](https://developer.adobe.com/console)에서 확인하세요.

> **Note**
>
> Python SDK에는 네이티브 앱 자격 증명 클래스가 포함되어 있지 않습니다. Python에는 사용자 지정
>
> URI 체계 핸들러를 등록하는 표준화된 방법이 없기 때문입니다. TypeScript SDK는 네이티브 앱을 포함한 4가지 자격 증명 유형을 모두 지원합니다.

---

## 수동 토큰 새로 고침

웹 앱, SPA 및 네이티브 앱 흐름의 경우, SDK는 `getToken()`을 통해 토큰을 자동으로 새로 고칩니다. 명시적인 제어가 필요한 경우 `refresh()`를 직접 호출할 수 있습니다.

```typescript
await auth.refresh(); // fetches a new access token using the refresh token
```

이 기능은 자동 새로 고침 버퍼에 의존하는 대신 중요한 작업을 앞두고 강제로 새로 고치고자 할 때 유용합니다.

`refresh()`는 `WebAppAuth`, `SPAAuth`, `NativeAppAuth`에서 사용할 수 있습니다. 새로 고침 토큰을 사용할 수 없는 경우 `ConfigurationError`를 발생시킵니다(즉, 먼저 `exchangeCode()`를 호출해야 함). `ServerToServerAuth`에는 `refresh()` 메서드가 없습니다 — 대신 `authenticate()`를 사용하여 클라이언트 자격 증명을 통해 새 토큰을 가져옵니다.

---

## 토큰 지속성

모든 인증 클래스는 재시작 시에도 토큰 상태를 유지할 수 있도록 `exportTokens()` 및 `importTokens()`를 지원합니다. 웹 앱, SPA, 네이티브 앱 흐름의 경우 액세스 토큰과 새로 고침 토큰이 기본적으로 메모리에 상주하므로 이는 특히 중요합니다. 이를 유지하지 않으면 애플리케이션이 다시 시작될 때 사용자가 재인증을 해야 합니다. 서버 간(S2S) 방식의 경우 지속성은 선택 사항이지만(클라이언트 자격 증명으로 항상 새 토큰을 발행할 수 있음), 캐시된 토큰을 가져오면 시작 시 한 번의 추가 왕복을 피할 수 있습니다.

### 내보내기 및 가져오기

```typescript
// After exchangeCode(), save the token state
const tokenData = auth.exportTokens();
// tokenData is: { access_token: "...", refresh_token: "...", expires_at: 1234567890.0 }
// Save it to your database, file, or secret store

// On next startup, restore it
auth.importTokens(tokenData);
const client = new FrameioClient({ token: () => auth.getToken() });
// The SDK will automatically refresh if the token is near expiry
```

> **Warning**
>
> 내보낸 토큰을 안전하게 보관하세요. 이 토큰에는 API 액세스 권한을 부여하는 액세스 토큰과 새로 고침 토큰이 포함되어 있습니다. 프로덕션 환경에서는 토큰을
>
> 일반 텍스트 파일에 저장하지 마세요.

### `onTokenRefreshed`를 활용한 자동 지속성

토큰이 새로 고쳐질 때마다 자동으로 유지하려면 `onTokenRefreshed` 콜백을 사용합니다.

```typescript
import fs from "fs/promises";

const TOKEN_FILE = "tokens.json";

const auth = new WebAppAuth({
    clientId: "...",
    clientSecret: "...",
    redirectUri: "...",
    onTokenRefreshed: (tokens) => {
        fs.writeFile(TOKEN_FILE, JSON.stringify(tokens));
    },
});

// On startup, restore if available
try {
    const saved = JSON.parse(await fs.readFile(TOKEN_FILE, "utf-8"));
    auth.importTokens(saved);
} catch {
    // No saved tokens — user will need to authenticate
}
```

콜백은 `exportTokens()`와 동일한 형태를 수신하며, 토큰 새로 고침이 성공할 때마다 실행됩니다.

---

## 토큰 해지

사용자를 로그아웃하고 Adobe IMS에서 해당 토큰을 무효화하려면 다음을 실행합니다.

```typescript
await auth.revoke();
```

이는 액세스 토큰 및 새로 고침 토큰 모두에 대해 최선의 노력으로 Adobe IMS에 해지 요청을 보낸 다음 모든 로컬 토큰 상태를 지웁니다. 기밀 클라이언트(`WebAppAuth`)의 경우 해지 요청 시 HTTP Basic Auth를 사용하며, 공개 클라이언트(`SPAAuth`, `NativeAppAuth`)의 경우 `client_id`가 쿼리 매개변수로 전송됩니다. 해지 과정의 오류는 로그로 기록되지만 에러를 발생시키지는 않습니다. 해지 후에는 사용자가 다시 인증을 거쳐야 합니다.

---

## 오류 처리

모든 인증 오류는 `FrameioAuthError`에서 상속되므로, 이를 포괄적으로 잡거나 특정 케이스를 분리하여 처리할 수 있습니다.

```typescript
import {
    FrameioAuthError,
    AuthenticationError,
    TokenExpiredError,
    ConfigurationError,
    NetworkError,
    RateLimitError,
} from "frameio";

try {
    await auth.exchangeCode("...");
} catch (error) {
    if (error instanceof TokenExpiredError) {
        // The refresh token has expired; redirect the user to sign in again
    } else if (error instanceof AuthenticationError) {
        // Token exchange failed
        console.error(`Error: ${error.errorCode} - ${error.errorDescription}`);
    } else if (error instanceof NetworkError) {
        // Timeout or connection failure (after retries)
    } else if (error instanceof RateLimitError) {
        // 429 from Adobe IMS; retry after error.retryAfter seconds
    } else if (error instanceof FrameioAuthError) {
        // Catch-all for any other auth error
    }
}
```

### 오류 참조

| 예외                    | 발생 조건                                                                              |
| --------------------- | ---------------------------------------------------------------------------------- |
| `ConfigurationError`  | 누락되었거나 유효하지 않은 구성(예: 비어 있는 `clientId`, HTTPS가 아닌 리디렉션 URI, HTTPS가 아닌 `imsBaseUrl`) |
| `AuthenticationError` | Adobe IMS에서 토큰 교환 또는 새로 고침 거절(`.errorCode` 및 `.errorDescription` 속성 포함)            |
| `TokenExpiredError`   | 새로 고침 토큰 자체가 만료됨. 사용자가 다시 인증해야 함                                                   |
| `NetworkError`        | 모든 재시도 후에도 HTTP 시간 초과 또는 연결 실패 발생                                                  |
| `RateLimitError`      | Adobe IMS에서 429 반환. 백오프 지침을 위해 `.retryAfter` 확인 필요                                 |
| `PKCEError`           | PKCE 흐름에서 소비자가 사용할 수 있도록 제공됨. SDK 내부적으로 발생하지는 않음                                   |

### 프로덕션 환경에서 만료된 새로 고침 토큰 처리하기

> **Warning**
>
> 웹 앱, SPA, 네이티브 앱 흐름의 경우 새로 고침 토큰은 결국 만료됩니다. 이 경우 `getToken()`은 `TokenExpiredError`를 발생시킵니다. 이를 잡아내어 사용자를 권한 부여 흐름을 통해 다시 리디렉션해야 합니다.

```typescript
import { TokenExpiredError } from "frameio";

try {
    const client = new FrameioClient({ token: () => auth.getToken() });
    const assets = await client.files.list({ projectId: "..." });
} catch (error) {
    if (error instanceof TokenExpiredError) {
        // Clear persisted tokens and redirect user to login
        await auth.revoke();
        return res.redirect("/login");
    }
}
```

---

## 구성 참조

이러한 매개변수들은 합리적인 기본값을 가지고 있어 변경해야 할 일이 드뭅니다. 스테이징 IMS를 가리키거나, 사용자 지정 `fetch`를 주입하거나, 시간 초과를 조정하거나, 또는 로거를 연결하는 등 동작을 사용자 지정해야 하는 경우, 인증 클래스를 구성할 때 선택적 매개변수로 전달할 수 있습니다.

| 매개 변수              | Default                          | 설명                                                                                                                                                |
| ------------------ | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `범위`               | 흐름별 기본값                          | 공백으로 구분된 OAuth 권한입니다. S2S의 기본값은 `openid AdobeID frame.s2s.all`이며, 사용자 대면 흐름의 기본값은 `openid email profile offline_access additional_info.roles`입니다. |
| `imsBaseUrl`       | `https://ims-na1.adobelogin.com` | Adobe IMS 기본 URL입니다. 스테이징 또는 비프로덕션 환경을 위해 이를 재정의할 수 있습니다. HTTPS를 사용해야 합니다.                                                                        |
| `fetch`            | `globalThis.fetch`               | 프록시, mTLS, 또는 사용자 지정 HTTP 처리를 위한 사용자 지정 `fetch` 구현입니다.                                                                                            |
| `timeout`          | `30000`                          | 토큰 엔드포인트 호출에 대한 HTTP 요청 시간 초과(밀리초 단위)입니다.                                                                                                         |
| `maxRetries`       | `2`                              | 일시적인 오류(5xx, 시간 초과) 발생 시 최대 재시도 횟수입니다. 속도 제한 재시도(429)는 별도로 추적됩니다.                                                                                 |
| `refreshBuffer`    | `60`                             | 사전 새로 고침을 트리거하기 위해 토큰이 만료되기 전까지 남은 시간(초 단위)입니다.                                                                                                   |
| `onTokenRefreshed` | `undefined`                      | 토큰 새로 고침이 성공할 때마다 실행되는 콜백입니다. `access_token`, `refresh_token`, `expires_at`이 포함된 오브젝트를 수신합니다.                                                     |
| `logger`           | No-op(무음)                        | `debug,`, `info`, `warn`, `error` 메서드(예: `console`, `pino`, `winston`)를 포함하는 로거 인스턴스입니다.                                                          |

### 스테이징 환경

`imsBaseUrl`을 재정의하여 스테이징 Adobe IMS 인스턴스를 지정합니다. 또한 프로그래밍 방식으로 프로덕션 값을 참조해야 하는 경우 SDK는 `DEFAULT_IMS_BASE_URL`(`https://ims-na1.adobelogin.com`)을 내보냅니다.

```typescript
const auth = new ServerToServerAuth({
    clientId: "...",
    clientSecret: "...",
    imsBaseUrl: "https://ims-na1-stg1.adobelogin.com",
});
```

### 사용자 지정 가져오기

프록시 지원 또는 사용자 지정 TLS 구성에 사용됩니다.

```typescript
import { ProxyAgent } from "undici";

const proxyAgent = new ProxyAgent("http://corporate-proxy:8080");

const auth = new ServerToServerAuth({
    clientId: "...",
    clientSecret: "...",
    fetch: (url, init) => fetch(url, { ...init, dispatcher: proxyAgent }),
});
```

---

## 동시성 안전

TypeScript SDK는 동시에 사용해도 안전합니다. 여러 개의 `getToken()` 호출이 동시에 발생하고 새로 고침이 필요한 경우 새로 고침 요청은 하나만 발생합니다. 나머지 호출은 동일한 프로미스를 대기하다가 동일한 결과를 받게 됩니다. 외부 잠금이 필요하지 않습니다. 이러한 중복 제거는 JavaScript의 단일 스레드 이벤트 루프와 공유 `Promise`를 사용합니다. 이미 새로 고침이 진행 중인 경우, 동시 호출자들은 두 번째 요청을 시작하는 대신 기존 요청에 합류합니다. 새로 고침이 진행 중일 때 `revoke()`가 호출되면, 해당 새로 고침은 `AuthenticationError`와 함께 거부되고 토큰은 지워진 상태로 유지됩니다. 즉, 해지가 항상 우선시됩니다.