> For the complete documentation index, see [llms.txt](https://developer.mediarithmics.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://developer.mediarithmics.io/advanced-usages/plugins/creation/destinations-and-authentication/authentication-methods.md).

# Authentication methods

## Authentication methods

This page describes the methods you need to implement in your plugin to handle destination authentication. The SDK routes all authentication-related HTTP calls to these methods automatically.

{% hint style="info" %}
**SDK requirement:** `@mediarithmics/plugins-nodejs-sdk` v0.40.0 or later. The `onTestAuthentication` `invalid_credentials` status requires v0.40.2 or later. The `feedDestinationCredentials` argument on `instanceContextBuilder` requires v0.41.0 or later.
{% endhint %}

### Methods per authentication type

<table><thead><tr><th width="241.26953125">Method</th><th width="112.30859375">OAUTH2</th><th width="110.20703125">TOKEN</th><th width="171.74609375">LOGIN_PASSWORD</th><th>NO_AUTH</th></tr></thead><tbody><tr><td><code>onCreateOAuthRedirectUrl</code></td><td>Required</td><td>-</td><td>-</td><td>-</td></tr><tr><td><code>onAuthentication</code></td><td>Required</td><td>Required</td><td>Required</td><td>-</td></tr><tr><td><code>onTestAuthentication</code></td><td>Required</td><td>Required</td><td>Required</td><td>-</td></tr><tr><td><code>onLogout</code></td><td>Required</td><td>Optional</td><td>Optional</td><td>-</td></tr></tbody></table>

### `onCreateOAuthRedirectUrl`

Called when the platform needs to generate the OAuth2 authorization URL. The frontend redirects the user to this URL so they can grant access on the partner platform.

```typescript
protected onCreateOAuthRedirectUrl(
  request: CreateOAuthRedirectUrlRequest
): Promise<CreateOAuthRedirectUrlPluginResponse>
```

#### Input: `CreateOAuthRedirectUrlRequest`

| Field                 | Type     | Description                                                                                                                                     |
| --------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `feed_destination_id` | `string` | The ID of the destination, created as a draft before the OAuth flow starts. Must be embedded in the `state` parameter of the authorization URL. |
| `datamart_id`         | `string` | The datamart ID for the current context.                                                                                                        |
| `plugin_version_id`   | `string` | The plugin version ID.                                                                                                                          |

#### Return: `CreateOAuthRedirectUrlPluginResponse`

| Field       | Type     | Description                                                                                           |
| ----------- | -------- | ----------------------------------------------------------------------------------------------------- |
| `login_url` | `string` | The full OAuth2 authorization URL. Must include a `state` parameter containing `feed_destination_id`. |

{% hint style="info" %}
**Important:** This method throws by default if not overridden (the SDK returns HTTP 400 `not_implemented`). Always override it when `auth_type = OAUTH2`.
{% endhint %}

{% hint style="info" %}
**Important:** The `redirect_uri` must point to the mediarithmics Navigator callback URL: `https://www.navigator.mediarithmics.com/auth/{plugin_id}/{plugin_version_id}?state={state}`. The `state` must contain `feed_destination_id` so the platform can look up the destination on the OAuth callback.
{% endhint %}

#### Code example

```typescript
protected onCreateOAuthRedirectUrl(
  request: core.CreateOAuthRedirectUrlRequest,
): Promise<core.CreateOAuthRedirectUrlPluginResponse> {
  const state = request.feed_destination_id;
  const loginUrl =
    `https://accounts.partner.com/oauth2/authorize` +
    `?client_id=${MY_CLIENT_ID}` +
    `&state=${state}` +
    `&redirect_uri=${MY_REDIRECT_URI}` +
    `&response_type=code` +
    `&scope=ads_management`;

  return Promise.resolve({ login_url: loginUrl });
}
```

### `onAuthentication`

Called after the user completes the authentication step:

* For **OAuth2**: after the partner redirects back with the authorization code.
* For **Token / Login/Password**: after the user submits the form fields.

The plugin must validate the provided credentials. When `feed_destination_id` is present in the request, it must also return them in the response: the SDK will **automatically store them in the vault** via `upsertFeedDestinationCredentials`.

```typescript
protected onAuthentication(
  request: ExternalSegmentAuthenticationRequest
): Promise<ExternalSegmentAuthenticationResponse>
```

#### Input: `ExternalSegmentAuthenticationRequest`

| Field                 | Type                                 | Description                                                                                                                            |
| --------------------- | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| `feed_destination_id` | `string` (optional)                  | Present in the new destination flow. When set, the SDK automatically upserts the `credentials` returned by this method into the vault. |
| `params`              | `Record<string, unknown>` (optional) | For OAuth2: contains the redirected URL with the authorization `code`. For Token/Login-Password: contains the form field values.       |
| `datamart_id`         | `string`                             | The datamart ID.                                                                                                                       |
| `plugin_version_id`   | `string`                             | The plugin version ID.                                                                                                                 |
| `user_id`             | `string`                             | The user who triggered the authentication.                                                                                             |

#### Return: `ExternalSegmentAuthenticationResponse`

| Field         | Type                                    | Description                                                                                |
| ------------- | --------------------------------------- | ------------------------------------------------------------------------------------------ |
| `status`      | `'ok'` \| `'error'`                     | Result of the authentication attempt.                                                      |
| `credentials` | `FeedDestinationCredentials` (optional) | Required when `feed_destination_id` is present. The credentials to be stored in the vault. |
| `message`     | `string` (optional)                     | Human-readable error message when `status` is `'error'`.                                   |

#### `FeedDestinationCredentials` structure

```typescript
interface FeedDestinationCredentials {
  scheme: 'OAUTH2' | 'TOKEN' | 'LOGIN_PASSWORD';
  credentials: Record<string, unknown>;
  // e.g. { refresh_token: '...' }        for OAUTH2
  // e.g. { api_token: '...' }            for TOKEN
  // e.g. { login: '...', password: '...' } for LOGIN_PASSWORD
}
```

#### Code example - OAuth2

```typescript
protected async onAuthentication(
  request: core.ExternalSegmentAuthenticationRequest,
): Promise<core.ExternalSegmentAuthenticationResponse> {
  const { code } = request.params as { code: string };

  // Exchange the authorization code for tokens
  const { refreshToken } = await this.partnerClient.exchangeCodeForTokens(code);

  // Return credentials - the SDK stores them in the vault automatically
  return {
    status: 'ok',
    credentials: {
      scheme: 'OAUTH2',
      credentials: { refresh_token: refreshToken },
    },
  };
}
```

#### Code example - Token

```typescript
protected async onAuthentication(
  request: core.ExternalSegmentAuthenticationRequest,
): Promise<core.ExternalSegmentAuthenticationResponse> {
  const { api_token } = request.params as { api_token: string };

  // Validate the token against the partner API before storing it
  const isValid = await this.partnerClient.validateToken(api_token);
  if (!isValid) {
    return { status: 'error', message: 'Invalid API token' };
  }

  return {
    status: 'ok',
    credentials: {
      scheme: 'TOKEN',
      credentials: { api_token },
    },
  };
}
```

### `onTestAuthentication`

Called by the platform immediately after `onAuthentication` succeeds, and then periodically by a background job to keep destination statuses up to date. The SDK automatically fetches the credentials from the vault and passes them as the second argument.

```typescript
protected onTestAuthentication(
  request: TestAuthenticationRequest,
  credentials: FeedDestinationCredentials
): Promise<TestAuthenticationPluginResponse>
```

#### Input: `TestAuthenticationRequest`

| Field                 | Type     | Description                                         |
| --------------------- | -------- | --------------------------------------------------- |
| `feed_destination_id` | `string` | The destination whose credentials are being tested. |
| `datamart_id`         | `string` | The datamart ID.                                    |
| `plugin_version_id`   | `string` | The plugin version ID.                              |

#### Input: `credentials`

The `FeedDestinationCredentials` object fetched automatically by the SDK from the vault using `feed_destination_id`. This is the object stored during `onAuthentication`.

#### Return: `TestAuthenticationPluginResponse`

| `status`                | HTTP code | When to use                                                                                                                                                                                      |
| ----------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `'ok'`                  | 200       | Credentials are valid. Destination status is set to **Ready**.                                                                                                                                   |
| `'invalid_credentials'` | 401       | The partner explicitly rejected the credentials (e.g. token revoked). Destination status is set to **Connection Error**. Use this to distinguish credential rejection from operational failures. |
| `'error'`               | 500       | Unexpected or operational error (network failure, partner API down).                                                                                                                             |
| `'not_implemented'`     | 400       | Method not overridden. Default SDK behavior.                                                                                                                                                     |

All statuses except `'ok'` can include an optional `message` string for additional context.

#### Code example

```typescript
protected async onTestAuthentication(
  request: core.TestAuthenticationRequest,
  credentials: core.FeedDestinationCredentials,
): Promise<core.TestAuthenticationPluginResponse> {
  if (!credentials?.credentials?.refresh_token) {
    return { status: 'error', message: 'No refresh token found in vault' };
  }

  try {
    await this.partnerClient.validateRefreshToken(
      credentials.credentials.refresh_token as string
    );
    return { status: 'ok' };
  } catch (e) {
    if (e.isUnauthorized) {
      // The partner rejected the credentials - distinct from an operational error
      return {
        status: 'invalid_credentials',
        message: 'Refresh token has been revoked or expired',
      };
    }
    return { status: 'error', message: `Unexpected error: ${e.message}` };
  }
}
```

### `onLogout`

Called when the user removes the authentication from a destination. The plugin should revoke the token at the partner's end and clean up any stored state.

```typescript
protected onLogout(
  request: ExternalSegmentLogoutRequest
): Promise<ExternalSegmentLogoutResponse>
```

#### Input: `ExternalSegmentLogoutRequest`

| Field                 | Type                | Description                       |
| --------------------- | ------------------- | --------------------------------- |
| `feed_destination_id` | `string` (optional) | The destination being logged out. |
| `datamart_id`         | `string`            | The datamart ID.                  |
| `plugin_version_id`   | `string`            | The plugin version ID.            |
| `user_id`             | `string`            | The user triggering the logout.   |

#### Return: `ExternalSegmentLogoutResponse`

| Field     | Type                | Description                               |
| --------- | ------------------- | ----------------------------------------- |
| `status`  | `'ok'` \| `'error'` | Result of the logout operation.           |
| `message` | `string` (optional) | Error message when `status` is `'error'`. |

#### Code example

```typescript
protected async onLogout(
  request: core.ExternalSegmentLogoutRequest,
): Promise<core.ExternalSegmentLogoutResponse> {
  if (request.feed_destination_id) {
    const credentials = await this.fetchFeedDestinationCredentials(
      request.feed_destination_id
    );
    await this.partnerClient.revokeToken(
      credentials.credentials.refresh_token as string
    );
  }
  return { status: 'ok' };
}
```

### SDK helper methods

The base plugin class exposes the following helpers for credential management. You can call them from any method in your plugin.

#### `fetchFeedDestinationCredentials`

Fetches the credentials stored in the vault for a given destination. Throws a `ResourceNotFoundError` if no credentials exist.

```typescript
protected async fetchFeedDestinationCredentials(
  feedDestinationId: string
): Promise<FeedDestinationCredentials>
```

#### `fetchFeedDestinationCredentialsOptional`

Same as above, but returns `undefined` instead of throwing when no credentials are found. Useful to implement a fallback on legacy credential files during migration.

```typescript
protected async fetchFeedDestinationCredentialsOptional(
  feedDestinationId: string
): Promise<FeedDestinationCredentials | undefined>
```

#### `upsertFeedDestinationCredentials`

Creates or updates credentials in the vault for a given destination. In the standard flow, the SDK calls this automatically from `onAuthentication`. Call it manually only when managing credentials outside the authentication flow.

```typescript
protected async upsertFeedDestinationCredentials(
  feedDestinationId: string,
  credentials: FeedDestinationCredentials
): Promise<void>
```

### Note on `instanceContextBuilder`

Since SDK v0.41.0, when a request carries a `feed_destination_id`, the SDK fetches the destination credentials from the vault and passes them as an optional second argument to `instanceContextBuilder`. Use them to initialize your partner client instead of reading from a credential file:

```typescript
protected async instanceContextBuilder(
  feedId: string,
  feedDestinationCredentials?: core.FeedDestinationCredentials,
): Promise<InstanceContext> {
  const ctx = await super.instanceContextBuilder(feedId, feedDestinationCredentials);

  // Use vault credentials when available, fall back to legacy credential file
  const refreshToken =
    (feedDestinationCredentials?.credentials?.refresh_token as string) ??
    (await this.fetchLegacyRefreshToken(ctx));

  const partnerClient = new PartnerClient({ refreshToken });

  return { ...ctx, partnerClient };
}
```

The same credentials are also passed to `onUserSegmentUpdate`, `onTroubleshoot`, and `onDynamicPropertyValuesQuery`.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://developer.mediarithmics.io/advanced-usages/plugins/creation/destinations-and-authentication/authentication-methods.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
