Last active
August 5, 2019 09:04
-
-
Save GeorgianStan/59be0b9a6100c74a7673f371cdf09699 to your computer and use it in GitHub Desktop.
JWT Ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
}; | |
}, |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* * 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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
}; | |
}, |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
), |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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