Self-hosted Authentication (dbAuth)
Redwood's own dbAuth provides several benefits:
- Use your own database for storing user credentials
- Use your own login, signup and forgot password pages (or use Redwood's pre-built ones)
- Customize login session length
- No external dependencies
- No user data ever leaves your servers
- No additional charges/limits based on number of users
- No third party service outages affecting your site
And potentially one large drawback:
- Use your own database for storing user credentials
However, we're following best practices for storing these credentials:
- Users' passwords are salted and hashed with PBKDF2 before being stored
- Plaintext passwords are never stored anywhere, and only transferred between client and server during the login/signup phase (and hopefully only over HTTPS)
- Our logger scrubs sensitive parameters (like
password
) before they are output
Even if you later decide you want to let someone else handle your user data for you, dbAuth is a great option for getting up and running quickly (we even have a generator for creating basic login and signup pages for you).
How It Works
dbAuth relies on good ol' fashioned cookies to determine whether a user is logged in or not. On an attempted login, a serverless function on the api-side checks whether a user exists with the given username (internally, dbAuth refers to this field as username but you can use anything you want, like an email address). If a user with that username is found, does their salted and hashed password match the one in the database?
If so, an HttpOnly, Secure, SameSite cookie (dbAuth calls this the "session cookie") is sent back to the browser containing the ID of the user. The content of the cookie is a simple string, but AES encrypted with a secret key (more on that later).
When the user makes a GraphQL call, we decrypt the cookie and make sure that the user ID contained within still exists in the database. If so, the request is allowed to proceed.
If there are any shenanigans detected (the cookie can't be decrypted properly, or the user ID found in the cookie does not exist in the database) the user is immediately logged out by expiring the session cookie.
Setup
A single CLI command will get you everything you need to get dbAuth working, minus the actual login/signup pages:
yarn rw setup auth dbAuth
Read the post-install instructions carefully as they contain instructions for adding database fields for the hashed password and salt, as well as how to configure the auth serverless function based on the name of the table that stores your user data. Here they are, but could change in future releases:
You will need to add a couple of fields to your User table in order to store a hashed password and salt:
model User {
id Int @id @default(autoincrement())
email String @unique
hashedPassword String // <─┐
salt String // <─┼─ add these lines
resetToken String? // <─┤
resetTokenExpiresAt DateTime? // <─┘
}If you already have existing user records you will need to provide a default value or Prisma complains, so change those to:
hashedPassword String @default("")
salt String @default("")You'll need to let Redwood know what field you're using for your users'
id
andusername
fields In this case we're usingid
andauthFields
config in/api/src/functions/auth.js
(this is also the place to tell Redwood if you used a different name for thehashedPassword
orsalt
fields):authFields: {
id: 'id',
username: 'email',
hashedPassword: 'hashedPassword',
salt: 'salt',
resetToken: 'resetToken',
resetTokenExpiresAt: 'resetTokenExpiresAt',
},To get the actual user that's logged in, take a look at
getCurrentUser()
in/api/src/lib/auth.js
. We default it to something simple, but you may use different names for your model or unique ID fields, in which case you need to update those calls (instructions are in the comment above the code).Finally, we created a
SESSION_SECRET
environment variable for you in.env
. This value should NOT be checked into version control and should be unique for each environment you deploy to. If you ever need to log everyone out of your app at once change this secret to a new value. To create a new secret, run:yarn rw g secret
Need simple Login, Signup and Forgot Password pages? Of course we have a generator for those:
yarn rw generate dbAuth
Note that if you change the fields named hashedPassword
and salt
, and you have some verbose logging in your app, you'll want to scrub those fields from appearing in your logs. See the Redaction docs for info.
Scaffolding Login/Signup/Forgot Password Pages
If you don't want to create your own login, signup and forgot password pages from scratch we've got a generator for that:
yarn rw g dbAuth
The default routes will make them available at /login
, /signup
, /forgot-password
, and /reset-password
but that's easy enough to change. Again, check the post-install instructions for one change you need to make to those pages: where to redirect the user to once their login/signup is successful.
If you'd rather create your own, you might want to start from the generated pages anyway as they'll contain the other code you need to actually submit the login credentials or signup fields to the server for processing.