Opinionated user management service for AdonisJs
Opinionated user management service for AdonisJs
Make sure @adonisjs/framework version is >= 5.0.6
AdonisJs is all about removing redundant code from your code base. This add-on tries to do the same.
Persona is a simple, functional service to let you create, verify and update user profiles.
Persona is not for everyone; if your login system is too complex and relies on many factors, Persona is not for you. However, persona works great for most use cases.
pending
state and re-generates the email verification token.Run the following command to grab the add-on from npm:
adonis install @adonisjs/persona
# for yarn
adonis install @adonisjs/persona --yarn
Follow up by registering the provider inside the providers array:
const providers = [
'@adonisjs/persona/providers/PersonaProvider'
]
You may then access it as follows:
const Persona = use('Persona')
The config file is saved as config/persona.js
.
Key | Value | Description |
---|---|---|
uids | ['email'] | An array of database columns that will be used as uids . If your system allows username and emails both, then simply add them to this array. |
The field to be used as email. Every time a user changes the value of this field, their account will be set to the pending state. |
||
password | password | The field to be used as password. |
model | App/Models/User | The user model to be used. |
newAccountState | pending | The default account state for new users. |
verifiedAccountState | active | The account state for users after verifying their email address. |
dateFormat | YYYY-MM-DD HH:mm:ss | Your database date format, required for determining if the token has been expired or not. |
validationMessages | function | A function that returns an object of messages to be used for validation. The syntax is the same as Validator custom messages. |
There are some intentional constraints in place.
Only works with Lucid
models.
The App/Models/User
must have a relationship setup with App/Models/Token
and vice-versa.
class User extends Model {
tokens () {
return this.hasMany('App/Models/Token')
}
}
class Token extends Model {
user () {
return this.belongsTo('App/Models/User')
}
}
User table must have a string
column called account_status
.
Let's go through the API of persona.
The optional
callback
is invoked with the original payload just before the user is saved to the database. You can use it if you need to attach any other properties to the payload.
The register method takes the user input data and performs the following actions on it.
uids
are unique.account_status = pending
.tokens
table.user::created
event. You can listen for this event to send an email to the user.Make sure to use
querystring
module to encode the token when sending via Email.
const Persona = use('Persona')
async register ({ request, auth, response }) {
const payload = request.only(['email', 'password', 'password_confirmation'])
const user = await Persona.register(payload)
// optional
await auth.login(user)
response.redirect('/dashboard')
}
The optional
callback
is invoked with the user instance just before the password verification. You can use it to check foruserRole
or any other property you want.
Verifies the user credentials. The value of uid
will be checked against all the uids
.
async login ({ request, auth, response }) {
const payload = request.only(['uid', 'password'])
const user = await Persona.verify(payload)
await auth.login(user)
response.redirect('/dashboard')
}
Verifies the user's email using the token. Ideally that should be after someone clicks a URL from their email address.
account_status = active
.async verifyEmail ({ params, session, response }) {
const user = await Persona.verifyEmail(params.token)
session.flash({ message: 'Email verified' })
response.redirect('back')
}
Updates the user columns inside the database. However, if the email is changed, it performs the following steps:
Please note that this method will throw an exception if the user is trying to change the password.
account_status = pending
.email::changed
event.async update ({ request, auth }) {
const payload = request.only(['firstname', 'email'])
const user = auth.user
await Persona.updateProfile(user, payload)
}
Updates the user's password by performing the following steps:
Make sure to have the
beforeSave
hook in place for hashing the password. Otherwise the password will be saved as a plain string.
old_password
matches the user's password.password::changed
event. You can listen for this event to send an email about the password change.async updatePassword ({ request, auth }) {
const payload = request.only(['old_password', 'password', 'password_confirmation'])
const user = auth.user
await Persona.updatePassword(user, payload)
}
Takes a forgot password request from the user by passing their uid
. Uid will be matched against all the uids
inside the config file.
forgot::password
event. You can listen for this event to send an email with the token to reset the password.async forgotPassword ({ request }) {
await Persona.forgotPassword(request.input('uid'))
}
Updates the user password using a token. This method performs the following checks:
async updatePasswordByToken ({ request, params }) {
const token = params.token
const payload = request.only(['password', 'password_confirmation'])
const user = await Persona.updatePasswordByToken(token, payload)
}
You can define a function inside the config/persona.js
file, which returns an object of messages to be used as validation messages. The syntax is the same as Validator
custom messages.
{
validationMessages (action) => {
return {
'email.required': 'Email is required',
'password.mis_match': 'Invalid password'
}
}
}
The validationMessages
method gets an action
parameter. You can use it to customize the messages for different actions. Following is the list of actions.
Below is the list of events emitted at different occasions.
Event | Payload | Description |
---|---|---|
user::created | { user, token } |
Emitted when a new user is created |
email::changed | { user, oldEmail, token } |
Emitted when a user changes their email address |
password::changed | { user } |
Emitted when a user changes their password by providing the old password |
forgot::password | { user, token } |
Emitted when a user asks for a token to change their password |
password::recovered | { user } |
Emitted when a user's password is changed using the token |
The entire API is driven by exceptions, which means you will hardly have to write if/else
statements.
This is great, since Adonis allows managing responses by catching exceptions globally.
Raised when validation fails. If you are already handling Validator
exceptions, you won't have to do anything special.
Raised when a supplied token, to verify an email or reset password with, is invalid.
At times, you may want to have custom set of rules when registering or login new users. You can override following methods for same.
The code can be added inside the hooks file or even in the registeration controller.
Persona.registerationRules = function () {
return {
email: 'required|email|unique:users,email',
password: 'required|confirmed'
}
}
Persona.updateEmailRules = function (userId) {
return {
email: `required|email|unique:users,email,id,${userId}`
}
}
Persona.updatePasswordRules = function (enforceOldPassword = true) {
if (!enforceOldPassword) {
return {
password: 'required|confirmed'
}
}
return {
old_password: 'required',
password: 'required|confirmed'
}
}
Persona.loginRules = function () {
return {
uid: 'required',
password: 'required'
}
}