1
0
forked from baron/baron-sso

rexp와 exp 토큰 파싱 및 유효기간 확인

This commit is contained in:
2026-01-20 15:03:19 +09:00
parent 79e5a22a97
commit c0083c1b69

View File

@@ -77,6 +77,20 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
}
}
// Helper to decode JWT and get User ID (sub claim)
String _getUserIdFromJwt(String jwt) {
try {
final parts = jwt.split('.');
if (parts.length != 3) return 'unknown';
final payload = utf8.decode(base64Url.decode(base64Url.normalize(parts[1])));
final data = json.decode(payload) as Map<String, dynamic>;
return data['sub'] as String? ?? 'unknown';
} catch (e) {
debugPrint("[JWT] Could not extract User ID (sub): $e");
return 'unknown';
}
}
void _handleTabSelection() {
if (_tabController.index == 1 && _qrPendingRef == null) {
_startQrFlow();
@@ -202,6 +216,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
'unknown', [], 0, displayName, null, '', false, '', false, {}, '', '', '', false, 'enabled', [], [], [],
);
final session = DescopeSession.fromJwt(jwt, jwt, dummyUser);
// Refresh Token을 LocalStorage에 저장
Descope.sessionManager.manageSession(session);
// Notify and Go to Dashboard
@@ -380,12 +395,55 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
}
}
void _logTokenDetails(String jwt) {
try {
// JWT는 세 부분(Header, Payload, Signature)이 '.'으로 구분된 문자열입니다. 이를 분리합니다.
final parts = jwt.split('.');
// 세 부분으로 정확히 나뉘지 않았다면 유효한 JWT가 아니므로 중단합니다.
if (parts.length != 3) return;
// JWT의 두 번째 부분(Payload)은 Base64Url로 인코딩된 JSON 데이터입니다.
// 1. Base64Url 문자열을 디코딩하여 바이트 배열로 변환합니다.
// normalize()는 Base64 패딩(=) 문제를 처리해줍니다.
final decodedPayload = base64Url.decode(base64Url.normalize(parts[1]));
// 2. 바이트 배열을 UTF-8 형식의 일반 문자열(JSON)로 변환합니다.
final payloadJson = utf8.decode(decodedPayload);
// 3. JSON 문자열을 Dart에서 사용할 수 있는 Map 객체로 변환합니다.
final data = json.decode(payloadJson) as Map<String, dynamic>;
// [FIX] 'exp'는 int 또는 double일 수 있으므로, 안전하게 num으로 처리합니다.
final accessExpValue = data['exp'] as num?;
// 'exp' (Expiration Time) 필드는 Access Token의 만료 시간을 나타냅니다. Unix 타임스탬프(초 단위) 값입니다.
// 이 값을 Dart의 DateTime 객체로 변환합니다. (1000을 곱해 밀리초 단위로 만듦)
final accessExp = accessExpValue != null
? DateTime.fromMillisecondsSinceEpoch(accessExpValue.toInt() * 1000)
: 'N/A';
// 'rexp' (Refresh Expiration) 필드는 Descope가 사용하는 커스텀 필드로, Refresh Token의 만료 시간을 ISO 8601 형식의 문자열로 나타냅니다.
final refreshExp = data['rexp'] ?? 'N/A';
// 확인된 만료 시간 정보들을 디버그 콘솔에 출력합니다.
debugPrint("""
[Auth] Session Token Details ---
- Access Token Expires: $accessExp
- Refresh Token Expires: $refreshExp
""");
} catch (e) {
// JWT를 해석하는 과정에서 오류가 발생하면 콘솔에 에러를 출력합니다.
debugPrint("[Auth] Failed to decode or log token details: $e");
}
}
void _onLoginSuccess(String token) {
if (!mounted) return;
_logTokenDetails(token);
// [FIX] 감사 로그에 실제 사용자 ID를 전송하기 위해 토큰에서 ID를 추출합니다.
final userId = _getUserIdFromJwt(token);
// Record Audit Log
AuditService.logEvent(
userId: "unknown", // In real apps, parse token to get user ID
userId: userId,
eventType: "LOGIN_SUCCESS",
status: "SUCCESS",
details: "User logged in via Baron SSO",