My Visual Studio Code extension is written in typescript, which I am just learning.
When the extension is initailized I want to open a webview panel (or a separate browser - if i can't get a webview to work - but that is a different question) and point it to my company's login page. When the user enters valid credentials the page is replaced with a callback page - and the url briefly includes the token info.
In C# (for a visual studio extension) I do that with a straight forward navigationcomplete event handler. I check for a certain string in the url, and if it is there I parse out the information I need.
In typescript my event handler is not picking up the event, and so I never gets the information I need.
I cannot see what i am doing wrong - and really appreciate any help.
Here is my current code in the 2 key files: the extension.ts and the showLoginWindow.ts file. I am showing it here with irrelevant stuff cut out. You'll see that I want to return my token info in a class object called TokenResponse. I didn't include that class because it is pretty obvious.
Here's the extension.ts file:
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import { showLoginWindow } from './helpers/showLoginWindow';
import { baseWebUrl } from './helpers/TokenUtils';
export async function activate(context: vscode.ExtensionContext) {
console.log('Congratulations, your extension "xyz" is now active!');
const targetUrl = loginUrl;
try {
const tokeninfo = await showLoginWindow(targetUrl);
} catch (error) {
console.error(error);
}
export function deactivate() {}
Here's the showLoginWindow.ts file:
import * as vscode from 'vscode';
import { TokenResponse } from '../classes/tokenResponse';
import { baseWebUrl } from '../helpers/TokenUtils';
import path from 'path';
export function showLoginWindow(context: vscode.ExtensionContext, targetUrl: string): Promise<TokenResponse> {
return new Promise((resolve, reject) => {
const panel = vscode.window.createWebviewPanel(
'loginWindow',
'Website Login',
vscode.ViewColumn.One,
{
enableScripts: true,
localResourceRoots: [vscode.Uri.file(path.join(context.extensionPath, 'src/scripts'))]
}
);
panel.webview.html = getWebviewContent(targetUrl);
// Handle messages from the webview
panel.webview.onDidReceiveMessage(
message => {
console.log('Received message from webview:', message);
switch (messagemand) {
case 'urlChanged':
const currentUrl = message.url;
if (currentUrl.includes("extension-callback")) {
const splitChar = currentUrl.includes("#") ? '#' : '?';
const fragment = currentUrl.split(splitChar).pop();
if (fragment) {
const queryParams = new URLSearchParams(fragment);
const accessToken = queryParams.get("access_token") || '';
const expiresAt = queryParams.get("expires_at") || '0';
const expiresIn = queryParams.get("expires_in") || '0';
const providerToken = queryParams.get("provider_token") || '';
const refreshToken = queryParams.get("refresh_token") || '';
const tokenType = queryParams.get("token_type") || '';
const expiryAt = parseInt(expiresAt, 10);
const expirySeconds = parseInt(expiresIn, 10);
const newTokenResponse = new TokenResponse(
accessToken,
expiryAt,
expirySeconds,
refreshToken,
tokenType
);
// Resolve the promise with the new token response
resolve(newTokenResponse);
// Close the panel
panel.dispose();
}
}
break;
}
},
undefined,
context.subscriptions
);
});
}
function getWebviewContent(targetUrl: string): string {
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login</title>
</head>
<body>
<iframe id="loginFrame" src="${targetUrl}" width="100%" height="800px"></iframe>
<script>
const vscode = acquireVsCodeApi();
let lastUrl = '';
document.getElementById('loginFrame').onload = function() {
const iframe = document.getElementById('loginFrame');
// Use setInterval to check for URL changes
setInterval(() => {
try {
const currentUrl = iframe.contentWindow.location.href;
if (currentUrl !== lastUrl) {
lastUrl = currentUrl;
console.log('Sending message to VS Code extension');
vscode.postMessage({
command: 'urlChanged',
url: currentUrl
});
}
} catch (e) {
// Handle cross-origin access error
console.error('Error accessing iframe content:', e);
}
}, 1000); // Check every second
};
</script>
</body>
</html>
`;
}
function handleLoginSuccess(tokenResponse: TokenResponse) {
vscode.window.showInformationMessage('Login succeeded!');
// Here you can handle the token response, e.g., store it or use it as needed
console.log('Token Response:', tokenResponse);
}