token exchange
This commit is contained in:
parent
876ec26e97
commit
3f94e4fe47
@ -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
@ -13,7 +13,7 @@
|
||||
<!-- Preload essential fonts and icons -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<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/radix-vendor-CLtqm-Ae.js">
|
||||
<link rel="modulepreload" crossorigin href="/assets/utils-vendor-BTBPSQfW.js">
|
||||
|
||||
@ -7,14 +7,40 @@ import logger from '../utils/logger';
|
||||
import { activityService, SYSTEM_EVENT_REQUEST_ID } from '../services/activity.service';
|
||||
import { getRequestMetadata } from '../utils/requestUtils';
|
||||
import { ACCESS_TOKEN_TTL_MS, REFRESH_TOKEN_TTL_MS } from '../config/sessionPolicy';
|
||||
import crypto from 'crypto';
|
||||
|
||||
export class AuthController {
|
||||
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() {
|
||||
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
|
||||
* POST /api/v1/auth/sso-callback
|
||||
@ -546,6 +572,17 @@ export class AuthController {
|
||||
const { code, redirectUri } = validateTokenExchange(req.body);
|
||||
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 result = await this.authService.exchangeCodeForTokens(code, redirectUri, userAgent);
|
||||
|
||||
@ -625,6 +662,14 @@ export class AuthController {
|
||||
} catch (error) {
|
||||
logger.error('Token exchange failed:', 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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user