module.exports = {
authenticator: 'session',
session: {
serializer: 'Lucid',
scheme: 'session',
model: 'App/Model/User',
uid: 'email',
password: 'password'
}
}
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. 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.
Sessions (session)
Basic Auth (basic)
JWT (jwt)
Personal API Tokens (api)
Lucid
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 required tooling & helpers to manage both types of authentication with ease.
Authentication provider will make use of the Hash module to verify passwords. Make sure to hash your passwords before saving them to the database.
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/Model/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 will be used for querying the database. |
table |
Database table name (Database Only) |
Applicable only when using Database serializer. |
Authentication provider can generate required migrations for you using an ace command. Just make sure following command is added to the list of commands.
const commands = [
// ...,
'Adonis/Commands/Auth:Setup'
]
./ace auth:setup
create: app/Model/User.js
create: database/migrations/1463212428511_User.js
create: app/Model/Token.js
create: database/migrations/1463212428512_Token.js
Let’s start with a basic example of creating a simple app to login and displaying the profile of a logged in user.
'use strict'
const Route = use('Route')
Route.get('profile', 'UsersController.profile')
Route.post('login', 'UsersController.login')
'use strict'
class UsersController {
* login (request, response) {
const email = request.input('email')
const password = request.input('password')
const login = yield request.auth.attempt(email, password) (1)
if (login) {
response.send('Logged In Successfully')
return
}
response.unauthorized('Invalid credentails')
}
}
1 | Once authentication provider is configured, you can make use of the auth property on the request instance to authenticate a user or check the authentication status. |
Let’s write another method to show the profile of a user only if they are logged in.
* profile (request, response) {
const user = yield request.auth.getUser()
if (user) {
response.ok(user)
return
}
response.unauthorized('You must login to view your profile')
}
Below is the list of methods you can use on session
authenticator.
Attempt to log in a user using the uid and password. It will throw an error if unable to find the user or if password mismatch.
try {
yield request.auth.attempt(uid, password)
} catch (e) {
yield request.with({error: e.message}).flash()
response.redirect('back')
}
Login a user using the user model instance.
const user = yield User.find(1)
try {
yield request.auth.login(user)
response.redirect('/dashboard')
} catch (e) {
yield request.with({error: e.message}).flash()
response.redirect('back')
}
Login user using the id. A database lookup will be performed to make sure that the user does exist otherwise an exception will be raised.
try {
yield request.auth.loginViaId(1)
response.redirect('/dashboard')
} catch (e) {
yield request.with({error: e.message}).flash()
response.redirect('back')
}
Check to see if a user is logged in or not.
const isLoggedIn = yield request.auth.check()
if (!isLoggedIn) {
response.redirect('/login')
}
Validate user credentials to see if they are valid. This method will never set any session/cookie.
try {
yield request.auth.validate(uid, password)
} catch (e) {
yield request.with({error: e.message}).flash()
response.redirect('back')
}
Below is the list of methods available for basic auth authenticator.
Basic auth credentials are base64 encoded and sent as the Authorization header. Example: Authorization=username:password
|
Check to see if basic auth credentials are present in the Authorization
header.
const isLoggedIn = yield request.auth.check()
if (!isLoggedIn) {
response.redirect('/login')
}
Validate user credentials to see if they are valid or not.
try {
yield request.auth.validate(uid, password)
} catch (e) {
response.unauthorized({error: e.message})
}
JWT authenticators require a couple of extra attributes to the configuration block.
The JWT is sent as the Authorization header. Example: Authorization=Bearer JWT_TOKEN
|
{
authenticator: 'jwt',
jwt: {
serializer: 'Lucid',
scheme: 'jwt',
model: 'App/Model/User',
secret: Config.get('app.appKey'),
options: {
// 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 |
Works same as others
const isLoggedIn = yield request.auth.check()
if (!isLoggedIn) {
response.unauthorized({error: 'Your must be loggedin to access this resource.'})
}
Generates a JWT for a given user.
const user = yield User.find(1)
const token = yield request.auth.generate(user)
The optional second parameter allows you to pass custom details and objects into the JWT for a given user.
const user = yield User.find(1)
const token = yield request.auth.generate(user, {
username: 'GreatScott',
account_type: 'admin'
})
Which would give you the following in your JWT on the client:
import jwtDecode from 'jwt-decode'
const decoded = jwtDecode(data.data.jwt)
console.log(decoded)
/*
{
iat: 1491568810
payload: {
uid: 1,
data: {
account_type: "admin",
username: "GreatScott"
}
}
}
*/
Validate user credentials to see if they are valid or not.
try {
yield request.auth.validate(uid, password)
} catch (e) {
response.unauthorized({error: e.message})
}
Validates the user credentials and generates a JWT if the credentials are valid. It can also accept an optional custom payload like within the generate()
example above.
try {
const token = yield request.auth.attempt(uid, password)
} catch (e) {
response.unauthorized({error: e.message})
}
Personal API tokens are like the password for a given account. The majority of Web applications offers API-based authentication so that their customers can generate these tokens for developers without sharing their actual Login details.
API token is sent as the Authorization header. Example: Authorization=Bearer API_TOKEN
|
API tokens are saved to the database corresponding to a given user.
You can set expiry for a token or set it null
for never expiring tokens.
You can revoke a single/all tokens for a given user.
authenticator: 'api',
api: {
serializer: 'Lucid',
scheme: 'api',
model: 'App/Model/Token',
expiry: '30d'
}
The Token
Model needs to have a relationship with the User
model to save tokens with ease. Make sure to define the relationship between both the models as defined below.
Make use of auth:setup command to generate the models/migrations and set relationships for you.
|
'use strict'
const Lucid = use('Lucid')
class Token extends Lucid {
user () {
return this.belongsTo('App/Model/User')
}
}
'use strict'
const Lucid = use('Lucid')
class User extends Lucid {
apiTokens () {
return this.hasMany('App/Model/Token')
}
}
Below is the list of available methods to be used with the api
authenticator.
Generate an API token for a given user and save it to the database.
const user = yield User.find(1)
const token = yield request.auth.generate(user)
Revoke/Delete given tokens for a given user.
const user = yield User.find(1)
yield request.auth.revoke(user, [token])
Revoke/Delete all tokens for a given user.
const user = yield User.find(1)
yield request.auth.revokeAll(user)
Revoke all tokens except the given ones.
const user = yield User.find(1)
yield request.auth.revokeExcept(user, [token])
So far you have been authenticating users manually, which can lead to duplicate code in multiple controllers. AdonisJs Auth Middleware can automatically authenticate the routes and makes sure to deny the requests when the end-user is not logged in.
Make sure the Auth Middleware is registered as a named middleware inside app/Http/kernel.js
file.
const namedMiddleware = {
auth: 'Adonis/Middleware/Auth'
}
Now you are all set to take advantage of the auth
middleware on your routes.
Route
.get('users/profile', 'UsersController.profile')
.middleware('auth')
Also, you can define a different authenticator by passing arguments to the auth middleware at runtime.
Route
.get('users/profile', 'UsersController.profile')
.middleware('auth:basic')
You can also switch between different authenticators using the authenticator
method.
const jwt = request.auth.authenticator('jwt')
const basicAuth = request.auth.authenticator('basic')
const api = request.auth.authenticator('api')
yield jwt.check()
yield basicAuth.check()
yield api.check()
Helpers makes it easier to retrieve the currently logged in user during an HTTP request.
You can access the currentUser
property on the request object and as a global inside your views when the end-user is logged in via session
authenticator.
request.currentUser // logged in user
{{ currentUser }}
All other authenticators like JWT, Basic Auth and API Token will have access to the currently logged in user as authUser
property on the request object.
request.authUser // authenticated user instance
It is fairly simple to extend the provider by adding new serializers or schemes. The important step is to understand the need of both.
Ioc.extend('Adonis/Src/AuthManager', 'mongo', function (app) {
return new MongoSerializer()
}, 'serializer')
// Or
Ioc.extend('Adonis/Src/AuthManager', 'fingerprint', function (app) {
// adonis will initiate the scheme itself for each request.
return FingerPrint
}, 'scheme')
Serializer is used to serialize/fetch the user from the data store using their unique identifier. Also serializer is suppose to verify the user password.
'use strict'
class MongoSerializer {
* findById (id, options) {
// ...
}
* findByCredentials (email, options) {
// ...
}
* findByToken (token, options) {
// ...
}
* getUserForToken (tokenPayload, options) {
// ...
}
* saveToken (userPayload, token, options, expiry) {
// ...
}
* revokeTokens (userPayload, tokens, reverse) {
// ...
}
* validateToken (tokenPayload, options) {
// ...
}
* validateCredentials (userPayload, password, options) {
// ...
}
primaryKey(authenticatorOptions) {
// ...
}
}
findById - This method should find a user using the unique identifier and return the user object. For example: Lucid serializer will return the User model instance.
findByCredentials - The method will find a user using the field name (uid) defined inside the config/auth.js
file and must return the user object.
findByToken - This method should return the token object
using a unique token.
getUserForToken - Here we return the user object
using the token object
returned by findByToken method.
saveToken - Save token for a given user. The token is generated by auth provider itself and you must save it for later use.
revokeTokens - Revoke a single/multiple tokens. If reverse=true
you must revoke all the tokens except the one passed as the 2nd parameter.
validateToken - Here you must validate the token payload returned by findByToken method. The most common check is to verify the expiry.
validateCredentials - This method is used to verify the user password against the plain password.
primaryKey — This method is used to get primary key definition to make sure that the primary key is not null for the user.
Schemes defines the way of authenticating users. For example: Session, JWT, Basic Auth etc. You can add your own schemes if required. Below is the list of methods your scheme should implement.
All the methods of your schemes are exposed to the end-user. Which means they can call these methods directly using the auth property on the request object.
|
'use strict'
class FingerPrint {
constructor (request, serializer, options) {
this.request = request
this.serializer = serializer
this.options = options // config options
}
* check () {
// ...
}
* getUser () {
// ...
}
}
check - Check method should return a boolean indicating whether a user is logged in or not. You can access the values of the current request using the request
parameter passed to the constructor.
getUser - Should return the user payload only if user is logged in. Otherwise it should return null