diff --git a/backend/src/api/auth.ts b/backend/src/api/auth.ts index 7ccf5639..d2c7235a 100644 --- a/backend/src/api/auth.ts +++ b/backend/src/api/auth.ts @@ -38,7 +38,7 @@ router.post('/login', async (req, res) => { } const user = userToToken(foundUser); - const token = sign(user, getJWTSecret(), { expiresIn: '2h' }); + const token = sign(user, getJWTSecret(), { expiresIn: '4h' }); const expiresAt = addHours(2); res.status(200).send({ token, user, expiresAt }); @@ -53,7 +53,7 @@ router.post('/register', async (req, res) => { const savedUser = await dalUser.create(body); const user = userToToken(savedUser); - const token = sign(user, getJWTSecret(), { expiresIn: '2h' }); + const token = sign(user, getJWTSecret(), { expiresIn: '4h' }); const expiresAt = addHours(2); res.status(200).send({ token, user, expiresAt }); diff --git a/backend/src/utils/auth.ts b/backend/src/utils/auth.ts index 476d3a18..0bb5fdf1 100644 --- a/backend/src/utils/auth.ts +++ b/backend/src/utils/auth.ts @@ -1,6 +1,5 @@ import { NextFunction, Request, Response } from 'express'; -import { sign, verify } from 'jsonwebtoken'; -import { Role, UserModel } from '../models/User'; +import { UserModel, Role } from '../models/User'; import { v4 as uuidv4 } from 'uuid'; import hmacSHA256 from 'crypto-js/hmac-sha256'; import dalUser from '../repository/dalUser'; @@ -11,6 +10,7 @@ import { ProjectModel } from '../models/Project'; import { NotFoundError } from '../errors/client.errors'; import { addUserToProject } from './project.helpers'; import { ApplicationError } from '../errors/base.errors'; +import { sign, verify, TokenExpiredError, JsonWebTokenError } from 'jsonwebtoken'; export interface Token { email: string; @@ -50,15 +50,27 @@ export const isAuthenticated = async ( ) => { try { if (!req.headers.authorization) { - return res.status(400).end('No authorization header found!'); + return res.status(401).json({ message: 'Authorization header is missing' }); } const token = req.headers.authorization.replace('Bearer ', ''); + + if (!token) { + return res.status(401).json({ message: 'Bearer token not found' }); + + } res.locals.user = verify(token, getJWTSecret()) as Token; next(); - } catch (e) { - return res.status(403).end('Unable to authenticate!'); + } catch (error) { + if (error instanceof TokenExpiredError) { + return res.status(401).json({ message: 'Token expired' }); + } else if (error instanceof JsonWebTokenError) { + return res.status(401).json({ message: 'Invalid token' }); + } else { + console.error('Error in isAuthenticated middleware:', error); + return res.status(500).json({ message: 'Internal server error' }); + } } }; @@ -224,7 +236,7 @@ export const signInUserWithSso = async ( export const generateSessionToken = (userModel: UserModel): any => { const user = userToToken(userModel); - const token = sign(user, getJWTSecret(), { expiresIn: '2h' }); + const token = sign(user, getJWTSecret(), { expiresIn: '4h' }); const expiresAt = addHours(2); return { token, user, expiresAt }; }; diff --git a/frontend/src/app/utils/interceptor.ts b/frontend/src/app/utils/interceptor.ts index 2b846e9b..6eb409fe 100644 --- a/frontend/src/app/utils/interceptor.ts +++ b/frontend/src/app/utils/interceptor.ts @@ -1,15 +1,17 @@ import { Injectable } from '@angular/core'; import { + HttpRequest, + HttpHandler, HttpEvent, HttpInterceptor, - HttpHandler, - HttpRequest, HttpResponse, + HttpErrorResponse, } from '@angular/common/http'; -import { Observable, of } from 'rxjs'; -import { tap, timeout } from 'rxjs/operators'; +import { Observable, of, throwError } from 'rxjs'; +import { catchError, tap, timeout } from 'rxjs/operators'; import { UserService } from '../services/user.service'; import { environment } from 'src/environments/environment'; +import { Router } from '@angular/router'; export const DEFAULT_TIMEOUT = 30000; @@ -17,7 +19,7 @@ export const DEFAULT_TIMEOUT = 30000; export class APIInterceptor implements HttpInterceptor { private cache: Map>; - constructor(public auth: UserService) { + constructor(public auth: UserService, private router: Router) { this.cache = new Map(); } @@ -52,10 +54,20 @@ export class APIInterceptor implements HttpInterceptor { this.cache.set(apiReq.urlWithParams, httpEvent.clone()); } }) + ) + .pipe( + catchError((error: HttpErrorResponse) => { + if (error.status === 401) { + // Token expired or invalid, clear user data and navigate to login + this.auth.logout(); + this.router.navigate(['/login']); + } + return throwError(() => error); + }) ); } shouldCache(req: HttpRequest): boolean { return req.method === 'GET' && req.headers.get('cache') === 'true'; } -} +} \ No newline at end of file