Skip to content

Commit 480b872

Browse files
fix: waitForToken
Signed-off-by: Roman Nikitenko <rnikiten@redhat.com>
1 parent 4cd0cef commit 480b872

2 files changed

Lines changed: 49 additions & 50 deletions

File tree

code/extensions/che-github-authentication/src/extension.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,9 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
4747
const deviceAuthentication = container.get(DeviceAuthentication);
4848
authenticationProvider.setDeviceAuthentication(deviceAuthentication);
4949

50-
await authenticationProvider.hydrateFromK8sToken();
51-
5250
vscode.authentication.registerAuthenticationProvider('github', 'GitHub', authenticationProvider);
5351

54-
await authenticationProvider.notifyExistingSessions();
52+
authenticationProvider.hydrateFromK8sToken();
5553
}
5654

5755
export function deactivate(): void {

code/extensions/che-github-authentication/src/github.ts

Lines changed: 48 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,6 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider {
6060
this.deviceAuthentication = deviceAuthentication;
6161
}
6262

63-
async notifyExistingSessions(): Promise<void> {
64-
const sessions = await this.sessionsPromise;
65-
if (sessions.length > 0) {
66-
this.logger.info(`GitHubAuthProvider: notifying about ${sessions.length} existing session(s)`);
67-
this.sessionChangeEmitter.fire({ added: sessions, removed: [], changed: [] });
68-
}
69-
}
70-
7163
async hydrateFromK8sToken(): Promise<void> {
7264
let sessions = await this.sessionsPromise;
7365
if (sessions.length > 0) {
@@ -96,51 +88,60 @@ export class GitHubAuthProvider implements vscode.AuthenticationProvider {
9688
}
9789
}
9890

99-
await this.tryHydrate(3, 500);
91+
await this.doHydrate();
10092
}
10193

102-
private async tryHydrate(maxAttempts: number, delayMs: number): Promise<void> {
103-
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
94+
private async waitForToken(timeoutMs: number, intervalMs: number): Promise<string | undefined> {
95+
const deadline = Date.now() + timeoutMs;
96+
while (Date.now() < deadline) {
10497
try {
105-
const token = await this.githubService.getToken();
106-
const tokenScopes = await this.githubService.getTokenScopes(token);
107-
if (tokenScopes.length === 0) {
108-
this.logger.info('GitHubAuthProvider: hydrate skipped, token has no scopes');
109-
return;
110-
}
98+
return await this.githubService.getToken();
99+
} catch {
100+
await new Promise(resolve => setTimeout(resolve, intervalMs));
101+
}
102+
}
103+
return undefined;
104+
}
111105

112-
const githubUser = await this.githubService.getUser();
113-
const matchingBundles = getMatchingHydrationScopeBundles(tokenScopes);
114-
if (matchingBundles.length === 0) {
115-
this.logger.info('GitHubAuthProvider: hydrate skipped, token scopes match no known bundle');
116-
return;
117-
}
106+
private async doHydrate(): Promise<void> {
107+
const token = await this.waitForToken(30000, 500);
108+
if (!token) {
109+
this.logger.warn('GitHubAuthProvider: hydrate failed, token not available after 30s');
110+
return;
111+
}
118112

119-
const isDeviceAuthToken = await this.githubService.isDeviceAuthToken();
120-
const account = { label: githubUser.login, id: githubUser.id.toString() };
121-
const hydratedSessions = matchingBundles.map(scopes => ({
122-
id: v4(),
123-
accessToken: token,
124-
account,
125-
scopes: isDeviceAuthToken ? scopes : [...scopes, WORKSPACE_PAT_SCOPE],
126-
}));
127-
128-
await this.storeSessions(hydratedSessions);
129-
this.sessionChangeEmitter.fire({ added: hydratedSessions, removed: [], changed: [] });
130-
const tokenSource = isDeviceAuthToken ? 'device authentication' : 'workspace PAT';
131-
this.logger.info(`GitHubAuthProvider: hydrated ${hydratedSessions.length} session(s) from K8s ${tokenSource}`);
113+
try {
114+
const tokenScopes = await this.githubService.getTokenScopes(token);
115+
if (tokenScopes.length === 0) {
116+
this.logger.info('GitHubAuthProvider: hydrate skipped, token has no scopes');
132117
return;
133-
} catch (error) {
134-
if (isUnauthorizedError(error)) {
135-
this.logger.warn('GitHubAuthProvider: hydrate failed, token is not valid');
136-
return;
137-
}
138-
if (attempt < maxAttempts) {
139-
this.logger.info(`GitHubAuthProvider: hydrate attempt ${attempt}/${maxAttempts} failed: ${(error as Error).message}, retrying in ${delayMs}ms`);
140-
await new Promise(resolve => setTimeout(resolve, delayMs));
141-
} else {
142-
this.logger.warn(`GitHubAuthProvider: hydrate failed after ${maxAttempts} attempts: ${(error as Error).message}`);
143-
}
118+
}
119+
120+
const githubUser = await this.githubService.getUser();
121+
const matchingBundles = getMatchingHydrationScopeBundles(tokenScopes);
122+
if (matchingBundles.length === 0) {
123+
this.logger.info('GitHubAuthProvider: hydrate skipped, token scopes match no known bundle');
124+
return;
125+
}
126+
127+
const isDeviceAuthToken = await this.githubService.isDeviceAuthToken();
128+
const account = { label: githubUser.login, id: githubUser.id.toString() };
129+
const hydratedSessions = matchingBundles.map(scopes => ({
130+
id: v4(),
131+
accessToken: token,
132+
account,
133+
scopes: isDeviceAuthToken ? scopes : [...scopes, WORKSPACE_PAT_SCOPE],
134+
}));
135+
136+
await this.storeSessions(hydratedSessions);
137+
this.sessionChangeEmitter.fire({ added: hydratedSessions, removed: [], changed: [] });
138+
const tokenSource = isDeviceAuthToken ? 'device authentication' : 'workspace PAT';
139+
this.logger.info(`GitHubAuthProvider: hydrated ${hydratedSessions.length} session(s) from K8s ${tokenSource}`);
140+
} catch (error) {
141+
if (isUnauthorizedError(error)) {
142+
this.logger.warn('GitHubAuthProvider: hydrate failed, token is not valid');
143+
} else {
144+
this.logger.warn(`GitHubAuthProvider: hydrate failed: ${(error as Error).message}`);
144145
}
145146
}
146147
}

0 commit comments

Comments
 (0)