UNPKG

fn-stats-cli-jnc

Version:

Command-line tool to pull Fortnite player statistics using Epic Games' API

155 lines (135 loc) 5.92 kB
#!/usr/bin/env node /** * Epic Games Authentication Script * * This script handles the OAuth2 authentication flow for Epic Games: * 1. Opens browser for user login * 2. Captures authorization code * 3. Exchanges code for access token * 4. Creates permanent device auth credentials * * Uses ES Modules and async/await pattern throughout */ // Import required dependencies import { EpicClient } from './src/epicWrapper.js'; // Our CommonJS wrapper for @squiddleton/epic import { writeFileSync } from 'node:fs'; // Node.js file system module import open from 'open'; // Opens URLs in default browser import * as readline from 'node:readline/promises'; // Promise-based readline import { mkdir } from 'node:fs/promises'; // Promise-based file system operations import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; // Get absolute path to package directory const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); // Initialize Epic Games client // This client handles all API communication with Epic's services const client = new EpicClient(); // Create readline interface for command-line interaction // Uses Node's built-in readline with Promise support const rl = readline.createInterface({ input: process.stdin, // Accept input from terminal output: process.stdout // Output to terminal }); // Epic OAuth configuration // These values are required for the OAuth2 authorization flow const clientId = '3f69e56c7649492c8cc29f1af08a8a12'; // Epic Games client identifier const fortniteUrl = 'https://www.fortnite.com/?lang=en-US'; // Initial login page const authUrl = `https://www.epicgames.com/id/api/redirect?clientId=${clientId}&responseType=code`; // OAuth endpoint /** * Prompts user to confirm their Epic Games login status * Uses async/await pattern with native Promise-based readline * * @returns {Promise<boolean>} True if user indicates they've signed in */ const confirmSignIn = async () => { const answer = await rl.question('Have you signed in to Epic Games? (yes/no): '); return answer.toLowerCase().startsWith('y'); }; /** * Retrieves and processes the authorization code from user input * Expects a JSON response from Epic's OAuth redirect containing: * { * "redirectUrl": "com.epicgames.fortnite://fnauth/?code=...", * "authorizationCode": "actual_code_here", * "ssoV2Enabled": true * } * * @returns {Promise<string|null>} Authorization code if successful, null if parsing fails */ const getAuthCode = async () => { try { // Get raw JSON response from user const response = await rl.question('Please paste the FULL JSON response from the browser (all the text in curly brackets): '); // Parse JSON and extract authorization code const jsonResponse = JSON.parse(response); const code = jsonResponse.authorizationCode; // Validate authorization code if (!code) { throw new Error('No authorization code found in response'); } return code; } catch (e) { console.error('Failed to parse JSON response:', e.message); return null; } }; try { // Step 1: Initiate login flow // Opens default browser to Fortnite login page console.log('Opening Fortnite website. Please sign in to your Epic Games account...'); await open(fortniteUrl); // Step 2: Wait for user login confirmation // Loops until user confirms successful login let isSignedIn = await confirmSignIn(); while (!isSignedIn) { console.log('Please sign in before continuing...'); isSignedIn = await confirmSignIn(); } // Step 3: Begin OAuth authorization // Opens authorization page and waits for user to complete flow console.log('Opening Epic Games authorization page...'); console.log('After authorizing, you will be redirected. Copy the FULL JSON response.'); await open(authUrl); // Step 4: Process authorization response // Gets and validates the authorization code const code = await getAuthCode(); if (!code) { throw new Error('Could not extract authorization code from response'); } // Step 5: Exchange auth code for access token // Uses Epic client to perform OAuth code exchange const loginResponse = await client.auth.authenticate({ grant_type: 'authorization_code', code: code }); // Step 6: Generate device authentication // Creates permanent device credentials for future use const deviceAuthResponse = await client.auth.getDeviceAuth( loginResponse.account_id, loginResponse.access_token ); // Step 7: Save device authentication grant // Creates reusable credentials for future authentication const deviceAuthGrant = { grant_type: 'device_auth', account_id: deviceAuthResponse.accountId, device_id: deviceAuthResponse.deviceId, secret: deviceAuthResponse.secret }; // Create config directory if it doesn't exist const configDir = join(__dirname, 'config'); await mkdir(configDir, { recursive: true }); // Save credentials to config directory using absolute path writeFileSync( join(configDir, 'deviceAuthGrant.json'), JSON.stringify(deviceAuthGrant, null, 2) ); console.log('Device authentication credentials saved successfully in package config directory!'); } catch (error) { // Handle any errors that occur during the process console.error('Authentication failed:', error.message); } finally { // Clean up resources // Ensures readline interface is properly closed rl.close(); }