1.0.0-alpha.1 • Published 3 years ago

@taylorgrinn/auth-mysql-redis v1.0.0-alpha.1

Weekly downloads
-
License
ISC
Repository
gitlab
Last release
3 years ago

Tay Auth MySQL Redis

This is an express authentication server meant to be used with Nginx's auth_request command.

Installation

npm i --save @taylorgrinn/auth-mysql-redis
yarn add @taylorgrinn/auth-mysql-redis

Usage

Use the default async function to create an authentication server.

const createServer = require('@taylorgrinn/auth-mysql-redis').default;

const PORT = 8080;

createServer({
  secret: 'sshhhh',
}).then((auth) =>
  auth.listen(PORT, () => console.log(`Auth server listening on port ${PORT}.`))
);

The secret option or AUTH_SECRET environment variable must be set.

By default, it will use an in-memory store and Users model which will be cleared every time the server restarts.

To connect to a Redis instance, either supply the option redis.url or the environment variable REDIS_URL.

To connect to a MySQL server, specify both the host and database using either mysql.host or MYSQL_HOST and either mysql.database or MYSQL_DB.

createServer({
  secret: 'sssshhh',
  redis: {
    url: 'redis://localhost:6379',
  },
  mysql: {
    host: 'localhost',
    database: 'tay_dev',
  },
});

The server will send a 401 Unauthorized status code for anyone not logged in and a 200 Success status code otherwise. To customize this behavior, pass in a verify RequestHandler. Within this handler, req.user will contain the user info.

createServer({
  secret: 'ssshhhh',
  verify(req, res) {
    console.log('Verifying request', req.originalUrl);
    if (req.user.email === 'taylor@mail.com') res.sendStatus(200);
    else res.sendStatus(403);
  },
});

Nginx setup

Create an internal location for verifying requests using the auth server.

location = /verify {
    internal;
    proxy_pass http://localhost:8080/verify$request_uri;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
}

Where localhost:8080 is the location of the authentication server accessible to Nginx.

In order to login or register an account, you'll need to expose the /auth REST API from the authentication server.

location /auth {
    proxy_pass http://localhost:8080/auth;
}

You can use auth_request /verify; to protect any location.

location / {
    auth_request /verify;
    index index.html;
    try_files $uri $uri.html $uri/ =404;
}

Finally you'll need to build a portal which will be served when a 401 status is received from the authentication server.

location = /portal.html {
    internal;
}

error_page 401 /portal.html;

portal.html must exist at the root of the website and should allow users to login or register an account.

You can see an example of all this together at demo/nginx.conf

Setup the external providers (Google, Github, Facebook)

In order to set up external providers, the authBaseUrl option or AUTH_BASE_URL environment variable must be set to the publicly available address of the auth REST API. In the Nginx config above, it is available at /auth, so the authBaseUrl will be https://taydev.org/auth.

Create API keys for each or any of the services:

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`

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

createServer({
  ...,
  authBaseUrl: 'https://taydev.org/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
  }
});

For the scope options, the email scope is already included in all authentication requests by default.

Auth Server 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.
verifyoptionalexpress.RequestHandlerThis handler will be called for each authentication request from the web server. It should call res.sendStatus(403) for forbidden requests or res.sendStatus(200) for an allowed request. If a request is not handled (ie next() was called), a 200 Success response will be sent.
sendCodeoptional(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
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
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

Redis Options

optionrequiredenv vartypedescription
redis.urlrequiredREDIS_URLstringThe Redis connection url to use.
redis.useroptionalREDIS_USERstringThe user to log in to the Redis instance as.
redis.passwordoptionalREDIS_PASSWORDstringPassword to use for connecting to the Redis instance.
redis.nameoptionalREDIS_NAMEstringA name to assign the Redis connection.
redis.databaseoptionalREDIS_DBnumberThe database to connect to 0-15
redis.ttloptionalREDIS_TTLnumberThe number of seconds a session should last (1 day by default).
redis.prefixoptionalREDIS_PREFIXstringA string to prepend each session in the Redis database (sess: by default).

Other options are documented in RedisClientOptions from the @redis/client package and RedisStoreOptions from the connect-redis package.

MySQL Options

When connecting to MySQL, this library creates a users table in the configured database. The host and database are required options.

optionrequiredenv vartypedescription
mysql.hostrequiredMYSQL_HOSTstringThe hostname to connect to.
mysql.portoptionalMYSQL_PORTnumberThe port the MySQL server is running on.
mysql.useroptionalMYSQL_USERstringThe username to use for the MySQL server.
mysql.passwordoptionalMYSQL_PASSstringThe password to use for the MySQL server.
mysql.databaserequiredMYSQL_DBstringThe database to create the 'users' table in.

Other options are specified in PoolOptions from the mysql2 package.

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 option or the CORS environment variable.

createServer({
  ...,
  cors: ['https://taydev.org', 'https://localhost:8081'],
});

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

The cors option or CORS environment variable 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 whitelist will be set to the comma-separated list value.

# .env file (or however you set env vars)
CORS=https://taydev.org,https://localhost:8081

/auth REST API

The auth server accepts application/json, multipart/form-data, and application/x-www-form-urlencoded bodies. The JSON format is shown below.

routemethodbodyreturndescription
/registerPOST{ email: string, password: string }UserRegister a new account.
/loginPOST{ email: string, password: string }UserLogin to an existing account.
/verifyGETUserVerify that you are logged in an access the current user.
/logoutPUT{ success: boolean }Logout of the current account.
/userDELETE{ success: boolean }Delete the current user account

Add a login link for each external provider configured (google, facebook, or github). The query paramter redirect is required and should be URI encoded.

<a href="/auth/google?redirect=https%3A%2F%2Ftaydev.org">Google Login</a>

In order to come back to the current page after authentication, use location.href for the redirect.

<script>
  function authFacebook() {
    const redirect = encodeURIComponent(location.href);
    location.href = '/auth/facebook?redirect=' + redirect;
  }
</script>
<button onclick="authFacebook()">Facebook Login</button>