{"id":19528890,"url":"https://github.com/adonisjs/persona","last_synced_at":"2025-04-26T11:33:38.127Z","repository":{"id":57095173,"uuid":"127308678","full_name":"adonisjs/persona","owner":"adonisjs","description":"Opinionated user management service for AdonisJs","archived":false,"fork":false,"pushed_at":"2021-01-31T11:48:06.000Z","size":32,"stargazers_count":178,"open_issues_count":13,"forks_count":24,"subscribers_count":9,"default_branch":"develop","last_synced_at":"2025-04-18T12:07:39.174Z","etag":null,"topics":["legacy"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/adonisjs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-03-29T15:09:44.000Z","updated_at":"2025-03-12T18:41:45.000Z","dependencies_parsed_at":"2022-08-22T21:41:20.113Z","dependency_job_id":null,"html_url":"https://github.com/adonisjs/persona","commit_stats":null,"previous_names":["adonisjs/adonis-persona"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Fpersona","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Fpersona/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Fpersona/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/adonisjs%2Fpersona/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/adonisjs","download_url":"https://codeload.github.com/adonisjs/persona/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250981021,"owners_count":21517762,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["legacy"],"created_at":"2024-11-11T01:20:35.230Z","updated_at":"2025-04-26T11:33:37.816Z","avatar_url":"https://github.com/adonisjs.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![](http://res.cloudinary.com/adonisjs/image/upload/q_100/v1522328931/adonis-persona_qlb1ix.svg)\n\n\u003e Opinionated user management service for AdonisJs\n\n**Make sure @adonisjs/framework version is \u003e= 5.0.6**\n\nAdonisJs is all about removing redundant code from your code base. This add-on tries to do the same.\n\n## What is Persona?\n\nPersona is a simple, functional service to let you **create**, **verify** and **update** user profiles.\n\nPersona 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**.\n\n## What does it do?\n1. Helps you register new users.\n2. Generates email verification tokens.\n3. Validates credentials on login.\n4. On email change, sets the user account to a `pending` state and re-generates the email verification token.\n5. Allows changing passwords.\n6. Allows recovering forgotten passwords.\n\n## What does it NOT do?\n1. Does not generate any routes, controllers or views for you.\n2. Does not send emails. However, it emits events that you can use to send emails.\n3. Does not create sessions or generate JWT tokens.\n\n\n## Setup\nRun the following command to grab the add-on from npm:\n\n```bash\nadonis install @adonisjs/persona\n\n# for yarn\nadonis install @adonisjs/persona --yarn\n```\n\nFollow up by registering the provider inside the providers array:\n\n```js\nconst providers = [\n  '@adonisjs/persona/providers/PersonaProvider'\n]\n```\n\nYou may then access it as follows:\n\n```js\nconst Persona = use('Persona')\n```\n\n## Config\n\nThe config file is saved as `config/persona.js`.\n\n| Key | Value | Description |\n|-----|--------|------------|\n| 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.\n| email | email | 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.\n| password | password | The field to be used as password.\n| model | App/Models/User | The user model to be used.\n| newAccountState | pending | The default account state for new users.\n| verifiedAccountState | active | The account state for users after verifying their email address.\n| dateFormat | YYYY-MM-DD HH:mm:ss | Your database date format, required for determining if the token has been expired or not.\n| validationMessages | function | A function that returns an object of messages to be used for validation. The syntax is the same as `Validator` custom messages.\n\n## Constraints\n\nThere are some intentional constraints in place.\n\n1. Only works with `Lucid` models.\n2. The `App/Models/User` must have a relationship setup with `App/Models/Token` and vice-versa.\n\n   ```js\n   class User extends Model {\n     tokens () {\n       return this.hasMany('App/Models/Token')\n     }\n   }\n\n   class Token extends Model {\n     user () {\n       return this.belongsTo('App/Models/User')\n     }\n   }\n   ```\n\n 3. User table must have a `string` column called `account_status`.\n\n## API\n\nLet's go through the API of persona.\n\n#### register(payload, [callback])\n\n\u003e 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.\n\nThe register method takes the user input data and performs the following actions on it.\n\n1. Validates that all `uids` are unique.\n2. Checks that email is unique and is a valid email address.\n3. Makes sure the password is confirmed.\n4. Creates a new user account with the `account_status = pending`.\n5. Generates and saves an email verification token inside the `tokens` table.\n5. Emits a `user::created` event. You can listen for this event to send an email to the user.\n\n\u003e Make sure to use `querystring` module to encode the token when sending via Email.\n\n```js\nconst Persona = use('Persona')\n\nasync register ({ request, auth, response }) {\n  const payload = request.only(['email', 'password', 'password_confirmation'])\n\n  const user = await Persona.register(payload)\n\n  // optional\n  await auth.login(user)\n  response.redirect('/dashboard')\n}\n```\n\n#### verify(payload, [callback])\n\n\u003e  The optional `callback` is invoked with the user instance just before the password verification. You can use it to check for `userRole` or any other property you want.\n\nVerifies the user credentials. The value of `uid` will be checked against all the `uids`.\n\n```js\nasync login ({ request, auth, response }) {\n  const payload = request.only(['uid', 'password'])\n  const user = await Persona.verify(payload)\n\n  await auth.login(user)\n  response.redirect('/dashboard')\n}\n```\n\n#### verifyEmail(token)\n\nVerifies the user's email using the token. Ideally that should be after someone clicks a URL from their email address.\n\n1. Removes the token from the tokens table.\n2. Set user `account_status = active`.\n\n```js\nasync verifyEmail ({ params, session, response }) {\n  const user = await Persona.verifyEmail(params.token)\n\n  session.flash({ message: 'Email verified' })\n  response.redirect('back')\n}\n```\n\n#### updateProfile(user, payload)\n\nUpdates the user columns inside the database. However, if the email is changed, it performs the following steps:\n\n\u003e Please note that this method will throw an exception if the user is trying to change the password.\n\n1. Sets the user's `account_status = pending`.\n2. Generates an email verification token.\n3. Fires the `email::changed` event.\n\n```js\nasync update ({ request, auth }) {\n  const payload = request.only(['firstname', 'email'])\n  const user = auth.user\n  await Persona.updateProfile(user, payload)\n}\n```\n\n#### updatePassword(user, payload)\n\nUpdates the user's password by performing the following steps:\n\n\u003e Make sure to have the `beforeSave` hook in place for hashing the password. Otherwise\n\u003e the password will be saved as a plain string.\n\n1. Ensures `old_password` matches the user's password.\n2. Makes sure the new password is confirmed.\n3. Updates the user password.\n4. Fires the `password::changed` event. You can listen for this event to send an email about the password change.\n\n```js\nasync updatePassword ({ request, auth }) {\n  const payload = request.only(['old_password', 'password', 'password_confirmation'])\n  const user = auth.user\n  await Persona.updatePassword(user, payload)\n}\n```\n\n#### forgotPassword(uid)\n\nTakes a forgot password request from the user by passing their `uid`. Uid will be matched against all the `uids` inside the config file.\n\n1. Finds a user with the matching uid.\n2. Generates a password change token.\n3. Emits the `forgot::password` event. You can listen for this event to send an email with the token to reset the password.\n\n```js\nasync forgotPassword ({ request }) {\n  await Persona.forgotPassword(request.input('uid'))\n}\n```\n\n#### updatePasswordByToken(token, payload)\n\nUpdates the user password using a token. This method performs the following checks:\n\n1. Makes sure the token is valid and not expired.\n2. Ensures the password is confirmed.\n3. Updates the user's password.\n\n```js\nasync updatePasswordByToken ({ request, params }) {\n  const token = params.token\n  const payload = request.only(['password', 'password_confirmation'])\n\n  const user = await Persona.updatePasswordByToken(token, payload)\n}\n```\n\n## Custom messages\nYou 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.\n\n```js\n{\n  validationMessages (action) =\u003e {\n    return {\n      'email.required': 'Email is required',\n      'password.mis_match': 'Invalid password'\n    }\n  }\n}\n```\n\nThe `validationMessages` method gets an `action` parameter. You can use it to customize the messages for different actions. Following is the list of actions.\n\n1. register\n2. login\n3. emailUpdate\n4. passwordUpdate\n\n## Events emitted\n\nBelow is the list of events emitted at different occasions.\n\n| Event | Payload | Description |\n|--------|--------|-------------|\n| user::created | `{ user, token }` | Emitted when a new user is created |\n| email::changed | `{ user, oldEmail, token }` | Emitted when a user changes their email address |\n| password::changed | `{ user }` | Emitted when a user changes their password by providing the old password |\n| forgot::password | `{ user, token }` | Emitted when a user asks for a token to change their password |\n| password::recovered | `{ user }` | Emitted when a user's password is changed using the token |\n\n## Exceptions raised\n\nThe entire API is driven by exceptions, which means you will hardly have to write `if/else` statements.\n\nThis is great, since Adonis allows managing responses by catching exceptions globally.\n\n#### ValidationException\nRaised when validation fails. If you are already handling `Validator` exceptions, you won't have to do anything special.\n\n#### InvalidTokenException\nRaised when a supplied token, to verify an email or reset password with, is invalid.\n\n## Custom rules\nAt times, you may want to have custom set of rules when registering or login new users. You can override following methods for same.\n\nThe code can be added inside the hooks file or even in the registeration controller.\n\n#### registerationRules\n\n```js\nPersona.registerationRules = function () {\n  return {\n    email: 'required|email|unique:users,email',\n    password: 'required|confirmed'\n  }\n}\n```\n\n#### updateEmailRules\n```js\nPersona.updateEmailRules = function (userId) {\n  return {\n    email: `required|email|unique:users,email,id,${userId}`\n  }\n}\n```\n\n#### updatePasswordRules\n```js\nPersona.updatePasswordRules = function (enforceOldPassword = true) {\n  if (!enforceOldPassword) {\n    return {\n      password: 'required|confirmed'\n    }\n  }\n\n  return {\n    old_password: 'required',\n    password: 'required|confirmed'\n  }\n}\n```\n\n#### loginRules\n```js\nPersona.loginRules = function () {\n  return {\n    uid: 'required',\n    password: 'required'\n  }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadonisjs%2Fpersona","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fadonisjs%2Fpersona","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fadonisjs%2Fpersona/lists"}