# Helpers  ## `user` model  Gadget by default sets the following fields within your `user` model: * `resetPasswordTokenExpiration` * `resetPasswordToken` * `emailVerificationTokenExpiration` * `emailVerificationToken` * `password` * `googleProfileId` * `googleImageUrl` * `emailVerified` * `email` * `lastName` * `firstName` * `lastSignedIn` * `roles` ### Google OAuth Sign Up trigger  This trigger executes when a new user signs up using Google OAuth. The entire profile payload from Google is included in the trigger. ### Google OAuth Sign In trigger  This trigger executes when an existing user signs in using Google OAuth. The entire profile payload from Google is included in the trigger. ### Email Password Sign Up trigger  This trigger executes when a new user signs up using Email-Password authentication. This trigger exposes the `signUp` action to your API. ### Email Password Sign In trigger  This trigger executes when an existing user signs in using Email-Password authentication. This trigger exposes the `signIn` action to your API. ### Verify Email trigger  This trigger executes the `verifyEmail` action. It finds a user record by `emailVerificationToken` and checks `emailVerificationTokenExpiration` to see if the token is still valid. If it is the trigger sets `emailVerified` to be true. ### Send Verify Email trigger  This trigger executes the `sendVerifyEmail` action. When the `sendVerifyEmail` action is called from your API, this trigger finds a user record by email and checks that `emailVerified` is false. It generates a random code and provides it to the action in `params.user.emailVerificationCode`. The user record then has `emailVerificationToken` set to the SHA256 hash of this code. ### Reset Password trigger  This trigger executes the `resetPassword` action. It finds a user record by `resetPasswordToken` and checks `resetPasswordTokenExpiration` to see if the token is still valid. If it is the trigger sets the new password. ### Send Reset Password trigger  This trigger executes the `sendResetPassword` action. When the `sendResetPassword` action is called from your API, this trigger finds a user record by email. It then generates a random code and provides it to the action in `params.user.resetPasswordCode`. The user record then has `resetPasswordToken` set to the SHA256 hash of this code. ### Change Password trigger  This trigger executes the `changePassword` action. It checks that the `currentPassword` matches the user's current password and then sets the new password. ### `signUp` action  A `user` model's `signUp` action is a create action by default. Its `run` function includes setting the `lastSignedIn` field of the user record to the current date and time. This indicates when the user last signed in or, in this context, when the user created their account. In addition, it associates the current user record with the active session. ```typescript import { applyParams, save, ActionOptions } from "gadget-server"; // Powers the form in web/routes/sign-up.tsx export const run: ActionRun = async ({ params, record, logger, api, session }) => { // Applies new 'email' and 'password' to the user record and saves to database applyParams(params, record); record.lastSignedIn = new Date(); await save(record); if (record.emailVerified) { // Assigns the signed-in user to the active session session?.set("user", { _link: record.id }); } return { result: "ok", }; }; export const onSuccess: ActionOnSuccess = async ({ params, record, logger, api, session, }) => { if (!record.emailVerified) { // Sends verification email by calling api/models/users/actions/sendVerifyEmail.ts await api.user.sendVerifyEmail({ email: record.email }); } }; export const options: ActionOptions = { actionType: "create", returnType: true, triggers: { googleOAuthSignUp: true, emailSignUp: true, }, }; ``` ### `signIn` action  A `user` model's `signIn` action is an update action by default. It updates the `lastSignedIn` field of the `user` record to the current date and time and associates the current user record with the active session. ```typescript import { applyParams, save, ActionOptions } from "gadget-server"; // Powers form in web/routes/sign-in.tsx export const run: ActionRun = async ({ params, record, logger, api, session }) => { applyParams(params, record); record.lastSignedIn = new Date(); await save(record); // Assigns the signed-in user to the active session session?.set("user", { _link: record.id }); }; export const onSuccess: ActionOnSuccess = async ({ params, record, logger, api, session, }) => { // Your logic goes here }; export const options: ActionOptions = { actionType: "update", triggers: { googleOAuthSignIn: true, emailSignIn: true, }, }; ``` ### `signOut` action  A `user` model's `signOut` action is an update action by default. It unsets the associated user on the active session, causing the session to be unauthenticated. ```typescript import { ActionOptions } from "gadget-server"; export const run: ActionRun = async ({ params, record, logger, api, session }) => { // Removes the user from the active session session?.set("user", null); }; export const onSuccess: ActionOnSuccess = async ({ params, record, logger, api, session, }) => { // Your logic goes here }; export const options: ActionOptions = { actionType: "update", triggers: { signOut: true, }, }; ``` ### `verifyEmail` and `sendVerifyEmail` actions  A `user` model's `verifyEmail` action is a default action file upon app creation, that is designated to handle email verification scenarios for users, as determined by the Gadget developer. ```typescript import { applyParams, save, ActionOptions } from "gadget-server"; // Powers the sign up flow, this action is called from the email generated in /actions/sendVerifyEmail.ts export const run: ActionRun = async ({ params, record, logger, api }) => { // Applies new 'emailVerified' status to the user record and saves to database applyParams(params, record); await save(record); return { result: "ok", }; }; export const onSuccess: ActionOnSuccess = async ({ params, record, logger, api, }) => { // Your logic goes here }; export const options: ActionOptions = { actionType: "custom", returnType: true, triggers: { verifiedEmail: true, }, }; ``` The `sendVerifyEmail` action, is a custom action that updates the `user` record with the provided parameters and then, if successful, it sends an email to the user containing a reset password link. ```typescript import { applyParams, save, ActionOptions, DefaultEmailTemplates, Config, } from "gadget-server"; // Powers the sign up flow, this action is called from api/models/user/actions/signUp.ts export const run: ActionRun = async ({ params, record, logger, api, emails }) => { // Applies new hashed code 'emailVerificationToken' to the user record and saves to database applyParams(params, record); await save(record); return { result: "ok", }; }; export const onSuccess: ActionOnSuccess = async ({ params, record, logger, api, emails, }) => { if ( !record.emailVerified && record.emailVerificationToken && params.user?.emailVerificationCode ) { // Generates link to verify email const url = new URL("/verify-email", Config.appUrl); url.searchParams.append("code", params.user?.emailVerificationCode); // Sends link to user await emails.sendMail({ to: record.email, subject: `Verify your email with ${Config.appName}`, html: DefaultEmailTemplates.renderVerifyEmailTemplate({ url: url.toString() }), }); } }; export const options: ActionOptions = { actionType: "custom", returnType: true, triggers: { sendVerificationEmail: true, }, }; ``` ### `resetPassword` and `sendResetPassword` actions  A `user` model's `resetPassword` action is a default action file upon app creation, that is designated to handle password reset scenarios for users, as determined by the Gadget developer. ```typescript import { applyParams, save, ActionOptions } from "gadget-server"; // Powers form in web/routes/reset-password.tsx export const run: ActionRun = async ({ params, record, logger, api, connections, }) => { // Applies new 'password' to the user record and saves to database applyParams(params, record); await save(record); return { result: "ok", }; }; export const onSuccess: ActionOnSuccess = async ({ params, record, logger, api, connections, }) => { // Your logic goes here }; export const options: ActionOptions = { actionType: "custom", returnType: true, triggers: { resetPassword: true, }, }; ``` The `sendResetPassword` action, is a custom action that updates the `user` record with the provided parameters and then, if successful, the action will send a verification email containing a unique link for the user to verify their email. ```typescript import { applyParams, save, ActionOptions, DefaultEmailTemplates, Config, } from "gadget-server"; // Powers form in web/routes/forgot-password.tsx export const run: ActionRun = async ({ params, record, logger, api, emails }) => { // Applies new hashed code 'resetPasswordToken' to the user record and saves to database applyParams(params, record); await save(record); return { result: "ok", }; }; export const onSuccess: ActionOnSuccess = async ({ params, record, logger, api, emails, }) => { if (record.resetPasswordToken && params.user?.resetPasswordCode) { // Generates link to reset password const url = new URL("/reset-password", Config.appUrl); url.searchParams.append("code", params.user?.resetPasswordCode); // Sends link to user await emails.sendMail({ to: record.email, subject: `Reset password request from ${Config.appName}`, html: DefaultEmailTemplates.renderResetPasswordTemplate({ url: url.toString(), }), }); } }; export const options: ActionOptions = { actionType: "custom", returnType: true, triggers: { sendResetPassword: true, }, }; ``` ### `changePassword` action  A `user` model's `changePassword` action is a default action file upon app creation, that is designated to handle scenarios for users where they have to change/update their password, as determined by the Gadget developer. ```typescript import { applyParams, save, ActionOptions } from "gadget-server"; // Powers form in web/routes/change-password.tsx export const run: ActionRun = async ({ params, record, logger, api, connections, }) => { // Applies new 'password' to the user record and saves to database applyParams(params, record); await save(record); }; export const onSuccess: ActionOnSuccess = async ({ params, record, logger, api, connections, }) => { // Your logic goes here }; export const options: ActionOptions = { actionType: "update", triggers: { changePassword: true, }, }; ``` ### Using `preventCrossUserDataAccess`  You can use the [`preventCrossUserDataAccess` function](https://docs.gadget.dev/reference/gadget-server#preventcrossuserdataaccess-params-record-options), imported from the `gadget-server` package, to ensure that users can only access their data in your model's actions. This function will automatically be added to any models you add. To opt out of this, simply remove the belongs to user field from your model, the `preventCrossUserDataAccess` function from your model actions, and the `read` permission filter. An example of how to use this function in a custom `blog` model's `create` action: ```typescript import { applyParams, save, ActionOptions } from "gadget-server"; // add `preventCrossUserDataAccess` to the import statement import { preventCrossUserDataAccess } from "gadget-server/auth"; export const run: ActionRun = async ({ params, record, logger, api, connections, }) => { applyParams(params, record); // add this line to prevent customers from accessing other customers' data await preventCrossUserDataAccess(params, record); await save(record); }; export const options: ActionOptions = { actionType: "create", }; ``` ### Adding a tenancy filter  Adding a tenancy filter to the access control configuration for your custom model will make sure that users can only access their own data. Note that we already add a filter on the `read` permission when you add a new model. These filtered model permissions are written in Gelly, Gadget's data access language. For example, this Gelly snippet could be used to filter a `todo` model data by customer. The `todo` model has a `user` relationship field that relates to the `user` model. ```gelly // in Example of a tenancy filter the todo model filter ($user: User, $session: Session) on Todo [ where userId == $user.id ] ``` For more information on tenancy filters and filtered model permissions, see the [access control guide](https://docs.gadget.dev/guides/access-control#filtered-model-permissions). ## `session` model  All Gadget apps have a `session` model that is used to power session management. The current session is always available in Gadget actions and route handlers: ```typescript export const run: ActionRun = async ({ params, record, logger, api, session }) => { logger.info({ session }, "the session associated with the current request"); }; ``` ### `csrfToken`  All `session` records have a special `csrfToken` property that can be used to prevent cross-site request forgery (CSRF) attacks. Gadget automatically checks CSRF tokens for all form submission requests. To submit a form as an authenticated user you must include the CSRF token in the form data. ```typescript import { RouteHandler } from "gadget-server"; const route: RouteHandler = async ({ request, reply, api, logger, connections }) => { // send an HTML response with a form that has a CSRF token await reply.send(`
`); }; export default route; ``` ## Custom auth state parameters  You can add custom auth action parameters to the built in auth flow by: 1. Adding a custom parameter to the signup and/or signin actions: ```typescript export const params = { username: { type: "string" }, }; ``` 2. Adding the query parameter to the auth route URL with Google OAuth: ```typescript {" "} Continue with Google ; ``` OR pass the parameter directly to the email-password auth forms. If you're using our default email/password implementation, make sure to use the `register` function from `useActionForm` to register the input field: ```tsx ``` 3. Pulling the value from the `params` action context: ```typescript export const run: ActionRun = async ({ params, record, logger, api, session }) => { applyParams(params, record); const { username } = params; // ... }; ``` ### Saving custom parameters to a `user` record  You can also save custom parameters to a `user` record by applying them in the `run` function of the signup or signin actions: ```typescript export const run: ActionRun = async ({ params, record, logger, api, session }) => { applyParams(params, record); record.lastSignedIn = new Date(); // save the custom parameter to the user record record.username = params.username; await save(record); // Assigns the signed-in user to the active session session?.set("user", { _link: record.id }); }; // define the username param export const params = { username: { type: "string" }, }; export const options: ActionOptions = { actionType: "update", triggers: { googleOAuthSignIn: true, emailSignUp: true }, }; ``` ## Hooks and components  When working with Gadget authentication, there are several hooks and components from our [`@gadgetinc/react` package](https://docs.gadget.dev/reference/react) that can help you manage the authentication state of your application. The hooks use the [Gadget client's `suspense: true` option](https://docs.gadget.dev/reference/react#suspense), making it easier to manage the async nature of the hooks without having to deal with loading state. | Hooks | Description | | --- | --- | | `useSession()` | Retrieves the current user session within the app. | | `useUser()` | If a user is present in the session, it returns the current user; otherwise, it returns null for unauthenticated sessions. | | `useAuth()` | Returns an object representing the current authentication state of the session. | | `useSignOut()` | Returns a callback that you can call to sign out your current Gadget `User` from the current `Session`. This calls the configured `signOutActionApiIdentifier` action, which is the `User` `signOut` action by default. | | Components | Description | | --- | --- | | `` | Conditionally renders its children if the current session has a user associated with it, similar to the `isSignedIn` property of the `useAuth()` hook. | | `` | Conditionally renders its children if the current session does not have a user associated with it. | | `` | Conditionally renders its children based on the user's sign-in status. If a user is currently signed in, it displays its children; otherwise, it redirects the browser using `window.location.assign`. This functionality is valuable for securing frontend routes. | | `` | Conditionally renders its children when there is no user associated with the current session. However, if the user is signed in, it redirects the browser using `window.location.assign`. Its purpose is to facilitate redirection of frontend routes based on the user's sign-in status. |