SOP 002: Authentication Setup
Fresh Last Updated: January 2025| Field | Value |
|---|---|
| SOP ID | SOP-002 |
| Version | 1.0 |
| Status | Active |
| Difficulty | Beginner |
Purpose
Configure secure authentication for your Notion API integration, including environment setup and the JavaScript SDK.
Prerequisites
- [ ] Completed SOP 001: Create Integration
- [ ] Node.js v14+ installed
- [ ] API secret from your integration
Authentication Flow
Step-by-Step Instructions
Step 1: Install Dependencies
bash
# Create a new project
mkdir my-notion-app
cd my-notion-app
npm init -y
# Install required packages
npm install @notionhq/client dotenvStep 2: Create Environment File
Create .env in your project root:
env
# Notion API Configuration
NOTION_API_KEY=secret_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Optional: Default page/database IDs
NOTION_DATABASE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
NOTION_PAGE_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxSecurity Warning
Add .env to your .gitignore immediately:
bash
echo ".env" >> .gitignoreStep 3: Initialize the Notion Client
Create notion.js:
javascript
require('dotenv').config();
const { Client } = require('@notionhq/client');
// Validate environment variable
if (!process.env.NOTION_API_KEY) {
throw new Error('NOTION_API_KEY environment variable is required');
}
// Initialize with authentication
const notion = new Client({
auth: process.env.NOTION_API_KEY,
// Optional: customize behavior
timeoutMs: 30000, // 30 second timeout
notionVersion: '2022-06-28' // API version
});
module.exports = notion;Step 4: Create a Reusable API Wrapper
For production applications, create a wrapper with error handling:
javascript
// api/notion-client.js
require('dotenv').config();
const { Client, APIErrorCode, isNotionClientError } = require('@notionhq/client');
class NotionAPI {
constructor() {
this.client = new Client({
auth: process.env.NOTION_API_KEY
});
}
async execute(operation) {
try {
return await operation();
} catch (error) {
if (isNotionClientError(error)) {
switch (error.code) {
case APIErrorCode.Unauthorized:
throw new Error('Invalid API key. Check your NOTION_API_KEY.');
case APIErrorCode.ObjectNotFound:
throw new Error('Resource not found. Ensure integration has access.');
case APIErrorCode.RateLimited:
// Wait and retry
const retryAfter = error.headers?.['retry-after'] || 1;
await this.sleep(retryAfter * 1000);
return this.execute(operation);
default:
throw error;
}
}
throw error;
}
}
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
// Convenience methods
async getPage(pageId) {
return this.execute(() =>
this.client.pages.retrieve({ page_id: pageId })
);
}
async queryDatabase(databaseId, filter = {}, sorts = []) {
return this.execute(() =>
this.client.databases.query({
database_id: databaseId,
filter,
sorts
})
);
}
async createPage(databaseId, properties, children = []) {
return this.execute(() =>
this.client.pages.create({
parent: { database_id: databaseId },
properties,
children
})
);
}
}
module.exports = new NotionAPI();Step 5: HTTP Header Authentication (Without SDK)
If you're not using the SDK, include the header manually:
javascript
const response = await fetch('https://api.notion.com/v1/users/me', {
method: 'GET',
headers: {
'Authorization': `Bearer ${process.env.NOTION_API_KEY}`,
'Notion-Version': '2022-06-28',
'Content-Type': 'application/json'
}
});
const data = await response.json();Step 6: Verify Authentication
Create test-auth.js:
javascript
const notion = require('./notion');
async function verifyAuth() {
console.log('Testing Notion API authentication...\n');
try {
// Test 1: Get bot user
const me = await notion.users.me({});
console.log('✅ Authentication successful');
console.log(` Bot: ${me.name}`);
console.log(` Type: ${me.type}`);
console.log(` ID: ${me.id}\n`);
// Test 2: List users (requires user read capability)
try {
const users = await notion.users.list({});
console.log(`✅ Can list users (${users.results.length} found)`);
} catch (e) {
console.log('⚠️ Cannot list users (capability not enabled)');
}
// Test 3: Search (requires content read capability)
try {
const search = await notion.search({ query: '', page_size: 1 });
console.log(`✅ Can search content (${search.results.length} results)`);
} catch (e) {
console.log('⚠️ Cannot search (no pages shared with integration)');
}
console.log('\n✅ All authentication tests passed!');
} catch (error) {
console.error('❌ Authentication failed:', error.message);
process.exit(1);
}
}
verifyAuth();Run the test:
bash
node test-auth.jsAuthentication Types
| Type | Use Case | Token Management |
|---|---|---|
| Internal | Your own workspace | Single API secret |
| Public | Third-party users | OAuth per user |
Verification Checklist
- [ ] Environment file created with API key
- [ ]
.envadded to.gitignore - [ ] Notion client initializes without errors
- [ ]
users.me()returns bot information - [ ] Error handling implemented for common cases
Troubleshooting
| Error | Code | Solution |
|---|---|---|
| Unauthorized | 401 | Check API key format starts with secret_ |
| Invalid token | 401 | Regenerate token in integration settings |
| Forbidden | 403 | Enable required capability |
| Not found | 404 | Share page with integration |