Skip to content

Commit 8784ca3

Browse files
committed
fix authentication - skip client cache
Signed-off-by: Adam Setch <adam.setch@outlook.com>
1 parent 9db3156 commit 8784ca3

4 files changed

Lines changed: 59 additions & 13 deletions

File tree

src/renderer/utils/api/client.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,11 @@ import { getNumberFromUrl } from './utils';
3737
/**
3838
* Fetch details of the currently authenticated GitHub user.
3939
*/
40-
export async function fetchAuthenticatedUserDetails(account: Account) {
41-
const octokit = await createOctokitClient(account, 'rest');
40+
export async function fetchAuthenticatedUserDetails(
41+
account: Account,
42+
skipClientCache = false,
43+
) {
44+
const octokit = await createOctokitClient(account, 'rest', skipClientCache);
4245

4346
return await octokit.rest.users.getAuthenticated();
4447
}

src/renderer/utils/api/octokit.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,34 @@ describe('renderer/utils/api/octokit.ts', () => {
127127
expect(getGitHubAPIBaseUrlSpy).toHaveBeenCalledTimes(1);
128128
});
129129

130+
it('should bypass cache when skipClientCache is true', async () => {
131+
const getGitHubAPIBaseUrlSpy = jest.spyOn(utils, 'getGitHubAPIBaseUrl');
132+
getGitHubAPIBaseUrlSpy.mockReturnValue(
133+
new URL('https://api.github.com/'),
134+
);
135+
136+
const cachedClient = await createOctokitClient(
137+
mockGitHubCloudAccount,
138+
'rest',
139+
);
140+
141+
const nonCachedClient = await createOctokitClient(
142+
mockGitHubCloudAccount,
143+
'rest',
144+
true,
145+
);
146+
147+
const cachedClientAgain = await createOctokitClient(
148+
mockGitHubCloudAccount,
149+
'rest',
150+
);
151+
152+
expect(cachedClient).toBe(cachedClientAgain);
153+
expect(nonCachedClient).not.toBe(cachedClient);
154+
expect(mockDecryptValue).toHaveBeenCalledTimes(2);
155+
expect(getGitHubAPIBaseUrlSpy).toHaveBeenCalledTimes(2);
156+
});
157+
130158
it('should create different clients for different accounts with same api type', async () => {
131159
const getGitHubAPIBaseUrlSpy = jest.spyOn(utils, 'getGitHubAPIBaseUrl');
132160
getGitHubAPIBaseUrlSpy.mockReturnValue(

src/renderer/utils/api/octokit.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,17 @@ export function clearOctokitClientCache(): void {
3939
export async function createOctokitClient(
4040
account: Account,
4141
type: APIClientType,
42+
skipClientCache = false,
4243
): Promise<OctokitClient> {
43-
const cacheKey = getClientCacheKey(account, type);
44+
let cacheKey: string;
4445

4546
// Return cached client if it exists
46-
const cachedClient = octokitClientCache.get(cacheKey);
47-
if (cachedClient) {
48-
return cachedClient;
47+
if (!skipClientCache) {
48+
cacheKey = getClientCacheKey(account, type);
49+
const cachedClient = octokitClientCache.get(cacheKey);
50+
if (cachedClient) {
51+
return cachedClient;
52+
}
4953
}
5054

5155
const decryptedToken = await decryptValue(account.token);
@@ -61,8 +65,10 @@ export async function createOctokitClient(
6165
userAgent: userAgent,
6266
});
6367

64-
// Cache the client keyed by account UUID + type
65-
octokitClientCache.set(cacheKey, client);
68+
// Cache the client keyed by account UUID + type unless explicitly skipped
69+
if (!skipClientCache) {
70+
octokitClientCache.set(cacheKey, client);
71+
}
6672

6773
return client;
6874
}

src/renderer/utils/auth/utils.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,9 @@ export async function exchangeAuthCodeForAccessToken(
9191
clientSecret: authOptions.clientSecret,
9292
code: authCode,
9393
request: request.defaults({
94-
baseUrl: getGitHubAuthBaseUrl(authOptions.hostname).toString(),
94+
baseUrl: getGitHubAuthBaseUrl(authOptions.hostname)
95+
.toString()
96+
.replace(/\/$/, ''),
9597
}),
9698
});
9799

@@ -114,7 +116,7 @@ export async function addAccount(
114116
token: encryptedToken,
115117
} as Account;
116118

117-
newAccount = await refreshAccount(newAccount);
119+
newAccount = await refreshAccount(newAccount, true);
118120
const newAccountUUID = getAccountUUID(newAccount);
119121

120122
const accountAlreadyExists = accountList.some(
@@ -145,9 +147,16 @@ export function removeAccount(auth: AuthState, account: Account): AuthState {
145147
};
146148
}
147149

148-
export async function refreshAccount(account: Account): Promise<Account> {
150+
export async function refreshAccount(
151+
account: Account,
152+
skipClientCache = false,
153+
): Promise<Account> {
149154
try {
150-
const response = await fetchAuthenticatedUserDetails(account);
155+
const response = await fetchAuthenticatedUserDetails(
156+
account,
157+
skipClientCache,
158+
);
159+
151160
const user = response.data;
152161

153162
// Refresh user data
@@ -185,7 +194,7 @@ export async function refreshAccount(account: Account): Promise<Account> {
185194
} catch (err) {
186195
rendererLogError(
187196
'refreshAccount',
188-
`failed to refresh account for user ${account.user.login}`,
197+
`failed to refresh account for user ${account.user?.login ?? account.hostname}`,
189198
err,
190199
);
191200
}

0 commit comments

Comments
 (0)