Compare commits
2 Commits
bugfix/tab
...
bugfix/get
| Author | SHA1 | Date | |
|---|---|---|---|
| c1cffbcafa | |||
| 6738ae225d |
@@ -11,6 +11,7 @@ import {
|
||||
HttpStatus,
|
||||
Get,
|
||||
Patch,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import type { Response } from 'express';
|
||||
import {
|
||||
@@ -173,6 +174,33 @@ export class AuthController {
|
||||
);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// REFRESH
|
||||
// =========================
|
||||
@Post('refresh')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: 'Refresh access token using refresh cookie' })
|
||||
@ApiResponse({ status: 200, description: 'Access token refreshed' })
|
||||
@ApiUnauthorizedResponse({ description: 'Invalid or missing refresh token' })
|
||||
async refresh(@Req() req, @Res({ passthrough: true }) res: Response) {
|
||||
const refreshToken = req?.cookies?.refreshToken;
|
||||
|
||||
if (!refreshToken) {
|
||||
throw new UnauthorizedException('Refresh token not found');
|
||||
}
|
||||
|
||||
const result = await this.authService.refreshToken(refreshToken);
|
||||
|
||||
this.setAccessToken(res, result.data.accessToken);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
accessToken: result.data.accessToken,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// =========================
|
||||
// LOGOUT
|
||||
// =========================
|
||||
|
||||
@@ -26,6 +26,18 @@ function isPublicInvitationRequest(url: string | undefined): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
/** Session bootstrap / auth endpoints where 401 means "not logged in", not "retry refresh". */
|
||||
function shouldSkipRefreshRetry(url: string | undefined): boolean {
|
||||
if (!url) return false;
|
||||
return (
|
||||
url.includes('/auth/profile') ||
|
||||
url.includes('/auth/refresh') ||
|
||||
url.includes('/auth/login') ||
|
||||
url.includes('/auth/register') ||
|
||||
url.includes('/auth/logout')
|
||||
);
|
||||
}
|
||||
|
||||
// ❌ REMOVE request interceptor completely (no Authorization header)
|
||||
|
||||
// ✅ Response interceptor
|
||||
@@ -37,7 +49,8 @@ apiClient.interceptors.response.use(
|
||||
if (
|
||||
error.response?.status === 401 &&
|
||||
!originalRequest._retry &&
|
||||
!isPublicInvitationRequest(originalRequest.url)
|
||||
!isPublicInvitationRequest(originalRequest.url) &&
|
||||
!shouldSkipRefreshRetry(originalRequest.url)
|
||||
) {
|
||||
originalRequest._retry = true;
|
||||
|
||||
|
||||
@@ -107,8 +107,16 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
setCurrentOrganization(null);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
} catch (err: unknown) {
|
||||
const status =
|
||||
(err as { statusCode?: number })?.statusCode ??
|
||||
(err as { response?: { status?: number } })?.response?.status;
|
||||
|
||||
// 401 on profile is expected when there is no session — not an application error.
|
||||
if (status !== 401) {
|
||||
console.error('Auth check failed:', err);
|
||||
}
|
||||
|
||||
// Only clear state — DO NOT redirect here
|
||||
setUser(null);
|
||||
setOrganizations([]);
|
||||
|
||||
Reference in New Issue
Block a user