Skip to content

Commit 9fd5650

Browse files
committed
add userId and sessionId to TokenRefreshError
Provides context for debugging when token refresh fails (e.g., rate limits).
1 parent b8bbc43 commit 9fd5650

File tree

3 files changed

+69
-2
lines changed

3 files changed

+69
-2
lines changed

src/core/AuthKitCore.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,14 @@ export class AuthKitCore {
152152
*
153153
* @param refreshToken - The refresh token
154154
* @param organizationId - Optional organization ID to switch to
155+
* @param context - Optional context for error reporting (userId, sessionId)
155156
* @returns New access token, refresh token, user, and impersonator
156157
* @throws TokenRefreshError if refresh fails
157158
*/
158159
async refreshTokens(
159160
refreshToken: string,
160161
organizationId?: string,
162+
context?: { userId?: string; sessionId?: string },
161163
): Promise<{
162164
accessToken: string;
163165
refreshToken: string;
@@ -179,7 +181,7 @@ export class AuthKitCore {
179181
impersonator: result.impersonator,
180182
};
181183
} catch (error) {
182-
throw new TokenRefreshError('Failed to refresh tokens', error);
184+
throw new TokenRefreshError('Failed to refresh tokens', error, context);
183185
}
184186
}
185187

@@ -226,9 +228,18 @@ export class AuthKitCore {
226228
}
227229
}
228230

231+
// Extract session ID for error context (works on expired tokens too)
232+
let sessionId: string | undefined;
233+
try {
234+
sessionId = this.parseTokenClaims(accessToken).sid;
235+
} catch {
236+
// Token parsing failed - continue without session context
237+
}
238+
229239
const newSession = await this.refreshTokens(
230240
session.refreshToken,
231241
organizationId,
242+
{ userId: session.user?.id, sessionId },
232243
);
233244
const newClaims = this.parseTokenClaims<TCustomClaims>(
234245
newSession.accessToken,

src/core/errors.spec.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,53 @@ describe('TokenRefreshError', () => {
9393

9494
expect(error.cause).toBe(originalError);
9595
});
96+
97+
it('creates error with userId and sessionId', () => {
98+
const error = new TokenRefreshError('Refresh failed', undefined, {
99+
userId: 'user_123',
100+
sessionId: 'session_456',
101+
});
102+
103+
expect(error.userId).toBe('user_123');
104+
expect(error.sessionId).toBe('session_456');
105+
});
106+
107+
it('creates error with cause and context', () => {
108+
const originalError = new Error('Network error');
109+
const error = new TokenRefreshError('Refresh failed', originalError, {
110+
userId: 'user_123',
111+
sessionId: 'session_456',
112+
});
113+
114+
expect(error.cause).toBe(originalError);
115+
expect(error.userId).toBe('user_123');
116+
expect(error.sessionId).toBe('session_456');
117+
});
118+
119+
it('creates error with partial context (userId only)', () => {
120+
const error = new TokenRefreshError('Refresh failed', undefined, {
121+
userId: 'user_123',
122+
});
123+
124+
expect(error.userId).toBe('user_123');
125+
expect(error.sessionId).toBeUndefined();
126+
});
127+
128+
it('creates error with partial context (sessionId only)', () => {
129+
const error = new TokenRefreshError('Refresh failed', undefined, {
130+
sessionId: 'session_456',
131+
});
132+
133+
expect(error.userId).toBeUndefined();
134+
expect(error.sessionId).toBe('session_456');
135+
});
136+
137+
it('has undefined properties when no context provided', () => {
138+
const error = new TokenRefreshError('Refresh failed');
139+
140+
expect(error.userId).toBeUndefined();
141+
expect(error.sessionId).toBeUndefined();
142+
});
96143
});
97144

98145
describe('error inheritance', () => {

src/core/errors.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,17 @@ export class TokenValidationError extends AuthKitError {
2424
}
2525

2626
export class TokenRefreshError extends AuthKitError {
27-
constructor(message: string, cause?: unknown) {
27+
readonly userId?: string;
28+
readonly sessionId?: string;
29+
30+
constructor(
31+
message: string,
32+
cause?: unknown,
33+
context?: { userId?: string; sessionId?: string },
34+
) {
2835
super(message, cause);
2936
this.name = 'TokenRefreshError';
37+
this.userId = context?.userId;
38+
this.sessionId = context?.sessionId;
3039
}
3140
}

0 commit comments

Comments
 (0)