Breaking changes:
- (ArcaneAuthInterface) Migrated `loginWithEmailAndPassword` to `login`
- (ArcaneAuthInterface) Migrated `signup` to `register`

Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
2024-10-10 15:56:38 +02:00
parent c6377e2efe
commit a67e4adfa2
5 changed files with 79 additions and 87 deletions
+11
View File
@@ -1,3 +1,14 @@
## 1.1.0
- [BREAKING] Updated the authentication service and interface to be more versatile
### Migration
| Class | Migration path |
| ------------------- | -------------------------------------------------------------------------------------- |
| ArcaneAuthInterface | `loginWtihEmailAndPassword({String email, String password})` -> `login<T>({T? input})` |
| ArcaneAuthInterface | `signup({String email, String password})` -> `register<T>({T? input})` |
## 1.0.8 ## 1.0.8
- Added the `extra` parameter to the `Arcane.log` shortcut method - Added the `extra` parameter to the `Arcane.log` shortcut method
@@ -1,6 +1,6 @@
import "package:arcane_framework/arcane_framework.dart"; import "package:arcane_framework/arcane_framework.dart";
typedef LoginInput = ({String email, String password}); typedef Credentials = ({String email, String password});
class DebugAuthInterface implements ArcaneAuthInterface { class DebugAuthInterface implements ArcaneAuthInterface {
DebugAuthInterface._internal(); DebugAuthInterface._internal();
@@ -32,15 +32,8 @@ class DebugAuthInterface implements ArcaneAuthInterface {
} }
@override @override
Future<Result<void, String>> loginWithEmailAndPassword({ Future<Result<void, String>> login<Credentials>({
required String email, Credentials? input,
required String password,
}) async =>
throw UnimplementedError();
@override
Future<Result<void, String>> login<LoginInput>({
LoginInput? input,
Future<void> Function()? onLoggedIn, Future<void> Function()? onLoggedIn,
}) async { }) async {
final bool alreadyLoggedIn = await isSignedIn; final bool alreadyLoggedIn = await isSignedIn;
@@ -66,11 +59,18 @@ class DebugAuthInterface implements ArcaneAuthInterface {
} }
@override @override
Future<Result<SignUpStep, String>> signup({ Future<Result<SignUpStep, String>> register<Credentials>({
required String password, Credentials? input,
required String email,
}) async { }) async {
if (input != null) {
final credentials = input as ({String email, String password});
final String email = credentials.email;
final String password = credentials.password;
Arcane.log("Creating account for $email with password $password"); Arcane.log("Creating account for $email with password $password");
}
return Result.ok(SignUpStep.confirmSignUp); return Result.ok(SignUpStep.confirmSignUp);
} }
@@ -14,13 +14,13 @@ abstract class ArcaneAuthInterface {
/// ///
/// This is used to retrieve the current session's access token for authenticated /// This is used to retrieve the current session's access token for authenticated
/// API requests. Returns `null` if the user is not signed in or the token is unavailable. /// API requests. Returns `null` if the user is not signed in or the token is unavailable.
Future<String?> get accessToken; Future<String?>? get accessToken;
/// Returns the refresh token if available. /// Returns the refresh token if available.
/// ///
/// The refresh token is used to renew access tokens when they expire. Returns `null` if /// The refresh token is used to renew access tokens when they expire. Returns `null` if
/// the user is not signed in or the token is unavailable. /// the user is not signed in or the token is unavailable.
Future<String?> get refreshToken; Future<String?>? get refreshToken;
/// Initializes the authentication interface. /// Initializes the authentication interface.
/// ///
@@ -34,24 +34,6 @@ abstract class ArcaneAuthInterface {
/// Returns a `Result` that either contains a `void` on success or an error message. /// Returns a `Result` that either contains a `void` on success or an error message.
Future<Result<void, String>> logout(); Future<Result<void, String>> logout();
/// Logs the user in using an email address and password.
///
/// This method authenticates the user with their email and password credentials.
/// Returns a `Result` that either contains a `void` on success or an error message.
///
/// Example:
/// ```dart
/// await authInterface.loginWithEmailAndPassword(
/// email: "user@example.com",
/// password: "password123",
/// );
/// ```
@Deprecated("Use `login` instead. Deprecated as of version 1.0.5")
Future<Result<void, String>> loginWithEmailAndPassword({
required String email,
required String password,
});
/// Logs the user in using an optional, generic `T` type of input. /// Logs the user in using an optional, generic `T` type of input.
/// This login method is a generic method that can be used to login with any /// This login method is a generic method that can be used to login with any
/// type of input. It is useful for login methods that do not require an email /// type of input. It is useful for login methods that do not require an email
@@ -78,32 +60,29 @@ abstract class ArcaneAuthInterface {
/// This method is typically used when the user hasn't received or has lost their initial /// This method is typically used when the user hasn't received or has lost their initial
/// verification code. Returns a `Result` that contains the verification code on success /// verification code. Returns a `Result` that contains the verification code on success
/// or an error message. /// or an error message.
Future<Result<String, String>> resendVerificationCode( Future<Result<String, String>>? resendVerificationCode(
String email, String email,
); );
/// Signs a user up with a username, password, and email. /// Registers a new account using user-supplied input.
/// ///
/// This method registers a new user in the system. Returns a `Result` that contains /// This method registers a new user in the system. Returns a `Result` that contains
/// the next [SignUpStep] in the process on success or an error message. /// the next [SignUpStep] in the process on success or an error message.
/// ///
/// Example: /// Example:
/// ```dart /// ```dart
/// await authInterface.signup( /// await authInterface.register(
/// email: "user@example.com", /// email: "user@example.com",
/// password: "password123", /// password: "password123",
/// ); /// );
/// ``` /// ```
Future<Result<SignUpStep, String>> signup({ Future<Result<SignUpStep, String>>? register<T>({T? input});
required String password,
required String email,
});
/// Confirms a user's signup using a username and a confirmation code. /// Confirms a user's signup using a username and a confirmation code.
/// ///
/// This method completes the sign-up process by verifying the user's confirmation code. /// This method completes the sign-up process by verifying the user's confirmation code.
/// Returns a `Result` that contains `true` on success or an error message. /// Returns a `Result` that contains `true` on success or an error message.
Future<Result<bool, String>> confirmSignup({ Future<Result<bool, String>>? confirmSignup({
required String username, required String username,
required String confirmationCode, required String confirmationCode,
}); });
@@ -113,7 +92,12 @@ abstract class ArcaneAuthInterface {
/// This method is used when a user requests to reset their password. The reset code /// This method is used when a user requests to reset their password. The reset code
/// they receive via email is used to verify the request. Optionally, a new password can /// they receive via email is used to verify the request. Optionally, a new password can
/// be provided. Returns a `Result` that contains `true` on success or an error message. /// be provided. Returns a `Result` that contains `true` on success or an error message.
Future<Result<bool, String>> resetPassword({ ///
/// This method may be called twice. In the first instance, the `email` is provided, which
/// triggers a password reset confirmation email to be sent, containing a password reset
/// code or pin. The second call should include the `email`, the user's new password
/// (`newPassword`), and the `code` they received in their email.
Future<Result<bool, String>>? resetPassword({
required String email, required String email,
String? newPassword, String? newPassword,
String? code, String? code,
@@ -52,11 +52,13 @@ class ArcaneAuthenticationService extends ArcaneService {
/// Returns a JWT access token if the registered `ArcaneAuthInterface` /// Returns a JWT access token if the registered `ArcaneAuthInterface`
/// provides one. This token is often used in the headers of HTTP requests /// provides one. This token is often used in the headers of HTTP requests
/// to the backend API. /// to the backend API.
Future<String?> get accessToken => authInterface.accessToken; Future<String?> get accessToken =>
authInterface.accessToken ?? Future.value("");
/// Returns a JWT refresh token if the registered `ArcaneAuthInterface` /// Returns a JWT refresh token if the registered `ArcaneAuthInterface`
/// provides one. /// provides one.
Future<String?> get refreshToken => authInterface.refreshToken; Future<String?> get refreshToken =>
authInterface.refreshToken ?? Future.value("");
/// Removes any registered `ArcaneAuthInterface` and resets all values to /// Removes any registered `ArcaneAuthInterface` and resets all values to
/// default. /// default.
@@ -174,36 +176,6 @@ class ArcaneAuthenticationService extends ArcaneService {
); );
} }
/// Attempts to log in the user using their `email` and `password`.
/// Upon successful login, `status` will be set to
/// `AuthenticationStatus.authenticated]` If `onLoggedIn` is specified, the
/// method will run after the authentication status has been updated.
/// When logging in with email and password, the user's email address will be
/// cached in `ArcaneSecureStorage`.
@Deprecated("Use `login` instead. Deprecated as of version 1.0.5")
Future<Result<void, String>> loginWithEmailAndPassword({
required String email,
required String password,
Future<void> Function()? onLoggedIn,
}) async {
if (status != AuthenticationStatus.unauthenticated) {
return Result.error("Cannot sign in. Status is already ${status.name}.");
}
final Result<void, String> result =
await authInterface.loginWithEmailAndPassword(
email: email,
password: password,
);
if (result.isSuccess) {
setAuthenticated();
if (onLoggedIn != null) await onLoggedIn();
}
return result;
}
/// Logs the user in using an optional, generic `T` type of input. /// Logs the user in using an optional, generic `T` type of input.
Future<Result<void, String>> login<T>({ Future<Result<void, String>> login<T>({
T? input, T? input,
@@ -225,22 +197,26 @@ class ArcaneAuthenticationService extends ArcaneService {
return result; return result;
} }
/// Attempts to register a new account using the provided `email` and /// Attempts to register a new account using user-defined input.
/// `password`. Upon success, returns a `SignUpStep` indicating the next step /// Upon success, returns a `SignUpStep` indicating the next step
/// in the signup process as a `SignUpStep`. /// in the signup process as a `SignUpStep`.
Future<Result<SignUpStep, String>> signup({ Future<Result<SignUpStep, String>> register<T>({
required String email, T? input,
required String password,
}) async { }) async {
if (_authInterface == null) { if (_authInterface == null) {
return Result.error("No ArcaneAuthInterface has been registered"); return Result.error("No ArcaneAuthInterface has been registered");
} }
final Result<SignUpStep, String> result = await authInterface.signup( final Result<SignUpStep, String>? result = await authInterface.register(
email: email, input: input,
password: password,
); );
if (result == null) {
return Result.error(
"Registered ArcaneAuthInterface returned a null value.",
);
}
return result; return result;
} }
@@ -254,11 +230,17 @@ class ArcaneAuthenticationService extends ArcaneService {
return Result.error("No ArcaneAuthInterface has been registered"); return Result.error("No ArcaneAuthInterface has been registered");
} }
final Result<bool, String> result = await authInterface.confirmSignup( final Result<bool, String>? result = await authInterface.confirmSignup(
username: email, username: email,
confirmationCode: confirmationCode, confirmationCode: confirmationCode,
); );
if (result == null) {
return Result.error(
"Registered ArcaneAuthInterface returned a null value.",
);
}
return result; return result;
} }
@@ -269,7 +251,16 @@ class ArcaneAuthenticationService extends ArcaneService {
return Result.error("No ArcaneAuthInterface has been registered"); return Result.error("No ArcaneAuthInterface has been registered");
} }
return authInterface.resendVerificationCode(email); final Future<Result<String, String>>? result =
authInterface.resendVerificationCode(email);
if (result == null) {
return Result.error(
"Registered ArcaneAuthInterface returned a null value.",
);
}
return result;
} }
/// Attempts to reset the user's password using their `email`. This method /// Attempts to reset the user's password using their `email`. This method
@@ -287,12 +278,18 @@ class ArcaneAuthenticationService extends ArcaneService {
return Result.error("No ArcaneAuthInterface has been registered"); return Result.error("No ArcaneAuthInterface has been registered");
} }
final Result<bool, String> result = await authInterface.resetPassword( final Result<bool, String>? result = await authInterface.resetPassword(
email: email, email: email,
newPassword: newPassword, newPassword: newPassword,
code: confirmationCode, code: confirmationCode,
); );
if (result == null) {
return Result.error(
"Registered ArcaneAuthInterface returned a null value.",
);
}
return result; return result;
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
name: arcane_framework name: arcane_framework
description: "Agnostic Reusable Component Architecture for New Ecosystems: a modern framework for bootstrapping new applications" description: "Agnostic Reusable Component Architecture for New Ecosystems: a modern framework for bootstrapping new applications"
version: 1.0.8 version: 1.1.0
repository: https://github.com/hanskokx/arcane_framework repository: https://github.com/hanskokx/arcane_framework
issue_tracker: https://github.com/hanskokx/arcane_framework/issues issue_tracker: https://github.com/hanskokx/arcane_framework/issues