@yotoplay/oauth-device-code-flow
v1.2.1
Published
Auth0 device code authentication flow for Node.js applications
Readme
@yotoplay/oauth-device-code-flow
A TypeScript library for implementing Auth0 Device Code Flow authentication in Node.js applications. Perfect for CLI tools, IoT devices, and applications that need to authenticate users on devices with limited input capabilities.
Features
- 🔐 Complete Device Code Flow: Full implementation of OAuth 2.0 Device Authorization Grant
- 🔄 Token Management: Built-in token storage, validation, and refresh capabilities
Installation
npm install @yotoplay/oauth-device-code-flowQuick Start
import { DeviceCodeAuth, TokenManager } from '@yotoplay/oauth-device-code-flow';
// Initialize the auth client
const auth = new DeviceCodeAuth({
domain: 'your-domain.auth0.com',
clientId: 'your-client-id',
audience: 'your-api-audience'
});
// Initialize token manager
const tokenManager = new TokenManager('./tokens.json');
// Start the device code flow
async function authenticate() {
// Step 1: Initiate device code flow (with custom scopes)
const deviceCodeResult = await auth.initiateDeviceCodeFlow('openid profile email offline_access content:write media:write');
if (!deviceCodeResult.success) {
console.error('Failed to initiate auth:', deviceCodeResult.error);
return;
}
// Step 2: Display instructions to user
console.log('Please visit:', deviceCodeResult.verificationUri);
console.log('And enter code:', deviceCodeResult.userCode);
// Step 3: Poll for tokens
const tokenResult = await auth.pollForToken(
deviceCodeResult.deviceCode,
deviceCodeResult.interval,
300000 // 5 minute timeout
);
if (tokenResult.success) {
// Step 4: Save tokens
await tokenManager.saveTokens(tokenResult.tokens);
console.log('Authentication successful!');
} else {
console.error('Authentication failed:', tokenResult.error);
}
}
authenticate();API Reference
DeviceCodeAuth
Main class for handling Auth0 device code authentication.
Constructor
new DeviceCodeAuth(config: Auth0Config)Parameters:
config.domain: Your Auth0 domain (e.g.,'your-domain.auth0.com')config.clientId: Your Auth0 application client IDconfig.audience: Your API audience identifier
Methods
initiateDeviceCodeFlow(scope?)
Initiates the device code flow and returns device code information.
Parameters:
scope: Optional OAuth scopes (default:"openid profile email offline_access")
Returns: Promise<DeviceCodeResult>
// Use default scopes
const result = await auth.initiateDeviceCodeFlow();
// Use custom scopes
const result = await auth.initiateDeviceCodeFlow('openid profile email offline_access content:write media:write');
if (result.success) {
console.log('Visit:', result.verificationUri);
console.log('Code:', result.userCode);
}pollForToken(deviceCode, interval, timeout?)
Polls Auth0 for token completion. Handles all OAuth2 error scenarios automatically.
Parameters:
deviceCode: Device code frominitiateDeviceCodeFlow()interval: Polling interval in secondstimeout: Optional timeout in milliseconds (default: 300000)
Returns: Promise<PollingResult>
const result = await auth.pollForToken(deviceCode, 5, 300000);
if (result.success) {
console.log('Access token:', result.tokens.accessToken);
}refreshToken(refreshToken)
Refreshes an expired access token using a refresh token.
Parameters:
refreshToken: The refresh token to use
Returns: Promise<AuthResult>
const result = await auth.refreshToken(refreshToken);
if (result.success) {
console.log('New access token:', result.tokens.accessToken);
}TokenManager
Utility class for managing stored authentication tokens.
Constructor
new TokenManager(tokenPath: string)Parameters:
tokenPath: File path where tokens will be stored
Methods
saveTokens(tokens)
Saves tokens to the filesystem with secure permissions (600).
await tokenManager.saveTokens({
accessToken: 'your-access-token',
refreshToken: 'your-refresh-token',
expiresAt: Date.now() + 3600000,
tokenType: 'Bearer'
});loadTokens()
Loads tokens from the filesystem.
Returns: Promise<StoredTokens | null>
const tokens = await tokenManager.loadTokens();
if (tokens) {
console.log('Loaded tokens:', tokens);
}clearTokens()
Removes stored tokens from the filesystem.
await tokenManager.clearTokens();areTokensValid(tokens)
Checks if tokens are valid (not expired, not expiring soon).
Returns: boolean
const isValid = tokenManager.areTokensValid(tokens);isTokenExpired(tokens)
Checks if tokens are expired.
Returns: boolean
const isExpired = tokenManager.isTokenExpired(tokens);Complete Example
Here's a complete example that demonstrates the full authentication flow:
import { DeviceCodeAuth, TokenManager } from '@yotoplay/oauth-device-code-flow';
const auth = new DeviceCodeAuth({
domain: 'your-domain.auth0.com',
clientId: 'your-client-id',
audience: 'your-api-audience'
});
const tokenManager = new TokenManager('./auth-tokens.json');
async function main() {
try {
// Check if we have valid stored tokens
const storedTokens = await tokenManager.loadTokens();
if (storedTokens && tokenManager.areTokensValid(storedTokens)) {
console.log('Using existing valid tokens');
return storedTokens;
}
// If tokens are expired but we have a refresh token, try to refresh
if (storedTokens && storedTokens.refreshToken) {
console.log('Refreshing expired tokens...');
const refreshResult = await auth.refreshToken(storedTokens.refreshToken);
if (refreshResult.success) {
await tokenManager.saveTokens(refreshResult.tokens);
console.log('Tokens refreshed successfully');
return refreshResult.tokens;
}
}
// Start new device code flow
console.log('Starting device code authentication...');
const deviceCodeResult = await auth.initiateDeviceCodeFlow();
if (!deviceCodeResult.success) {
throw new Error(`Failed to initiate auth: ${deviceCodeResult.error}`);
}
console.log('\n🔐 Authentication Required');
console.log('Please visit:', deviceCodeResult.verificationUri);
console.log('And enter this code:', deviceCodeResult.userCode);
console.log('\nWaiting for authentication...');
const tokenResult = await auth.pollForToken(
deviceCodeResult.deviceCode,
deviceCodeResult.interval,
300000 // 5 minute timeout
);
if (!tokenResult.success) {
throw new Error(`Authentication failed: ${tokenResult.error}`);
}
await tokenManager.saveTokens(tokenResult.tokens);
console.log('✅ Authentication successful!');
return tokenResult.tokens;
} catch (error) {
console.error('Authentication error:', error);
throw error;
}
}
// Run the authentication
main().then(tokens => {
console.log('Ready to make authenticated API calls!');
console.log('Access token:', tokens.accessToken);
}).catch(error => {
console.error('Failed to authenticate:', error);
process.exit(1);
});Error Handling
The library provides comprehensive error handling for all OAuth2 scenarios:
authorization_pending: User hasn't completed authorization yet (handled automatically)slow_down: Polling too frequently (interval automatically increased)expired_token: Device code expired (returns error immediately)access_denied: User denied authorization (returns error immediately)- Network errors: Properly caught and returned as error messages
TypeScript Support
The library is built with TypeScript and includes comprehensive type definitions:
import type {
Auth0Config,
DeviceCodeResponse,
TokenResponse,
StoredTokens,
AuthError,
AuthResult,
DeviceCodeResult,
PollingResult
} from '@yotoplay/oauth-device-code-flow';Requirements
- Node.js 16+ (for ES modules support)
- TypeScript 4.9+ (if using TypeScript)
License
MIT
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Support
If you encounter any issues or have questions, please open an issue on GitHub.
