mirror of
https://github.com/wassname/talk.git
synced 2026-07-01 19:15:29 +08:00
Merge pull request #647 from coralproject/auth-token-hooks
Added new tokenUserNotFound plugin hook
This commit is contained in:
+35
@@ -280,6 +280,41 @@ send data to the client. If the type in question contains args, clients may subs
|
||||
|
||||
For more information, see the [Apollo Docs](https://github.com/apollographql/graphql-subscriptions).
|
||||
|
||||
#### Field: `tokenUserNotFound`
|
||||
|
||||
```js
|
||||
tokenUserNotFound: async ({jwt, token}) => {
|
||||
let profile = await someExternalService(token);
|
||||
if (!profile) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let user = await UserModel.findOneAndUpdate({
|
||||
id: profile.id
|
||||
}, {
|
||||
id: profile.id,
|
||||
username: profile.username,
|
||||
lowercaseUsername: profile.username.toLowerCase(),
|
||||
roles: [],
|
||||
profiles: []
|
||||
}, {
|
||||
setDefaultsOnInsert: true,
|
||||
new: true,
|
||||
upsert: true
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
```
|
||||
|
||||
The `tokenUserNotFound` hook allows auth integrations to hook into the event
|
||||
when a valid token is provided but a user can't be found in the database that
|
||||
matches the provided id.
|
||||
|
||||
The function is async, and should return the user object that was created in the
|
||||
database, or null if the user wasn't found. The `jwt` paramenter of the object
|
||||
is the unpacked token, while `token` is the original jwt token string.
|
||||
|
||||
#### Field: `router`
|
||||
|
||||
```js
|
||||
|
||||
+19
-3
@@ -172,6 +172,7 @@ const CheckBlacklisted = (jwt) => new Promise((resolve, reject) => {
|
||||
});
|
||||
});
|
||||
|
||||
const jwt = require('jsonwebtoken');
|
||||
const JwtStrategy = require('passport-jwt').Strategy;
|
||||
const ExtractJwt = require('passport-jwt').ExtractJwt;
|
||||
|
||||
@@ -185,6 +186,19 @@ let cookieExtractor = function(req) {
|
||||
return token;
|
||||
};
|
||||
|
||||
// Override the JwtVerifier method on the JwtStrategy so we can pack the
|
||||
// original token into the payload.
|
||||
JwtStrategy.JwtVerifier = (token, secretOrKey, options, callback) => {
|
||||
return jwt.verify(token, secretOrKey, options, (err, jwt) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// Attach the original token onto the payload.
|
||||
return callback(false, {token, jwt});
|
||||
});
|
||||
};
|
||||
|
||||
// Extract the JWT from the 'Authorization' header with the 'Bearer' scheme.
|
||||
passport.use(new JwtStrategy({
|
||||
|
||||
@@ -207,10 +221,10 @@ passport.use(new JwtStrategy({
|
||||
// Enable only the HS256 algorithm.
|
||||
algorithms: ['HS256'],
|
||||
|
||||
// Pass the request objecto back to the callback so we can attach the JWT to
|
||||
// Pass the request object back to the callback so we can attach the JWT to
|
||||
// it.
|
||||
passReqToCallback: true
|
||||
}, async (req, jwt, done) => {
|
||||
}, async (req, {token, jwt}, done) => {
|
||||
|
||||
// Load the user from the environment, because we just got a user from the
|
||||
// header.
|
||||
@@ -219,7 +233,9 @@ passport.use(new JwtStrategy({
|
||||
// Check to see if the token has been revoked
|
||||
await CheckBlacklisted(jwt);
|
||||
|
||||
let user = await UsersService.findById(jwt.sub);
|
||||
// Try to get the user from the database or crack it from the token and
|
||||
// plugin integrations.
|
||||
let user = await UsersService.findOrCreateByIDToken(jwt.sub, {token, jwt});
|
||||
|
||||
// Attach the JWT to the request.
|
||||
req.jwt = jwt;
|
||||
|
||||
@@ -9,6 +9,7 @@ const {
|
||||
JWT_SECRET,
|
||||
ROOT_URL
|
||||
} = require('../config');
|
||||
const debug = require('debug')('talk:services:users');
|
||||
|
||||
const redis = require('./redis');
|
||||
const redisClient = redis.createClient();
|
||||
@@ -526,6 +527,29 @@ module.exports = class UsersService {
|
||||
return UserModel.findOne({id});
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} id the id of the current user
|
||||
* @param {Object} token a jwt token used to sign in the user
|
||||
*/
|
||||
static async findOrCreateByIDToken(id, token) {
|
||||
|
||||
// Try to get the user.
|
||||
let user = await UserModel.findOne({
|
||||
id
|
||||
});
|
||||
|
||||
// If the user was not found, try to look it up.
|
||||
if (user === null) {
|
||||
|
||||
// If the user wasn't found, it will return null and the variable will be
|
||||
// unchanged.
|
||||
user = await lookupUserNotFound(token);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds users in an array of ids.
|
||||
* @param {Array} ids array of user identifiers (uuid)
|
||||
@@ -903,3 +927,25 @@ module.exports = class UsersService {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Extract all the tokenUserNotFound plugins so we can integrate with other
|
||||
// providers.
|
||||
const tokenUserNotFoundHooks = require('./plugins')
|
||||
.get('server', 'tokenUserNotFound')
|
||||
.map(({plugin, tokenUserNotFound}) => {
|
||||
debug(`added plugin '${plugin.name}' to tokenUserNotFound hooks`);
|
||||
|
||||
return tokenUserNotFound;
|
||||
});
|
||||
|
||||
// Provide a function that
|
||||
const lookupUserNotFound = async (token) => {
|
||||
for (let hook of tokenUserNotFoundHooks) {
|
||||
let user = await hook(token);
|
||||
if (user !== null && typeof user !== 'undefined') {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user