authenticate method

Future<User> authenticate({
  1. String? challengeToken,
  2. required String email,
  3. bool? emailVerified,
  4. String? name,
  5. String? password,
  6. List<String>? tags,
  7. UserAuthenticationPolicy? policy,
  8. bool? doNotNotify,
})

Authenticates a user and returns a User object.

This is the main authentication method that handles email/password authentication. It creates a new user if they don't exist (based on the policy) or signs in an existing user. The method integrates with device attestation for enhanced security.

Parameters

  • challengeToken: Optional challenge token (will be generated if not provided)
  • email: The user's email address (required)
  • emailVerified: Whether the email is already verified
  • name: The user's display name (for new users)
  • password: The user's password (required for email/password auth)
  • tags: List of tags to assign to the user (for new users)
  • policy: Authentication policy controlling user creation/sign-in behavior
  • doNotNotify: If true, suppresses sending welcome/notification emails

Returns

A Future that resolves to a User object representing the authenticated user

Throws

  • Exception if password is not provided for email/password authentication
  • HttpException if there's a network error
  • CalljmpException if authentication fails or policy restrictions apply

Example

// Sign in existing user or create new user
final user = await calljmp.users.auth.email.authenticate(
  email: 'user@example.com',
  password: 'secure_password',
  name: 'John Doe',
  tags: ['role:member', 'plan:free'],
  policy: UserAuthenticationPolicy.signInOrCreate,
);

print('Authenticated user: ${user.name} (${user.email})');

Implementation

Future<User> authenticate({
  String? challengeToken,
  required String email,
  bool? emailVerified,
  String? name,
  String? password,
  List<String>? tags,
  UserAuthenticationPolicy? policy,
  bool? doNotNotify,
}) async {
  if (password == null) {
    throw Exception("Password is required for email/password authentication");
  }

  if (challengeToken == null) {
    final result = await _auth.challenge();
    challengeToken = result.challengeToken;
  }

  final attestationHash = base64.encode(
    sha256.convert(utf8.encode("$email:$challengeToken")).bytes,
  );
  final attest = await _attestation
      .attest({"hash": attestationHash})
      .catchError((error) {
        developer.log(
          "Failed to attest, this is fatal error unless it is in debug mode",
          name: "calljmp",
          error: error,
        );
        return Null;
      });
  final attestationToken = base64.encode(utf8.encode(jsonEncode(attest)));

  final result = await http
      .request("${_config.serviceUrl}/users/auth/email")
      .use(http.context(_config))
      .use(http.access())
      .post({
        "token": challengeToken,
        "attestationToken": attestationToken,
        "email": email,
        "password": password,
        if (emailVerified != null) "emailVerified": emailVerified,
        if (name != null) "name": name,
        if (tags != null) "tags": tags,
        if (policy != null) "policy": policy.value,
        "doNotNotify": doNotNotify ?? false,
      })
      .json(
        (json) => (
          accessToken: json["accessToken"],
          user: User.fromJson(json["user"]),
        ),
      );

  await CalljmpStore.instance.put(
    CalljmpStoreKey.accessToken,
    result.accessToken,
  );

  return result.user;
}