Skip to content

Commit 5cba6bd

Browse files
committed
feat(auth): Add email link sign-in
- Implemented link sending - Added sign-in with link - Added states for link flow
1 parent f5815d3 commit 5cba6bd

File tree

3 files changed

+91
-22
lines changed

3 files changed

+91
-22
lines changed

lib/authentication/bloc/authentication_bloc.dart

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import 'dart:async';
22

33
import 'package:bloc/bloc.dart';
44
import 'package:equatable/equatable.dart';
5-
import 'package:ht_authentication_firebase/ht_authentication_firebase.dart';
65
import 'package:ht_authentication_repository/ht_authentication_repository.dart';
76

87
part 'authentication_event.dart';
@@ -18,9 +17,14 @@ class AuthenticationBloc
1817
required HtAuthenticationRepository authenticationRepository,
1918
}) : _authenticationRepository = authenticationRepository,
2019
super(AuthenticationInitial()) {
21-
on<AuthenticationEmailSignInRequested>(
22-
_onAuthenticationEmailSignInRequested,
20+
// Register new event handlers
21+
on<AuthenticationSendSignInLinkRequested>(
22+
_onAuthenticationSendSignInLinkRequested,
2323
);
24+
on<AuthenticationSignInWithLinkAttempted>(
25+
_onAuthenticationSignInWithLinkAttempted,
26+
);
27+
// Keep existing handlers
2428
on<AuthenticationGoogleSignInRequested>(
2529
_onAuthenticationGoogleSignInRequested,
2630
);
@@ -35,22 +39,62 @@ class AuthenticationBloc
3539

3640
final HtAuthenticationRepository _authenticationRepository;
3741

38-
/// Handles [AuthenticationEmailSignInRequested] events.
39-
Future<void> _onAuthenticationEmailSignInRequested(
40-
AuthenticationEmailSignInRequested event,
42+
/// Handles [AuthenticationSendSignInLinkRequested] events.
43+
Future<void> _onAuthenticationSendSignInLinkRequested(
44+
AuthenticationSendSignInLinkRequested event,
4145
Emitter<AuthenticationState> emit,
4246
) async {
43-
emit(AuthenticationLoading());
47+
// Validate email format (basic check)
48+
if (event.email.isEmpty || !event.email.contains('@')) {
49+
emit(const AuthenticationFailure('Please enter a valid email address.'));
50+
return;
51+
}
52+
emit(AuthenticationLinkSending()); // Indicate link sending
4453
try {
45-
await _authenticationRepository.signInWithEmailAndPassword(
54+
await _authenticationRepository.sendSignInLinkToEmail(email: event.email);
55+
emit(AuthenticationLinkSentSuccess()); // Confirm link sent
56+
} on SendSignInLinkException catch (e) {
57+
emit(AuthenticationFailure('Failed to send link: ${e.error}'));
58+
} catch (e) {
59+
// Catch any other unexpected errors
60+
emit(
61+
AuthenticationFailure(
62+
'An unexpected error occurred: $e',
63+
),
64+
);
65+
// Optionally log the stackTrace here
66+
}
67+
}
68+
69+
/// Handles [AuthenticationSignInWithLinkAttempted] events.
70+
/// This assumes the event is dispatched after the app receives the deep link.
71+
Future<void> _onAuthenticationSignInWithLinkAttempted(
72+
AuthenticationSignInWithLinkAttempted event,
73+
Emitter<AuthenticationState> emit,
74+
) async {
75+
emit(AuthenticationLoading()); // General loading for sign-in attempt
76+
try {
77+
await _authenticationRepository.signInWithEmailLink(
4678
email: event.email,
47-
password: event.password,
79+
emailLink: event.emailLink,
4880
);
81+
// On success, AppBloc should react to the user stream change from the repo.
82+
// Resetting to Initial state here.
4983
emit(AuthenticationInitial());
50-
} on EmailSignInException catch (e) {
51-
emit(AuthenticationFailure(e.toString()));
84+
} on InvalidSignInLinkException catch (e) {
85+
emit(
86+
AuthenticationFailure(
87+
'Sign in failed: Invalid or expired link. ${e.error}',
88+
),
89+
);
5290
} catch (e) {
53-
emit(AuthenticationFailure(e.toString()));
91+
// Catch any other unexpected errors
92+
emit(
93+
AuthenticationFailure(
94+
'An unexpected error occurred during sign in: $e',
95+
),
96+
);
97+
// Optionally log the stackTrace here
5498
}
5599
}
56100

lib/authentication/bloc/authentication_event.dart

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,39 @@ sealed class AuthenticationEvent extends Equatable {
1111
List<Object> get props => [];
1212
}
1313

14-
/// {@template authentication_email_sign_in_requested}
15-
/// Event triggered when the user requests to sign in with email and password.
14+
/// {@template authentication_send_sign_in_link_requested}
15+
/// Event triggered when the user requests a sign-in link to be sent to their email.
1616
/// {@endtemplate}
17-
final class AuthenticationEmailSignInRequested extends AuthenticationEvent {
18-
/// {@macro authentication_email_sign_in_requested}
19-
const AuthenticationEmailSignInRequested({
17+
final class AuthenticationSendSignInLinkRequested extends AuthenticationEvent {
18+
/// {@macro authentication_send_sign_in_link_requested}
19+
const AuthenticationSendSignInLinkRequested({required this.email});
20+
21+
/// The user's email address.
22+
final String email;
23+
24+
@override
25+
List<Object> get props => [email];
26+
}
27+
28+
/// {@template authentication_sign_in_with_link_attempted}
29+
/// Event triggered when the app attempts to sign in using an email link.
30+
/// This is typically triggered by a deep link handler.
31+
/// {@endtemplate}
32+
final class AuthenticationSignInWithLinkAttempted extends AuthenticationEvent {
33+
/// {@macro authentication_sign_in_with_link_attempted}
34+
const AuthenticationSignInWithLinkAttempted({
2035
required this.email,
21-
required this.password,
36+
required this.emailLink,
2237
});
2338

24-
/// The user's email address.
39+
/// The email associated with the sign-in attempt.
2540
final String email;
2641

27-
/// The user's password.
28-
final String password;
42+
/// The sign-in link received by the app.
43+
final String emailLink;
2944

3045
@override
31-
List<Object> get props => [email, password];
46+
List<Object> get props => [email, emailLink];
3247
}
3348

3449
/// {@template authentication_google_sign_in_requested}

lib/authentication/bloc/authentication_state.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ final class AuthenticationAuthenticated extends AuthenticationState {
4040
/// {@endtemplate}
4141
final class AuthenticationUnauthenticated extends AuthenticationState {}
4242

43+
/// {@template authentication_link_sending}
44+
/// State indicating that the sign-in link is being sent.
45+
/// {@endtemplate}
46+
final class AuthenticationLinkSending extends AuthenticationState {}
47+
48+
/// {@template authentication_link_sent_success}
49+
/// State indicating that the sign-in link was sent successfully.
50+
/// {@endtemplate}
51+
final class AuthenticationLinkSentSuccess extends AuthenticationState {}
52+
4353
/// {@template authentication_failure}
4454
/// Represents an authentication failure.
4555
/// {@endtemplate}

0 commit comments

Comments
 (0)