1
0
forked from baron/baron-sso

userfront 로그인 후 /dashboard로 이동하게 변경

This commit is contained in:
Lectom C Han
2026-02-23 22:06:00 +09:00
parent 19d3bade30
commit 2bdfc2eb51
37 changed files with 1504 additions and 222 deletions

View File

@@ -9,9 +9,11 @@ import '../../../core/widgets/language_selector.dart';
import '../../../core/services/web_auth_integration.dart';
import '../../../core/services/auth_proxy_service.dart';
import '../../../core/services/auth_token_store.dart';
import '../../../core/i18n/locale_utils.dart';
import '../../../core/services/oidc_redirect_guard.dart';
import '../../../core/notifiers/auth_notifier.dart';
import '../domain/login_challenge_resolver.dart';
import '../domain/cookie_session_policy.dart';
import '../../profile/domain/notifiers/profile_notifier.dart';
import '../../../core/services/web_window.dart';
@@ -65,6 +67,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
bool _verificationOnly = false;
bool _verificationApproved = false;
bool _dismissedOverlays = false;
bool _localNavigationCompleted = false;
String _verificationMessage = '';
String _verificationTitle = tr('ui.userfront.login.verification.title');
String _verificationPageTitle = tr(
@@ -125,7 +128,11 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
if (hasLoginCode) {
_verifyLoginCode(loginIdParam, codeParam, pendingRef: pendingRefParam);
} else if (hasVerificationToken) {
_verifyToken(widget.verificationToken ?? uri.queryParameters['t']!);
final verificationToken =
widget.verificationToken ?? uri.queryParameters['t'];
if (verificationToken != null && verificationToken.isNotEmpty) {
_verifyToken(verificationToken);
}
}
if (!_noticeHandled && notice == 'qr_login_required') {
@@ -142,8 +149,12 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
}
Future<void> _tryCookieSession({bool silent = true}) async {
if (AuthTokenStore.getToken() != null &&
(_loginChallenge == null || _loginChallenge!.isEmpty)) {
final loginChallenge = _loginChallenge;
final token = AuthTokenStore.getToken();
if (!shouldPromoteCookieSession(
currentToken: token,
loginChallenge: loginChallenge,
)) {
return;
}
final pendingProvider = AuthTokenStore.getPendingProvider();
@@ -151,6 +162,12 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
try {
await AuthProxyService.checkCookieSession();
if (!shouldPromoteCookieSession(
currentToken: AuthTokenStore.getToken(),
loginChallenge: loginChallenge,
)) {
return;
}
AuthTokenStore.setCookieMode(provider: provider);
AuthTokenStore.clearPendingProvider();
if (mounted) {
@@ -171,7 +188,6 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
Future<void> _onCookieLoginSuccess(String provider) async {
debugPrint("[Auth] Cookie-based login success. Provider: $provider");
AuthNotifier.instance.notify();
if (_hasLoginChallenge) {
final accepted = await _acceptOidcLoginAndRedirect();
if (accepted) {
@@ -185,8 +201,9 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
final token = AuthTokenStore.getToken();
if (token != null && token.isNotEmpty) {
final redirectUrl = _redirectUrl;
if (WebAuthIntegration.isPopup() ||
(_redirectUrl != null && _redirectUrl!.isNotEmpty)) {
(redirectUrl != null && redirectUrl.isNotEmpty)) {
debugPrint(
"[Auth] Cookie session with external integration. Notifying...",
);
@@ -196,14 +213,23 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
}
if (mounted) {
context.go('/');
_goLocalizedHomeOnce();
}
}
void _goLocalizedHomeOnce() {
if (!mounted || _localNavigationCompleted) {
return;
}
_localNavigationCompleted = true;
context.go(buildLocalizedHomePath(Uri.base));
}
Future<void> _attemptOidcAutoAccept() async {
if (_oidcAutoAcceptTried) return;
_oidcAutoAcceptTried = true;
if (_loginChallenge == null || _loginChallenge!.isEmpty) {
final loginChallenge = _loginChallenge;
if (loginChallenge == null || loginChallenge.isEmpty) {
return;
}
@@ -227,12 +253,13 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
}
Future<bool> _acceptOidcLoginAndRedirect({String? token}) async {
if (_loginChallenge == null || _loginChallenge!.isEmpty) {
final loginChallenge = _loginChallenge;
if (loginChallenge == null || loginChallenge.isEmpty) {
return false;
}
try {
final res = await AuthProxyService.acceptOidcLogin(
_loginChallenge!,
loginChallenge,
token: token,
);
final redirectTo = res['redirectTo'] as String?;
@@ -274,8 +301,10 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
}
}
bool get _hasLoginChallenge =>
_loginChallenge != null && _loginChallenge!.isNotEmpty;
bool get _hasLoginChallenge {
final loginChallenge = _loginChallenge;
return loginChallenge != null && loginChallenge.isNotEmpty;
}
LoginChallengeResolution _resolveLoginChallenge(Uri uri) {
return resolveLoginChallenge(
@@ -486,7 +515,11 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
}
try {
final res = await AuthProxyService.pollQrStatus(_qrPendingRef!);
final pendingRef = _qrPendingRef;
if (pendingRef == null || pendingRef.isEmpty) {
return;
}
final res = await AuthProxyService.pollQrStatus(pendingRef);
if (res['error'] == 'slow_down') {
final interval = res['interval'];
if (interval is int && interval > 0) {
@@ -656,9 +689,11 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
FilledButton(
onPressed: () {
final hasLocalSession =
AuthTokenStore.getToken() != null ||
(AuthTokenStore.getToken()?.isNotEmpty ?? false) ||
AuthTokenStore.usesCookie();
final target = hasLocalSession ? '/' : '/signin';
final target = hasLocalSession
? buildLocalizedHomePath(Uri.base)
: buildLocalizedSigninPath(Uri.base);
if (mounted) {
setState(() {
_verificationOnly = false;
@@ -691,7 +726,9 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
final jwt = res['token'] ?? res['sessionJwt'] ?? res['sessionToken'];
final status = res['status']?.toString();
final hasLocalSession = await _hasValidLocalSession();
final actionPath = hasLocalSession ? '/' : '/signin';
final actionPath = hasLocalSession
? buildLocalizedHomePath(Uri.base)
: buildLocalizedSigninPath(Uri.base);
if (status == 'approved' || (jwt == null && _verificationOnly)) {
if (mounted) {
@@ -754,7 +791,9 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
"[Auth] Code verification successful for loginId: $sanitizedLoginId",
);
final hasLocalSession = await _hasValidLocalSession();
final actionPath = hasLocalSession ? '/' : '/signin';
final actionPath = hasLocalSession
? buildLocalizedHomePath(Uri.base)
: buildLocalizedSigninPath(Uri.base);
if (jwt == null && status == 'approved') {
if (mounted) {
@@ -814,7 +853,9 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
final status = res['status']?.toString();
debugPrint("[Auth] Short code verification successful");
final hasLocalSession = await _hasValidLocalSession();
final actionPath = hasLocalSession ? '/' : '/signin';
final actionPath = hasLocalSession
? buildLocalizedHomePath(Uri.base)
: buildLocalizedSigninPath(Uri.base);
if (jwt == null && status == 'approved') {
if (mounted) {
@@ -1147,14 +1188,15 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
}
// [Priority 2] OIDC Challenge Handling
if (_loginChallenge != null && _loginChallenge!.isNotEmpty) {
final loginChallenge = _loginChallenge;
if (loginChallenge != null && loginChallenge.isNotEmpty) {
try {
// Save token first, it's needed for acceptance
final providerName = provider ?? AuthTokenStore.getProvider();
AuthTokenStore.setToken(token, provider: providerName);
final res = await AuthProxyService.acceptOidcLogin(
_loginChallenge!,
loginChallenge,
token: token,
);
final nextRedirectTo = res['redirectTo'] as String?;
@@ -1196,9 +1238,8 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
return;
}
AuthNotifier.instance.notify();
if (mounted) {
context.go('/');
_goLocalizedHomeOnce();
}
} catch (globalErr) {
// ignore
@@ -1237,7 +1278,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
title: Text(_verificationPageTitle),
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () => context.go('/'),
onPressed: () => context.go(buildLocalizedHomePath(Uri.base)),
),
),
body: _buildVerificationResultView(),