Skip to content

Commit

Permalink
fix(users): fixed token & cookies invalidate function when user logs …
Browse files Browse the repository at this point in the history
…out. (#3071)
  • Loading branch information
skylinetenger committed Oct 27, 2021
1 parent db93bcc commit 4dad5ff
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 3 deletions.
30 changes: 30 additions & 0 deletions api/src/__tests__/userDb.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -709,4 +709,34 @@ describe('User db utils', () => {
);
}
});

test('Logout user & check token exist', async () => {
expect.assertions(6);

// invalid token ==============
const loggedout = await Users.logout(_user, '');
expect(loggedout).toBe('token not found');


// valid login
const response = await Users.login({
email: _user.email.toUpperCase(),
password: 'pass'
});

expect(response.token).toBeDefined();
expect(response.refreshToken).toBeDefined();

// wrong token
const result = await Users.logout(_user, response.refreshToken);
expect(result).toBe('token not found');

// valid Logout
const message = await Users.logout(_user, response.token);
expect(message).toBe('loggedout');

// check token exist.
const userFull = await Users.getUser(_user._id);
expect(userFull.validatedTokens).not.toContain(response.token);
});
});
2 changes: 1 addition & 1 deletion api/src/__tests__/userMutations.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ describe('User mutations', () => {

const response = await graphqlRequest(mutation, 'logout', {}, { res });

expect(response).toBe('loggedout');
expect(response).toBe('token not found');
});

test('Reset member password', async () => {
Expand Down
5 changes: 3 additions & 2 deletions api/src/data/resolvers/mutations/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,10 @@ const userMutations = {
return login(args, res, requestInfo.secure);
},

async logout(_root, _args, { res }) {
async logout(_root, _args, { res, user, requestInfo }: IContext ) {
const loggedout = await Users.logout(user, requestInfo.cookies['auth-token']);
res.clearCookie('auth-token');
return 'loggedout';
return loggedout;
},

/*
Expand Down
2 changes: 2 additions & 0 deletions api/src/db/factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ interface IUserFactoryInput {
registrationTokenExpires?: Date;
isSubscribed?: string;
isShowNotification?: boolean;
validatedTokens?: string;
}

export const userFactory = async (
Expand Down Expand Up @@ -212,6 +213,7 @@ export const userFactory = async (
deviceTokens: params.deviceTokens,
isSubscribed: params.isSubscribed,
isShowNotification: params.isShowNotification,
validatedTokens: params.validatedTokens,
...(params.code ? { code: params.code } : {})
};

Expand Down
25 changes: 25 additions & 0 deletions api/src/db/models/Users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export interface IUserModel extends Model<IUserDocument> {
): { token: string; refreshToken: string; user: IUserDocument };
login(params: ILoginParams): { token: string; refreshToken: string };
getTokenFields(user: IUserDocument);
logout(_user: IUserDocument, token: string): string;
}

export const loadClass = () => {
Expand Down Expand Up @@ -664,6 +665,13 @@ export const loadClass = () => {
this.getSecret()
);

// storing tokens in user collection.
if(token){
const validatedTokens: string[] = user.validatedTokens || [];
validatedTokens.push(token);
await user.update({ $set: { validatedTokens } });
}

if (deviceToken) {
const deviceTokens: string[] = user.deviceTokens || [];

Expand All @@ -683,6 +691,23 @@ export const loadClass = () => {
};
}

/**
* Logging out user from database
*/
public static async logout(user: IUserDocument, currentToken: string) {
const currentUser:any = await this.getUser(user._id);
let validatedTokens: string[] = currentUser.validatedTokens || [];

if(validatedTokens.includes(currentToken)){
// invalidating token.
validatedTokens = await validatedTokens.filter(token => token !== currentToken)
await Users.updateOne({ _id: currentUser._id }, { $set: { validatedTokens } });
return 'loggedout';
}

return 'token not found';
}

public static async generateUserCodeField() {
const users = await Users.find({ code: { $exists: false } });

Expand Down
6 changes: 6 additions & 0 deletions api/src/db/models/definitions/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export interface IUser {
isShowNotification?: boolean;
score?: number;
customFieldsData?: ICustomField[];
validatedTokens?: string[];
}

export interface IUserDocument extends IUser, Document {
Expand Down Expand Up @@ -137,6 +138,11 @@ export const userSchema = schemaHooksWrapper(
default: [],
label: 'Device tokens'
}),
validatedTokens: field({
type: [String],
default: [],
label: 'Validated access tokens'
}),
code: field({ type: String }),
doNotDisturb: field({
type: String,
Expand Down
6 changes: 6 additions & 0 deletions api/src/middlewares/userMiddleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,12 @@ const userMiddleware = async (req, _res, next) => {
// verify user token and retrieve stored user information
const { user } = jwt.verify(token, Users.getSecret());

// invalid token access.
const currentUser = await Users.getUser(user._id);
if(!currentUser.validatedTokens?.includes(token)){
return next();
}

// save user in request
req.user = user;
req.user.loginToken = token;
Expand Down

0 comments on commit 4dad5ff

Please sign in to comment.