Skip to main content
Internal Documentation Only: If you’re not a Null Tools developer, you can close this documentation or visit the Apps section to learn more about using Null Pass in your applications.

Overview

Null Pass uses JWT (JSON Web Tokens) for authentication. After successful registration or login, you’ll receive a JWT token that must be included in subsequent API requests.

Authentication Flow

1

Register or Login

Users register with email and password, or login with existing credentials. If 2FA is enabled, an additional verification step is required.
2

Receive Token

Upon successful authentication, you receive a JWT token. Store this securely in your application.
3

Include in Requests

Include the token in the Authorization header for all protected endpoints:
Authorization: Bearer <your_token>
4

Token Expiration

Tokens expire after 7 days. Implement token refresh logic or prompt users to log in again.

Token Format

Tokens are standard JWT tokens that contain user information:
{
  "userId": "clx1234567890",
  "email": "user@example.com",
  "iat": 1234567890,
  "exp": 1235173890
}
Never expose tokens in client-side code, URLs, or logs. Treat tokens as sensitive credentials.

Two-Factor Authentication

Null Pass supports TOTP (Time-based One-Time Password) for enhanced security. When 2FA is enabled:
  1. User logs in with email and password
  2. API responds with requires2FA: true and a pendingToken
  3. User provides verification code from authenticator app
  4. API validates code and returns full authentication token

Session Management

Null Pass tracks user sessions for security and audit purposes. Each login creates a new session that:
  • Is tied to the user’s IP address (encrypted)
  • Expires after 7 days
  • Can be viewed and managed via the sessions API

Manage Sessions

View and delete active sessions

Security Best Practices

  • Use secure storage (keychain, secure storage, environment variables)
  • Never store tokens in localStorage for sensitive applications
  • Consider using httpOnly cookies for web applications
  • Implement token refresh logic
  • Handle 401 responses gracefully
  • Prompt users to re-authenticate when tokens expire
  • Always use HTTPS in production
  • Never send tokens over unencrypted connections
  • Validate SSL certificates
  • Respect rate limits on your end
  • Implement exponential backoff
  • Cache responses where appropriate

Example: Complete Authentication Flow

class NullPassAuth {
  constructor(apiUrl) {
    this.apiUrl = apiUrl;
    this.token = null;
  }

  async register(email, password, displayName) {
    const response = await fetch(`${this.apiUrl}/auth/register`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password, displayName })
    });
    
    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.error);
    }
    
    const data = await response.json();
    this.token = data.token;
    return data;
  }

  async login(email, password, verificationCode = null) {
    const body = { email, password };
    if (verificationCode) {
      body.verificationCode = verificationCode;
    }

    const response = await fetch(`${this.apiUrl}/auth/login`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(body)
    });
    
    if (!response.ok) {
      const error = await response.json();
      throw new Error(error.error);
    }
    
    const data = await response.json();
    
    if (data.requires2FA) {
      return { requires2FA: true, pendingToken: data.pendingToken };
    }
    
    this.token = data.token;
    return data;
  }

  async getProfile() {
    if (!this.token) {
      throw new Error('Not authenticated');
    }

    const response = await fetch(`${this.apiUrl}/auth/me`, {
      headers: { 'Authorization': `Bearer ${this.token}` }
    });
    
    if (!response.ok) {
      if (response.status === 401) {
        this.token = null; // Token expired
        throw new Error('Authentication required');
      }
      const error = await response.json();
      throw new Error(error.error);
    }
    
    return await response.json();
  }
}

// Usage
const auth = new NullPassAuth('https://auth.nullpass.xyz/api');

// Register
try {
  const result = await auth.register('user@example.com', 'password123', 'John Doe');
  console.log('Registered:', result.user);
} catch (error) {
  console.error('Registration failed:', error.message);
}

// Login
try {
  const result = await auth.login('user@example.com', 'password123');
  if (result.requires2FA) {
    // Prompt for 2FA code
    const code = prompt('Enter 2FA code:');
    const finalResult = await auth.login('user@example.com', 'password123', code);
    console.log('Logged in:', finalResult.user);
  } else {
    console.log('Logged in:', result.user);
  }
} catch (error) {
  console.error('Login failed:', error.message);
}

// Get profile
try {
  const profile = await auth.getProfile();
  console.log('Profile:', profile.user);
} catch (error) {
  console.error('Failed to get profile:', error.message);
}

Next Steps