@rakered/accounts

An account package for managing user accounts in mongodb.

Usage no npm install needed!

<script type="module">
  import rakeredAccounts from 'https://cdn.skypack.dev/@rakered/accounts';
</script>

README

@rakered/accounts

An account package for managing user accounts in mongodb.

social

This project aims to be somewhat compatible with Meteor accounts storage, Except for the email verification tokens, resume tokens, and restrictions on usernames. See the FAQ for more specifics about that subject.

Usage

import init from '@rakered/accounts';

const accounts = init();
await accounts.createUser({ username: 'hunter', password: 'hunter2' });

Options can be provided in two ways, passing them as object to init, or by providing them as environment variables.

Options

There are various options available. In development mode, this library does not require a MAIL_URL to be set, instead emails will be logged to console.

Environment Variables

  • MAIL_URL String

    The smtp url for the mail server to use.

    Optional when running in development mode

  • JWT_SECRET String

    The secret to sign the jwt tokens with.

  • EMAIL_FROM String

    The email address that's being used as sender.

    Optional if options.email.from is provided

  • BASE_URL String

    The url that will be prefixed to magic urls and provided to the email template.

    Optional if options.email.siteUrl is provided

  • SITE_NAME String

    The site name that will be provided to the email template.

    Optional if options.email.siteName is provided

  • LOGO_URL String

    The url for the logo that will be shown in the email.

    Optional if options.email.logoUrl is provided

Collection

First things first, the database connection. If you don't specify anything, it will be managed for you with @rakered/mongo. The account system will connect to the MongoDB instance that's available under process.env.MONGO_URL, and will use the users collection to save and read the documents.

custom collection name

If you just wish to specify a different collection, provide the collection name during initialization.

import init from '@rakered/accounts';

const accounts = init({
  collection: 'accounts',
});

custom collection instance

If you need more control, it's also possible to pass in a MongoDB Collection instance to it.

import db from '@rakered/mongo';
import init from '@rakered/accounts';

const accounts = init({
  collection: db.users,
});

using native MongoDB driver

Or even using a native MongoDB Collection

import { MongoClient } from 'mongodb';
import init from '@rakered/accounts';

const client = await MongoClient.connect(process.env.MONGO_URL);
const db = await client.db();

const accounts = init({
  collection: db.collection('users'),
});

using Meteor.users collection

When migrating away from Meteor, the rawCollection can be used:

import init from '@rakered/accounts';

const accounts = init({
  collection: Meteor.users.rawCollection(),
});

Email

This library uses @rakered/email to send emails for email address verification and to send password reset tokens. The address for the smtp server is read from process.env.MAIL_URL, be sure to set it to any valid smtp url.

Provide the following options before you deploy to production. The values are used in the email template, and should be replaced with your own.

import init from '@rakered/accounts';

const accounts = init({
  email: {
    from: 'noreply@example.com',
    siteName: 'Example',
    siteUrl: 'https://example.com',
    logoUrl: 'https://example.com/logo.png',
  },
});

Token URLs

When using the email system, it's likely that you need to configure the token URLs. By default, the URLs are configured to use the format as below, but with https://example.com replaced by the siteUrl that you've configured under email. Meaning, the verify-email URL, would default to ${siteUrl}/verify-email/${token}. If this schema matches your needs, there is no need to customize. Otherwise, adjust them to accordingly.

import init from '@rakered/accounts';

const accounts = init({
  urls: {
    verifyEmail: (token) => `https://example.com/verify-email/${token}`,
    enrollAccount: (token) => `https://example.com/enroll-account/${token}`,
    resetPassword: (token) => `https://example.com/reset-password/${token}`,
  },
});

Methods

Methods. This library comes with various methods to manage the accounts.

createUser(options)

Create a new user.

options

Both username as well as email are optional, but one of those must be provided.

  • username String

    A unique name for this user. Usernames need to match the same requirements as GitHub usernames.

    • may only contain alphanumeric characters and hyphens.
    • cannot have multiple consecutive hyphens.
    • cannot begin or end with a hyphen.
    • has a minimum length of 3 characters
    • has a maximum length of 20 characters

    In addition to the GitHub requirements, there is also a list of reserved usernames, that cannot be claimed. Think .wellknown, admin, own, owner, you, yourself, and a bunch more.

    Lastly, usernames are not only guaranteed to be unique, but also not look-alike. To prevent confusion by end users, It's not possible to register both smeijer as well as s-meijer.

    Optional if email is provided

  • email String

    The user's email address.

    Optional if username is provided

  • password String | { digest: String, algorithm: 'sha-256' }

    Optional - The user's password. The password should not be send in plain text over the wire. Instead, use @rakered/hash to hash it with sha-256 before sending it to the server. When left out, this function won't be returning refresh tokens. We assume that no password, means that the user is being invited. Use sendEnrollmentEmail to let them set their initial password.

  • name String Optional - The full name of the user.

  • roles String[]

    Optional - A string array with roles that should be assigned. These roles will be included in the request token, enabling permissions checks without database requests.

setUsername(options)

Change a user's username. Use this instead of updating the database directly. The operation will fail if there is an existing user with a similar looking username, or if the username does not match the conditions as specified under createUser.

options

  • userId String

    The ID of the user to update

  • username String

    The new username for the user

addEmail(options)

Add an email address for a user. Use this instead of directly updating the database. The operation will fail if there is a different user with the same email address.

options

  • userId String

    The ID of the user to update.

  • email String

    The new email address for the user

  • verified Boolean

    Optional - whether the new email address should be marked as verified. Defaults to false.

removeEmail(options)

Remove an email address for a user. Use this instead of updating the database directly.

options

  • userId String

    The ID of the user to update.

  • email String

    The email address to remove.

verifyEmail(options)

Marks the user's email address as verified and returns an access token.

options

  • token String

    The token retrieved from the verification URL.

resetPassword(options)

Reset the password for a user using a token received in email.

options

  • token String

    The token retrieved from the reset password URL.

  • password String | { digest: String, algorithm: 'sha-256' }

    A new password for the user. The password should not be send in plain text over the wire. Instead, use @rakered/hash to hash it with sha-256 before sending it to the server.

login(options)

Obtain a refreshToken and accessToken by using password based authentication.

options

  • identity String

    The username or email from the user for which the token should be created.

  • password String | { digest: String, algorithm: 'sha-256' }

    The user's password. The password should not be send in plain text over the wire. Instead, use @rakered/hash to hash it with sha-256 before sending it to the server.

refreshToken(options)

Obtain a fresh refreshToken and accessToken. Note that the current refreshToken is revoked.

options

  • refreshToken String

    The current refresh token.

  • accessToken String

    The current access token.

revokeToken(options)

Revoke the refreshToken so it can no longer be used to obtain new accessTokens.

options

  • refreshToken String

    The token that should be revoked.

  • accessToken String

    The current access token.

sendEnrollmentEmail(options)

Send an email with a link the user can use to set their initial password.

options

  • userId String

    The id of the user to send email to.

  • email String

    Optional. Which address of the user's to send the email to. This address must be in the user's emails list. Defaults to the first email in the list.

sendVerificationEmail(options)

Send an email with a link the user can use verify their email address.

options

  • userId String

    The id of the user to send email to.

  • email String

    Optional. Which address of the user's to send the email to. This address must be in the user's emails list. Defaults to the first unverified email in the list.

sendResetPasswordEmail(options)

Send an email with a link the user can use to reset their password.

options

  • userId String

    The id of the user to send email to.

  • email String

    Optional. Which address of the user's to send the email to. This address must be in the user's emails list. Defaults to the first email in the list.

disconnect

Close the database connection. Useful when using accounts in console scripts.

Properties

collection

The MongoDB Collection is exposed under the accounts.collection property, for when you need direct access to the collection.

Good to knows

  • The email verification tokens are incompatible with Meteor, because I've taken a different approach there, to limit the database request to a maximum of 1 call per method. The way Meteor manages the verification tokens requires two requests per email verification attempt.

    I've weighted the pros and cons, and came to the conclusion that a single request outweighs the benefit of Meteor compatibility in this area. In the case where this project is connected to an existing Meteor database, it would be trivial to either migrate the tokens to the format of this project, or to let the user request a new verification token.

    To migrate existing tokens, check our migration script example

  • Usernames are more strict than in Meteor, because of this it can be that your existing user collection cannot be used as source for this library. Meteor usernames are case sensitive. Meaning, alex, Alex, and AleX are different users. We do not support this. We can write a detection script for this, but that's something we still have to do.