feat: returns queried user alongside refreshed token (#2813)

Co-authored-by: Dan Ribbens <dan.ribbens@gmail.com>
This commit is contained in:
Jacob Fletcher
2023-07-17 09:35:34 -04:00
committed by GitHub
parent 7927dd485a
commit 2fc03f196e
16 changed files with 350 additions and 112 deletions

View File

@@ -0,0 +1,39 @@
import { User } from '..';
import { CollectionConfig } from '../../collections/config/types';
import { Field, fieldAffectsData, fieldHasSubFields } from '../../fields/config/types';
export const getFieldsToSign = (args: {
collectionConfig: CollectionConfig,
user: User
email: string
}): Record<string, unknown> => {
const {
collectionConfig,
user,
email,
} = args;
return collectionConfig.fields.reduce((signedFields, field: Field) => {
const result = {
...signedFields,
};
if (!fieldAffectsData(field) && fieldHasSubFields(field)) {
field.fields.forEach((subField) => {
if (fieldAffectsData(subField) && subField.saveToJWT) {
result[subField.name] = user[subField.name];
}
});
}
if (fieldAffectsData(field) && field.saveToJWT) {
result[field.name] = user[field.name];
}
return result;
}, {
email,
id: user.id,
collection: collectionConfig.slug,
});
};

View File

@@ -6,13 +6,13 @@ import { PayloadRequest } from '../../express/types';
import getCookieExpiration from '../../utilities/getCookieExpiration';
import isLocked from '../isLocked';
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields';
import { Field, fieldHasSubFields, fieldAffectsData } from '../../fields/config/types';
import { User } from '../types';
import { Collection } from '../../collections/config/types';
import { afterRead } from '../../fields/hooks/afterRead';
import unlock from './unlock';
import { incrementLoginAttempts } from '../strategies/local/incrementLoginAttempts';
import { authenticateLocalStrategy } from '../strategies/local/authenticate';
import { getFieldsToSign } from './getFieldsToSign';
export type Result = {
user?: User,
@@ -121,28 +121,10 @@ async function login<TSlug extends keyof GeneratedTypes['collections']>(
});
}
const fieldsToSign = collectionConfig.fields.reduce((signedFields, field: Field) => {
const result = {
...signedFields,
};
if (!fieldAffectsData(field) && fieldHasSubFields(field)) {
field.fields.forEach((subField) => {
if (fieldAffectsData(subField) && subField.saveToJWT) {
result[subField.name] = user[subField.name];
}
});
}
if (fieldAffectsData(field) && field.saveToJWT) {
result[field.name] = user[field.name];
}
return result;
}, {
const fieldsToSign = getFieldsToSign({
collectionConfig,
user,
email,
id: user.id,
collection: collectionConfig.slug,
});
await collectionConfig.hooks.beforeLogin.reduce(async (priorHook, hook) => {

View File

@@ -1,10 +1,12 @@
import jwt from 'jsonwebtoken';
import { Response } from 'express';
import url from 'url';
import { Collection, BeforeOperationHook } from '../../collections/config/types';
import { Forbidden } from '../../errors';
import getCookieExpiration from '../../utilities/getCookieExpiration';
import { Document } from '../../types';
import { PayloadRequest } from '../../express/types';
import { getFieldsToSign } from './getFieldsToSign';
export type Result = {
exp: number,
@@ -51,16 +53,32 @@ async function refresh(incomingArgs: Arguments): Promise<Result> {
},
} = args;
const opts = {
expiresIn: args.collection.config.auth.tokenExpiration,
};
if (typeof args.token !== 'string') throw new Forbidden(args.req.t);
const payload = jwt.verify(args.token, secret, {}) as Record<string, unknown>;
delete payload.iat;
delete payload.exp;
const refreshedToken = jwt.sign(payload, secret, opts);
const parsedURL = url.parse(args.req.url);
const isGraphQL = parsedURL.pathname === config.routes.graphQL;
const user = await args.req.payload.findByID({
id: args.req.user.id,
collection: args.req.user.collection,
req: args.req,
depth: isGraphQL ? 0 : args.collection.config.auth.depth,
});
const fieldsToSign = getFieldsToSign({
collectionConfig,
user: args?.req?.user,
email: user?.email as string,
});
const refreshedToken = jwt.sign(
fieldsToSign,
secret,
{
expiresIn: collectionConfig.auth.tokenExpiration,
},
);
const exp = (jwt.decode(refreshedToken) as Record<string, unknown>).exp as number;
if (args.res) {
@@ -78,6 +96,12 @@ async function refresh(incomingArgs: Arguments): Promise<Result> {
args.res.cookie(`${config.cookiePrefix}-token`, refreshedToken, cookieOptions);
}
let response: Result = {
user,
refreshedToken,
exp,
};
// /////////////////////////////////////
// After Refresh - Collection
// /////////////////////////////////////
@@ -85,23 +109,19 @@ async function refresh(incomingArgs: Arguments): Promise<Result> {
await collectionConfig.hooks.afterRefresh.reduce(async (priorHook, hook) => {
await priorHook;
args = (await hook({
response = (await hook({
req: args.req,
res: args.res,
exp,
token: refreshedToken,
})) || args;
})) || response;
}, Promise.resolve());
// /////////////////////////////////////
// Return results
// /////////////////////////////////////
return {
refreshedToken,
exp,
user: payload,
};
return response;
}
export default refresh;