Skip to main content

Authentication

Narrative SDK v1.5 uses JWT-based access and refresh token flows.

Auth endpoints

  • POST /api/auth/login
  • POST /api/auth/refresh
  • POST /api/auth/logout
  • GET /api/auth/me

Request authorization

Most API calls are authenticated with:
Authorization: Bearer <accessToken>

Endpoint examples

POST /api/auth/login

curl -X POST "<NSDK_API_BASE_URL>/api/auth/login" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "<password>"
  }'
{
  "accessToken": "jwt.access.token",
  "refreshToken": "jwt.refresh.token",
  "userId": "1005",
  "tenantId": "2001"
}
{
  "detail": "Unauthorized",
  "request_id": "req_abc123"
}

POST /api/auth/refresh

curl -X POST "<NSDK_API_BASE_URL>/api/auth/refresh" \
  -H "Content-Type: application/json" \
  -d '{
    "refreshToken": "<refresh-jwt>"
  }'
{
  "accessToken": "jwt.new.access.token",
  "refreshToken": "jwt.new.refresh.token"
}
{
  "detail": "Unauthorized",
  "request_id": "req_abc123"
}

POST /api/auth/logout

curl -X POST "<NSDK_API_BASE_URL>/api/auth/logout" \
  -H "Authorization: Bearer <access-jwt>"
{
  "ok": true
}
{
  "detail": "Rate limit exceeded",
  "request_id": "req_abc123"
}

GET /api/auth/me

curl "<NSDK_API_BASE_URL>/api/auth/me" \
  -H "Authorization: Bearer <access-jwt>"
{
  "userId": "1005",
  "tenantId": "2001",
  "email": "user@example.com"
}
{
  "detail": "Unauthorized",
  "request_id": "req_abc123"
}

Refresh behavior

1

Send the API request

Send the request with the current access token.
2

Handle expired token

If the API returns 401, call POST /api/auth/refresh.
3

Retry once

Retry the original request once with the new access token.
4

Fail closed

If refresh fails with 401, clear session state and redirect to sign-in.

Embedded auth contract (tenant integration)

Narrative embedded auth uses a token-exchange pattern:
  1. Tenant backend signs an Embed Token (JWT).
  2. Embedded frontend passes the token to NSDK loader/container.
  3. NSDK backend verifies token and issues tenant-scoped session tokens.

Embed Token requirements

  • JWT standard: RFC 7519
  • Algorithm: HS256
  • Audience (aud): nsdk-embed
  • Recommended expiry: 5-15 minutes
Required claims:
  • iss: connected app client id
  • sub: tenant user identifier
  • aud: nsdk-embed
  • iat: issued-at unix timestamp (seconds)
  • exp: expiry unix timestamp (seconds)
  • jti: unique token id
Required user fields in nsdk namespace:
  • nsdk.user.name
  • nsdk.user.email
Example payload:
{
  "iss": "your-connected-app-client-id",
  "sub": "tenant-user-id",
  "aud": "nsdk-embed",
  "iat": 1710000000,
  "exp": 1710000900,
  "jti": "uuid",
  "nsdk": {
    "user": { "name": "Alice", "email": "alice@company.com" },
    "metadata": { "source": "tenant_app" }
  }
}

Security baseline

Keep connected app secrets on the tenant backend only. Never expose secrets or token-signing logic to browser code.
  • Do not store Embed Tokens in localStorage.
  • Prefer same-origin token minting endpoints or strict CORS controls.
  • Use HTTPS end-to-end.