NIN Connect Docs
Platform (Self-Host)Security

Connect Tokens

HMAC-signed tokens that prevent unauthenticated credential injection and OAuth flow abuse.

Overview

The connect flow lets end-users submit credentials (API keys, tokens) or start OAuth authorization for a plugin. These endpoints need to be publicly accessible because the user's browser hits them directly, but they also need to be tied back to an authenticated session.

Connect tokens solve this by binding every connect flow to a specific developer, user, and plugin with a time-limited HMAC signature.

The Problem

Without connect tokens, the credential submission endpoints accept a developerId, userId, and pluginSlug in the request body. An attacker who knows these values could:

  • Submit fake credentials for another user
  • Overwrite legitimate credentials with their own
  • Initiate OAuth flows on behalf of arbitrary users

How It Works

Token Generation

When the SDK or dashboard calls the connection initiation endpoint, the server generates a connect token:

const token = generateConnectToken(developerId, userId, pluginSlug);
// returns: base64url(payload) + "." + hmac_signature

The payload contains:

FieldDescription
developerIdThe developer who owns the plugin
userIdThe end-user being connected
pluginSlugThe target plugin
expExpiration timestamp (30 minutes from creation)
nonceRandom value to prevent replay

The payload is signed with HMAC-SHA256 using ENCRYPTION_KEY.

Token Verification

When the user submits credentials or starts an OAuth flow, the server verifies:

  1. The signature is valid (not tampered with)
  2. The token has not expired
  3. The developerId, userId, and pluginSlug match the request

If any check fails, the request is rejected with a 403.

Protected Endpoints

POST /api/connect/submit

Accepts direct credential submissions (API keys, bearer tokens). The request body must include a connectToken field:

{
  "developerId": "uuid",
  "userId": "user_123",
  "pluginSlug": "slack",
  "authMethodId": "uuid",
  "credentials": { "api_key": "xoxb-..." },
  "connectToken": "eyJ...signature"
}

POST /api/connect/oauth-start

Initiates OAuth authorization. The connect token ensures that only legitimately initiated flows can start an OAuth handshake. The mcpClientSecret is sent in the POST body instead of as a query parameter, preventing accidental logging of secrets in server access logs.

Token Flow

1. SDK calls POST /api/v1/connections/initiate (authenticated via API key)
   → Server generates connectToken
   → Returns connect page URL with token embedded

2. User lands on /connect/[plugin] page
   → Page receives connectToken from URL

3. User submits credentials → POST /api/connect/submit { connectToken, ... }
   → Server verifies token → accepts or rejects

   OR

   User starts OAuth → POST /api/connect/oauth-start { connectToken, ... }
   → Server verifies token → redirects to OAuth provider

Expiration

Tokens expire after 30 minutes by default. This window is long enough for a user to complete the connect flow but short enough to limit the attack surface if a token leaks.

Relationship to OAuth State Tokens

Connect tokens and OAuth state tokens serve different purposes:

Connect TokenOAuth State Token
WhenBefore OAuth or direct submissionDuring OAuth redirect flow
PurposeBinds the connect flow to an authenticated sessionPrevents CSRF during OAuth callback
Signed withHMAC-SHA256 (ENCRYPTION_KEY)HMAC-SHA256 (ENCRYPTION_KEY)
Lifetime30 minutesDuration of the OAuth redirect
Verified at/api/connect/submit, /api/connect/oauth-start/api/v1/oauth/callback

On this page