|
|
@ -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 cookie from 'cookie'; |
|
|
|
import { Strategy } from 'passport-local'; |
|
|
|
import { Strategy } from 'passport-local'; |
|
|
|
import passport from 'passport'; |
|
|
|
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 = () => { |
|
|
|
export const configure_sessions = () => { |
|
|
|
if(session_store === "redis") { |
|
|
|
if(session_store === "redis") { |
|
|
|
log.info("Using redis to store sessions."); |
|
|
|
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(); |
|
|
|
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; |
|
|
|
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; |
|
|
|
export const cookie_domain = auth.cookie_domain; |
|
|
|
assert(cookie_domain !== undefined, "secrets/config.json:auth.cookie_domain isn't set."); |
|
|
|
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 = () => { |
|
|
|
export const login = () => { |
|
|
|
return (req, res, next) => { |
|
|
|
return (req, res, next) => { |
|
|
|
passport.authenticate('local', (err, user, info) => { |
|
|
|
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 = () => { |
|
|
|
export const required = () => { |
|
|
|
return (req, res, next) => { |
|
|
|
return (req, res, next) => { |
|
|
|
const user = req.user; |
|
|
|
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) => { |
|
|
|
export const socket = async (socket) => { |
|
|
|
try { |
|
|
|
try { |
|
|
|
if(!socket.handshake.headers.cookie) { |
|
|
|
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) => { |
|
|
|
export const init = (app) => { |
|
|
|
app.use(passport.initialize()); |
|
|
|
app.use(passport.initialize()); |
|
|
|
app.use(passport.session()); |
|
|
|
app.use(passport.session()); |
|
|
|