This package defines the abstract interface (AuthClient
) for authentication operations within the Headlines Toolkit ecosystem. It provides a contract that concrete implementations (e.g., API clients, Firebase clients, in-memory mocks) must adhere to.
The interface supports both an email+code password-less authentication and an anonymous authentication flow.
This package is intended to be used as an interface dependency. Add it to your pubspec.yaml
:
dependencies:
auth_client:
git:
url: https://github.com/flutter-news-app-full-source-code/auth-client.git
# Consider adding a ref: tag for version pinning
Then import the library:
import 'package:auth_client/auth_client.dart';
You will typically interact with a concrete implementation of AuthClient
provided via dependency injection.
The AuthClient
interface defines the following core authentication capabilities:
authStateChanges
: AStream<User?>
that emits the current authenticatedUser
ornull
whenever the authentication state changes (sign-in, sign-out). Ideal for reactive UI updates.getCurrentUser()
: An asynchronous methodFuture<User?>
to retrieve the currently signed-inUser
, if any.requestSignInCode(String email, {bool isDashboardLogin = false})
: Initiates the passwordless sign-in flow. It's context-aware: for standard sign-in, it sends a code; for dashboard login (isDashboardLogin: true
), it first validates the user's existence and permissions.verifySignInCode(String email, String code, {bool isDashboardLogin = false})
: Verifies the email code. For standard flows, it signs in or creates a user. For dashboard login (isDashboardLogin: true
), it strictly performs a login and does not create new accounts. Returns aFuture<AuthSuccessResponse>
.signInAnonymously()
: Signs the user in anonymously, creating a temporary user identity on the backend and returning aFuture<AuthSuccessResponse>
containing the anonymousUser
and token.signOut()
: Signs out the current user (normal or anonymous).linkEmail(String email)
: Initiates the process of linking an email to an existing anonymous account.verifyLinkEmail(String code)
: Completes the email linking process by verifying the code. On success, it returns aFuture<AuthSuccessResponse>
with the now-permanent user and a new token.deleteAccount()
: Allows an authenticated user to delete their own account.
Error handling is standardized using exceptions defined in the core
package. Implementations must map underlying errors to appropriate HttpException
subtypes.
Here's a conceptual example of how a consuming application (like a Flutter app using BLoC) might interact with an injected AuthClient
instance:
// Assuming 'authClient' is an instance of a concrete AuthClient implementation
// Listen to authentication state changes
final authSubscription = authClient.authStateChanges.listen((user) {
if (user != null) {
print('User signed in: ${user.id}, Roles: ${user.roles}');
// Navigate to home screen, update UI
} else {
print('User signed out');
// Navigate to login screen
}
});
// --- Email+Code Flow (Dashboard Example) ---
Future<void> startDashboardSignIn(String email) async {
try {
// For a privileged dashboard login, set the flag to true
await authClient.requestSignInCode(email, isDashboardLogin: true);
// Navigate to code entry screen
} on UnauthorizedException {
print('This email is not registered for dashboard access.');
} on ForbiddenException {
print('You do not have permission to access the dashboard.');
} catch (e) {
print('Failed to request code: $e');
}
}
// --- Email+Code Flow ---
Future<void> startEmailSignIn(String email) async {
try {
await authClient.requestSignInCode(email);
// Navigate to code entry screen
} on InvalidInputException catch (e) {
print('Invalid email format: ${e.message}');
} on NetworkException {
print('Network error requesting code.');
} catch (e) {
print('Failed to request code: $e');
}
}
Future<void> verifyCode(String email, String code) async {
try {
final authResponse = await authClient.verifySignInCode(email, code);
final user = authResponse.user;
final token = authResponse.token;
print('Successfully signed in/up user: ${user.id}, token: $token');
// authStateChanges will emit the new user
} on AuthenticationException catch (e) {
print('Invalid code: ${e.message}');
} on InvalidInputException catch (e) {
print('Invalid input: ${e.message}');
} on NetworkException {
print('Network error verifying code.');
} catch (e) {
print('Failed to verify code: $e');
}
}
// --- Anonymous Flow ---
Future<void> signInAnon() async {
try {
final authResponse = await authClient.signInAnonymously();
final user = authResponse.user;
final token = authResponse.token;
print('Signed in anonymously: ${user.id}, token: $token');
// authStateChanges will emit the new anonymous user
} on NetworkException {
print('Network error signing in anonymously.');
} catch (e) {
print('Failed to sign in anonymously: $e');
}
}
// --- Sign Out ---
Future<void> logOut() async {
try {
await authClient.signOut();
print('Signed out successfully.');
// authStateChanges will emit null
} catch (e) {
print('Failed to sign out: $e');
}
}
// Remember to cancel streams
// authSubscription.cancel();
This package is source-available and licensed under the PolyForm Free Trial 1.0.0. Please review the terms before use.
For commercial licensing options that grant the right to build and distribute unlimited applications, please visit the main Flutter News App - Full Source Code Toolkit organization.