Skip to content

Commit fa3a111

Browse files
Merge pull request #5 from commitdev/bug/incorrect-session-num
fixed issue of incorrect session number
2 parents caeeb79 + c4ee4fd commit fa3a111

5 files changed

Lines changed: 230 additions & 390 deletions

File tree

Lines changed: 151 additions & 134 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
import fetch from "node-fetch";
2-
import { v4 as uuid } from "uuid";
32
import {
3+
authentication,
4+
AuthenticationProvider,
5+
AuthenticationProviderAuthenticationSessionsChangeEvent,
46
AuthenticationSession,
7+
Disposable,
58
env,
9+
Event,
10+
EventEmitter,
11+
ExtensionContext,
612
ProgressLocation,
713
Uri,
814
window,
@@ -13,54 +19,61 @@ import {
1319
UserDeviceCode,
1420
UserInfo,
1521
} from "../@types/types";
16-
import { AuthProvider } from "../common/authProviderInterface";
1722
import {
1823
COMMIT_API_BASE_URL,
1924
COMMIT_APP_BASE_URL,
2025
COMMIT_AUTH0_DOMAIN,
26+
COMMIT_AUTH_NAME,
27+
COMMIT_AUTH_TYPE,
2128
COMMIT_CLIENT_ID,
29+
COMMIT_SESSIONS_SECRET_KEY,
2230
} from "../common/constants";
2331

24-
export const AUTH_TYPE = `commit-auth0`;
25-
const AUTH_NAME = `Commit`;
26-
const SESSIONS_SECRET_KEY = `${AUTH_TYPE}.sessions`;
32+
export class Auth0AuthProvider implements AuthenticationProvider, Disposable {
33+
private _secretSessionKey: string = COMMIT_SESSIONS_SECRET_KEY;
2734

28-
export class Auth0AuthenticationProvider extends AuthProvider {
29-
private _pendingStates: string[] = [];
35+
// On Session Change Event Emitter
36+
private _onDidChangeSessions =
37+
new EventEmitter<AuthenticationProviderAuthenticationSessionsChangeEvent>();
3038

31-
/**
32-
* Methid get authentication type
33-
* @returns string
34-
*/
35-
getAuthType(): string {
36-
return AUTH_TYPE;
39+
// Disposable
40+
private _disposable: Disposable;
41+
42+
constructor(private readonly context: ExtensionContext) {
43+
this._disposable = Disposable.from(
44+
authentication.registerAuthenticationProvider(
45+
COMMIT_AUTH_TYPE,
46+
COMMIT_AUTH_NAME,
47+
this,
48+
{ supportsMultipleAccounts: false }
49+
)
50+
);
3751
}
3852

53+
dispose() {
54+
this._disposable.dispose();
55+
}
3956
/**
40-
* Method to get authentication name
41-
* @returns string
57+
* Event to listen to session changes
4258
*/
43-
getAuthName(): string {
44-
return AUTH_NAME;
59+
get onDidChangeSessions(): Event<AuthenticationProviderAuthenticationSessionsChangeEvent> {
60+
return this._onDidChangeSessions.event;
4561
}
4662

47-
/**
48-
* Method to get session secret key
49-
* @returns string
50-
*/
51-
getSecretKey(): string {
52-
return SESSIONS_SECRET_KEY;
63+
async getSessions(
64+
scopes?: readonly string[] | undefined
65+
): Promise<readonly AuthenticationSession[]> {
66+
const allSessions = await this.context.secrets.get(this._secretSessionKey);
67+
if (!allSessions) {
68+
return [];
69+
}
70+
71+
const sessions = (await JSON.parse(allSessions)) as AuthenticationSession[];
72+
return sessions;
5373
}
5474

55-
/**
56-
* Create a new auth session
57-
* @param scopes
58-
* @returns
59-
*/
60-
public async createSession(scopes: string[]): Promise<AuthenticationSession> {
75+
async createSession(scopes: string[]): Promise<AuthenticationSession> {
6176
try {
62-
// Append the default scopes if does not include openid and email
63-
6477
if (!scopes.includes("openid")) {
6578
scopes.push("openid");
6679
}
@@ -69,84 +82,113 @@ export class Auth0AuthenticationProvider extends AuthProvider {
6982
scopes.push("email");
7083
}
7184

72-
const { accessToken, expiresIn } = await this._login(scopes);
85+
// Login
86+
const { accessToken } = (await this._login(scopes)) as AuthDetails;
7387
if (!accessToken) {
74-
// console.log("Invalid access token");
75-
throw new Error(`Commit login failed`);
88+
throw new Error("Unable to login, not access token found");
7689
}
7790

78-
const userInfo: UserInfo = await this._getUserInfo(accessToken);
91+
// Get User Info
92+
const userInfo = (await this._getUserInfo(accessToken)) as UserInfo;
7993

94+
// Create Session
8095
const session: AuthenticationSession = {
81-
id: "commit-" + uuid(),
96+
id: userInfo.id,
8297
accessToken: accessToken,
8398
account: {
84-
label: `${userInfo.name} <${userInfo.email}>`,
8599
id: userInfo.id,
100+
label: userInfo.email,
86101
},
87102
scopes: scopes,
88103
};
89104

90-
await this.getContext().secrets.store(
91-
SESSIONS_SECRET_KEY,
105+
// Add the session to secret storage
106+
await this.context.secrets.store(
107+
this._secretSessionKey,
92108
JSON.stringify([session])
93109
);
94110

95-
this.getOnDidChangeSessions().fire({
111+
this._onDidChangeSessions.fire({
96112
added: [session],
97113
removed: [],
98114
changed: [],
99115
});
100116

101117
return session;
102-
} catch (e) {
103-
window.showErrorMessage(`${e}`);
104-
throw e;
118+
} catch (error: any) {
119+
window.showErrorMessage(error.message);
120+
throw error;
105121
}
106122
}
107123

108-
/**
109-
* Get User Info
110-
* @param token
111-
* @returns
112-
* @private
113-
*/
114-
private async _getUserInfo(token: string): Promise<UserInfo> {
115-
// Make POST request to get user info
116-
try {
117-
const response = await fetch(
118-
`${COMMIT_API_BASE_URL}/v1/auth.get-own-user`,
119-
{
120-
method: "POST",
121-
headers: {
122-
// eslint-disable-next-line @typescript-eslint/naming-convention
123-
"content-type": "application/json",
124-
cookie: `helix_session_token=${token}`,
125-
origin: COMMIT_APP_BASE_URL || "",
126-
referer: COMMIT_APP_BASE_URL || "",
127-
},
128-
}
129-
);
124+
async removeSessions(): Promise<void> {
125+
await this.context.secrets.delete(this._secretSessionKey);
126+
this._onDidChangeSessions.fire({
127+
added: [],
128+
removed: [],
129+
changed: [],
130+
});
131+
}
130132

131-
if (!response.ok) {
132-
throw new Error(`Get user info invalid status: ${response.statusText}`);
133-
}
133+
async removeSession(sessionId: string): Promise<void> {
134+
const allSessions = await this.context.secrets.get(this._secretSessionKey);
135+
if (!allSessions) {
136+
return;
137+
}
134138

135-
const data = (await response.json()) as any;
136-
return {
137-
email: data.email,
138-
id: data.id,
139-
name: data.name,
140-
commits: [],
141-
};
142-
} catch (e) {
143-
throw new Error(`Get user info failed: ${e}`);
139+
const sessions = (await JSON.parse(allSessions)) as AuthenticationSession[];
140+
const session = sessions.find((s) => s.id === sessionId);
141+
if (!session) {
142+
return;
144143
}
144+
145+
await this.context.secrets.delete(this._secretSessionKey);
146+
this._onDidChangeSessions.fire({
147+
added: [],
148+
removed: [session],
149+
changed: [],
150+
});
151+
}
152+
153+
// Local Methods
154+
private async _login(scopes: string[] = []) {
155+
return await window.withProgress<AuthDetails>(
156+
{
157+
location: ProgressLocation.Notification,
158+
title: "Signing in to Commit...",
159+
cancellable: true,
160+
},
161+
async (_, __) => {
162+
// Get the device Code from Auth0
163+
const registerDeviceResponse = await this._registerDeviceCode(scopes);
164+
const endTime = Date.now() + registerDeviceResponse.expiresIn * 1000;
165+
166+
// Extract the user code from the verificationUriComplete
167+
const userCode =
168+
registerDeviceResponse.verificationUriComplete.match(
169+
/user_code=([^&]+)/
170+
)?.[1];
171+
172+
// Show the user code in a message
173+
window.showInformationMessage(
174+
`Confirm Device Code: ${userCode} in your browser`
175+
);
176+
177+
// Open the verification URL
178+
await env.openExternal(
179+
Uri.parse(registerDeviceResponse.verificationUriComplete)
180+
);
181+
182+
// Poll Access Token
183+
return await this._pollAccessToken(
184+
endTime,
185+
registerDeviceResponse.deviceCode,
186+
registerDeviceResponse.interval
187+
);
188+
}
189+
);
145190
}
146191

147-
/**
148-
* Get the device code from Auth0
149-
*/
150192
private async _registerDeviceCode(scopes: string[]): Promise<UserDeviceCode> {
151193
try {
152194
const response = await fetch(`${COMMIT_AUTH0_DOMAIN}/oauth/device/code`, {
@@ -185,64 +227,39 @@ export class Auth0AuthenticationProvider extends AuthProvider {
185227
}
186228
}
187229

188-
/**
189-
* Login to Auth0
190-
*/
191-
private async _login(scopes: string[] = []) {
192-
return await window.withProgress<AuthDetails>(
193-
{
194-
location: ProgressLocation.Notification,
195-
title: "Signing in to Commit...",
196-
cancellable: true,
197-
},
198-
async (_, __) => {
199-
const stateId = uuid();
200-
201-
this._pendingStates.push(stateId);
202-
203-
// Get the device Code from Auth0
204-
const registerDeviceResponse = await this._registerDeviceCode(scopes);
205-
const endTime = Date.now() + registerDeviceResponse.expiresIn * 1000;
206-
207-
// Extract the user code from the verificationUriComplete
208-
const userCode =
209-
registerDeviceResponse.verificationUriComplete.match(
210-
/user_code=([^&]+)/
211-
)?.[1];
212-
213-
// Show the user code in a message
214-
window.showInformationMessage(
215-
`Confirm Device Code: ${userCode} in your browser`
216-
);
217-
218-
// Open the verification URL
219-
await env.openExternal(
220-
Uri.parse(registerDeviceResponse.verificationUriComplete)
221-
);
222-
223-
try {
224-
// Poll Access Token
225-
return await this._pollAccessToken(
226-
endTime,
227-
registerDeviceResponse.deviceCode,
228-
registerDeviceResponse.interval
229-
);
230-
} finally {
231-
this._pendingStates = this._pendingStates.filter(
232-
(s) => s !== stateId
233-
);
230+
private async _getUserInfo(token: string): Promise<UserInfo> {
231+
// Make POST request to get user info
232+
try {
233+
const response = await fetch(
234+
`${COMMIT_API_BASE_URL}/v1/auth.get-own-user`,
235+
{
236+
method: "POST",
237+
headers: {
238+
// eslint-disable-next-line @typescript-eslint/naming-convention
239+
"content-type": "application/json",
240+
cookie: `helix_session_token=${token}`,
241+
origin: COMMIT_APP_BASE_URL || "",
242+
referer: COMMIT_APP_BASE_URL || "",
243+
},
234244
}
245+
);
246+
247+
if (!response.ok) {
248+
throw new Error(`Get user info invalid status: ${response.statusText}`);
235249
}
236-
);
250+
251+
const data = (await response.json()) as any;
252+
return {
253+
email: data.email,
254+
id: data.id,
255+
name: data.name,
256+
commits: [],
257+
};
258+
} catch (e) {
259+
throw new Error(`Get user info failed: ${e}`);
260+
}
237261
}
238262

239-
/**
240-
* Poll for the access token
241-
* @param deviceCode
242-
* @param interval
243-
* @param expiresIn
244-
* @returns
245-
*/
246263
private async _pollAccessToken(
247264
endTime: number,
248265
deviceCode: string,

0 commit comments

Comments
 (0)