vodia-teams
Version:
Microsoft Teams Direct Routing configuration tool
272 lines (232 loc) • 8.69 kB
JavaScript
// auth.js - Authentication and app registration module
const fs = require('fs');
const path = require('path');
const axios = require('axios');
const readline = require('readline');
// Function to authenticate with device code and create app registration
async function authenticateWithDeviceCode(outputFile) {
// Create readline interface for user input
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
// Function to prompt user
function prompt(question) {
return new Promise(resolve => {
rl.question(question, answer => {
resolve(answer);
});
});
}
try {
console.log('=== Microsoft 365 Tenant Automation - Setup ===');
console.log('This script will authenticate and create necessary app registrations\n');
// Step 1: Initiate device code flow using Microsoft's public client ID
console.log('Step 1: Initiating device code authentication...');
const deviceCodeResponse = await axios.post(
'https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode',
new URLSearchParams({
client_id: '04b07795-8ddb-461a-bbee-02f9e1bf7b46', // Microsoft's Azure CLI client ID
scope: 'https://graph.microsoft.com/.default offline_access'
}).toString(),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);
const { device_code, user_code, verification_uri, expires_in, interval } = deviceCodeResponse.data;
console.log('\nTo sign in, use a web browser to open the page:', verification_uri);
console.log('Enter the code', user_code, 'to authenticate');
console.log('NOTE: You must sign in with a Global Administrator account\n');
// Step 2: Poll for token
console.log('Waiting for authentication...');
let tokenReceived = false;
let accessToken = null;
let tenantId = null;
let attempts = Math.floor(expires_in / interval);
while (!tokenReceived && attempts > 0) {
try {
await new Promise(resolve => setTimeout(resolve, interval * 1000));
const tokenResponse = await axios.post(
'https://login.microsoftonline.com/organizations/oauth2/v2.0/token',
new URLSearchParams({
grant_type: 'urn:ietf:params:oauth:grant-type:device_code',
device_code: device_code,
client_id: '04b07795-8ddb-461a-bbee-02f9e1bf7b46'
}).toString(),
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
);
accessToken = tokenResponse.data.access_token;
tokenReceived = true;
console.log('Authentication successful!\n');
} catch (error) {
if (error.response && error.response.data && error.response.data.error === 'authorization_pending') {
attempts--;
console.log('Waiting for you to complete authentication...');
continue;
}
throw error;
}
}
if (!tokenReceived) {
throw new Error('Authentication timed out. Please try again.');
}
// Step 3: Get tenant details
console.log('Step 2: Getting tenant details...');
const orgResponse = await axios.get(
'https://graph.microsoft.com/v1.0/organization',
{
headers: {
'Authorization': `Bearer ${accessToken}`
}
}
);
tenantId = orgResponse.data.value[0].id;
const tenantName = orgResponse.data.value[0].displayName;
console.log(`Tenant identified: ${tenantName} (${tenantId})\n`);
// Step 4: Create application registration
console.log('Step 3: Creating application registration...');
const appName = `VodiaTeamsAutomation-${new Date().getTime()}`;
const appRegistration = {
displayName: appName,
signInAudience: "AzureADMyOrg",
requiredResourceAccess: [
{
resourceAppId: "00000003-0000-0000-c000-000000000000", // Microsoft Graph
resourceAccess: [
{
id: "741f803b-c850-494e-b5df-cde7c675a1ca", // User.ReadWrite.All
type: "Role"
},
{
id: "19dbc75e-c2e2-444c-a770-ec69d8559fc7", // Directory.ReadWrite.All
type: "Role"
},
{
id: "7ab1d382-f21e-4acd-a863-ba3e13f7da61", // Directory.Read.All
type: "Role"
},
{
id: "df021288-bdef-4463-88db-98f22de89214", // User.Read.All
type: "Role"
},
{
id: "292d869f-3427-49a8-9dab-8c70152b74e9", // Organization.ReadWrite.All
type: "Role"
},
{
id: "7e05723c-0bb0-42da-be95-ae9f08a6e53c", // Domain.ReadWrite.All
type: "Role"
},
{
id: "bdd80a03-d9bc-451d-b7c4-ce7c63fe3c8f", // TeamSettings.ReadWrite.All
type: "Role"
},
{
id: "0121dc95-1b9f-4aed-8bac-58c5ac466691", // TeamMember.ReadWrite.All
type: "Role"
}
]
}
],
web: {
redirectUris: [
"https://login.microsoftonline.com/common/oauth2/nativeclient",
"http://localhost"
],
implicitGrantSettings: {
enableIdTokenIssuance: false,
enableAccessTokenIssuance: false
}
}
};
// Create the application
const appResponse = await axios.post(
'https://graph.microsoft.com/v1.0/applications',
appRegistration,
{
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
}
);
const appId = appResponse.data.appId;
const appObjectId = appResponse.data.id;
console.log(`Application created: ${appName}`);
console.log(`Application (client) ID: ${appId}`);
console.log(`Application Object ID: ${appObjectId}`);
// Step 5: Create client secret
console.log('\nStep 4: Creating client secret...');
const secretResponse = await axios.post(
`https://graph.microsoft.com/v1.0/applications/${appObjectId}/addPassword`,
{
passwordCredential: {
displayName: "Auto-generated Secret",
endDateTime: "2027-12-31T00:00:00Z"
}
},
{
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
}
);
const clientSecret = secretResponse.data.secretText;
// Step 6: Create service principal
console.log('Step 5: Creating service principal...');
const spResponse = await axios.post(
'https://graph.microsoft.com/v1.0/servicePrincipals',
{
appId: appId
},
{
headers: {
'Authorization': `Bearer ${accessToken}`,
'Content-Type': 'application/json'
}
}
);
console.log(`Service principal created: ${spResponse.data.id}`);
// Step 7: Save credentials to file
const credentials = {
tenantId: tenantId,
clientId: appId,
clientSecret: clientSecret,
appObjectId: appObjectId
};
// Create directory for output file if it doesn't exist
const outputDir = path.dirname(outputFile);
if (outputDir && outputDir !== '.' && !fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
fs.writeFileSync(outputFile, JSON.stringify(credentials, null, 2));
console.log(`\nCredentials saved to ${outputFile}`);
console.log('\nIMPORTANT: Keep this file secure as it contains sensitive information.');
// Step 8: Generate admin consent URL
const adminConsentUrl = `https://login.microsoftonline.com/${tenantId}/adminconsent?client_id=${appId}&redirect_uri=http://localhost`;
console.log('\nFinal Step: Please open the following URL in your browser to grant admin consent:');
console.log(adminConsentUrl);
console.log('\nAfter granting consent, you can use the credentials in the saved file for API calls.');
await prompt('\nPress Enter to continue...');
rl.close();
return {
success: true,
tenantId: tenantId,
clientId: appId,
clientSecret: clientSecret,
outputFile: outputFile
};
} catch (error) {
console.error('Error:', error.response?.data || error.message);
rl.close();
throw error;
}
}
module.exports = { authenticateWithDeviceCode };