From fb690f1072ad8d18ae6409ab34a802b597c524b9 Mon Sep 17 00:00:00 2001 From: Mohammad Al Faiyaz Date: Fri, 17 Oct 2025 13:43:45 -0400 Subject: [PATCH] chore: draft pr to showcase how access tokens are utilized Ticket: WP-000000 --- examples/ts/long-lived-access-token.ts | 226 ++++++++++++++++++++++++ examples/ts/short-lived-access-token.ts | 135 ++++++++++++++ 2 files changed, 361 insertions(+) create mode 100644 examples/ts/long-lived-access-token.ts create mode 100644 examples/ts/short-lived-access-token.ts diff --git a/examples/ts/long-lived-access-token.ts b/examples/ts/long-lived-access-token.ts new file mode 100644 index 0000000000..804b4ae7fe --- /dev/null +++ b/examples/ts/long-lived-access-token.ts @@ -0,0 +1,226 @@ +/** + * Long-Lived Access Token Example + * + * This example demonstrates how to: + * 1. Initialize BitGo with a long-lived access token (two methods) + * 2. Verify the token is correctly set + * 3. Test the token by listing wallets for an enterprise + * + * Long-lived access tokens are generated from the BitGo dashboard or via API and can be set to + * expire after extended periods (days, months, or never). They are ideal for server applications, + * automated scripts, and integrations where interactive authentication is not feasible. + * + * To create a long-lived access token: + * 1. Log in to your BitGo account (test.bitgo.com or www.bitgo.com) + * 2. Go to Settings > Developer Options > Access Tokens + * 3. Create a new token with the required permissions and duration + * 4. Copy the token and store it securely + * + * Copyright 2025, BitGo, Inc. All Rights Reserved. + */ + +import { BitGoAPI } from '@bitgo/sdk-api'; +import { Tbtc } from '@bitgo/sdk-coin-btc'; +require('dotenv').config({ path: '../../.env' }); + +// TODO: Set your long-lived access token from BitGo dashboard +// You can set this in your .env file as LONG_LIVED_ACCESS_TOKEN +const longLivedAccessToken = process.env.LONG_LIVED_ACCESS_TOKEN || 'your-long-lived-access-token-here'; + +// TODO: Set your enterprise ID (you can find this in your BitGo dashboard) +const enterpriseId = process.env.ENTERPRISE_ID || 'your-enterprise-id'; + +const coin = 'tbtc'; + +/** + * Method 1: Initialize BitGo with access token in constructor + * This is the recommended approach as it sets up the BitGo instance ready to use + */ +async function method1_initializeWithToken() { + console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.log('Method 1: Initialize with Token in Constructor'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); + + // Initialize BitGo with the access token + const bitgo = new BitGoAPI({ + accessToken: longLivedAccessToken, + env: 'test', // Change this to env: 'prod' when you are ready for production + }); + + bitgo.register(coin, Tbtc.createInstance); + + console.log('✓ BitGo initialized with access token in constructor\n'); + + // Access the token (note: _token is a protected property, but we can verify it's set) + console.log('Token Information:'); + console.log( + ` Preview: ${longLivedAccessToken.substring(0, 20)}...${longLivedAccessToken.substring( + longLivedAccessToken.length - 10 + )}` + ); + console.log(` Length: ${longLivedAccessToken.length} characters\n`); + + return bitgo; +} + +/** + * Method 2: Authenticate with access token after initialization + * This method can be used when you need to set the token after creating the BitGo instance + */ +async function method2_authenticateWithToken() { + console.log('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.log('Method 2: Authenticate with Token After Initialization'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); + + // Initialize BitGo without an access token + const bitgo = new BitGoAPI({ + env: 'test', // Change this to env: 'prod' when you are ready for production + }); + + bitgo.register(coin, Tbtc.createInstance); + + console.log('Step 1: BitGo initialized without token'); + + // Authenticate using the access token + bitgo.authenticateWithAccessToken({ accessToken: longLivedAccessToken }); + + console.log('Step 2: ✓ Authenticated with access token using authenticateWithAccessToken() method\n'); + + console.log('Token Information:'); + console.log( + ` Preview: ${longLivedAccessToken.substring(0, 20)}...${longLivedAccessToken.substring( + longLivedAccessToken.length - 10 + )}` + ); + console.log(` Length: ${longLivedAccessToken.length} characters\n`); + + return bitgo; +} + +/** + * Test the access token by listing wallets for an enterprise + */ +async function testTokenByListingWallets(bitgo: BitGoAPI) { + console.log('Testing Access Token:'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); + + try { + console.log('Fetching wallets for enterprise...'); + + const wallets = await bitgo.coin(coin).wallets().list({ + enterprise: enterpriseId, + }); + + console.log('✓ Successfully retrieved wallets using the access token!\n'); + console.log('Wallet List:'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + + if (wallets.wallets && wallets.wallets.length > 0) { + wallets.wallets.forEach((wallet, index) => { + console.log(`\nWallet ${index + 1}:`); + console.log(` Label: ${wallet.label()}`); + console.log(` ID: ${wallet.id()}`); + console.log(` Coin: ${wallet.coin()}`); + console.log(` Balance: ${wallet.balanceString()}`); + }); + console.log(`\nTotal wallets found: ${wallets.wallets.length}`); + } else { + console.log('No wallets found for this enterprise.'); + console.log('\nNote: This might be expected if:'); + console.log(' • The enterprise has no wallets for this coin'); + console.log(' • The access token does not have permission to view wallets'); + console.log(' • The enterprise ID is incorrect'); + } + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); + } catch (walletError: any) { + console.error('❌ Error listing wallets:', walletError.message); + if (walletError.status) { + console.error(`Status: ${walletError.status}`); + } + if (walletError.message.includes('enterprise')) { + console.log('\n💡 Troubleshooting:'); + console.log(' • Make sure to set the correct ENTERPRISE_ID in your .env file'); + console.log(' • Verify your access token has permission to view wallets'); + } + if (walletError.message.includes('unauthorized') || walletError.status === 401) { + console.log('\n💡 Troubleshooting:'); + console.log(' • Verify your access token is valid and not expired'); + console.log(' • Check that you are using the correct environment (test/prod)'); + console.log(' • Ensure the token has the necessary scopes/permissions'); + } + throw walletError; + } +} + +async function main() { + try { + console.log('========================================'); + console.log('Long-Lived Access Token Example'); + console.log('========================================'); + + // Validate that the token is set + if (!longLivedAccessToken || longLivedAccessToken === 'your-long-lived-access-token-here') { + console.error('\n❌ Error: Long-lived access token not set!'); + console.log('\n📝 To run this example:'); + console.log(' 1. Create a long-lived access token in your BitGo dashboard:'); + console.log(' • Log in to test.bitgo.com (or www.bitgo.com for production)'); + console.log(' • Go to Settings > Developer Options > Access Tokens'); + console.log(' • Click "Add Token" and configure permissions and duration'); + console.log(' • Copy the generated token\n'); + console.log(' 2. Set the token:'); + console.log(' • Option A: Add LONG_LIVED_ACCESS_TOKEN=your-token to .env file'); + console.log(' • Option B: Set it directly in this file (not recommended for production)\n'); + console.log(' 3. Set your ENTERPRISE_ID in the same way\n'); + return; + } + + // Demonstrate Method 1 + console.log('\n' + '='.repeat(50)); + console.log('Demonstrating Method 1'); + console.log('='.repeat(50)); + const bitgo1 = await method1_initializeWithToken(); + await testTokenByListingWallets(bitgo1); + + // Demonstrate Method 2 + console.log('\n' + '='.repeat(50)); + console.log('Demonstrating Method 2'); + console.log('='.repeat(50)); + const bitgo2 = await method2_authenticateWithToken(); + await testTokenByListingWallets(bitgo2); + + // Summary + console.log('\n========================================'); + console.log('Example completed successfully!'); + console.log('========================================'); + + console.log('\n📝 Summary:'); + console.log(' Both methods successfully authenticated and retrieved wallets!'); + console.log('\n Method 1 (Constructor):'); + console.log(' • Best for: Setting token at initialization'); + console.log(' • Use when: You have the token available when creating BitGo instance'); + console.log('\n Method 2 (authenticateWithAccessToken):'); + console.log(' • Best for: Setting/changing token after initialization'); + console.log(' • Use when: You need to switch tokens or set it dynamically'); + + console.log('\n📝 Important Notes:'); + console.log(' • Long-lived tokens should be stored securely (environment variables, secrets manager)'); + console.log(' • Never commit access tokens to version control'); + console.log(' • Rotate tokens regularly for security'); + console.log(' • Use appropriate scopes/permissions for each token'); + console.log(' • Monitor token usage in your BitGo dashboard'); + console.log(' • Set appropriate expiration times based on your security requirements\n'); + } catch (error: any) { + console.error('\n❌ Error occurred:'); + console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.error(`Message: ${error.message}`); + if (error.status) { + console.error(`Status: ${error.status}`); + } + if (error.result) { + console.error(`Details: ${JSON.stringify(error.result, null, 2)}`); + } + console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); + } +} + +main().catch((e) => console.error('Unhandled error:', e)); diff --git a/examples/ts/short-lived-access-token.ts b/examples/ts/short-lived-access-token.ts new file mode 100644 index 0000000000..b55390e6bc --- /dev/null +++ b/examples/ts/short-lived-access-token.ts @@ -0,0 +1,135 @@ +/** + * Short-Lived Access Token Example + * + * This example demonstrates how to: + * 1. Authenticate with username, password, and OTP to generate a short-lived access token + * 2. Retrieve and display the access token + * 3. Test the token by listing wallets for an enterprise + * + * Short-lived access tokens are generated through the login process and expire after a set period. + * They are ideal for interactive applications where users authenticate with their credentials. + * + * Copyright 2025, BitGo, Inc. All Rights Reserved. + */ + +import { BitGoAPI } from '@bitgo/sdk-api'; +import { Tbtc } from '@bitgo/sdk-coin-btc'; +require('dotenv').config({ path: '../../.env' }); + +// TODO: Set your test environment credentials in .env file or directly here +const username = process.env.TEST_EMAIL || 'your-email@example.com'; +const password = process.env.TEST_PASS || 'your-password'; +const otp = process.env.TEST_OTP || '000000'; // Use your current OTP code + +// TODO: Set your enterprise ID (you can find this in your BitGo dashboard) +const enterpriseId = process.env.ENTERPRISE_ID || 'your-enterprise-id'; + +// Initialize BitGo without an access token +const bitgo = new BitGoAPI({ + env: 'test', // Change this to env: 'prod' when you are ready for production +}); + +const coin = 'tbtc'; +bitgo.register(coin, Tbtc.createInstance); + +async function main() { + try { + console.log('========================================'); + console.log('Short-Lived Access Token Example'); + console.log('========================================\n'); + + // Step 1: Authenticate with username, password, and OTP + console.log('Step 1: Authenticating with username, password, and OTP...'); + const authResponse = await bitgo.authenticate({ + username, + password, + otp, + }); + + console.log('✓ Authentication successful!\n'); + + // Step 2: Display the access token + console.log('Step 2: Retrieving access token...'); + const accessToken = authResponse.access_token; + + if (!accessToken) { + throw new Error('No access token received from authentication'); + } + + console.log('✓ Access token generated successfully!\n'); + console.log('Access Token Details:'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.log(`Token: ${accessToken}`); + console.log(`Token Preview: ${accessToken.substring(0, 20)}...${accessToken.substring(accessToken.length - 10)}`); + console.log(`Token Length: ${accessToken.length} characters`); + console.log(`User ID: ${authResponse.user?.id || 'N/A'}`); + console.log(`Username: ${authResponse.user?.username || 'N/A'}`); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); + + // Step 3: Test the token by listing wallets for an enterprise + console.log('Step 3: Testing the access token by listing wallets...'); + + try { + const wallets = await bitgo.coin(coin).wallets().list({ + enterprise: enterpriseId, + }); + + console.log('✓ Successfully retrieved wallets using the access token!\n'); + console.log('Wallet List:'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + + if (wallets.wallets && wallets.wallets.length > 0) { + wallets.wallets.forEach((wallet, index) => { + console.log(`\nWallet ${index + 1}:`); + console.log(` Label: ${wallet.label()}`); + console.log(` ID: ${wallet.id()}`); + console.log(` Coin: ${wallet.coin()}`); + console.log(` Balance: ${wallet.balanceString()}`); + }); + console.log(`\nTotal wallets found: ${wallets.wallets.length}`); + } else { + console.log('No wallets found for this enterprise.'); + } + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); + } catch (walletError: any) { + console.error('Error listing wallets:', walletError.message); + if (walletError.message.includes('enterprise')) { + console.log( + '\nNote: Make sure to set the correct ENTERPRISE_ID in your .env file or at the top of this script.' + ); + } + } + + console.log('========================================'); + console.log('Example completed successfully!'); + console.log('========================================'); + + // Note about token expiration + console.log('\n📝 Important Notes:'); + console.log(' • This access token is short-lived and will expire'); + console.log(' • Typical expiration time is 1 hour (check your BitGo settings)'); + console.log(' • You will need to re-authenticate to get a new token after expiration'); + console.log(' • Store this token securely and never commit it to version control\n'); + } catch (error: any) { + console.error('\n❌ Error occurred:'); + console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.error(`Message: ${error.message}`); + if (error.status) { + console.error(`Status: ${error.status}`); + } + if (error.result) { + console.error(`Details: ${JSON.stringify(error.result, null, 2)}`); + } + console.error('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); + + if (error.message.includes('otp') || error.message.includes('401')) { + console.log('💡 Troubleshooting:'); + console.log(' • Verify your username and password are correct'); + console.log(' • Make sure you are using a valid OTP code (not 000000 for production)'); + console.log(' • Check if your account has 2FA enabled'); + console.log(' • Ensure you are using the correct environment (test/prod)\n'); + } + } +} + +main().catch((e) => console.error('Unhandled error:', e));