Skip to content

Commit

Permalink
feat: AppMap: Enter License Key
Browse files Browse the repository at this point in the history
Command to enter email address and license key directly.
  • Loading branch information
kgilpin committed Nov 30, 2023
1 parent 0ee6086 commit c251774
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 19 deletions.
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,10 @@
"command": "appmap.logout",
"title": "AppMap: Logout"
},
{
"command": "appmap.enterLicenseKey",
"title": "AppMap: Enter License Key"
},
{
"command": "appmap.updateAppMapTestConfig",
"title": "AppMap: Add AppMap Test Configuration"
Expand Down Expand Up @@ -464,7 +468,7 @@
"mocha-suppress-logs": "^0.3.1",
"mockery": "^2.1.0",
"node-libs-browser": "^2.2.1",
"openapi-types": "^11.0.1",
"openapi-types": "^12.1.3",
"prettier": "^2.8.4",
"prettier-eslint": "^12.0.0",
"project-root-directory": "^1.0.3",
Expand Down
47 changes: 40 additions & 7 deletions src/authentication/appmapServerAuthenticationProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DEBUG_EXCEPTION, Telemetry } from '../telemetry';
import ErrorCode from '../telemetry/definitions/errorCodes';
import { AUTHN_PROVIDER_NAME } from './index';
import { debuglog } from 'node:util';
import { LicenseKey } from '@appland/client';

const debug = debuglog('appmap-vscode:AppMapServerAuthenticationProvider');

Expand All @@ -20,6 +21,33 @@ enum AuthFailure {
SignInAttempt = 'SignInAttempt',
}

function enterLicenseKeyCommand(authProvider: AppMapServerAuthenticationProvider): {
dispose(): any;
} {
const command = vscode.commands.registerCommand('appmap.enterLicenseKey', async () => {
const email = await vscode.window.showInputBox({
title: `Enter your AppMap email address`,
ignoreFocusOut: true,
});
if (!email) return;

const licenseKey = await vscode.window.showInputBox({
title: `Enter your AppMap license key`,
ignoreFocusOut: true,
});
if (!licenseKey) return;

if (!(await LicenseKey.check(licenseKey))) {
vscode.window.showErrorMessage('Invalid license key');
return;
}

const session = AppMapServerAuthenticationHandler.buildSession(email, licenseKey);
authProvider.setSession(session);
});
return command;
}

export default class AppMapServerAuthenticationProvider implements vscode.AuthenticationProvider {
private _onDidChangeSessions =
new vscode.EventEmitter<vscode.AuthenticationProviderAuthenticationSessionsChangeEvent>();
Expand All @@ -40,6 +68,7 @@ export default class AppMapServerAuthenticationProvider implements vscode.Authen
{ supportsMultipleAccounts: false }
);
context.subscriptions.push(registration);
context.subscriptions.push(enterLicenseKeyCommand(provider));
return provider;
}

Expand Down Expand Up @@ -67,19 +96,23 @@ export default class AppMapServerAuthenticationProvider implements vscode.Authen
return this.session ? [this.session] : [];
}

async createSession(): Promise<vscode.AuthenticationSession> {
this.session = await this.performSignIn();
debug('createSession(); session %savailable', this.session ? '' : 'not ');

if (!this.session) throw new Error('AppMap authentication was not completed');

setSession(session: vscode.AuthenticationSession): void {
this.session = session;
this.context.secrets
.store(APPMAP_SERVER_SESSION_KEY, JSON.stringify(this.session))
.then(undefined, (err) => console.warn('error storing session key: %s', err));

this._onDidChangeSessions.fire({ added: [this.session], removed: [], changed: [] });
}

async createSession(): Promise<vscode.AuthenticationSession> {
const session = await this.performSignIn();
debug('createSession(); session %savailable', session ? '' : 'not ');

if (!session) throw new Error('AppMap authentication was not completed');

return this.session;
this.setSession(session);
return session;
}

async removeSession(): Promise<void> {
Expand Down
4 changes: 2 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,8 @@ export async function activate(context: vscode.ExtensionContext): Promise<AppMap
uriHandler
);
appmapServerAuthenticationProvider.onDidChangeSessions((e) => {
if (e.added) vscode.window.showInformationMessage('Logged in to AppMap');
if (e.removed) vscode.window.showInformationMessage('Logged out of AppMap');
if (e.added?.length) vscode.window.showInformationMessage('Logged in to AppMap');
if (e.removed?.length) vscode.window.showInformationMessage('Logged out of AppMap');
AppMapServerConfiguration.updateAppMapClientConfiguration();
});
vscode.commands.registerCommand('appmap.login', async () => {
Expand Down
24 changes: 15 additions & 9 deletions src/uri/appmapServerAuthenticationHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,30 @@ export default class AppMapServerAuthenticationHandler implements RequestHandler
return;
}

const apiKeyParam = queryParams.get('api_key');
if (!apiKeyParam) {
const licenseKeyParam = queryParams.get('api_key');
if (!licenseKeyParam) {
this._onError.fire(new Error('missing parameter "api_key"'));
return;
}

const buffer = Buffer.from(apiKeyParam, 'base64');
const buffer = Buffer.from(licenseKeyParam, 'base64');
const [email] = buffer.toString('utf-8').split(':');

this._onCreateSession.fire({
id: email,
account: { id: email, label: email },
scopes: ['default'],
accessToken: apiKeyParam,
});
this._onCreateSession.fire(
AppMapServerAuthenticationHandler.buildSession(email, licenseKeyParam)
);
}

dispose(): void {
// do nothing, we have nothing to dispose
}

static buildSession(email: string, licenseKey: string): vscode.AuthenticationSession {
return {
id: email,
account: { id: email, label: email },
scopes: ['default'],
accessToken: licenseKey,
};
}
}

0 comments on commit c251774

Please sign in to comment.