From 233aae8662f8dd0cd30a3855ff950831c35a7130 Mon Sep 17 00:00:00 2001 From: "Zed A. Shaw" Date: Fri, 23 Dec 2022 12:42:32 +0700 Subject: [PATCH] Documented the auth system. --- lib/auth.js | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/lib/auth.js b/lib/auth.js index 47b100b..f42df56 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -1,3 +1,16 @@ +/* + Implements everything needed for a [Passport.js](https://www.passportjs.org) middleware. + It supports either a Redis store or a MemoryStore. The MemoryStore can be used in development + if you can't or won't run Redis while you work. Ultimately you should be using the Redis + store even in development as it keeps you logged in while you work which is easier. + + The type of store is configured in the `secrets/config.json` file (which is loaded into `lib/config.js`) using the `session_store` option. See `configure_sessions` for more information on this + configuration option. + + The most important part of this module is the `socket` function, which adds `Passport.js` + authentication to `socket.io` which is something missing from `Passport.js`. Look at that + if you wondered how to do that. + */ import cookie from 'cookie'; import { Strategy } from 'passport-local'; import passport from 'passport'; @@ -43,6 +56,17 @@ passport.deserializeUser(async (id, cb) => { } }); +/* + Configures `Passport.js` to use either `RedisStore` or `MemoryStore`. If you want to + add a different store then modify this function. If the `config.json:session_store` is + `"redis"` you'll get the `RedisStore`. If it's set to `"memory"` then it will use the + `MemoryStore`. + + ___WARNING___: You don't really call this and instead use the `sessionStore` variable in + this module. + + + ___return___ (RedisStore|MemoryStore|undefined) -- The store to use or `undefined` on error. + */ export const configure_sessions = () => { if(session_store === "redis") { log.info("Using redis to store sessions."); @@ -56,12 +80,46 @@ export const configure_sessions = () => { } } +/* + * The configured session store being used. Just use this instead of + * calling `configure_sessions` yourself. + */ export const sessionStore = configure_sessions(); +/* + The cookie secret configured in `secrets/config.json:auth`. + + ___FOOTGUN___: Don't expose this to the internet! + */ export const cookie_secret = auth.cookie_secret; + +/* + The cookie _domain_ configured in `secrets/config.json:auth`. If this isn't + exactly the same as your domain then you'll not be able to log in. You can + run: + + ```shell + node bando.js api --cookies-suck + ``` + + This will output cookie debugging information so you can figure out what's going on. + Incidentally, debugging the stupid cookies ends up a huge chunk of the code in `commands/api.js` + because, you guessed it, `--cookies-suck`. + */ export const cookie_domain = auth.cookie_domain; assert(cookie_domain !== undefined, "secrets/config.json:auth.cookie_domain isn't set."); +/* + This does the actual `Passport.js` login, but you wouldn't necessarily use this + directly unless you are making something custom. Instead, you "tag" handlers + in `api/` handlers with `.login = true` and then the `commands/api.js` will + automatically run this before running your handler. Look in `api/login.js` to see + how to use `post.login = true` to actually use this. + + If you want to see how this is used, look for `auth.login` in `commands/api.js`. + + + ___return__ Function -- Returns a Express.js handler that does the `passport.authenticate`. + */ export const login = () => { return (req, res, next) => { passport.authenticate('local', (err, user, info) => { @@ -89,6 +147,13 @@ export const login = () => { } } +/* + An Express.js handler that make authentication required. Just like with `login` it + is normally used by adding the `.authenticated = true` tag to your handlers. If you + do `get.authenticated = true` then that `get` will require a login before it runs. + The `commands/api.js` command is the one that uses this `require` function to add that + feature to your handlers in `api/`. + */ export const required = () => { return (req, res, next) => { const user = req.user; @@ -101,7 +166,17 @@ export const required = () => { } } - +/* + * This is added to any `socket/` handlers tagged with `.authenticated`. It does + * the same thing as `required` above but instead does all the fairly stupid things + * you need to authenticate a `socket.io` connection. + * + * It's main purpose is to authenticate, but _also_ to connect the `req.user` to the + * socket so you can access the user in your `socket/` handlers. + * + * + `socket Object` -- A socket.io socket to authenticate. + * + ___return___ `boolean` -- A `Promise` that performs the authentication then adds the `req.user` to the socket. + */ export const socket = async (socket) => { try { if(!socket.handshake.headers.cookie) { @@ -157,7 +232,9 @@ export const socket = async (socket) => { } } - +/* + Initializes your Express.js application with the `Passport.js` required handlers. + */ export const init = (app) => { app.use(passport.initialize()); app.use(passport.session());