Skip to content

Fix_auth_verify_code_in_demo #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 26 additions & 29 deletions lib/authentication/view/authentication_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,18 @@ class AuthenticationPage extends StatelessWidget {
backgroundColor: Colors.transparent,
elevation: 0,
// Conditionally add the leading close button only in linking context
leading:
isLinkingContext
? IconButton(
icon: const Icon(Icons.close),
tooltip:
MaterialLocalizations.of(
context,
).closeButtonTooltip, // Accessibility
onPressed: () {
// Navigate back to the account page when close is pressed
context.goNamed(Routes.accountName);
},
)
: null, // No leading button if not linking (relies on system back if pushed)
leading: isLinkingContext
? IconButton(
icon: const Icon(Icons.close),
tooltip: MaterialLocalizations.of(
context,
).closeButtonTooltip, // Accessibility
onPressed: () {
// Navigate back to the account page when close is pressed
context.goNamed(Routes.accountName);
},
)
: null, // No leading button if not linking (relies on system back if pushed)
),
body: SafeArea(
child: BlocConsumer<AuthenticationBloc, AuthenticationState>(
Expand Down Expand Up @@ -130,15 +128,15 @@ class AuthenticationPage extends StatelessWidget {
// --- Email Sign-In Button ---
ElevatedButton.icon(
icon: const Icon(Icons.email_outlined),
onPressed:
isLoading
? null
: () {
context.goNamed(
Routes.requestCodeName,
extra: isLinkingContext,
);
},
onPressed: isLoading
? null
: () {
context.goNamed(
isLinkingContext
? Routes.linkingRequestCodeName
: Routes.requestCodeName,
);
},
label: Text(l10n.authenticationEmailSignInButton),
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(
Expand All @@ -153,12 +151,11 @@ class AuthenticationPage extends StatelessWidget {
if (showAnonymousButton) ...[
OutlinedButton.icon(
icon: const Icon(Icons.person_outline),
onPressed:
isLoading
? null
: () => context.read<AuthenticationBloc>().add(
const AuthenticationAnonymousSignInRequested(),
),
onPressed: isLoading
? null
: () => context.read<AuthenticationBloc>().add(
const AuthenticationAnonymousSignInRequested(),
),
label: Text(l10n.authenticationAnonymousSignInButton),
style: OutlinedButton.styleFrom(
padding: const EdgeInsets.symmetric(
Expand Down
7 changes: 5 additions & 2 deletions lib/authentication/view/request_code_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ class _RequestCodeView extends StatelessWidget {
// If linking, go back to Auth page preserving the linking query param.
context.goNamed(
Routes.authenticationName,
queryParameters: {'context': 'linking'},
queryParameters:
isLinkingContext ? {'context': 'linking'} : const {},
);
} else {
// If normal sign-in, just go back to the Auth page.
Expand All @@ -84,7 +85,9 @@ class _RequestCodeView extends StatelessWidget {
} else if (state is AuthenticationCodeSentSuccess) {
// Navigate to the code verification page on success, passing the email
context.goNamed(
Routes.verifyCodeName,
isLinkingContext
? Routes.linkingVerifyCodeName
: Routes.verifyCodeName,
pathParameters: {'email': state.email},
);
}
Expand Down
1 change: 1 addition & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ void main() async {
enabled: true,
builder: (context) => appWidget,
tools: const [DeviceSection()],
backgroundColor: Colors.black87,
),
);
} else {
Expand Down
99 changes: 70 additions & 29 deletions lib/router/router.dart
Original file line number Diff line number Diff line change
Expand Up @@ -135,23 +135,47 @@ GoRouter createRouter({
appStatus == AppStatus.authenticated) {
print(' Redirect Decision: User is $appStatus.');

final isLinkingContext =
currentUri.queryParameters['context'] == 'linking';
final isLinkingContextQueryPresent = state.uri.queryParameters['context'] == 'linking';
final isLinkingPathSegmentPresent = currentLocation.contains('/linking/');

// If an authenticated/anonymous user tries to access the BASE /authentication path
// AND it's NOT for account linking, redirect them to the feed.
if (currentLocation == authenticationPath && !isLinkingContext) {
// Determine if the current location is part of any linking flow (either via query or path segment)
final isAnyLinkingContext = isLinkingContextQueryPresent || isLinkingPathSegmentPresent;

// If an authenticated/anonymous user is on any authentication-related path:
if (currentLocation.startsWith(authenticationPath)) {
print(' Debug: Auth path detected. Current Location: $currentLocation');
print(' Debug: URI Query Parameters: ${state.uri.queryParameters}');
print(' Debug: isLinkingContextQueryPresent: $isLinkingContextQueryPresent');
print(' Debug: isLinkingPathSegmentPresent: $isLinkingPathSegmentPresent');
print(' Debug: isAnyLinkingContext evaluated to: $isAnyLinkingContext');

// If the user is authenticated, always redirect away from auth paths.
if (appStatus == AppStatus.authenticated) {
print(
' Action: Authenticated user on auth path ($currentLocation). Redirecting to $feedPath',
);
return feedPath;
}

// If the user is anonymous, allow navigation within auth paths if in a linking context.
// Otherwise, redirect anonymous users trying to access non-linking auth paths to feed.
if (isAnyLinkingContext) {
print(
' Action: Anonymous user on auth linking path ($currentLocation). Allowing navigation.',
);
return null;
} else {
print(
' Action: Anonymous user trying to access non-linking auth path ($currentLocation). Redirecting to $feedPath',
);
return feedPath;
}
}
// Allow access to other routes (non-auth paths)
print(
' Action: $appStatus user trying to access base auth path without linking context. Redirecting to $feedPath',
' Action: Allowing navigation to $currentLocation for $appStatus user (non-auth path).',
);
return feedPath;
}

// Allow access to other routes (including auth sub-routes if linking, or any other app route)
print(
' Action: Allowing navigation to $currentLocation for $appStatus user.',
);
return null;
return null;
}

// Fallback (should ideally not be reached if all statuses are handled)
Expand Down Expand Up @@ -200,24 +224,41 @@ GoRouter createRouter({
);
},
routes: [
// Nested route for account linking flow (defined first for priority)
GoRoute(
path: Routes.requestCode, // Use new path
name: Routes.requestCodeName, // Use new name
builder: (context, state) {
// Extract the linking context flag from 'extra', default to false.
final isLinking = (state.extra as bool?) ?? false;
return RequestCodePage(isLinkingContext: isLinking);
},
path: Routes.accountLinking, // This is 'linking'
name: Routes.accountLinkingName, // Name for the linking segment
builder: (context, state) => const SizedBox.shrink(), // Placeholder
routes: [
GoRoute(
path: Routes.requestCode, // Path: /authentication/linking/request-code
name: Routes.linkingRequestCodeName,
builder: (context, state) =>
const RequestCodePage(isLinkingContext: true),
),
GoRoute(
path: '${Routes.verifyCode}/:email', // Path: /authentication/linking/verify-code/:email
name: Routes.linkingVerifyCodeName,
builder: (context, state) {
final email = state.pathParameters['email']!;
return EmailCodeVerificationPage(email: email);
},
),
],
),
// Non-linking authentication routes (defined after linking routes)
GoRoute(
path:
'${Routes.verifyCode}/:email', // Use new path with email parameter
name: Routes.verifyCodeName, // Use new name
path: Routes.requestCode,
name: Routes.requestCodeName,
builder: (context, state) =>
const RequestCodePage(isLinkingContext: false),
),
GoRoute(
path: '${Routes.verifyCode}/:email',
name: Routes.verifyCodeName,
builder: (context, state) {
final email = state.pathParameters['email']!; // Extract email
return EmailCodeVerificationPage(
email: email,
); // Use renamed page
final email = state.pathParameters['email']!;
return EmailCodeVerificationPage(email: email);
},
),
],
Expand Down Expand Up @@ -731,7 +772,7 @@ GoRouter createRouter({
.read<
HtDataRepository<Headline>
>(),
),
),
),
BlocProvider(
create:
Expand Down
6 changes: 6 additions & 0 deletions lib/router/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ abstract final class Routes {
static const verifyCode = 'verify-code';
static const verifyCodeName = 'verifyCode';

// Linking-specific authentication routes
static const linkingRequestCode = 'linking/request-code';
static const linkingRequestCodeName = 'linkingRequestCode';
static const linkingVerifyCode = 'linking/verify-code';
static const linkingVerifyCodeName = 'linkingVerifyCode';

// --- Settings Sub-Routes (relative to /account/settings) ---
static const settingsAppearance = 'appearance';
static const settingsAppearanceName = 'settingsAppearance';
Expand Down
42 changes: 37 additions & 5 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
url: "https://pub.dev"
source: hosted
version: "2.0.4"
cli_config:
dependency: transitive
description:
Expand All @@ -97,6 +105,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.2.0"
cli_util:
dependency: transitive
description:
name: cli_util
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
url: "https://pub.dev"
source: hosted
version: "0.4.2"
clock:
dependency: transitive
description:
Expand Down Expand Up @@ -270,6 +286,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "9.1.1"
flutter_launcher_icons:
dependency: "direct main"
description:
name: flutter_launcher_icons
sha256: "10f13781741a2e3972126fae08393d3c4e01fa4cd7473326b94b72cf594195e7"
url: "https://pub.dev"
source: hosted
version: "0.14.4"
flutter_localizations:
dependency: "direct main"
description: flutter
Expand Down Expand Up @@ -356,7 +380,7 @@ packages:
description:
path: "."
ref: HEAD
resolved-ref: "028ebfa29ebc8f95c2da30fee68566723d1e6897"
resolved-ref: "3a8dc5ff81c59805fa59996517eb0fdf136a0b67"
url: "https://github.com/headlines-toolkit/ht-auth-inmemory"
source: git
version: "0.0.0"
Expand Down Expand Up @@ -501,10 +525,18 @@ packages:
dependency: transitive
description:
name: js
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
version: "0.7.2"
version: "0.6.7"
js_interop:
dependency: "direct main"
description:
name: js_interop
sha256: "7ec859c296958ccea34dc770504bd3ff4ae52fdd9e7eeb2bacc7081ad476a1f5"
url: "https://pub.dev"
source: hosted
version: "0.0.1"
json_annotation:
dependency: transitive
description:
Expand Down Expand Up @@ -1050,10 +1082,10 @@ packages:
dependency: transitive
description:
name: watcher
sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104"
sha256: "0b7fd4a0bbc4b92641dbf20adfd7e3fd1398fe17102d94b674234563e110088a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
version: "1.1.2"
web:
dependency: transitive
description:
Expand Down
Loading