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>"
{
"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
Send the API request
Send the request with the current access token.
Handle expired token
If the API returns 401, call POST /api/auth/refresh.
Retry once
Retry the original request once with the new access token.
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:
- Tenant backend signs an Embed Token (JWT).
- Embedded frontend passes the token to NSDK loader/container.
- 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.