Setting Up YouTube OAuth2 Authentication
YouTube Data API requires OAuth2 authentication for uploading videos and managing channels.
Prerequisites
- Google Cloud Project
- YouTube Data API v3 enabled
- OAuth 2.0 Client ID and Secret
Step 1: Create Google Cloud Project
- Go to Google Cloud Console
- Create a new project
- Enable YouTube Data API v3:
- Navigate to APIs & Services → Library
- Search "YouTube Data API v3"
- Click "Enable"
Step 2: Create OAuth Credentials
- Navigate to APIs & Services → Credentials
- Click "Create Credentials" → "OAuth client ID"
- Configure:
- Application type: Desktop app
- Name:
Your App YouTube Uploader
- Click "Create"
- Save your Client ID and Client Secret
Step 3: Configure Redirect URI
The redirect URI is constructed automatically:
http://localhost:{port}/oauth/callback
Default port: 9671
If port is in use, change it in config and register new redirect URI in Google Cloud Console.
Step 4: Store Credentials
Create config.json (gitignored):
{
"youtube": {
"enabled": true,
"clientId": "YOUR_CLIENT_ID.apps.googleusercontent.com",
"clientSecret": "GOCSPX-YOUR_CLIENT_SECRET",
"redirectUri": "/oauth/callback",
"port": 9671,
"defaultPrivacy": "private",
"autoUpload": false
}
}
Step 5: Implement OAuth Flow
import { google } from 'googleapis';
import { OAuth2Client } from 'google-auth-library';
interface YouTubeConfig {
clientId: string;
clientSecret: string;
redirectUri: string;
port: number;
}
class AuthManager {
private oauth2Client: OAuth2Client;
private config: YouTubeConfig;
constructor(config: YouTubeConfig) {
this.config = config;
this.oauth2Client = new OAuth2Client(
config.clientId,
config.clientSecret,
`http://localhost:${config.port}${config.redirectUri}`
);
}
getOAuth2Client(): OAuth2Client {
return this.oauth2Client;
}
async authenticate(): Promise<void> {
// Check for existing tokens
if (this.hasStoredTokens()) {
this.oauth2Client.setCredentials(this.loadStoredTokens());
return;
}
// Generate auth URL
const authUrl = this.oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: [
'https://www.googleapis.com/auth/youtube.upload',
'https://www.googleapis.com/auth/youtube.readonly',
'https://www.googleapis.com/auth/yt-analytics.readonly',
],
});
// Open browser for user consent
console.log(`Opening browser: ${authUrl}`);
await this.openBrowser(authUrl);
// Wait for callback...
const code = await this.waitForCallback();
// Exchange code for tokens
const { tokens } = await this.oauth2Client.getToken(code);
this.oauth2Client.setCredentials(tokens);
this.saveTokens(tokens);
}
}
OAuth Scopes
| Scope | Purpose |
|---|---|
https://www.googleapis.com/auth/youtube.upload |
Upload videos |
https://www.googleapis.com/auth/youtube.readonly |
View channel data |
https://www.googleapis.com/auth/yt-analytics.readonly |
View analytics (24hr views) |
Step 6: Handle Callback
import http from 'http';
async function waitForCallback(): Promise<string> {
return new Promise((resolve, reject) => {
const server = http.createServer((req, res) => {
if (req.url?.startsWith('/oauth/callback')) {
const url = new URL(req.url, `http://localhost:${this.config.port}`);
const code = url.searchParams.get('code');
if (code) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Authentication successful! You can close this window.');
server.close();
resolve(code);
} else {
res.writeHead(400);
res.end('No code found');
reject(new Error('No authorization code'));
}
}
});
server.listen(this.config.port);
});
}
Token Storage
import fs from 'fs';
import path from 'path';
const TOKEN_FILE = '.youtube-tokens.json';
function saveTokens(tokens: any): void {
fs.writeFileSync(TOKEN_FILE, JSON.stringify(tokens, null, 2));
}
function loadStoredTokens(): any {
if (fs.existsSync(TOKEN_FILE)) {
return JSON.parse(fs.readFileSync(TOKEN_FILE, 'utf-8'));
}
return null;
}
function hasStoredTokens(): boolean {
return fs.existsSync(TOKEN_FILE);
}
Re-authentication
To re-authenticate (different account or new scopes):
rm .youtube-tokens.json
# Run app again - will trigger OAuth flow