3.1.0-alpha.0 • Published 2 years ago

@taylorgrinn/auth-server v3.1.0-alpha.0

Weekly downloads
-
License
ISC
Repository
github
Last release
2 years ago

Tay Auth Server Middleware

This is an express middleware and router to be used with the @taylorgrinn/auth react component.

Installation

npm i --save @taylorgrinn/auth-server
yarn add @taylorgrinn/auth-server

Add the middleware and router to your app

import bodyParser from 'body-parser';
import express from 'express';
import useAuth from '@taylorgrinn/auth-server';

const [authMiddleware, authRouter] = useAuth({
  // Provide a secret key or list of keys to use for creating sessions.
  secret: 'ssshhh',
  // In order to implement the reset password form, use this option to send
  // an email using nodemailer or another email solution. Must return a promise
  async sendCode(email, code) {
    console.log(`Emailing code: ${code} to ${email}`);
    /* Implementation not shown */
  },
  delay: 1000, // Add an optional delay to all authentication requests. Useful for preventing brute force attacks from a single IP or just letting users appreciate your loading screens.
});

const app = express();
export default app;

app.use(
  bodyParser.json(), // JSON necessary to parse requests from the @tygr/auth react component
  authMiddleware
);

app.use('/auth', authRouter);

This is all the setup you need for a minimal working example with local login, register, and reset password functionality.

Setup the external providers (Google, Github, Facebook)

Create API keys for each or any of the services:

Add the keys, as well as the external location of your authentication server, to the useAuth options:

const [authMiddleware, authRouter] = useAuth({
  ...,
  authBaseUrl: 'https://tygr.info/api/auth', // Used for callbacks after authentication
  google: {
    clientId: 'blahblahblah',
    clientSecret: 'blahblahblah',
    scope: [], // Optional: scopes allow you to get more user information
  },
  github: {
    clientId: 'blahblahblah',
    clientSecret: 'blahblahblah',
    scope: [], // Optional: scopes allow you to get more user information
  },
  facebook: {
    clientId: 'blahblahblah',
    clientSecret: 'blahblahblah',
    scope: [], // Optional: scopes allow you to get more user information
    profileFields: [] // Optional: profile fields need to be specified for user object
  }
});

...

/**
 * Make sure to include the pathname you decide to use here ('/auth')
 * in the `authBaseUrl` option above.
 */
app.use('/auth', authRouter)

For the scope options, the email scope is already included in all authentication requests by default for both github and google.

When creating the keys for each provider, you'll also have to specify the callback urls in this format:

  • Google: `${authBaseUrl}/google/callback`
  • Github: `${authBaseUrl}/github/callback`
  • Facebook: `${authBaseUrl}/facebook/callback`

Setup better storage options

By default, the sessions and user data are stored in-memory. This is volatile and not recommended for production use. You may pass in a store for the sessions and a Users model for storing users.

The store interface is described here in the express-session library.

The Users interface defines what will probably be static methods on your Users' model class. I designed it specifically to work with sequelize right out of the gate, but it can be adapted for any storage mechanism you'd like. There are five methods to implement and four required fields on the User object.


BaseUser (You may add any other properties you'd like) | property | type | description | | -------- | --------------------- | ---------------------------------------------------------------------------- | | id | string | Unique identifier generated by uuid. This should be the 'primary key' | | email | string | Unique email address for each user. | | password | string (or undefined) | Hashed password for the user if they signed in locally | | provider | string | The authentication method (either 'local', 'google', or 'github') |


BaseUsers | method | param | returns | description | | -------------- | --------- | --------------------- | ----------------------------------------------------------------------------------------------------------- | | create | User | Promise\ | Create a new user. No need to check if the user already exists. For sequelize, no setup is necessary as the base model includes this method with the correct signature, given that the model extends the BaseUser interface correctly | | findByEmail | string | Promise\<User | null> | Find a user account by an email address | | findAndUpdate | User | Promise\ | Find a user using the id, which will not change, and update the rest of the user properties | | findAndDestroy | User | Promise\ | Find a user using either the id or email then delete them | | sanitize | User | Partial\ | Synchronously create a user object that is safe to be sent back to the user. The password hash should be removed at least |

All Options

Order of precedence: env var > option > default\ Array environment variables are comma separated: MY_VAR=thing1,thing2

optionrequiredenv vartypedescription
secretrequiredAUTH_SECRETstring | string[]Provide a secret key or list of keys to use for creating sessions
sendCoderequired(emailAddress: string, code: string) => Promise\Send a reset code to a specified email address. The user can then copy this code into the reset password form to recover access to their account
storerecommendedSee hereProvide a store to use for session data. Defaults to an in memory store which is not recommended for a production environment
UsersrecommendedSee aboveProvide a Users model to store user data. Defaults to an in memory Users model which is not recommended for a production environment
delayoptionalAUTH_DELAYnumberMilliseconds to delay each request to the auth server.
corsoptionalCORSboolean (string[] if using env var)Set the cookie properties: sameSite: true, secure: true. This will allow cookies to be loaded from any domain and requires the authentication server to be served over https. See the demo folder for an example of serving https://localhost using custom certificates. See below for more info
httpOnlyoptionalHTTP_ONLYbooleanWhether the cookie willl have the httpOnly flag. True, by default.
authBaseUrlrequired for externalAUTH_BASE_URLstringThe external address of your authentication server. This option is required for an identity provider like google or github to redirect users back after signing in but not for local sign in, register or reset password. Can be a full URL or an absolute path, in which case it will use the request origin.
google.clientIDrequired for googleGOOGLE_CLIENT_IDstringClient ID given by Google.
google.clientSecretrequired for googleGOOGLE_CLIENT_SECRETstringClient secret given by Google.
google.scopeoptionalGOOGLE_SCOPEstring[]Scope of information or abilities to be requested by you to the user for their Google account. The 'email' scope is added to all requests in addition to any you specify here
github.clientIDrequired for githubGITHUB_CLIENT_IDstringClient ID given by Github.
github.clientSecretrequired for githubGITHUB_CLIENT_SECRETstringClient secret given by Github.
github.scopeoptionalGITHUB_SCOPEstring[]Scope of information or abilities to be requested by you to the user for their Github account. The 'user:email' scope is added to all requests in addition to any you specify here
facebook.clientIDrequired for facebookFACEBOOK_CLIENT_IDstringClient ID given by Facebook.
facebook.clientSecretrequired for facebookFACEBOOK_CLIENT_SECRETstringClient secret given by Facebook.
facebook.scopeoptionalFACEBOOK_SCOPEstring[]Scope of information or abilities to be requested by you to the user for their Facebook account. The 'email' scope is added to all requests in addition to any you specify here
facebook.profileFieldsoptionalFACEBOOK_PROFILE_FIELDSstring[]Profile fields to include when authenticating. The 'email' field is added to all requests in addition to any you specify here

Setup cors for cross-domain authentication

If your authentication server is on a different domain than your client, or you have multiple clients logging in to the same authentication server, you can use the cors middleware and the cors option:

import useAuth, { cors } from '@taylorgrinn/auth-server';


const [authMiddleware, authRouter] = useAuth({
  ...,
  cors: true,
});

app.use(cors('https://taydev.org', 'http://localhost:8081'));

The cors middleware takes in any number of whitelisted domains and adds the relevant headers to all requests from each of them.

The cors option changes the way cookies are set. Specifically, it makes them available on any domain and makes them secure, only available when the authentication server is served over https. Check out the demo folder to see an example of serving https://localhost with a self-signed certificate.

If the CORS environment variable is set, the cors options will be set to true and the whitelist will be set to the comma-separated list value of the CORS environment variable:

# .env file (or however you set env vars)
CORS=https://taydev.org,http://localhost:8081
/**
 * You still have to use the cors middleware but the whitelist
 * will be supplied by the environment variable
 */
app.use(cors());