@@ -26,10 +26,38 @@ export class AuthService {
2626 private readonly keycloakService : KeycloakService
2727 ) { }
2828
29+ private getClientIpForKeycloak ( request : Request ) : string | undefined {
30+ const ipFromExpress = normalizeIpForForwarding ( request ?. ip ) ;
31+ if ( ipFromExpress ) return ipFromExpress ;
32+
33+ // Fallback: only trust X-Forwarded-For if the direct connection is internal (proxy hop).
34+ const remoteAddress = normalizeIpForForwarding ( request ?. socket ?. remoteAddress ) ;
35+ const isInternal =
36+ ! ! remoteAddress &&
37+ ( remoteAddress === '127.0.0.1' ||
38+ remoteAddress === '::1' ||
39+ remoteAddress . startsWith ( '10.' ) ||
40+ remoteAddress . startsWith ( '192.168.' ) ||
41+ ( remoteAddress . startsWith ( '172.' ) &&
42+ ( ( ) => {
43+ const second = parseInt ( remoteAddress . slice ( 4 , 7 ) , 10 ) ;
44+ return second >= 16 && second <= 31 ;
45+ } ) ( ) ) ) ;
46+
47+ if ( ! isInternal ) return undefined ;
48+
49+ const xff = request ?. headers ?. [ 'x-forwarded-for' ] ;
50+ const raw =
51+ typeof xff === 'string' ? xff : Array . isArray ( xff ) ? xff . join ( ',' ) : undefined ;
52+ if ( ! raw ) return undefined ;
53+ const first = raw . split ( ',' ) [ 0 ] ?. trim ( ) ;
54+ return normalizeIpForForwarding ( first ) ;
55+ }
56+
2957 async login ( request : Request , authDto , response : Response ) {
3058 const apiId = APIID . LOGIN ;
3159 const { username, password } = authDto ;
32- const clientIp = normalizeIpForForwarding ( request ?. ip ) ;
60+ const clientIp = this . getClientIpForKeycloak ( request ) ;
3361
3462 try {
3563 // Optimized: Only check user status (no tenant/role data needed for login)
@@ -126,7 +154,7 @@ export class AuthService {
126154 response : Response
127155 ) : Promise < LoginResponse > {
128156 const apiId = APIID . REFRESH ;
129- const clientIp = normalizeIpForForwarding ( request ?. ip ) ;
157+ const clientIp = this . getClientIpForKeycloak ( request ) ;
130158 const { access_token, expires_in, refresh_token, refresh_expires_in } =
131159 await this . keycloakService . refreshToken ( refreshToken , clientIp ) . catch ( ( ) => {
132160 throw new UnauthorizedException ( ) ;
@@ -149,7 +177,7 @@ export class AuthService {
149177
150178 async logout ( request : Request , refreshToken : string , response : Response ) {
151179 const apiId = APIID . LOGOUT ;
152- const clientIp = normalizeIpForForwarding ( request ?. ip ) ;
180+ const clientIp = this . getClientIpForKeycloak ( request ) ;
153181 try {
154182 const logout = await this . keycloakService . logout ( refreshToken , clientIp ) ;
155183 return APIResponse . success (
0 commit comments