adonis install @adonisjs/auth
You are viewing the legacy version of AdonisJS. Visit https://adonisjs.com for newer docs. This version will receive security patches until the end of 2021.
Every Web application deals with User Management and Authentication at some stage. The AdonisJs Authentication provider is a fully featured system to authenticate HTTP requests using multiple authenticators. Using authenticators, you can build traditional session based login systems to secure REST API’s.
Each authenticator is a combination of serializers and an authentication scheme.
Lucid ( lucid )
Database ( database )
Authentication is divided into two broad categories of Stateful authentication and Stateless authentication.
Session based authentication is considered Stateful Authentication since once logged in the user can navigate to different areas of the application without resending the credentials.
Stateless Authentication requires the end-user to send the credentials on each HTTP request which is very common with REST API’s.
AdonisJs provides necessary tooling & helpers to manage both types of authentication with ease.
Authentication provider makes use of the Hash module to verify passwords. Make sure to hash your passwords before saving them to the database.
Authentication provider comes pre-installed with fullstack
and api
boilerplates. If not installed, follow the defined steps.
Install the package from npm and automatically set it up.
adonis install @adonisjs/auth
Next, you need to register the provider inside the start/app.js
file.
const providers = [
'@adonisjs/auth/providers/AuthProvider'
]
const globalMiddleware = [
'Adonis/Middleware/AuthInit'
]
const namedMiddleware = {
auth: 'Adonis/Middleware/Auth'
}
Configuration for authentication is saved inside config/auth.js
file. By default, it makes use of the session
authenticator to authenticate requests.
module.exports = {
authenticator: 'session',
session: {
serializer: 'Lucid',
scheme: 'session',
model: 'App/Models/User',
uid: 'email',
password: 'password'
}
}
Key | Values | Description |
---|---|---|
serializer |
lucid, database |
Serializer to be used for fetching the user from the database. |
scheme |
session, basic, jwt, api |
Scheme to be used for fetching and authenticating user credentials. |
uid |
Database field name |
Database field to be used as unique identifier for a given user. |
password |
Database field name |
Field to be used for verifying user password |
model |
Model Namespace (Lucid Only) |
Applicable only when using Lucid serializer. Given model is used for querying the database. |
table |
Database table name (Database Only) |
Applicable only when using Database serializer. |
Let’s start with a basic example of making a user login and then showing them their profile, only if they are logged in.
Route
.get('users/:id', 'UserController.show')
.middleware('auth')
Route.post('login', 'UserController.login')
Let’s create the UserController
using the adonis
command.
adonis make:controller User
Let’s start by creating the login
method on the UserController, which takes the user credentials and log in the user if they are correct.
class UserController {
async login ({ request, auth }) {
const { email, password } = request.all()
await auth.attempt(email, password)
return 'Logged in successfully'
}
}
Yes, that is all you need to do to log in a user using their email
and password
.
Let’s write the show
method too, which returns the user profile as JSON.
class UserController {
async login () {
...
}
show ({ auth, params }) {
if (auth.user.id !== Number(params.id)) {
return 'You cannot see someone else\'s profile'
}
return auth.user
}
}
The auth.user
is the instance of the User model, so we can just return it. Also, we want to make sure the id
received as params is same as currently logged in user id.
The session authenticator exposes following methods to login or authenticate a user.
Attempt to log in a user using their uid
and password
. It throws an exception if unable to find the user or if password mismatch.
await auth.attempt(uid, password)
Login a user using the user
model instance. This method does not verify anything and simply mark the user as logged in.
const user = await User.find(1)
await auth.login(user)
Login a user using their id. This method queries the database to make sure the user exists.
await auth.loginViaId(1)
Anytime you call methods like attempt
, login
or loginViaId
, you can chain the remember
method to make sure that users are logged in again even after closing their browser.
The remember method creates a token for the user inside the tokens table. If you ever want to revoke the long-lived session of a particular user, simply set is_revoked to true.
|
await auth
.remember(true)
.attempt(email, password)
Check if a user is already logged in by reading the session.
try {
await auth.check()
} catch (error) {
response.send('You are not logged in')
}
This method calls the check
method internally and returns the user details if they are logged in.
try {
return await auth.getUser()
} catch (error) {
response.send('You are not logged in')
}
The basic authentication is stateless, where the end-user is supposed to pass the credentials on each request, so there is no concept of login
and logout
.
Check to see if the user has passed basic auth
credentials in the request header or not. Also, this method verifies the user existence and their password
Set the Authorization = Basic <credentials> header to authenticate the request. The credentials are a base64 encoded string of uid:password where uid is the uid database field designated in the auth config.
|
try {
await auth.check()
} catch (error) {
response.send(error.message)
}
Calls check
internally and returns the user details.
try {
return await auth.getUser()
} catch (error) {
response.send('Credentials missing')
}
The jwt authentication is an industry standard to implement stateless authentication using tokens, and AdonisJs has out of the box support for JWT.
Set the Authorization = Bearer <token> header to authenticate the request.
|
{
authenticator: 'jwt',
jwt: {
serializer: 'Lucid',
model: 'App/Model/User',
scheme: 'jwt',
uid: 'email',
password: 'password',
options: {
secret: Config.get('app.appKey'),
// Options to be used while generating token
}
}
}
Key | Available Values | Default Value | Description |
---|---|---|---|
algorithm |
HS256, HS384 |
HS256 |
Algorithm to be used for generating token |
expiresIn |
valid time in seconds or ms string |
null |
When to expire the token |
notBefore |
valid time in seconds or ms string |
null |
Till when at least to keep the token valid |
audience |
String |
null |
A value to be checked against the |
issuer |
Array or String |
null |
Value to be used for |
subject |
String |
null |
A value to be checked against the |
Validate the user credentials and generate a JWT token in exchange.
await auth.attempt(uid, password)
{
type: 'type',
token: '.....',
refreshToken: '....'
}
Generate JWT token for a given user. Optionally you can pass a custom object to be encoded within the token. Passing jwtPayload=true
encodes the user object within the token.
const user = await User.find(1)
await auth.generate(user)
Instruct JWT authenticator to generate a refresh token as well. The refresh token is generated so that the clients can refresh the actual jwt
token without asking for user credentials again.
await auth
.withRefreshToken()
.attempt(uid, password)
Generate a new JWT token using the refresh token.
const refreshToken = request.input('refresh_token')
await auth.generateForRefreshToken(refreshToken)
When generating a new jwt
token, auth provider does not reissue a new refresh token and instead uses the old one. If you want, you can also regenerate a new refresh token.
await auth
.newRefreshToken()
.generateForRefreshToken(refreshToken)
Check if the token has been sent in the Authorization
header or not.
try {
await auth.check()
} catch (error) {
response.send('Missing or invalid jwt token')
}
Calls the check
method internally and returns the user if authentication passes.
try {
return await auth.getUser()
} catch (error) {
response.send('Missing or invalid jwt token')
}
The personal API tokens become popular by Github, to use a token for automated scripts, where it is not possible to manually type the email and password every time.
AdonisJs allows you to build applications, where your users can create personal API tokens and can use them to get authenticated.
Set the Authorization = Bearer <token> header to authenticate the request.
|
Generate a new token by validating the user credentials
await auth.attempt(uid, password)
{
type: 'bearer',
token: '...'
}
Generate token for a given user
const user = await User.find(1)
await auth.generate(user)
Check if the token has been passed as the Authorization
header or not.
try {
await auth.check()
} catch (error) {
response.send('Missing or invalid api token')
}
Calls the check
method internally and returns the user if authentication passes.
try {
await auth.getUser()
} catch (error) {
response.send('Missing or invalid api token')
}
The auth provider makes it so simple to use and switch between multiple authenticators at runtime by calling the authenticator
method.
Assuming the user is logged in using session
authenticator, we can generate a JWT token for them as follows.
// loggedin user via sessions
const user = auth.user
const auth
.authenticator('jwt')
.generate(user)
The auth
middleware automates the flow of authenticating specific routes by adding the middleware on them. The middleware is registered as a name middleware inside start/kernel.js
const namedMiddleware = {
auth: 'Adonis/Middleware/Auth'
}
Usage
Route
.get('users/profile', 'UserController.profile')
.middleware(['auth'])
The auth provider does add a couple of helpers to the view instance so that you can write HTML around the state of a logged-in user.
The loggedIn
tag can be used to write if/else
around the loggedin user.
@loggedIn
<h2> Hello {{ auth.user.username }} </h2>
@else
<p> Please login </p>
@endloggedIn
Almost every scheme stores some tokens in the database. These tokens are mainly used for extended authentication. For example:
Refresh tokens when the actual token has been expired.
Remember me token when actual user session has expired, this token is used to re-login them transparently.
API tokens which are sort of like passwords.
If you ever have a security breach, feel free to set is_revoked = true
, to revoke all the tokens for a given or all the users.
After revoking the tokens, some users may logout unexpectedly, but that is better than giving bad access to a hacker.
Make sure the route that revokes the user token is protected with the auth middleware. Otherwise, you will not get the token in the request header.
|
class UserController {
async revokeUserToken ({ auth }) {
const user = auth.current.user
const token = auth.getAuthHeader()
await user
.tokens()
.where('token', token)
.update({ is_revoked: true })
}
}
The tokens are saved in plain format inside the database, whereas these tokens are sent in encrypted
form to the end-user.
It is done to ensure, that if someone gets access to your database, they are not able to use these tokens directly and have to figure out how to encrypt them using the secret key.