1
0
forked from baron/baron-sso

라우터 구조 개선 및 리다이렉트 시 파라미터 유실 수정

This commit is contained in:
2026-02-19 13:48:30 +09:00
parent b675159510
commit 95136cd5df
2 changed files with 77 additions and 118 deletions

View File

@@ -75,14 +75,16 @@ String buildLocalizedPath(String localeCode, Uri uri) {
restSegments = segments.skip(1);
}
}
final newSegments = [localeCode, ...restSegments];
final path = '/${newSegments.join('/')}';
final queryPart = uri.hasQuery ? '?${uri.query}' : '';
final fragmentPart = uri.fragment.isNotEmpty ? '#${uri.fragment}' : '';
return '$path$queryPart$fragmentPart';
}
String buildSigninRedirectPath(String localeCode, Uri uri) {
final queryPart = uri.hasQuery ? '?${uri.query}' : '';
return '/$localeCode/signin$queryPart';
final newPath = '/${[localeCode, ...restSegments].join('/')}';
// Return only the path and query part to avoid GoRouter confusion with full URLs
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

@@ -101,8 +101,6 @@ void main() async {
}
// Router Configuration
final _routerLogger = Logger('Router');
final _router = GoRouter(
initialLocation: '/',
debugLogDiagnostics: !kReleaseMode,
@@ -117,11 +115,15 @@ final _router = GoRouter(
routes: [
GoRoute(
path: '/:locale',
builder: (context, state) {
_routerLogger.info("Navigating to root (DashboardScreen)");
return const DashboardScreen();
},
// Note: Removed direct builder here to prevent interference with sub-routes
routes: [
GoRoute(
path: '', // Matches /:locale
builder: (context, state) {
print("[Router] Building Dashboard (Root)");
return const DashboardScreen();
},
),
GoRoute(
path: 'profile',
builder: (context, state) => const ProfilePage(),
@@ -129,14 +131,10 @@ final _router = GoRouter(
GoRoute(
path: 'signin',
builder: (context, state) {
final loginChallenge =
state.uri.queryParameters['login_challenge'];
final redirectUrl =
state.uri.queryParameters['redirect_uri'] ??
state.uri.queryParameters['redirect_url'];
_routerLogger.info(
"Navigating to /signin with login_challenge: $loginChallenge, redirect: $redirectUrl",
);
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,
@@ -147,14 +145,11 @@ final _router = GoRouter(
GoRoute(
path: 'login',
builder: (context, state) {
final loginChallenge =
state.uri.queryParameters['login_challenge'];
final redirectUrl =
state.uri.queryParameters['redirect_uri'] ??
state.uri.queryParameters['redirect_url'];
_routerLogger.info(
"Navigating to /login with login_challenge: $loginChallenge, redirect: $redirectUrl",
);
// IMPORTANT: Match signin logic to handle OIDC challenges
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,
@@ -165,48 +160,33 @@ final _router = GoRouter(
GoRoute(
path: 'consent',
builder: (BuildContext context, GoRouterState state) {
final consentChallenge =
state.uri.queryParameters['consent_challenge'];
final consentChallenge = state.uri.queryParameters['consent_challenge'];
if (consentChallenge == null) {
_routerLogger.warning(
"Consent screen loaded without a challenge.",
);
print("[Router] WARNING: Consent screen without challenge.");
return const Scaffold(
body: Center(
child: Text('Error: Consent challenge is missing.'),
),
body: Center(child: Text('Error: Consent challenge is missing.')),
);
}
_routerLogger.info("Navigating to /consent with challenge.");
print("[Router] Building /consent. Challenge: $consentChallenge");
return ConsentScreen(consentChallenge: consentChallenge);
},
),
GoRoute(
path: 'signup',
builder: (context, state) {
_routerLogger.info("Navigating to /signup");
return const SignupScreen();
},
builder: (context, state) => const SignupScreen(),
),
GoRoute(
path: 'registration',
builder: (context, state) {
_routerLogger.info("Navigating to /registration");
return const SignupScreen();
},
builder: (context, state) => const SignupScreen(),
),
GoRoute(
path: 'verify',
builder: (context, state) {
_routerLogger.info("Navigating to /verify (query)");
return LoginScreen(key: state.pageKey);
},
builder: (context, state) => LoginScreen(key: state.pageKey),
),
GoRoute(
path: 'verify/:token',
builder: (context, state) {
final token = state.pathParameters['token'];
_routerLogger.info("Navigating to /verify with token: $token");
return LoginScreen(
key: state.pageKey,
verificationToken: token,
@@ -215,45 +195,30 @@ final _router = GoRouter(
),
GoRoute(
path: 'verification',
builder: (context, state) {
_routerLogger.info("Navigating to /verification");
return LoginScreen(key: state.pageKey);
},
builder: (context, state) => LoginScreen(key: state.pageKey),
),
GoRoute(
path: 'l/:shortCode',
builder: (context, state) {
final shortCode = state.pathParameters['shortCode'];
_routerLogger.info("Navigating to /l with code: $shortCode");
return LoginScreen(key: state.pageKey);
},
),
GoRoute(
path: 'forgot-password',
builder: (context, state) {
_routerLogger.info("Navigating to /forgot-password");
return const ForgotPasswordScreen();
},
builder: (context, state) => const ForgotPasswordScreen(),
),
GoRoute(
path: 'recovery',
builder: (context, state) {
_routerLogger.info("Navigating to /recovery");
return const ForgotPasswordScreen();
},
builder: (context, state) => const ForgotPasswordScreen(),
),
GoRoute(
// Supports both /reset-password and /reset-password?token=...
path: 'reset-password',
builder: (context, state) {
_routerLogger.info("Navigating to /reset-password");
return const ResetPasswordScreen();
},
builder: (context, state) => const ResetPasswordScreen(),
),
GoRoute(
path: 'error',
builder: (context, state) {
_routerLogger.info("Navigating to /error");
final params = state.uri.queryParameters;
return ErrorScreen(
errorId: params['id'],
@@ -264,43 +229,30 @@ final _router = GoRouter(
),
GoRoute(
path: 'settings',
builder: (context, state) {
_routerLogger.info("Navigating to /settings (disabled)");
return ErrorScreen(
errorCode: 'settings_disabled',
description: tr('msg.userfront.settings.disabled'),
);
},
builder: (context, state) => ErrorScreen(
errorCode: 'settings_disabled',
description: tr('msg.userfront.settings.disabled'),
),
),
GoRoute(
path: 'approve',
builder: (context, state) {
final ref = state.uri.queryParameters['ref'];
_routerLogger.info("Navigating to /approve with ref: $ref");
return ApproveQrScreen(pendingRef: ref);
},
builder: (context, state) => ApproveQrScreen(
pendingRef: state.uri.queryParameters['ref'],
),
),
GoRoute(
path: 'ql/:ref',
builder: (context, state) {
final ref = state.pathParameters['ref'];
_routerLogger.info("Navigating to /ql with ref: $ref");
return ApproveQrScreen(pendingRef: ref);
},
builder: (context, state) => ApproveQrScreen(
pendingRef: state.pathParameters['ref'],
),
),
GoRoute(
path: 'scan',
builder: (context, state) {
_routerLogger.info("Navigating to /scan");
return const QRScanScreen();
},
builder: (context, state) => const QRScanScreen(),
),
GoRoute(
path: 'admin/users',
builder: (context, state) {
_routerLogger.info("Navigating to /admin/users");
return const UserManagementScreen();
},
builder: (context, state) => const UserManagementScreen(),
),
],
),
@@ -308,18 +260,23 @@ final _router = GoRouter(
),
],
redirect: (context, state) {
final requestedLocale = extractLocaleFromPath(state.uri);
final uri = state.uri;
final requestedLocale = extractLocaleFromPath(uri);
final preferredLocale = resolvePreferredLocaleCode();
print("[Router] Redirect check for: $uri");
if (requestedLocale == null) {
return buildLocalizedPath(preferredLocale, state.uri);
final localizedPath = buildLocalizedPath(preferredLocale, uri);
print("[Router] Locale missing. Redirecting to: $localizedPath");
return localizedPath;
}
final hasStoredToken = AuthTokenStore.getToken() != null;
final hasCookieSession = AuthTokenStore.usesCookie();
final isLoggedIn = hasStoredToken || hasCookieSession;
final path = stripLocalePath(state.uri);
final token = AuthTokenStore.getToken();
final isLoggedIn = (token != null && token.isNotEmpty) || AuthTokenStore.usesCookie();
final path = stripLocalePath(uri);
// Public paths that don't require login
// Precise public path detection
final isPublicPath =
path == '/signin' ||
path == '/signup' ||
@@ -335,28 +292,28 @@ final _router = GoRouter(
path == '/reset-password' ||
path == '/error' ||
path == '/settings' ||
path == '/consent'; // Consent page is public
path == '/consent' ||
path.startsWith('/consent/') ||
uri.path.contains('/consent');
_routerLogger.fine("Redirect check - Path: $path, IsLoggedIn: $isLoggedIn");
print("[Router] Path: $path, IsLoggedIn: $isLoggedIn, IsPublic: $isPublicPath");
// 0. ALWAYS allow public paths to proceed so they can function
if (isPublicPath) {
return null;
}
// If not logged in and trying to access a protected page, redirect to /signin
if (!isLoggedIn) {
_routerLogger.info("Not logged in, redirecting to /signin");
return buildSigninRedirectPath(requestedLocale, state.uri);
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;
}
// If logged in and trying to access login page, redirect to root (dashboard)
// This is now implicitly handled by the isPublicPath check, but kept for clarity.
// if (isLoggedIn && path == '/signin') {
// _routerLogger.info("Logged in, redirecting to /");
// return '/';
// }
return null;
},
);