Nest Authbeta

Authentication

Signup, login (password / passwordless / social), logout, password reset, verification, and change password.

Every call below is a method on NestAuthClient. Login and signup persist the returned tokens automatically, so a subsequent getSessionUserData() is authenticated without any extra wiring. Non-2xx responses throw NestAuthException.

Sign up

Register a new account with an email or phone plus a password. The returned AuthResponse carries the token pair, which is persisted for you:

final res = await auth.signup(
  email: 'a@b.com',
  password: 'super-secret',
);
print(res.accessToken);

The full signature accepts an optional tenantId and an extra map merged into the request body for app-specific signup fields:

Future<AuthResponse> signup({
  String? email,
  String? phone,
  required String password,
  String? tenantId,
  Map<String, dynamic>? extra,
})
await auth.signup(
  email: 'a@b.com',
  password: 'super-secret',
  tenantId: 'acme',
  extra: {'firstName': 'Ada', 'lastName': 'Lovelace'},
);

Log in with email & password

The simplest path is loginWithEmail:

final res = await auth.loginWithEmail('a@b.com', 'super-secret');

If the account has MFA enabled, res.isRequiresMfa is true and no tokens are stored yet — you must complete the challenge. See MFA & Tenancy.

For full control use login, which targets a named provider and takes a free-form credentials map:

Future<AuthResponse> login({
  String providerName = 'email',
  required Map<String, dynamic> credentials,
  bool createUserIfNotExists = false,
  String? tenantId,
})
// Equivalent to loginWithEmail:
await auth.login(credentials: {'email': 'a@b.com', 'password': 'super-secret'});
 
// Scope the login to a tenant, creating the user on first sign-in:
await auth.login(
  credentials: {'email': 'a@b.com', 'password': 'super-secret'},
  tenantId: 'acme',
  createUserIfNotExists: true,
);

Passwordless login

A two-step flow: send a one-time code to an email or phone, then exchange the code for a session.

Step 1 — send the code. channel is 'email' or 'sms', and identifier is the matching address:

await auth.passwordlessSend(
  identifier: 'a@b.com',
  channel: 'email',
);
Future<Map<String, dynamic>> passwordlessSend({
  required String identifier,
  required String channel, // 'email' or 'sms'
  String? tenantId,
})

Step 2 — verify the code. The user reads the code from their inbox / SMS and enters it; this persists tokens on success:

final res = await auth.passwordlessLogin(
  identifier: 'a@b.com',
  code: enteredCode,
  channel: 'email',
);
Future<AuthResponse> passwordlessLogin({
  required String identifier,
  required String code,
  String channel = 'email',
  String? tenantId,
})

Social login (native — no browser)

Google and Apple sign-in use the platform's native flow. Get the token from google_sign_in / sign_in_with_apple, then exchange it with the NativeSignIn helpers (signInWithGoogleIdToken / signInWithAppleCredential), which handle the token type and assemble Apple's first-login name for you.

Google

Set serverClientId to your web OAuth client ID so the returned ID token's audience matches the backend's google.clientId (or a google.audiences entry):

import 'package:google_sign_in/google_sign_in.dart';
 
final account = await GoogleSignIn(serverClientId: webClientId).signIn();
final googleAuth = await account!.authentication;
 
await auth.signInWithGoogleIdToken(googleAuth.idToken!);

Apple

Apple returns the user's name only on the first sign-in — pass givenName/familyName so the backend can persist it. nonce is optional replay protection.

import 'package:sign_in_with_apple/sign_in_with_apple.dart';
 
final credential = await SignInWithApple.getAppleIDCredential(
  scopes: [AppleIDAuthorizationScopes.email, AppleIDAuthorizationScopes.fullName],
  nonce: hashedNonce,
);
 
await auth.signInWithAppleCredential(
  identityToken: credential.identityToken!,
  givenName: credential.givenName,
  familyName: credential.familyName,
  nonce: rawNonce,
);

Backend config. Native Apple identityTokens are verified against Apple's JWKS — no client secret needed for mobile. Set apple.audiences to include your iOS Bundle ID alongside the web Service ID (clientId). For Google, add any native client IDs to google.audiences.

socialLogin remains available for a token you obtained another way:

await auth.socialLogin('google', idToken, type: 'idToken');
await auth.socialLogin('apple', identityToken, nonce: nonce, name: 'Ada Lovelace');

The current session

After a successful login, fetch the signed-in user with getSessionUserData (GET /auth/me). The typed SessionUser exposes id, email, and phone; the full payload is on raw for any app-specific fields:

final user = await auth.getSessionUserData();
print('${user.id} / ${user.email}');
final plan = user.raw['plan']; // any extra field returned by your backend

Log out

logout revokes the session server-side (best effort) and clears the local tokens regardless of the server response:

await auth.logout();

Password reset

A three-step flow for users who have forgotten their password.

Step 1 — request a code. Pass the account's email or phone:

await auth.forgotPassword(email: 'a@b.com');

Step 2 — verify the code. The response contains a token (also exposed as resetToken) to use in the final step:

final res = await auth.verifyForgotPasswordOtp(
  email: 'a@b.com',
  code: enteredCode,
);
final resetToken = (res['token'] ?? res['resetToken']) as String;

Step 3 — set the new password using that token:

await auth.resetPassword(
  token: resetToken,
  newPassword: 'a-brand-new-secret',
);

The signatures:

Future<Map<String, dynamic>> forgotPassword({String? email, String? phone});
 
Future<Map<String, dynamic>> verifyForgotPasswordOtp({
  String? email,
  String? phone,
  required String code,
  String? tenantId,
});
 
Future<Map<String, dynamic>> resetPassword({
  required String token,
  required String newPassword,
});

Change password

For a signed-in user who knows their current password:

await auth.changePassword(
  currentPassword: 'super-secret',
  newPassword: 'an-even-better-secret',
);
Future<Map<String, dynamic>> changePassword({
  required String currentPassword,
  required String newPassword,
})

Email verification

Two steps: send a verification code to the signed-in user's email, then verify the code they enter.

await auth.sendEmailVerification();
// ...user reads the code from their inbox...
await auth.verifyEmail(code: enteredCode);
Future<Map<String, dynamic>> sendEmailVerification({String? tenantId});
Future<Map<String, dynamic>> verifyEmail({required String code, String? tenantId});

Phone verification

The same two-step pattern over SMS:

await auth.sendPhoneVerification();
// ...user reads the code from the SMS...
await auth.verifyPhone(code: enteredCode);
Future<Map<String, dynamic>> sendPhoneVerification({String? tenantId});
Future<Map<String, dynamic>> verifyPhone({required String code, String? tenantId});

Next steps

On this page