Express
This page will help you get started with Auth using the Express.js framework.
If you want to interact with a working version of the Auth + Express integration that we'll be building in this guide, you can checkout the following GitHub repository, or clone it with the command below:
Getting Started
To add support for Auth to your Express.js backend, you'll need to install the @thirdweb-dev/auth
package as well as its peer dependencies (as well as any other packages you'll need for your wallet configuration, we'll need the ethers
package for this example as we'll be using the PrivateKeyWallet
):
Now we can setup our Express.js server by using the @thirdweb-dev/auth/express
entrypoint. First, we need to setup our Auth API by adding the auth router to our app:
Here, we first configure our ThirdwebAuth
instance with the domain
of our app used for anti-phishing, as well as the admin wallet used to issue and verify JWTs. In this case, we use the PrivateKeyWallet
from the @thirdweb-dev/auth/evm
entrypoint, but there are a number of other supported wallet setups, which you can learn more about on the wallet configuration page:
Finally, we add the authRouter
to our app with the app.use("/auth", authRouter)
line to set up all the /auth/*
endpoints automatically. We'll also need to add the authMiddleware
to any routes that we want to protect with Auth.
Usage
Authenticating the user on other routes
The getUser
function can be used to authenticate the user on the server. It will return the user's address if the user is authenticated, or null
if the user is not authenticated. It can be used in any API route that uses the authMiddleware
:
Alternatively, you can add the authMiddleware
to the entire app if you want to use the getUser
function across multiple routes.
Validating the login request
By default, the Auth API will validate the login request by checking that the user requesting to login successfully signed a valid sign-in with wallet message. However, this doesn't perform specific checks on the exact contents of the payload, aside from the domain used for anti-phishing.
If you want to add specific checks to enforce the exact data on the login payload signed by users, you can use the authOptions
configuration:
Note that when you enforce these checks on the server-side, you'll also want to pass in the proper parameters to the login
function on your client-side application to ensure that the login payload gets the correct format. You can see an example of how to do this in the React section.
Prevent replay attacks
Since the sign-in with wallet payload is used to login to your server, it's important to prevent third parties from being able to reuse old login payloads to falsely authenticate as other users. This reuse of old login payloads is called a replay attack.
Luckily, all sign-in with wallet payloads include a nonce
field which is a random string generated when the request was created. If you are using a database, or have somewhere to store nonces, you can ensure that each nonce is only used once:
Changing the token validity duration
By default, the JWTs issued by the server are valid for 3 days (259200 seconds), after which the user will have to login again. If you want to change this duration, you can use the authOptions.tokenDurationInSeconds
configuration:
Changing the token refresh interval
As discussed above, each JWT has a validity duration for which it is valid, and once that duration passes, the JWT will no longer be accepted by the backend. In this case, the user would have to login again. However, for users who login frequently, having to re-login every few days would be an inconvenience.
This is where the refresh token flow comes in - for users who come back to your website frequently, they can get their valid JWTs refreshed to have an extended validity duration.
The refreshIntervalInSeconds
option allows you to configure how long after a JWT has initially been issued must pass before the token can be refreshed. Ideally, you don't want the token to be refresh too frequently because you'll received an unnecessary amount of requests to your API, but you want it to be refreshed frequently enough to increase convenience for frequently returning users. The default value for the refreshIntervalInSeconds
value is 3 hours (10800 seconds), meaning users who come back to your site after 3 hours of initially logging in will get their tokens refreshed.
Customizing cookie settings
The Auth API will set a cookie on the user's browser with the issued JWT token (learn more about cookie settings from the Mozilla cookie reference). This cookie is set to be httpOnly
and secure
(only sent over HTTPS), which is necessary for security purposes - and is also set to use the domain
of your API, with a path
of /
, and SameSite=None
.
If you want to overwrite these, settings, you can with the cookieOptions
configuration. For example, you may want to set your domain to be less specific - like if you have your api on api.example.com
and a client on client.example.com
, you may want to set your domain to .example.com
, or you want want to set your SameSite
setting to strict
if your API and client are on the same domain:
Saving users in your database
We can use the callbacks.onLogin
function to remember users in a database whenever they login, enabling a traditional database enabled authentication flow:
Enhancing session data
When the server issues a JWT, the default JWT claims are added to the token (as elaborated in the how auth works section). However, you may want to add additional data to the JWT claims to serve as session data that will persist on the token, such as certain user level permissions or access information. You can do this by returning data from the callbacks.onLogin
callback function which treats your return value as session data to store onto the JWT.
Enhancing user data
Alternatively, you can populate data about a user every time user data is requested from the /user
endpoint or getUser
function. This data is not session-wide as it is not stored on the JWT, so it isn't persisted an can change between requests. It can be useful for populating data that is not stored on the JWT, such as user profile information. We can do this by returning data from the callbacks.onUser
callback function.