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
option | required | env var | type | description |
---|---|---|---|---|
secret | required | AUTH_SECRET | string | string[] | Provide a secret key or list of keys to use for creating sessions. |
verify | optional | express.RequestHandler | This 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. |
|
sendCode | optional | (emailAddress: string, code: string) => Promise<any> | 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 | |
delay | optional | AUTH_DELAY | number | Milliseconds to delay each request to the auth server. |
cors | optional | CORS | boolean (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 |
httpOnly | optional | HTTP_ONLY | boolean | Whether the cookie willl have the httpOnly flag. True, by default. |
authBaseUrl | required for external | AUTH_BASE_URL | string | The 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.clientID | required for google | GOOGLE_CLIENT_ID | string | Client ID given by Google. |
google.clientSecret | required for google | GOOGLE_CLIENT_SECRET | string | Client secret given by Google. |
google.scope | optional | GOOGLE_SCOPE | string[] | 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.clientID | required for github | GITHUB_CLIENT_ID | string | Client ID given by Github. |
github.clientSecret | required for github | GITHUB_CLIENT_SECRET | string | Client secret given by Github. |
github.scope | optional | GITHUB_SCOPE | string[] | 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.clientID | required for facebook | FACEBOOK_CLIENT_ID | string | Client ID given by Facebook. |
facebook.clientSecret | required for facebook | FACEBOOK_CLIENT_SECRET | string | Client secret given by Facebook. |
facebook.scope | optional | FACEBOOK_SCOPE | string[] | 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.profileFields | optional | FACEBOOK_PROFILE_FIELDS | string[] | Profile fields to include when authenticating. The 'email' field is added to all requests in addition to any you specify here |
Redis Options
option | required | env var | type | description |
---|---|---|---|---|
redis.url | required | REDIS_URL | string | The Redis connection url to use. |
redis.user | optional | REDIS_USER | string | The user to log in to the Redis instance as. |
redis.password | optional | REDIS_PASSWORD | string | Password to use for connecting to the Redis instance. |
redis.name | optional | REDIS_NAME | string | A name to assign the Redis connection. |
redis.database | optional | REDIS_DB | number | The database to connect to [0-15] |
redis.ttl | optional | REDIS_TTL | number | The number of seconds a session should last (1 day by default). |
redis.prefix | optional | REDIS_PREFIX | string | A 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.
option | required | env var | type | description |
---|---|---|---|---|
mysql.host | required | MYSQL_HOST | string | The hostname to connect to. |
mysql.port | optional | MYSQL_PORT | number | The port the MySQL server is running on. |
mysql.user | optional | MYSQL_USER | string | The username to use for the MySQL server. |
mysql.password | optional | MYSQL_PASS | string | The password to use for the MySQL server. |
mysql.database | required | MYSQL_DB | string | The 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.
route | method | body | return | description |
---|---|---|---|---|
/register | POST | { email: string, password: string } | User | Register a new account. |
/login | POST | { email: string, password: string } | User | Login to an existing account. |
/verify | GET | User | Verify that you are logged in an access the current user. | |
/logout | PUT | { success: boolean } | Logout of the current account. | |
/user | DELETE | { 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>