Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion src/core/AuthKitCore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,12 +152,14 @@ export class AuthKitCore {
*
* @param refreshToken - The refresh token
* @param organizationId - Optional organization ID to switch to
* @param context - Optional context for error reporting (userId, sessionId)
* @returns New access token, refresh token, user, and impersonator
* @throws TokenRefreshError if refresh fails
*/
async refreshTokens(
refreshToken: string,
organizationId?: string,
context?: { userId?: string; sessionId?: string },
): Promise<{
accessToken: string;
refreshToken: string;
Expand All @@ -179,7 +181,7 @@ export class AuthKitCore {
impersonator: result.impersonator,
};
} catch (error) {
throw new TokenRefreshError('Failed to refresh tokens', error);
throw new TokenRefreshError('Failed to refresh tokens', error, context);
}
}

Expand Down Expand Up @@ -226,9 +228,18 @@ export class AuthKitCore {
}
}

// Extract session ID for error context (works on expired tokens too)
let sessionId: string | undefined;
try {
sessionId = this.parseTokenClaims(accessToken).sid;
} catch {
// Token parsing failed - continue without session context
}

const newSession = await this.refreshTokens(
session.refreshToken,
organizationId,
{ userId: session.user?.id, sessionId },
);
const newClaims = this.parseTokenClaims<TCustomClaims>(
newSession.accessToken,
Expand Down
47 changes: 47 additions & 0 deletions src/core/errors.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,53 @@ describe('TokenRefreshError', () => {

expect(error.cause).toBe(originalError);
});

it('creates error with userId and sessionId', () => {
const error = new TokenRefreshError('Refresh failed', undefined, {
userId: 'user_123',
sessionId: 'session_456',
});

expect(error.userId).toBe('user_123');
expect(error.sessionId).toBe('session_456');
});

it('creates error with cause and context', () => {
const originalError = new Error('Network error');
const error = new TokenRefreshError('Refresh failed', originalError, {
userId: 'user_123',
sessionId: 'session_456',
});

expect(error.cause).toBe(originalError);
expect(error.userId).toBe('user_123');
expect(error.sessionId).toBe('session_456');
});

it('creates error with partial context (userId only)', () => {
const error = new TokenRefreshError('Refresh failed', undefined, {
userId: 'user_123',
});

expect(error.userId).toBe('user_123');
expect(error.sessionId).toBeUndefined();
});

it('creates error with partial context (sessionId only)', () => {
const error = new TokenRefreshError('Refresh failed', undefined, {
sessionId: 'session_456',
});

expect(error.userId).toBeUndefined();
expect(error.sessionId).toBe('session_456');
});

it('has undefined properties when no context provided', () => {
const error = new TokenRefreshError('Refresh failed');

expect(error.userId).toBeUndefined();
expect(error.sessionId).toBeUndefined();
});
});

describe('error inheritance', () => {
Expand Down
11 changes: 10 additions & 1 deletion src/core/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,17 @@ export class TokenValidationError extends AuthKitError {
}

export class TokenRefreshError extends AuthKitError {
constructor(message: string, cause?: unknown) {
readonly userId?: string;
readonly sessionId?: string;

constructor(
message: string,
cause?: unknown,
context?: { userId?: string; sessionId?: string },
) {
super(message, cause);
this.name = 'TokenRefreshError';
this.userId = context?.userId;
this.sessionId = context?.sessionId;
}
}
10 changes: 10 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ export { ConfigurationProvider } from './core/config/ConfigurationProvider.js';
// ============================================
export { getWorkOS } from './core/client/workos.js';

// ============================================
// Errors
// ============================================
export {
AuthKitError,
SessionEncryptionError,
TokenValidationError,
TokenRefreshError,
} from './core/errors.js';

// ============================================
// Type Exports
// ============================================
Expand Down