forked from baron/baron-sso
라우터 구조 개선 및 리다이렉트 시 파라미터 유실 수정
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user