token exchange

This commit is contained in:
Aaditya Jaiswal 2026-04-09 16:52:49 +05:30
parent 876ec26e97
commit 3f94e4fe47
4 changed files with 63 additions and 18 deletions

View File

@ -1 +1 @@
import{a as s}from"./index-BVr6jLdd.js";import"./radix-vendor-CLtqm-Ae.js";import"./charts-vendor-CmYZJIYl.js";import"./utils-vendor-BTBPSQfW.js";import"./ui-vendor-DgwXkk2Y.js";import"./socket-vendor-TjCxX7sJ.js";import"./redux-vendor-tbZCm13o.js";import"./router-vendor-HW_ujxKo.js";async function m(n){return(await s.post(`/conclusions/${n}/generate`)).data.data}async function f(n,t){return(await s.post(`/conclusions/${n}/finalize`,{finalRemark:t})).data.data}async function d(n){var t;try{return(await s.get(`/conclusions/${n}`)).data.data}catch(o){if(((t=o.response)==null?void 0:t.status)===404)return null;throw o}}export{f as finalizeConclusion,m as generateConclusion,d as getConclusion}; import{a as s}from"./index-PHQVkQpb.js";import"./radix-vendor-CLtqm-Ae.js";import"./charts-vendor-CmYZJIYl.js";import"./utils-vendor-BTBPSQfW.js";import"./ui-vendor-DgwXkk2Y.js";import"./socket-vendor-TjCxX7sJ.js";import"./redux-vendor-tbZCm13o.js";import"./router-vendor-HW_ujxKo.js";async function m(n){return(await s.post(`/conclusions/${n}/generate`)).data.data}async function f(n,t){return(await s.post(`/conclusions/${n}/finalize`,{finalRemark:t})).data.data}async function d(n){var t;try{return(await s.get(`/conclusions/${n}`)).data.data}catch(o){if(((t=o.response)==null?void 0:t.status)===404)return null;throw o}}export{f as finalizeConclusion,m as generateConclusion,d as getConclusion};

File diff suppressed because one or more lines are too long

View File

@ -13,7 +13,7 @@
<!-- Preload essential fonts and icons --> <!-- Preload essential fonts and icons -->
<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<script type="module" crossorigin src="/assets/index-BVr6jLdd.js"></script> <script type="module" crossorigin src="/assets/index-PHQVkQpb.js"></script>
<link rel="modulepreload" crossorigin href="/assets/charts-vendor-CmYZJIYl.js"> <link rel="modulepreload" crossorigin href="/assets/charts-vendor-CmYZJIYl.js">
<link rel="modulepreload" crossorigin href="/assets/radix-vendor-CLtqm-Ae.js"> <link rel="modulepreload" crossorigin href="/assets/radix-vendor-CLtqm-Ae.js">
<link rel="modulepreload" crossorigin href="/assets/utils-vendor-BTBPSQfW.js"> <link rel="modulepreload" crossorigin href="/assets/utils-vendor-BTBPSQfW.js">

View File

@ -7,14 +7,40 @@ import logger from '../utils/logger';
import { activityService, SYSTEM_EVENT_REQUEST_ID } from '../services/activity.service'; import { activityService, SYSTEM_EVENT_REQUEST_ID } from '../services/activity.service';
import { getRequestMetadata } from '../utils/requestUtils'; import { getRequestMetadata } from '../utils/requestUtils';
import { ACCESS_TOKEN_TTL_MS, REFRESH_TOKEN_TTL_MS } from '../config/sessionPolicy'; import { ACCESS_TOKEN_TTL_MS, REFRESH_TOKEN_TTL_MS } from '../config/sessionPolicy';
import crypto from 'crypto';
export class AuthController { export class AuthController {
private authService: AuthService; private authService: AuthService;
// One-time code usage guard (in-memory, per instance).
private readonly consumedAuthCodes = new Map<string, number>();
private readonly authCodeTtlMs = 10 * 60 * 1000;
constructor() { constructor() {
this.authService = new AuthService(); this.authService = new AuthService();
} }
private getCodeDigest(code: string): string {
return crypto.createHash('sha256').update(code).digest('hex');
}
private pruneConsumedCodes(now: number): void {
for (const [digest, ts] of this.consumedAuthCodes.entries()) {
if (now - ts > this.authCodeTtlMs) this.consumedAuthCodes.delete(digest);
}
}
private hasConsumedCode(code: string): boolean {
const now = Date.now();
this.pruneConsumedCodes(now);
return this.consumedAuthCodes.has(this.getCodeDigest(code));
}
private markCodeConsumed(code: string): void {
const now = Date.now();
this.pruneConsumedCodes(now);
this.consumedAuthCodes.set(this.getCodeDigest(code), now);
}
/** /**
* Handle SSO callback from frontend * Handle SSO callback from frontend
* POST /api/v1/auth/sso-callback * POST /api/v1/auth/sso-callback
@ -546,6 +572,17 @@ export class AuthController {
const { code, redirectUri } = validateTokenExchange(req.body); const { code, redirectUri } = validateTokenExchange(req.body);
logger.info('Token exchange validation passed', { redirectUri }); logger.info('Token exchange validation passed', { redirectUri });
if (this.hasConsumedCode(code)) {
ResponseHandler.error(
res,
'Token exchange failed',
400,
'RELOGIN_REQUIRED'
);
return;
}
this.markCodeConsumed(code);
const userAgent = req.headers['user-agent'] || getRequestMetadata(req).userAgent; const userAgent = req.headers['user-agent'] || getRequestMetadata(req).userAgent;
const result = await this.authService.exchangeCodeForTokens(code, redirectUri, userAgent); const result = await this.authService.exchangeCodeForTokens(code, redirectUri, userAgent);
@ -625,6 +662,14 @@ export class AuthController {
} catch (error) { } catch (error) {
logger.error('Token exchange failed:', error); logger.error('Token exchange failed:', error);
const errorMessage = error instanceof Error ? error.message : 'Unknown error'; const errorMessage = error instanceof Error ? error.message : 'Unknown error';
const normalized = String(errorMessage || '').toLowerCase();
const isExpiredOrInvalidCode =
normalized.includes('authorization code is invalid or has expired') ||
normalized.includes('invalid_grant');
if (isExpiredOrInvalidCode) {
ResponseHandler.error(res, 'Token exchange failed', 400, 'RELOGIN_REQUIRED');
return;
}
ResponseHandler.error(res, 'Token exchange failed', 400, errorMessage); ResponseHandler.error(res, 'Token exchange failed', 400, errorMessage);
} }
} }