1
0
forked from baron/baron-sso

flutter 린트 적용

This commit is contained in:
2026-02-19 16:08:42 +09:00
parent 466e7f1e54
commit 3eb7ed01ee
8 changed files with 35 additions and 73 deletions

View File

@@ -88,3 +88,16 @@ String buildLocalizedPath(String localeCode, Uri uri) {
}
return result;
}
String buildSigninRedirectPath(String localeCode, Uri uri) {
final newPath = '/$localeCode/signin';
final newUri = uri.replace(path: newPath);
String result = newUri.path;
if (newUri.hasQuery) {
result += '?${newUri.query}';
}
if (newUri.hasFragment) {
result += '#${newUri.fragment}';
}
return result;
}

View File

@@ -50,7 +50,7 @@ class AuthTokenStore {
_localStorage.setItem(_providerKey, provider);
}
} catch (e) {
print("[AuthTokenStore] CRITICAL - Failed to set token: $e");
// ignore
}
}

View File

@@ -12,25 +12,17 @@ class WebWindow {
void redirectTo(String url) {
final currentHref = web.window.location.href;
Uri? targetUri;
try {
targetUri = Uri.parse(url);
} catch (_) {
debugPrint("[WebWindow] redirectTo parse failed: url=$url");
}
debugPrint(
"[WebWindow] redirectTo start: current=$currentHref, target=$url",
);
print("[WebWindow] FINAL REDIRECT ATTEMPT. URL: $url");
// Most direct and safe way for WASM: location.href assignment via package:web
Future.delayed(Duration.zero, () {
try {
web.window.location.href = url;
} catch (e) {
print("[WebWindow] CRITICAL JS ERROR: $e");
debugPrint("[WebWindow] CRITICAL JS ERROR: $e");
}
});

View File

@@ -38,11 +38,10 @@ class LanguageSelector extends StatelessWidget {
}
LocaleStorage.write(value);
await context.setLocale(Locale(value));
if (!context.mounted) return;
final uri = GoRouterState.of(context).uri;
final target = buildLocalizedPath(value, uri);
if (context.mounted) {
context.go(target);
}
context.go(target);
},
),
);

View File

@@ -12,7 +12,6 @@ import '../../../core/services/auth_token_store.dart';
import '../../../core/services/oidc_redirect_guard.dart';
import '../../../core/notifiers/auth_notifier.dart';
import '../domain/login_challenge_resolver.dart';
import '../domain/password_login_flow_policy.dart';
import '../../profile/domain/notifiers/profile_notifier.dart';
import '../../../core/services/web_window.dart';
@@ -742,7 +741,6 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
final localSessionMessage = tr(
'msg.userfront.login.verification.approved_local',
);
final linkLoginMessage = tr('msg.userfront.login.link.approved');
try {
final res = await AuthProxyService.verifyLoginCode(
sanitizedLoginId,
@@ -872,7 +870,6 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
}
Future<void> _handlePasswordLogin() async {
print("[Auth] _handlePasswordLogin START");
final input = _passwordLoginIdController.text.trim();
final password = _passwordController.text.trim();
if (input.isEmpty || password.isEmpty) {
@@ -889,29 +886,23 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
}
try {
print("[Auth] Calling AuthProxyService.loginWithPassword...");
final res = await AuthProxyService.loginWithPassword(
loginId,
password,
loginChallenge: _loginChallenge,
);
print("[Auth] loginWithPassword response: $res");
final jwt = res['sessionJwt'] ?? res['sessionToken'] ?? res['token'];
final provider = res['provider'] as String?;
final redirectTo = res['redirectTo'] as String?;
if (jwt != null) {
print("[Auth] JWT found, calling _onLoginSuccess. RedirectTo: $redirectTo");
_onLoginSuccess(jwt, provider: provider, redirectTo: redirectTo);
} else if (redirectTo != null && redirectTo.isNotEmpty) {
print("[Auth] Only redirectTo found. Redirecting...");
webWindow.redirectTo(redirectTo);
} else {
print("[Auth] No JWT and no redirectTo found.");
}
} catch (e) {
print("[Auth] _handlePasswordLogin Error: $e");
if (e.toString().contains("User not registered")) {
_showUnregisteredDialog();
} else {
@@ -1134,56 +1125,44 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
}
Future<void> _onLoginSuccess(String token, {String? provider, String? redirectTo}) async {
print("[Auth] _onLoginSuccess ENTRY. RedirectTo: $redirectTo, Token len: ${token.length}");
try {
if (!mounted) {
print("[Auth] _onLoginSuccess: Not mounted, returning.");
return;
}
// [Priority 1] Immediate External Redirection
if (redirectTo != null && redirectTo.isNotEmpty) {
print("[Auth] _onLoginSuccess: Has redirectTo. Saving token and redirecting...");
try {
final providerName = provider ?? AuthTokenStore.getProvider();
print("[Auth] _onLoginSuccess: Provider resolved: $providerName");
AuthTokenStore.setToken(token, provider: providerName);
print("[Auth] _onLoginSuccess: Token saved to store.");
} catch (stErr) {
print("[Auth] _onLoginSuccess: FAILED to save token: $stErr");
// ignore
}
print("[Auth] Calling webWindow.redirectTo: $redirectTo");
webWindow.redirectTo(redirectTo); // Removed await as it's void
return;
}
// [Priority 2] OIDC Challenge Handling
if (_loginChallenge != null && _loginChallenge!.isNotEmpty) {
print("[Auth] _onLoginSuccess: Has loginChallenge. Attempting auto-accept...");
try {
// Save token first, it's needed for acceptance
final providerName = provider ?? AuthTokenStore.getProvider();
AuthTokenStore.setToken(token, provider: providerName);
print("[Auth] _onLoginSuccess: Token saved for auto-accept.");
final res = await AuthProxyService.acceptOidcLogin(
_loginChallenge!,
token: token,
);
final nextRedirectTo = res['redirectTo'] as String?;
print("[Auth] Auto-accept response: $res");
if (nextRedirectTo != null && nextRedirectTo.isNotEmpty) {
print("[Auth] OIDC login accepted. Redirecting to: $nextRedirectTo");
webWindow.redirectTo(nextRedirectTo); // Removed await
return;
} else {
print("[Auth] Auto-accept successful but no redirectTo provided.");
}
} catch (e) {
print("[Auth] Auto-accept failed: $e");
_showError(
tr(
'msg.userfront.login.oidc_failed',
@@ -1193,7 +1172,6 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
}
}
print("[Auth] _onLoginSuccess: Standard Login Flow");
_logTokenDetails(token);
final providerName = provider ?? AuthTokenStore.getProvider();
@@ -1205,7 +1183,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
try {
await ref.read(profileProvider.notifier).loadProfile();
} catch (e) {
print("[Auth] Failed to pre-fetch profile: $e");
// ignore
}
final uri = Uri.base;
@@ -1215,21 +1193,17 @@ class _LoginScreenState extends ConsumerState<LoginScreen>
redirectParam != null && redirectParam.isNotEmpty;
if (WebAuthIntegration.isPopup() || hasRedirectParam) {
print(
"[Auth] External integration detected (popup or redirect). Notifying...",
);
WebAuthIntegration.sendLoginSuccess(token);
AuthNotifier.instance.notify();
return;
}
print("[Auth] Login success. Navigating to root.");
AuthNotifier.instance.notify();
if (mounted) {
context.go('/');
}
} catch (globalErr) {
print("[Auth] CRITICAL ERROR in _onLoginSuccess: $globalErr");
// ignore
}
}

View File

@@ -1286,10 +1286,12 @@ class _SignupScreenState extends State<SignupScreen> {
@override
Widget build(BuildContext context) {
bool canGoNext = false;
if (_currentStep == 1 && _termsAccepted && _privacyAccepted)
if (_currentStep == 1 && _termsAccepted && _privacyAccepted) {
canGoNext = true;
if (_currentStep == 2 && _isEmailVerified && _isPhoneVerified)
}
if (_currentStep == 2 && _isEmailVerified && _isPhoneVerified) {
canGoNext = true;
}
if (_currentStep == 3) {
final nameOk = _nameController.text.trim().isNotEmpty;
if (_affiliationType == 'GENERAL') {

View File

@@ -1,3 +1,4 @@
// ignore_for_file: avoid_print
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -120,7 +121,6 @@ final _router = GoRouter(
GoRoute(
path: '', // Matches /:locale
builder: (context, state) {
print("[Router] Building Dashboard (Root)");
return const DashboardScreen();
},
),
@@ -134,7 +134,6 @@ final _router = GoRouter(
final loginChallenge = state.uri.queryParameters['login_challenge'];
final redirectUrl = state.uri.queryParameters['redirect_uri'] ??
state.uri.queryParameters['redirect_url'];
print("[Router] Building /signin. Challenge: $loginChallenge");
return LoginScreen(
key: state.pageKey,
loginChallenge: loginChallenge,
@@ -149,7 +148,6 @@ final _router = GoRouter(
final loginChallenge = state.uri.queryParameters['login_challenge'];
final redirectUrl = state.uri.queryParameters['redirect_uri'] ??
state.uri.queryParameters['redirect_url'];
print("[Router] Building /login (as signin). Challenge: $loginChallenge");
return LoginScreen(
key: state.pageKey,
loginChallenge: loginChallenge,
@@ -162,12 +160,10 @@ final _router = GoRouter(
builder: (BuildContext context, GoRouterState state) {
final consentChallenge = state.uri.queryParameters['consent_challenge'];
if (consentChallenge == null) {
print("[Router] WARNING: Consent screen without challenge.");
return const Scaffold(
body: Center(child: Text('Error: Consent challenge is missing.')),
);
}
print("[Router] Building /consent. Challenge: $consentChallenge");
return ConsentScreen(consentChallenge: consentChallenge);
},
),
@@ -200,7 +196,6 @@ final _router = GoRouter(
GoRoute(
path: 'l/:shortCode',
builder: (context, state) {
final shortCode = state.pathParameters['shortCode'];
return LoginScreen(key: state.pageKey);
},
),
@@ -264,11 +259,8 @@ final _router = GoRouter(
final requestedLocale = extractLocaleFromPath(uri);
final preferredLocale = resolvePreferredLocaleCode();
print("[Router] Redirect check for: $uri");
if (requestedLocale == null) {
final localizedPath = buildLocalizedPath(preferredLocale, uri);
print("[Router] Locale missing. Redirecting to: $localizedPath");
return localizedPath;
}
@@ -296,22 +288,12 @@ final _router = GoRouter(
path.startsWith('/consent/') ||
uri.path.contains('/consent');
print("[Router] Path: $path, IsLoggedIn: $isLoggedIn, IsPublic: $isPublicPath");
if (isPublicPath) {
return null;
}
if (!isLoggedIn) {
print("[Router] ACCESS DENIED. Redirecting to /signin");
final locale = requestedLocale;
final newPath = '/$locale/signin';
// Preserve ALL query parameters
final finalRedirect = uri.replace(path: newPath);
String result = finalRedirect.path;
if (finalRedirect.hasQuery) result += '?${finalRedirect.query}';
return result;
return buildSigninRedirectPath(requestedLocale, uri);
}
return null;

View File

@@ -1,36 +1,36 @@
// ignore_for_file: avoid_web_libraries_in_flutter
import 'dart:html' as html;
import 'package:web/web.dart' as web;
class WebStorage {
bool get isWeb => true;
String? get(String key) => html.window.localStorage[key];
String? get(String key) => web.window.localStorage.getItem(key);
void set(String key, String value) {
html.window.localStorage[key] = value;
web.window.localStorage.setItem(key, value);
}
String? getSession(String key) => html.window.sessionStorage[key];
String? getSession(String key) => web.window.sessionStorage.getItem(key);
void setSession(String key, String value) {
html.window.sessionStorage[key] = value;
web.window.sessionStorage.setItem(key, value);
}
void removeSession(String key) {
html.window.sessionStorage.remove(key);
web.window.sessionStorage.removeItem(key);
}
void clearSession() {
html.window.sessionStorage.clear();
web.window.sessionStorage.clear();
}
void remove(String key) {
html.window.localStorage.remove(key);
web.window.localStorage.removeItem(key);
}
void clear() {
html.window.localStorage.clear();
web.window.localStorage.clear();
}
}