Auth & User Management
The 14 methods that create, authenticate and manage users. The client keeps a cookie jar, so the session established by signupUser or loginUser is replayed on every later call automatically.
Most of these are reachable with a publishable key, which is what makes them safe to call straight from a Flutter app. Three are not: getUser and checkIfUserExists enumerate arbitrary users and require a secret key.
Setup
1import 'package:neuctra_authix_dart_package/neuctra_authix_dart_package.dart';
2
3final authix = NeuctraAuthix(
4 const NeuctraAuthixConfig(
5 appId: 'your_app_id_here',
6 publishableKey: 'pk_live_xxxxxxxx_xxxxxxxxxxxxxxxx',
7 appName: 'MyApp',
8 ),
9);Authentication
signupUser
POST /users/signup
Future<Map<String, dynamic>> signupUser(SignupParams params)
| Param | Type | Required | Description |
|---|---|---|---|
| name | String | Yes | Full name. |
| String | Yes | Email address. | |
| password | String | Yes | Plain password — hashed server-side. |
| username | String? | No | Unique handle. |
| phone | String? | No | Phone number. |
| address | String? | No | Postal address. |
| avatarUrl | String? | No | Profile image URL. |
| isActive | bool? | No | Account active flag. |
| role | String? | No | Role name, e.g. 'admin'. |
| extra | Map<String, dynamic> | No | Any further fields your app stores on the user record. |
Returns: Map<String, dynamic>
1final result = await authix.signupUser(
2 const SignupParams(
3 name: 'John Doe',
4 email: '[email protected]',
5 password: 'secret123',
6 username: 'johndoe',
7 role: 'user',
8 ),
9);
10
11final userId = result['user']?['id']?.toString() ?? '';Throws: ConfigurationError when name, email or password is empty — before any request is sent.
On success the backend sets the session cookie, so the user is signed in immediately — no separate login call needed. Null fields are omitted from the request body; use the extra map for app-specific fields.
loginUser
POST /users/login
Future<Map<String, dynamic>> loginUser(LoginParams params)
| Param | Type | Required | Description |
|---|---|---|---|
| String | Yes | Registered email. | |
| password | String | Yes | Account password. |
Returns: Map<String, dynamic>
1try {
2 await authix.loginUser(
3 const LoginParams(email: '[email protected]', password: 'secret123'),
4 );
5 await persistSession();
6} on AuthenticationError {
7 showError('Wrong email or password');
8} on NetworkError {
9 showError('Could not reach the server');
10}Throws: ConfigurationError if either field is empty; AuthenticationError when the credentials are rejected.
The session cookie returned by the backend is captured into authix.session. Persist session.sessionCookie if you want the login to survive an app restart.
logoutUser
POST /users/logout
Future<bool> logoutUser({FutureOr<void> Function()? onLoggedOut})| Param | Type | Required | Description |
|---|---|---|---|
| onLoggedOut | Function? | No | Awaited after the session is cleared — use it to navigate or wipe storage. |
Returns: bool
1final ok = await authix.logoutUser(
2 onLoggedOut: () async {
3 await forgetSession();
4 navigatorKey.currentState?.pushReplacementNamed('/login');
5 },
6);The local session is cleared whether or not the server call succeeds — a failed logout must never leave a usable session on the device. Unlike the JavaScript SDK there is no forced page reload; you control what happens next through onLoggedOut.
checkUserSession
GET /users/session
Future<CheckSessionResponse> checkUserSession()
Returns: CheckSessionResponse
1final session = await authix.checkUserSession();
2
3if (session.authenticated) {
4 final name = session.user?['name'];
5 print('Welcome back, $name');
6} else {
7 goToLogin();
8}The only method that never throws. A rejected or missing cookie resolves to authenticated: false and clears the local session, so it is safe to call on every app launch.
Email verification
requestEmailVerificationOTP
POST /users/send-verify-otp/:userId
Future<Map<String, dynamic>> requestEmailVerificationOTP({required String userId, required String email})| Param | Type | Required | Description |
|---|---|---|---|
| userId | String | Yes | ID of the user to verify. |
| String | Yes | Address the OTP is sent to. |
Returns: Map<String, dynamic>
1await authix.requestEmailVerificationOTP(
2 userId: userId,
3 email: '[email protected]',
4);Sends a one-time code by email. Call it right after signup, then collect the code and pass it to verifyEmail.
verifyEmail
POST /users/verify-email
Future<Map<String, dynamic>> verifyEmail({required String email, required String otp})| Param | Type | Required | Description |
|---|---|---|---|
| String | Yes | Address the OTP was sent to. | |
| otp | String | Yes | Code the user entered. |
Returns: Map<String, dynamic>
1await authix.verifyEmail(
2 email: '[email protected]',
3 otp: '123456',
4);Throws: Status 400 for an empty field; the backend returns an error for an expired or wrong code.
User records
getUser
GET /users/:appId/user
Future<Map<String, dynamic>> getUser({String? id, String? username})| Param | Type | Required | Description |
|---|---|---|---|
| id | String? | No | Look up by user ID. |
| username | String? | No | Look up by username. |
Returns: Map<String, dynamic>
1final byId = await authix.getUser(id: 'user_123');
2final byName = await authix.getUser(username: 'johndoe');Throws: ConfigurationError when neither id nor username is given. InsufficientScopeError when called with a publishable key.
Secret key only — looking up arbitrary users is an account-level operation. Pass exactly one of the two; values are URL-encoded, so usernames with spaces or symbols are safe.
getUserProfile
POST /users/profile
Future<Map<String, dynamic>> getUserProfile({required String userId})| Param | Type | Required | Description |
|---|---|---|---|
| userId | String | Yes | ID of the profile to load. |
Returns: Map<String, dynamic>
1final profile = await authix.getUserProfile(userId: userId);
2print(profile['email']);With a publishable key the target is pinned to the signed-in session — userId cannot be used to read somebody else's profile. A secret key may read any user's.
updateUser
PUT /users/update/:userId
Future<Map<String, dynamic>> updateUser(UpdateUserParams params)
| Param | Type | Required | Description |
|---|---|---|---|
| userId | String | Yes | ID of the user to update. |
| name | String? | No | Full name. |
| username | String? | No | Unique handle. |
| String? | No | Email address. | |
| password | String? | No | Prefer changePassword instead — see the warning below. |
| phone | String? | No | Phone number. |
| address | String? | No | Postal address. |
| avatarUrl | String? | No | Profile image URL. |
| isActive | bool? | No | Account active flag. |
| role | String? | No | Role name. |
Returns: Map<String, dynamic>
1await authix.updateUser(
2 UpdateUserParams(
3 userId: userId,
4 name: 'John A. Doe',
5 phone: '+1 555 0100',
6 ),
7);Only non-null fields are sent, so this is a partial update. A null value means 'leave unchanged', not 'clear the field'.
deleteUser
DELETE /users/delete/:userId
Future<Map<String, dynamic>> deleteUser(DeleteUserParams params)
| Param | Type | Required | Description |
|---|---|---|---|
| userId | String | Yes | ID of the user to delete. |
Returns: Map<String, dynamic>
1await authix.deleteUser(DeleteUserParams(userId: userId));
2authix.session.clearSession();Permanent. Clear the local session afterwards if the deleted user is the one signed in.
Passwords & recovery
changePassword
PUT /users/change-password/:userId
Future<Map<String, dynamic>> changePassword(ChangePasswordParams params)
| Param | Type | Required | Description |
|---|---|---|---|
| userId | String | Yes | ID of the signed-in user. |
| currentPassword | String | Yes | Existing password — verified server-side. |
| newPassword | String | Yes | Replacement password. |
Returns: Map<String, dynamic>
1await authix.changePassword(
2 ChangePasswordParams(
3 userId: userId,
4 currentPassword: 'secret123',
5 newPassword: 'newSecret456',
6 ),
7);Throws: ConfigurationError when any field is empty; AuthenticationError when currentPassword is wrong.
The safe path for a signed-in user changing their own password, because it proves knowledge of the current one. Every session for that user is invalidated, including the one you are calling from — sign them back in or send them to the login screen afterwards.
requestResetUserPasswordOTP
POST /users/forgot-password
Future<Map<String, dynamic>> requestResetUserPasswordOTP({required String email})| Param | Type | Required | Description |
|---|---|---|---|
| String | Yes | Address of the account to recover. |
Returns: Map<String, dynamic>
1await authix.requestResetUserPasswordOTP(
2 email: '[email protected]',
3);No session required, which is the point: the user cannot log in. Still needs a publishable key and is rate limited, so it cannot be used to flood someone's inbox anonymously.
resetUserPassword
POST /users/reset-password
Future<Map<String, dynamic>> resetUserPassword({required String email, required String otp, required String newPassword})| Param | Type | Required | Description |
|---|---|---|---|
| String | Yes | Address the OTP was sent to. | |
| otp | String | Yes | Code from the recovery email. |
| newPassword | String | Yes | Replacement password. |
Returns: Map<String, dynamic>
1await authix.resetUserPassword(
2 email: '[email protected]',
3 otp: '123456',
4 newPassword: 'newSecret456',
5);Completes the forgot-password flow and invalidates every existing session for that account. The user still has to log in afterwards — this does not create a session.
checkIfUserExists
GET /users/check-user/:userId
Future<CheckUserResponse> checkIfUserExists(String userId)
| Param | Type | Required | Description |
|---|---|---|---|
| userId | String | Yes | Passed positionally, not as a params object. |
Returns: CheckUserResponse
1final result = await authix.checkIfUserExists('user_123');
2
3if (result.exists) {
4 print('User is registered for this app');
5}Secret key only. Lightweight existence probe — appId is appended to the query string automatically.
updateUser can set a password
UpdateUserParams accepts a password field, and unlike changePassword it does not require the current one. Use changePassword for user-initiated changes.
How sessions flow
signupUser and loginUser capture the authix_user_session cookie into the client's jar, and every later call replays it. logoutUser and checkUserSession clear it when the session is gone. Persist authix.session.sessionCookie in secure storage to survive restarts, and seed it back with session.setSessionCookie() on launch.
Changing or resetting a password invalidates every session for that user server-side, so a stale cookie elsewhere stops working immediately.
Related