notiflowsDocs

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:

When Security Mode is enabled (default), requests require:

  • x-notiflows-api-key: Your project's public API key
  • x-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 key
  • x-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:

  1. Go to ProjectsSettingsAPI Keys
  2. Find the Client Authentication section
  3. Click Generate signing key
  4. 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

ClaimRequiredDescription
subYesThe user's external ID (must match the user registered in Notiflows)
iatRecommendedUnix timestamp when the token was issued
expRecommendedUnix timestamp when the token expires
issOptionalIssuer 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:

  1. Generate a new signing key in the dashboard
  2. Update your backend with the new private key
  3. Deploy the changes
  4. 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-id header with the user's external ID

401 Unauthorized - User not found

  • The user ID (from JWT sub claim or x-notiflows-user-id header) doesn't match any user in your project
  • Create the user via the Admin API first

On this page