Skip to content

Instantly share code, notes, and snippets.

@GeorgianStan
Last active August 5, 2019 09:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GeorgianStan/59be0b9a6100c74a7673f371cdf09699 to your computer and use it in GitHub Desktop.
Save GeorgianStan/59be0b9a6100c74a7673f371cdf09699 to your computer and use it in GitHub Desktop.
JWT Ex
const whoAmI = async () => {
/**
* * 1. Create the query
* * 2. Send the query and get the response as object
* * 3. If there was an error then throw it
* * 4. Otherwise return the result
*/
// * 1
const query = "{ whoAmI { id profilePicture email confirmed } }";
// * 2
const response: Response = await fetch(process.env.API_URL, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
query
})
});
const responseAsJSON: any = await response.json();
// * 3
if (responseAsJSON.errors) {
// TODO Notifiy developer; this err should never occur
throw new Error("Eroare identificare user");
}
// * 4
return responseAsJSON.data.whoAmI;
};
try {
// * 1
const user: IUserForMain = await whoAmI();
// * 2
if (!user) {
router.menu.setView("menu-unauthorised-user");
router.shortView.setView("authenticate");
// router.shortView.setView("login");
return;
}
// * 3
store.user = user;
// * 4
if (!user.confirmed) {
// TODO ShortView setView confirmAcc
}
// * 5
router.menu.setView("menu-authorised-user", user);
} catch (err) {
// * 2
router.menu.setView("menu-unauthorised-user");
router.shortView.setView("authenticate");
}
async function getUserFromCookie(cookieToken: string, prisma: any) {
/**
* * 0. If no cookie was provided then assume that the user is not logged
* * 1. Decode the cookie & get the userId
* * 2. Get the user from the database & return it
*/
//* 0
if (!cookieToken) return undefined;
//* 1
const decoded: any = jwt.verify(cookieToken, process.env.APP_SECRET),
{ userId } = decoded;
//* 2
const user = await prisma.user({ id: userId });
return user;
}
login: async (root: any, args: any, context: any) => {
/**
* * 1. Get the user from the DB using the email send in the request
* * 2. Check if there is any user with that email in the database
* * 3. Check if the password is correct
* * 4. Create a new token
* * 5. Set the token in the user agent as a cookie with httpOnly so it can be accessed only by the back-end code
* * 6. Return the token and the new user object
*/
// * 1
const { input }: { input: any } = args;
const user = await context.prisma.user({ email: input.email });
// * 2
if (!user) {
throw new Error("Adresa de email sau parola este incorecta");
}
// * 3
const valid = await bcrypt.compare(input.password, user.password);
if (!valid) {
throw new Error("Parola incorecta");
}
// * 4 & 5
const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET);
context.res.cookie("session", token, {
expires: new Date(new Date().setMonth(new Date().getMonth() + 1)),
httpOnly: true,
signed: true
});
// * 6
return {
token,
user
};
},
async function getUserFromCookie(cookieToken: string, prisma: any) {
/**
* * 0. If no cookie was provided then assume that the user is not logged
* * 1. Decode the cookie & get the userId
* * 2. Get the user from the database & return it
*/
//* 0
if (!cookieToken) return undefined;
//* 1
const decoded: any = jwt.verify(cookieToken, process.env.APP_SECRET),
{ userId } = decoded;
//* 2
const user = await prisma.user({ id: userId });
return user;
}
const apollo: any = new ApolloServer({
// introspection: false,
// playground: false,
schema,
// * send in the context the req, res and the prisma object so they can be accesed by all the resolvers
context: async ({ req, res }: { req: any; res: any }) => {
const user = await getUserFromCookie(req.signedCookies["session"], prisma);
// console.log(user);
// let user;
return {
req,
res,
prisma,
user
};
}
});
/**
* * This high order function is used to check if a user is authorized or not and what's it's role (role based auth)
* @param resolver the resolver to execute
*/
const securityLayer = (
role: string,
confirmedAccount: boolean,
userNameWasSet: boolean
) => (resolver: any) => {
return (root: any, args: any, context: any, info: any) => {
/**
* * 1. Get the user from the context (if any)
* * 2. Check if it's account must be confirmed & is confirmed
* * 3. Check if it's account must have a user name & it has
*/
// * 1
if (context.user && (!role || context.user.role === role)) {
// * 2
if (confirmedAccount) {
if (context.user.confirmed) {
// * 3
if (userNameWasSet) {
if (context.user.name) {
return resolver(root, args, context, info);
} else {
return new AuthenticationError(
"Nu ati setat numele de utilizator"
);
}
} else {
return resolver(root, args, context, info);
}
} else {
return new AuthenticationError("Contul nu este confirmat");
}
} else return resolver(root, args, context, info);
} else {
return new AuthenticationError("Nu esti autorizat");
}
};
};
const membersOnly = securityLayer("MEMBER", false, false);
const membersConfirmedOnly = securityLayer("MEMBER", true, false);
const membersConfirmedWithName = securityLayer("MEMBER", true, true);
const adminOnly = securityLayer("ADMIN", false, false);
signup: async (root: any, args: any, context: any) => {
/**
* * 1. Hash password
* * 2. Create user in the database with the supplied args and the hashed password
* * 3. Create a new token
* * 4. Create a new activationCode for this account
* * 5. Send confirmation email & check if it was send
* * 6. Set the token in the user agent as a cookie with httpOnly so it can be accessed only by the back-end code
* * 7. Return the token and the new user object
*/
const { input }: { input: any } = args;
const password = await bcrypt.hash(input.password, 10), // * 1
user = await context.prisma.createUser({ ...input, password }), // * 2
token = jwt.sign({ userId: user.id }, process.env.APP_SECRET); // * 3
// * 4
activationCodesStorage.userId = user.id;
// * 5
try {
await sendConfirmationEmail(
input.email,
activationCodesStorage[user.id].code
);
} catch (err) {
delete activationCodesStorage[user.id];
// throw new Error(
// "Email-ul de confirmare a contului nu a putut fi trimis"
// );
}
// * 6
context.res.cookie("session", token, {
expires: new Date(new Date().setMonth(new Date().getMonth() + 1)),
httpOnly: true,
signed: true
});
// * 7
return {
token,
user
};
},
updateUser: membersConfirmedOnly(
async (root: any, args: any, context: any) => {
/**
* * 1. Get the user from the context & and the userInput from args
* * 2. Verify user password
* * 3. If the user send a wrong password then reject the req
* * 4. Update the user data ! NOT THE PASSWORD TOO
* * 5. Return the new user
*/
// * 1
const { user } = context;
const { input }: { input: any } = args;
// * 2
const valid = await bcrypt.compare(input.password, user.password);
// * 3
if (!valid) {
throw new Error("Parola incorecta");
}
// * 4
delete input.password;
const updatedUser: any = await context.prisma.updateUser({
data: { ...input },
where: { id: user.id }
});
// * 5
return updatedUser;
}
),
whoAmI: (root: any, args: any, context: any) => {
/**
* * 1. Get the user from the context
* * 2. Return the user
*/
// * 1
const { user }: { user: any } = context;
// * 2
return user;
},
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment