{"id":14975738,"url":"https://github.com/lykmapipo/irina","last_synced_at":"2025-10-27T14:31:11.176Z","repository":{"id":31178158,"uuid":"34738636","full_name":"lykmapipo/irina","owner":"lykmapipo","description":"Simple and flexible authentication workflows for mongoose","archived":false,"fork":false,"pushed_at":"2022-12-07T17:53:45.000Z","size":970,"stargazers_count":8,"open_issues_count":27,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-04-24T12:26:47.687Z","etag":null,"topics":["authenticate","authentication","confirm","lock","mongodb","mongoose","recover","register"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lykmapipo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-04-28T15:23:45.000Z","updated_at":"2021-10-04T07:17:59.000Z","dependencies_parsed_at":"2023-01-14T18:29:45.582Z","dependency_job_id":null,"html_url":"https://github.com/lykmapipo/irina","commit_stats":null,"previous_names":[],"tags_count":49,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lykmapipo%2Firina","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lykmapipo%2Firina/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lykmapipo%2Firina/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lykmapipo%2Firina/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lykmapipo","download_url":"https://codeload.github.com/lykmapipo/irina/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219861084,"owners_count":16556007,"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":["authenticate","authentication","confirm","lock","mongodb","mongoose","recover","register"],"created_at":"2024-09-24T13:52:28.183Z","updated_at":"2025-10-27T14:31:10.828Z","avatar_url":"https://github.com/lykmapipo.png","language":"JavaScript","readme":"# irina\n\n[![Build Status](https://travis-ci.org/lykmapipo/irina.svg?branch=master)](https://travis-ci.org/lykmapipo/irina)\n[![npm version](https://badge.fury.io/js/irina.svg)](http://badge.fury.io/js/irina)\n\nSimple and flexible authentication workflows for [mongoose](https://github.com/Automattic/mongoose) inspired by \n[devise](https://github.com/plataformatec/devise).\n\n## Guide\n\n* [Installation](#installation)\n* [Usage](#usage)\n* [Modules](#modules)\n    * [Authenticable](#authenticable)\n        * [encryptPassword()](#encryptpasswordcallbackerrorauthenticable-)\n        * [comparePassword()](#comparepasswordpassword-callbackerrorauthenticable-)\n        * [changePassword()](#changepasswordnewpassword-callbackerrorauthenticable-)\n        * [authenticate()](#authenticatecredentials-callbackerrorauthenticable-)\n    * [Confirmable](#confirmable)\n        * [generateConfirmationToken](#generateconfirmationtokencallbackerrorconfirmable-)\n        * [sendConfirmation](#sendconfirmationcallbackerrorconfirmable-)\n        * [confirm](#confirmconfirmationtoken-callbackerrorconfirmable-)\n    * [Lockable](#lockable)\n        * [generateUnlockToken](#generateunlocktokencallbackerrorlockable-)\n        * [sendLock](#sendlockcallbackerrorlockable-)\n        * [lock](#lockcallbackerrorlockable-)\n        * [unlock](#unlockunlocktoken-callbackerrorlockable-)\n    * [Recoverable](#recoverable)\n        * [generateRecoveryToken](#generaterecoverytokencallbackerrorrecoverable-)\n        * [sendRecovery](#sendrecoverycallbackerrorrecoverable-)\n        * [requestRecover](#requestrecovercriteria-callbackerrorrecoverable-)\n        * [recover](#recoverrecoverytoken-newpassword-callbackerrorrecoverable-)\n    * [Registerable](#registerable)\n        * [register](#registercredentials-callbackerrorregisterable-)\n        * [unregister](#unregistercallbackerrorregisterable-)\n    * [Trackable](#trackable)\n        * [track](#trackipaddresscallbackerrortrackable-)\n    * [Send Notifications](#sending-notifications)\n    * [Testing](#testing)\n    * [Contribute](#contribute)\n    * [Licence](#licence)\n\n## Installation\n```sh\n$ npm install --save mongoose irina\n```\n\n## Usage\n\n```javascript\nvar mongoose = require('mongoose');\nvar irina = require('irina');\n\n//define User schema\nvar UserSchema = new Schema({ \n        ... \n});\n\n//plugin irina to User schema\nUser.plugin(irina);\n\n//register user schema\nmongoose.model('User', UserSchema);\n\n...\n\n//register a new user account\nUser.register(credentials, done);\n\n//confirm user registration\nUser.confirm('confirmationToken', done);\n\n//authenticate provided user credentials\nUser.authenticate(credentials, done);\n\n//unlock locked user account\nUser.unlock('unlockToken', done);\n\n//request password recovering\nUser.requestRecover(criteria, done)\n\n//recover user password and set new password\nUser.recover(recoveryToken, newPassword, done);\n\n//see Modules docs for more\n\n...\n\n```\n\n## Modules\n\n### Authenticable\nIt lays down the infrastructure for authenticating a user. It extend `mongoose model` with the following:\n\n#### `email` : \nAn attribute used to store user email address. `irina` opt to use email address but it can be overriden by supply a custom attribute to use.\n\n#### `password` : \nAn attribute which is used to store user password hash.\n\n#### `encryptPassword(callback(error,authenticable))` : \nAn instance method which encrypt the current model instance password using [bcryptjs](https://github.com/dcodeIO/bcrypt.js).\n\nExample\n\n```js\n\n....\n\n//encypt instance password\nuser\n    .encryptPassword(function(error, authenticable) {\n        if (error) {\n            console.log(error);\n        } else {\n            console.log(authenticable);\n        }\n    });\n\n...\n\n```\n\n#### `comparePassword(password, callback(error,authenticable))` : \nAn instance method which takes in a plain string `password` and compare with the instance hashed password to see if they match.\n\nExample\n\n```js\n\n...\n\n//after having model instance\nuser\n    .comparePassword(password,function(error, authenticable) {\n        if (error) {\n            console.log(error);\n        } else {\n            console.log(authenticable);\n        }\n});\n\n...\n\n```\n\n#### `changePassword(newPassword, callback(error,authenticable))` : \nAn instance method which takes in a plain string `newPassword`, hash it and set it as a current password for this authenticable instance.\n\nExample\n\n```js\n\n...\n\n//after having model instance\nuser\n    .changePassword(password,function(error, authenticable) {\n        if (error) {\n            console.log(error);\n        } else {\n            console.log(authenticable);\n        }\n});\n\n...\n\n```\n\n#### `authenticate(credentials, callback(error,authenticable))` : \nA model static method which takes in credentials and additional `mongoose query criteria` in the format below : \n\n```js\nvar faker = require('faker');\nvar credentials = {\n            email: faker.internet.email(),\n            password: faker.internet.password(),\n            ...\n            //additional mongoose criteria\n            ....\n        };\n```\n\nwhere `email` is valid email and `password` is valid password of already registered user. It will then authenticate the given credentials. If they are valid credential a user with the supplied credentials will be returned otherwise corresponding errors will be returned.\n\nExample\n\n```js\nvar faker = require('faker');\n\n//you may obtain this credentials from anywhere\n//this is just a demostration for how credential must be\nvar credentials = {\n            email: faker.internet.email(),\n            password: faker.internet.password()\n        };\n\nUser\n    .authenticate(credentials,function(error, authenticable) {\n            if (error) {\n                console.log(error);\n            } else {\n                console.log(authenticable);\n            }\n    });\n\n...\n\n```\n\n### Confirmable\nProvide a means to confirm user account registration. It extend `mongoose model` with the following:\n\n#### `confirmationToken` : \nAn attribute which used to store current register user confirmation token.\n\n#### `confirmationTokenExpiryAt` : \nAn attribute that keep tracks of when \nthe confirmation token will expiry. Beyond that, new confirmation token will be generated and notification will be send.\n\n#### `confirmedAt` : \nAn attribute that keep tracks of when user account is confirmed.\n\n#### `confirmationSentAt` : \nAn attribute that keep tracks of when confirmation request is sent.\n\n#### `generateConfirmationToken(callback(error,confirmable))` : \nThis instance method will generate `confirmationToken` and `confirmationTokenExpiryAt` time. It also update and persist an instance before return it.\n\nExample\n```js\n\n...\n\nuser\n    .generateConfirmationToken(function(error, confirmable) {\n        if (error) {\n            console.log(error);\n        } else {\n           console.log(confirmable);\n        }\n    });\n\n...\n\n```\n\n#### `sendConfirmation(callback(error,confirmable))` : \nThis instance method which utilizes [model.send()](https://github.com/lykmapipo/irina#sending-notifications) and send the confirmation notification. On successfully send, it will update `confirmationSentAt` instance attribute with the current time stamp and persist the instance before return it.\n\nExample\n```js\n \n ...\n\nuser\n    .sendConfirmation(function(error, confirmable) {\n        if (error) {\n            consle.log(error);\n        } else {\n           consle.log(confirmable);\n        }\n});\n\n...\n\n```\n\n#### `confirm(confirmationToken, callback(error,confirmable))` : \nThis static/class method taken the given `confirmationToken` and confirm un-confirmed registration which match the given confirmation token. It \nwill update `confirmedAt` instance attribute and persist the instance \nbefore return it.\n\nExample\n```js\n \n ...\n\nUser\n    .confirm('confirmationToken',function(error, confirmable) {\n            if (error) {\n                console.log(error);\n            } else {\n                console.log(confirmable);\n            }\n        });\n\n...\n\n```\n\n### Lockable\nProvide a means of locking an account after a specified number of failed sign-in attempts `(defaults to 3 attempts)`. user can unlock account through unlock instructions sent. It extend the model with the following:\n\n#### `failedAttempt` : \nAn attribute which keeps track of failed login attempts.\n\n#### `lockedAt` : \nAn attribute which keeps track of when account is locked.\n\n#### `unlockedAt` : \nAn attribute which keeps track of when and account is unlocked.\n\n#### `unlockToken` : \nAn attribute which store the current unlock token of the locked account.\n\n#### `unlockTokenSentAt` : \nAn attribute which keeps track of when the unlock token notification sent.\n\n#### `unlockTokenExpiryAt` : \nAn attribute which keep track of `unlockToken` expiration. If `unlockToken` is expired new token will get generated and set.\n\n#### `generateUnlockToken(callback(error,lockable))` : \nAn instance method that generate `unlockToken` and `unlockTokenExpiryAt`. Instance will get persisted before returned otherwise corresponding errors will get returned.\n\nExample\n```js\n\n...\n\nuser\n    .generateUnlockToken(function(error, lockable) {\n        if (error) {\n            console.log(error)\n        } else {\n           console.log(lockable)\n        }\n});\n\n...\n\n```\n \n#### `sendLock(callback(error,lockable))` : \nAn instance method which send account locked notification to the owner. It will set `unlockTokenSentAt` to track when the lock notification is sent. Instance will get update before returned otherwise corresponding errors will get returned.\n\nExample\n```js\n\n...\n\nuser\n    .sendLock(function(error, lockable) {\n        if (error) {\n            console.log(error);\n        } else {\n            console.log(lockable);\n        }\n    });\n\n...\n\n```\n\n#### `lock(callback(error,lockable))` : \nAn instance method that used to lock an account. When invoked, it will check if the number of `failedAttempts` is greater that the configured `maximum allowed login attempts`, if so the account will get locked by setting `lockedAt` to the current timestamp of `lock` invocation. Instance will get persisted before returned otherwise corresponding errors will get returned.\n\nExample\n```js\n\n...\n\nuser\n    .lock(function(error, lockable) {\n        if (error) {\n            console.log(error);\n        } else {\n            console.log(lockable);\n        }\n    });\n\n...\n\n```\n\n#### `unlock(unlockToken, callback(error,lockable))` : \nA model static method which unlock a locked account with the provided `unlockToken`. If the token expired the new `unlockToken` will get generated. If token is valid, locked account will get unlocked and `unlockedAt` attribute will be set to current timestamp and `failedAttempts` will get set to 0. Instance unlocked will get persisted before returned otherwise corrensponding errors will get returned.\n\nExample\n```js\n\n...\n\nUser\n    .unlock(unlockToken, function(error, lockable) {\n            if (error) {\n                console.log(error);\n            } else {\n                  console.log(lockable);\n            }\n    });\n\n...\n\n```\n\n### Recoverable\nLays out infrastructure of resets the user password and sends reset instructions. It extend model with the following:\n\n#### `recoveryToken` : \nAn attribute that store recovery token\n\n#### `recoveryTokenExpiryAt` : \nAn attribute that track when the recoverable token is expiring.\n\n#### `recoverySentAt` : \nAn attribute that keep track as of when the recovery notification is sent.\n\n#### `recoveredAt` : \nAn attribute which keeps track of when the password was recovered.\n\n#### `generateRecoveryToken(callback(error,recoverable))` : \nAn instance method which used to generate `recoveryToken` and set `recoveryTokenExpiryAt` timestamp. Instance will get persisted before returned othewise corresponding errors will get returned.\n\nExample\n```js\n...\n\nuser\n    .generateRecoveryToken(function(error, recoverable) {\n        if (error) {\n            console.log(error)\n        } else {\n            console.log(recoverable);\n        }\n    });\n\n...\n\n```\n\n#### `sendRecovery(callback(error,recoverable))` : \nAn instance method which is used to send recovery notification to the user. It will set `recoveryTokenSentAt` timestamp. Instance will get persisted before returned othewise corresponding errors will get returned.\n\nExample\n```js\n...\n\nuser\n    .sendRecovery(function(error, recoverable) {\n        if (error) {\n            console.log(error);\n        } else {\n            console.log(recoverable);\n        }\n    });\n\n...\n\n```\n\n\n#### `requestRecover(criteria, callback(error,recoverable))` : \nA model static method which is used to request account password recovery. It utilize `generateRecoveryToken` and `sendRecovery` to generate recovery token and send it.  \n\nExample\n```js\n\n...\n\nUser\n    .requestRecover({email:'ex@ex.com'},function(error, recoverable) {\n            if (error) {\n                console.log(error);\n            } else {\n                console.log(recoverable);\n            }\n        });\n\n...\n\n```\n\n\n#### `recover(recoveryToken, newPassword, callback(error,recoverable))` : \nA model static method which is used to recover an account with the matched `recoverToken`. The `newPassword` provided will get encrypted before set as user password. It will set `recoveredAt` before persist the model. \n\nExample\n```js\n\n...\n\nUser\n    .recover(\n        recoveryToken,\n        faker.internet.password(),\n        function(error, recoverable) {\n            if (error) {\n                console.log(error);\n            } else {\n                console.log(recoverable);\n            }\n        });\n\n...\n\n```\n\n### Registerable\nHandles signing up users through a registration process, also allowing them to edit and destroy their account. It extend model with the following:\n\n#### `registeredAt` : \nAn attribute which keeps track of whn an account is registered.\n\n#### `unregisteredAt` : \nAn attribute which keep tracks of when an account is unregistered.\n\n#### `register(credentials, callback(error,registerable))` : \nA model static method which is used to register provided credentials. It takes care of checking if email is taken and validating credentials. It will return registered user otherwise corresponding registration errors.\n\nExample\n```js\nvar faker = require('faker');\nvar credentials = {\n            email: faker.internet.email(),\n            password: faker.internet.password()\n        }\n\nUser\n    .register(credentials, function(error, registerable) {\n        if (error) {\n            console.log(error);\n        } else {\n            console.log(registerable);\n        }\n    });\n\n...\n\n```\n\n#### `unregister(callback(error,registerable))` : \nAn instance method which allow to unregister(destroy a user). The currently implementation is to set `unregiesteredAt` to current timestamp of the invocation. Instance will get persisted before returned otherwise corresponding errors will be returned.\n\nExample:\n```js\n\n...\n\nuser\n    .unregister(function(error, registerable) {\n                    if (error) {\n                        console.log(error);\n                    } else {\n                        console.log(registerable);\n                    }\n                });\n\n...\n\n```\n\n### Trackable\nProvide a means of tracking user signin activities. It extend provided \nmodel with the followings:\n\n#### `signInCount` : \nKeeps track of number of count a user have been sign in into you API\n\n#### `currentSignInAt` : \nKeeps track of the latest time when user signed in into you API\n\n#### `currentSignInIpAddress` : \nKeeps track of the latest IP address a user used to log with into your API\n\n#### `lastSignInAt` : \nKeeps track of the previous sign in time prior to the current sign in.\n\n#### `lastSignInIpAddress` : \nKeeps track of the previous IP address user used to log with into your API\n\n#### `track(ipAddress,callback(error,trackable))` : \nThis is model instance method, which when called with the IP address, it will update current tracking details and set the provided IP address as the `currentSignInIpAddress`. On successfully tracking, a provided `callback` will be get invoked and provided with error if occur and the current updated model instance.\n\nExample\n```js\n\n...\n\nUser\n    .findOne({email:'validEmail'})\n    .exec(function(error,user){\n        if(error){\n            console.log(error);\n        }\n        else{\n            user\n                .track('validIpAddress',function(error,trackable){\n                    if(error){\n                        console.log(error);\n                    }\n                    else{\n                        console.log(trackable);\n                    }\n                });\n        }\n    });\n\n...\n\n``` \n\n## Sending Notifications\nThe default implementation of `irina` to send notifications is `noop`. This is because there are different use case(s) when it come on sending notifications.\n\nDue to that reason, `irina` requires your model to implement `send` method which accept `type, authentication, done` as it argurments.\n\n#### `type` : \nRefer to the type of notifcation to be sent. There are just three types which are `Account confirmation`, `Account recovery` and `Password recover` which are sent when new account is registered, an account is locked and need to be unlocked and when account is requesting to recover the password repsectively.\n\n#### `authenticable` : \nRefer to the current user model instance.\n\n#### `done` : \nIs the callback that you must call after finish sending the notification. By default this callback will update notification send details based on the usage.\n\n### How to implement a send\nSimple add `send` into your model as `instance methods`.\n\n```js\nvar UserSchema = new Schema({\n    ...\n});\n\n//the add send\nUserSchema.methods.send = function(type, authenticable, done) {\n    //if we send confirmation email\n    if (type === 'Account confirmation') {\n        //your notification sending implementation\n        //i.e email, sms, etc\n        console\n            .log(\n                'Notification type: %s.\\nAuthenticable: %s \\n',\n                type,\n                JSON.stringify(authenticable)\n            );\n            done();\n    }\n    //if we send account recovery\n    if (type === 'Password recovery') {\n       //your notification sending implementation\n        //i.e email, sms, etc\n        console\n            .log(\n                'Notification type: %s.\\nAuthenticable: %s \\n',\n                type,\n                JSON.stringify(authenticable)\n            );\n            done();\n    }\n    //if we send account locked information\n    if (type === 'Account recovery') {\n        //your notification sending implementation\n        //i.e email, sms, etc\n        console\n            .log(\n                'Notification type: %s.\\nAuthenticable: %s \\n',\n                type,\n                JSON.stringify(authenticable)\n            );\n            done();\n    }\n    \n    ...\n\n};\n\n...\n\n```\nThats all needed and `irina` will be able to utilize your `send` implementation.\n\n### Sending Issues\nIt is recommended to use job queue like [kue](https://github.com/learnboost/kue) when implementing your `send` to reduce your API response time.\n\n\n## Testing\n* Clone this repository\n\n* Install all development dependencies\n```sh\n$ npm install\n```\n* Then run test\n```sh\n$ npm test\n```\n\n## Contribute\nIt will be nice, if you open an issue first so that we can know what is going on, then, fork this repo and push in your ideas. Do not forget to add a bit of test(s) of what value you adding.\n\n\n## Licence\nThe MIT License (MIT)\n\nCopyright (c) 2015 lykmapipo \u0026 Contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flykmapipo%2Firina","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flykmapipo%2Firina","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flykmapipo%2Firina/lists"}