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 -->
|
<!-- 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">
|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user