forked from baron/baron-sso
userfront 로그인 후 /dashboard로 이동하게 변경
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import '../../../../core/i18n/locale_utils.dart';
|
||||
import '../../../../core/services/auth_proxy_service.dart';
|
||||
import '../../../../core/services/auth_token_store.dart';
|
||||
|
||||
@@ -47,14 +48,15 @@ class _ApproveQrScreenState extends State<ApproveQrScreen> {
|
||||
|
||||
void _redirectIfNotLoggedIn() {
|
||||
if (_redirectingToLogin || !mounted) return;
|
||||
final hasStoredToken = AuthTokenStore.getToken() != null;
|
||||
final hasStoredToken = AuthTokenStore.getToken()?.isNotEmpty ?? false;
|
||||
final usesCookie = AuthTokenStore.usesCookie();
|
||||
final isLoggedIn = hasStoredToken || usesCookie;
|
||||
if (!isLoggedIn) {
|
||||
_redirectingToLogin = true;
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) return;
|
||||
context.go('/signin?notice=qr_login_required');
|
||||
final target = buildLocalizedSigninPath(Uri.base);
|
||||
context.go('$target?notice=qr_login_required');
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -70,7 +72,8 @@ class _ApproveQrScreenState extends State<ApproveQrScreen> {
|
||||
}
|
||||
if (storedToken == null && !hasCookie) {
|
||||
if (mounted) {
|
||||
context.go('/signin?notice=qr_login_required');
|
||||
final target = buildLocalizedSigninPath(Uri.base);
|
||||
context.go('$target?notice=qr_login_required');
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -94,7 +97,7 @@ class _ApproveQrScreenState extends State<ApproveQrScreen> {
|
||||
|
||||
// Automatically go to dashboard after a short delay
|
||||
Future.delayed(const Duration(seconds: 1), () {
|
||||
if (mounted) context.go('/');
|
||||
if (mounted) context.go(buildLocalizedHomePath(Uri.base));
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() => _message = "Error: $e");
|
||||
@@ -105,7 +108,7 @@ class _ApproveQrScreenState extends State<ApproveQrScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final hasStoredToken = AuthTokenStore.getToken() != null;
|
||||
final hasStoredToken = AuthTokenStore.getToken()?.isNotEmpty ?? false;
|
||||
final usesCookie = AuthTokenStore.usesCookie();
|
||||
final isLoggedIn = hasStoredToken || usesCookie || _isCheckingSession;
|
||||
|
||||
@@ -163,14 +166,15 @@ class _ApproveQrScreenState extends State<ApproveQrScreen> {
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 16),
|
||||
child: TextButton(
|
||||
onPressed: () => context.go('/signin'),
|
||||
onPressed: () =>
|
||||
context.go(buildLocalizedSigninPath(Uri.base)),
|
||||
child: const Text("Login on this device first"),
|
||||
),
|
||||
),
|
||||
|
||||
if (_success)
|
||||
FilledButton(
|
||||
onPressed: () => context.go('/'),
|
||||
onPressed: () => context.go(buildLocalizedHomePath(Uri.base)),
|
||||
child: const Text("Go to My Dashboard"),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:userfront/core/i18n/locale_utils.dart';
|
||||
import 'package:userfront/core/services/auth_proxy_service.dart';
|
||||
import 'package:userfront/core/services/web_window.dart';
|
||||
|
||||
@@ -153,7 +154,7 @@ class _ConsentScreenState extends State<ConsentScreen> {
|
||||
if (redirectTo != null) {
|
||||
webWindow.redirectTo(redirectTo);
|
||||
} else {
|
||||
if (mounted) context.go('/');
|
||||
if (mounted) context.go(buildLocalizedHomePath(Uri.base));
|
||||
}
|
||||
} catch (e) {
|
||||
setState(() => _isSubmitting = false);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import '../../../core/constants/error_whitelist.dart';
|
||||
import '../../../core/i18n/locale_utils.dart';
|
||||
import '../../../core/services/auth_proxy_service.dart';
|
||||
import 'package:userfront/i18n.dart';
|
||||
|
||||
@@ -130,7 +131,8 @@ class ErrorScreen extends StatelessWidget {
|
||||
child: Text(tr('ui.userfront.error.go_login')),
|
||||
),
|
||||
OutlinedButton(
|
||||
onPressed: () => context.go('/'),
|
||||
onPressed: () =>
|
||||
context.go(buildLocalizedHomePath(Uri.base)),
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: const Color(0xFF111827),
|
||||
padding: const EdgeInsets.symmetric(
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:userfront/core/i18n/locale_utils.dart';
|
||||
import 'package:userfront/i18n.dart';
|
||||
|
||||
class LoginSuccessScreen extends StatelessWidget {
|
||||
@@ -54,7 +55,7 @@ class LoginSuccessScreen extends StatelessWidget {
|
||||
const SizedBox(height: 24),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
context.go('/');
|
||||
context.go(buildLocalizedHomePath(Uri.base));
|
||||
},
|
||||
child: Text(
|
||||
tr('ui.userfront.login_success.later'),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import '../../../core/i18n/locale_utils.dart';
|
||||
import '../../../core/services/auth_proxy_service.dart';
|
||||
import 'package:userfront/i18n.dart';
|
||||
|
||||
@@ -89,7 +90,7 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
|
||||
backgroundColor: Colors.green,
|
||||
),
|
||||
);
|
||||
context.go('/signin');
|
||||
context.go(buildLocalizedSigninPath(Uri.base));
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:userfront/i18n.dart';
|
||||
import '../../../core/i18n/locale_utils.dart';
|
||||
import '../../../core/services/auth_proxy_service.dart';
|
||||
|
||||
class SignupScreen extends StatefulWidget {
|
||||
@@ -345,7 +346,7 @@ class _SignupScreenState extends State<SignupScreen> {
|
||||
content: Text(tr('msg.userfront.signup.success.body')),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => context.go('/signin'),
|
||||
onPressed: () => context.go(buildLocalizedSigninPath(Uri.base)),
|
||||
child: Text(tr('ui.userfront.signup.success.action')),
|
||||
),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user