Authentication
Secure your client-side API requests with application signing keys
User API Authentication
The User API authenticates requests using your project's API key and user identification.
Security Modes
Notiflows supports two security modes for user authentication:
Secure Mode (Recommended)
When Security Mode is enabled (default), requests require:
x-notiflows-api-key: Your project's public API keyx-notiflows-user-key: A JWT token signed with your Application Signing Key (RS256)
How it works:
- Your backend signs JWTs with the private key
- Notiflows verifies JWTs with the public key (stored securely in your project)
- The private key never leaves your infrastructure
Development Mode
When Security Mode is disabled, requests require:
x-notiflows-api-key: Your project's public API keyx-notiflows-user-id: The user's external ID (no signing required)
This mode is useful for local development and testing, as it doesn't require JWT signing.
Development mode is NOT recommended for production. Anyone with your API key could impersonate any user. Always enable Security Mode and use Application Signing Keys for production deployments.
Setting Up Application Signing Keys
1. Generate a Signing Key
Navigate to your project settings in the Notiflows dashboard:
- Go to Projects → Settings → API Keys
- Find the Client Authentication section
- Click Generate signing key
- Save the private key securely - it will only be shown once
The private key is provided in two formats:
- Base64-encoded PEM: Single-line format, ideal for environment variables
- Raw PEM: Standard PEM format for file storage
2. Enable Security Mode
Security Mode is enabled by default. If disabled, enable it in the same Client Authentication section by toggling the Security mode switch.
Generating User Tokens
Your backend must generate a JWT for each user that needs to access the User API. The JWT should contain:
{
"sub": "user_external_id", // Required: The user's external ID in your system
"iat": 1608600116, // Recommended: Issued at timestamp
"exp": 1608603716 // Recommended: Expiry timestamp
}Token Claims
| Claim | Required | Description |
|---|---|---|
sub | Yes | The user's external ID (must match the user registered in Notiflows) |
iat | Recommended | Unix timestamp when the token was issued |
exp | Recommended | Unix timestamp when the token expires |
iss | Optional | Issuer identifier |
The sub claim must match the externalId of a user created in your Notiflows project via the Admin API.
Code Examples
Node.js
import jwt from 'jsonwebtoken';
// Load your private key (from environment variable or file)
const privateKey = Buffer.from(process.env.NOTIFLOWS_SIGNING_KEY, 'base64').toString();
function generateUserToken(userExternalId) {
const payload = {
sub: userExternalId,
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + (60 * 60), // 1 hour expiry
};
return jwt.sign(payload, privateKey, { algorithm: 'RS256' });
}
// Generate a token for a user
const userToken = generateUserToken('user_123');Python
import jwt
import time
import base64
import os
# Load your private key (from environment variable)
private_key_b64 = os.environ.get('NOTIFLOWS_SIGNING_KEY')
private_key = base64.b64decode(private_key_b64).decode('utf-8')
def generate_user_token(user_external_id: str) -> str:
payload = {
'sub': user_external_id,
'iat': int(time.time()),
'exp': int(time.time()) + 3600, # 1 hour expiry
}
return jwt.encode(payload, private_key, algorithm='RS256')
# Generate a token for a user
user_token = generate_user_token('user_123')Ruby
require 'jwt'
require 'base64'
# Load your private key (from environment variable)
private_key_pem = Base64.decode64(ENV['NOTIFLOWS_SIGNING_KEY'])
private_key = OpenSSL::PKey::RSA.new(private_key_pem)
def generate_user_token(user_external_id)
payload = {
sub: user_external_id,
iat: Time.now.to_i,
exp: Time.now.to_i + 3600 # 1 hour expiry
}
JWT.encode(payload, private_key, 'RS256')
end
# Generate a token for a user
user_token = generate_user_token('user_123')Elixir
defmodule MyApp.Notiflows do
def generate_user_token(user_external_id) do
private_key_b64 = System.get_env("NOTIFLOWS_SIGNING_KEY")
private_key_pem = Base.decode64!(private_key_b64)
payload = %{
"sub" => user_external_id,
"iat" => :os.system_time(:second),
"exp" => :os.system_time(:second) + 3600
}
jwk = JOSE.JWK.from_pem(private_key_pem)
{_, token} = JOSE.JWT.sign(jwk, %{"alg" => "RS256"}, payload) |> JOSE.JWS.compact()
token
end
end
# Generate a token for a user
user_token = MyApp.Notiflows.generate_user_token("user_123")Making API Requests
Secure Mode (Production)
Include the signed JWT in the headers:
curl -X GET "https://api.notiflows.com/api/user/v1/channels/{channel_id}/feed/entries" \
-H "x-notiflows-api-key: pk_your_api_key" \
-H "x-notiflows-user-key: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."Development Mode
Pass the user ID directly in the header:
curl -X GET "https://api.notiflows.com/api/user/v1/channels/{channel_id}/feed/entries" \
-H "x-notiflows-api-key: pk_your_api_key" \
-H "x-notiflows-user-id: user_123"Client-Side SDK Example
import { NotiflowsClient } from '@notiflows/client';
// Secure mode: Initialize with user token from your backend
const notiflows = new NotiflowsClient({
apiKey: 'pk_your_api_key',
userToken: userTokenFromBackend,
});
// Development mode: Initialize with user ID directly (NOT for production)
const notiflowsDev = new NotiflowsClient({
apiKey: 'pk_your_api_key',
userId: 'user_123',
});
// Fetch the user's notification feed
const entries = await notiflows.feed.list();Best Practices
Token Expiration
Always set a reasonable expiration time on tokens:
- Short-lived tokens (1 hour or less) are more secure
- Implement token refresh in your frontend when tokens expire
- Never use tokens without expiration in production
Private Key Security
- Store the private key securely (environment variables, secrets manager)
- Never expose the private key in client-side code
- Never commit the private key to version control
- Rotate keys periodically or if compromised
Key Rotation
To rotate your signing key:
- Generate a new signing key in the dashboard
- Update your backend with the new private key
- Deploy the changes
- Existing tokens will be invalidated immediately
Regenerating a signing key invalidates all existing user tokens. Users will need to re-authenticate.
Troubleshooting
Common Errors
401 Unauthorized - Invalid user key signature
- Verify you're using the correct private key
- Ensure Security Mode is enabled if using
x-notiflows-user-key - Check the token hasn't expired
401 Unauthorized - Signing key not configured
- Generate a signing key in your project settings
- Security Mode is enabled but no signing key has been generated
401 Unauthorized - Missing x-notiflows-user-key header
- Security Mode is enabled but you're not sending the JWT
- Either provide a signed JWT or disable Security Mode for development
401 Unauthorized - Missing x-notiflows-user-id header
- Security Mode is disabled but you're not sending the user ID header
- Provide
x-notiflows-user-idheader with the user's external ID
401 Unauthorized - User not found
- The user ID (from JWT
subclaim orx-notiflows-user-idheader) doesn't match any user in your project - Create the user via the Admin API first