fix: auth join - try multiple Teams v2 internal URL formats, skip launcher dialog, better session establishment
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
857299dcac
commit
d0cbcbcb29
1 changed files with 95 additions and 23 deletions
|
|
@ -154,10 +154,19 @@ export class BotOrchestrator {
|
||||||
waitUntil: 'domcontentloaded',
|
waitUntil: 'domcontentloaded',
|
||||||
timeout: 30000,
|
timeout: 30000,
|
||||||
});
|
});
|
||||||
// Wait for Teams v2 to load (look for any Teams UI element)
|
// Wait for Teams v2 to fully load and establish the auth session
|
||||||
|
// Teams v2 does multiple redirects (OAuth callback -> v2 app) which takes time
|
||||||
|
try {
|
||||||
|
await this._page!.waitForSelector('[data-tid="app-layout"], [data-tid="left-rail"], [class*="teams-"]', {
|
||||||
|
timeout: 15000
|
||||||
|
});
|
||||||
|
this._logger.info('Teams v2 app loaded (UI elements detected)');
|
||||||
|
} catch {
|
||||||
|
// Timeout waiting for UI elements, but auth session may still be established
|
||||||
await this._page!.waitForTimeout(5000);
|
await this._page!.waitForTimeout(5000);
|
||||||
|
}
|
||||||
const teamsUrl = this._page!.url();
|
const teamsUrl = this._page!.url();
|
||||||
this._logger.info(`Teams session established at: ${teamsUrl}`);
|
this._logger.info(`Teams session established at: ${teamsUrl.substring(0, 80)}...`);
|
||||||
} catch (teamsNavError) {
|
} catch (teamsNavError) {
|
||||||
this._logger.warn(`Teams session establishment failed (non-fatal): ${teamsNavError}`);
|
this._logger.warn(`Teams session establishment failed (non-fatal): ${teamsNavError}`);
|
||||||
}
|
}
|
||||||
|
|
@ -169,30 +178,93 @@ export class BotOrchestrator {
|
||||||
this._setState('navigating');
|
this._setState('navigating');
|
||||||
|
|
||||||
if (authenticate) {
|
if (authenticate) {
|
||||||
// AUTHENTICATED JOIN: Navigate directly to the original meeting URL
|
// AUTHENTICATED JOIN: Navigate directly to the meeting URL within the
|
||||||
// within the Teams v2 web app context. The launcher (/dl/launcher/) always
|
// Teams v2 web app context. The external launcher (/dl/launcher/) and its
|
||||||
// redirects to the "light-meetings" anonymous experience regardless of auth.
|
// "Continue on this browser" button always redirect to the anonymous
|
||||||
// Instead, navigate directly to the original meeting URL -- Teams v2 will
|
// light-meetings experience, even with an active auth session.
|
||||||
// recognize the auth session and show the authenticated pre-join screen.
|
//
|
||||||
this._logger.info(`Authenticated: navigating directly to meeting URL: ${this._meetingUrl}`);
|
// Strategy: Use the Teams v2 internal URL format that keeps the user
|
||||||
await this._page!.goto(this._meetingUrl, {
|
// within the authenticated Teams web app. The key is to NOT go through
|
||||||
|
// the launcher dialog at all.
|
||||||
|
this._logger.info(`Authenticated: navigating to meeting within Teams v2 app...`);
|
||||||
|
|
||||||
|
// Parse the meeting URL to extract meeting code and passcode
|
||||||
|
const { parseMeetingUrl } = await import('./meetingUrlParser');
|
||||||
|
const parsed = parseMeetingUrl(this._meetingUrl);
|
||||||
|
|
||||||
|
// Try multiple URL formats to find one that works within Teams v2
|
||||||
|
const meetingUrlFormats = [
|
||||||
|
// Format 1: Teams v2 internal pre-join (most likely to keep auth)
|
||||||
|
`https://teams.microsoft.com/v2/#/meeting-join/${parsed.meetingId || ''}${parsed.passcode ? '?p=' + parsed.passcode : ''}`,
|
||||||
|
// Format 2: Direct /meet/ URL (original meeting URL)
|
||||||
|
this._meetingUrl,
|
||||||
|
// Format 3: Teams v2 hash-based deep link
|
||||||
|
`https://teams.microsoft.com/_#/meet/${parsed.meetingId || ''}${parsed.passcode ? '?p=' + parsed.passcode : ''}`,
|
||||||
|
];
|
||||||
|
|
||||||
|
let joinedSuccessfully = false;
|
||||||
|
|
||||||
|
for (const url of meetingUrlFormats) {
|
||||||
|
this._logger.info(`Trying auth join URL: ${url}`);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this._page!.goto(url, {
|
||||||
waitUntil: 'domcontentloaded',
|
waitUntil: 'domcontentloaded',
|
||||||
timeout: 30000,
|
timeout: 20000,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Teams may show the launcher dialog even for direct URLs -- handle it
|
|
||||||
await this._joinProcedure.handleLauncherIfPresent();
|
|
||||||
|
|
||||||
// Wait for the pre-join page to stabilize
|
|
||||||
await this._page!.waitForTimeout(3000);
|
await this._page!.waitForTimeout(3000);
|
||||||
|
|
||||||
// Verify we're on an authenticated page (no "Type your name" input)
|
// Check if we landed on the authenticated pre-join page
|
||||||
|
const currentUrl = this._page!.url();
|
||||||
const pageText = await this._page!.evaluate(() => document.body?.innerText?.substring(0, 500) || '');
|
const pageText = await this._page!.evaluate(() => document.body?.innerText?.substring(0, 500) || '');
|
||||||
if (pageText.includes('Enter the name') || pageText.includes('Type your name')) {
|
|
||||||
this._logger.warn('Still on anonymous page after auth navigation - auth session may not have transferred');
|
this._logger.info(`After navigation - URL: ${currentUrl.substring(0, 100)}`);
|
||||||
} else {
|
|
||||||
this._logger.info('On authenticated pre-join page (no name input required)');
|
// If we see the launcher, DON'T click "Continue on this browser" -- that leads to anon
|
||||||
|
if (pageText.includes('Continue on this browser') || pageText.includes('Join on the Teams app')) {
|
||||||
|
this._logger.info('Launcher dialog detected - skipping it (would redirect to anonymous mode)');
|
||||||
|
// Instead, try to find a "Use the web app" or direct join link
|
||||||
|
const useWebAppButton = await this._page!.$('a:has-text("Use the web app"), button:has-text("Use the web app")');
|
||||||
|
if (useWebAppButton) {
|
||||||
|
await useWebAppButton.click();
|
||||||
|
await this._page!.waitForTimeout(3000);
|
||||||
}
|
}
|
||||||
|
continue; // Try next URL format
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we're on the authenticated pre-join (no name input, has "Join now")
|
||||||
|
if (!pageText.includes('Enter the name') && !pageText.includes('Type your name') &&
|
||||||
|
(pageText.includes('Join now') || pageText.includes('Join'))) {
|
||||||
|
this._logger.info('On authenticated pre-join page (no name input required)');
|
||||||
|
joinedSuccessfully = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we ended up on anon page (light-meetings)
|
||||||
|
if (currentUrl.includes('anon=true') || currentUrl.includes('light-meetings')) {
|
||||||
|
this._logger.warn(`URL redirected to anonymous mode: ${currentUrl.substring(0, 80)}`);
|
||||||
|
continue; // Try next URL format
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if we're already in the meeting (Teams v2 loaded meeting directly)
|
||||||
|
if (pageText.includes('Leave') && pageText.includes('Mute')) {
|
||||||
|
this._logger.info('Already in meeting after navigation (Teams v2 joined directly)');
|
||||||
|
joinedSuccessfully = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (navError) {
|
||||||
|
this._logger.warn(`Navigation failed for URL ${url.substring(0, 60)}: ${navError}`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!joinedSuccessfully) {
|
||||||
|
this._logger.warn('All authenticated URL formats failed - falling back to launcher flow');
|
||||||
|
// Last resort: use the launcher flow (will end up anonymous, but at least in the meeting)
|
||||||
|
await this._joinProcedure.startMeetingLauncherFlow(this._meetingUrl);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// ANONYMOUS JOIN: Use the launcher flow (resolves URL, adds anon params)
|
// ANONYMOUS JOIN: Use the launcher flow (resolves URL, adds anon params)
|
||||||
await this._joinProcedure.startMeetingLauncherFlow(this._meetingUrl);
|
await this._joinProcedure.startMeetingLauncherFlow(this._meetingUrl);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue