Skip to content

Commit 3342f05

Browse files
authored
feat: Improve OAuth handling (#402)
- Added error codes to oauth service - Retry logic with exponential backoff - Singleton promise for token init and refresh - Preserve session on network errors
1 parent e3e613b commit 3342f05

File tree

3 files changed

+244
-126
lines changed

3 files changed

+244
-126
lines changed

apps/array/src/main/services/oauth/schemas.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@ import { z } from "zod";
33
export const cloudRegion = z.enum(["us", "eu", "dev"]);
44
export type CloudRegion = z.infer<typeof cloudRegion>;
55

6+
/**
7+
* Error codes for OAuth operations.
8+
* - network_error: Transient network issue, should retry
9+
* - auth_error: Authentication failed (invalid token, 401/403), should logout
10+
* - unknown_error: Other errors
11+
*/
12+
export const oAuthErrorCode = z.enum([
13+
"network_error",
14+
"auth_error",
15+
"unknown_error",
16+
]);
17+
export type OAuthErrorCode = z.infer<typeof oAuthErrorCode>;
18+
619
export const oAuthTokenResponse = z.object({
720
access_token: z.string(),
821
expires_in: z.number(),
@@ -23,6 +36,7 @@ export const startFlowOutput = z.object({
2336
success: z.boolean(),
2437
data: oAuthTokenResponse.optional(),
2538
error: z.string().optional(),
39+
errorCode: oAuthErrorCode.optional(),
2640
});
2741
export type StartFlowOutput = z.infer<typeof startFlowOutput>;
2842

@@ -36,6 +50,7 @@ export const refreshTokenOutput = z.object({
3650
success: z.boolean(),
3751
data: oAuthTokenResponse.optional(),
3852
error: z.string().optional(),
53+
errorCode: oAuthErrorCode.optional(),
3954
});
4055
export type RefreshTokenOutput = z.infer<typeof refreshTokenOutput>;
4156

apps/array/src/main/services/oauth/service.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,13 @@ export class OAuthService {
178178
});
179179

180180
if (!response.ok) {
181-
throw new Error(`Token refresh failed: ${response.statusText}`);
181+
// 401/403 are auth errors - the token is invalid
182+
const isAuthError = response.status === 401 || response.status === 403;
183+
return {
184+
success: false,
185+
error: `Token refresh failed: ${response.statusText}`,
186+
errorCode: isAuthError ? "auth_error" : "unknown_error",
187+
};
182188
}
183189

184190
const tokenResponse: OAuthTokenResponse = await response.json();
@@ -187,10 +193,11 @@ export class OAuthService {
187193
success: true,
188194
data: tokenResponse,
189195
};
190-
} catch (error) {
196+
} catch {
191197
return {
192198
success: false,
193-
error: error instanceof Error ? error.message : "Unknown error",
199+
error: "Network error",
200+
errorCode: "network_error",
194201
};
195202
}
196203
}

0 commit comments

Comments
 (0)