diff --git a/package.json b/package.json index 903b97c..defd378 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,13 @@ }, "dependencies": { "bcryptjs": "2.4.3", + "cookie-parser": "^1.4.4", "graphql-yoga": "1.17.4", "jsonwebtoken": "8.5.0" }, "devDependencies": { "@types/bcryptjs": "2.4.2", + "@types/cookie-parser": "^1.4.1", "@types/node": "10.12.27", "dotenv-cli": "1.4.0", "nodemon": "1.18.10", diff --git a/src/index.ts b/src/index.ts index 6f78f71..0e51606 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,6 +1,7 @@ -import { GraphQLServer } from 'graphql-yoga' +import { GraphQLServer, Options } from 'graphql-yoga' import { prisma } from './generated/prisma-client' import resolvers from './resolvers' +import * as cookieParser from 'cookie-parser' const server = new GraphQLServer({ typeDefs: './src/schema.graphql', @@ -11,7 +12,13 @@ const server = new GraphQLServer({ }), }) -const options = { +server.express.use(cookieParser()); + +const options: Options = { port: process.env.GRAPHQL_SERVER_PORT, + cors: { + credentials: true, + origin: process.env.CLIENT_HOST + } } server.start(options, ({ port }) => console.log(`Server is running on http://localhost:${port}`)) diff --git a/src/resolvers/Mutation/auth.ts b/src/resolvers/Mutation/auth.ts index 07f86f2..c46a802 100644 --- a/src/resolvers/Mutation/auth.ts +++ b/src/resolvers/Mutation/auth.ts @@ -1,14 +1,21 @@ import * as bcrypt from 'bcryptjs' import * as jwt from 'jsonwebtoken' import { Context } from '../../utils' +const ONE_YEAR = 1000 * 60 * 60 * 24 * 365 export const auth = { async signup(parent, args, ctx: Context) { const password = await bcrypt.hash(args.password, 10) const user = await ctx.prisma.createUser({ ...args, password }) + const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET) + + ctx.response.cookie('token', token, { + httpOnly: true, + maxAge: ONE_YEAR, + }); return { - token: jwt.sign({ userId: user.id }, process.env.APP_SECRET), + token, user, } }, @@ -24,9 +31,21 @@ export const auth = { throw new Error('Invalid password') } + const token = jwt.sign({ userId: user.id }, process.env.APP_SECRET) + + ctx.response.cookie('token', token, { + httpOnly: true, + maxAge: ONE_YEAR, + }); + return { - token: jwt.sign({ userId: user.id }, process.env.APP_SECRET), + token, user, } }, + + async logout(parent, args, ctx: Context) { + ctx.response.clearCookie('token') + return { message: 'logout' } + } } diff --git a/src/schema.graphql b/src/schema.graphql index 199f498..fe8afe8 100644 --- a/src/schema.graphql +++ b/src/schema.graphql @@ -1,3 +1,7 @@ +type SuccessMessage { + message: String +} + type Query { feed: [Post!]! drafts: [Post!]! @@ -8,6 +12,7 @@ type Query { type Mutation { signup(email: String!, password: String!, name: String!): AuthPayload! login(email: String!, password: String!): AuthPayload! + logout: SuccessMessage! createDraft(title: String!, content: String!): Post! publish(id: ID!): Post! deletePost(id: ID!): Post! diff --git a/src/utils.ts b/src/utils.ts index e7bb17c..aa15a65 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,9 +1,9 @@ import * as jwt from 'jsonwebtoken' import { Prisma } from './generated/prisma-client' +import { ContextParameters } from 'graphql-yoga/dist/types' -export interface Context { +export interface Context extends ContextParameters { prisma: Prisma - request: any } export function getUserId(ctx: Context) { diff --git a/yarn.lock b/yarn.lock index 5fda140..f9af837 100644 --- a/yarn.lock +++ b/yarn.lock @@ -46,6 +46,13 @@ dependencies: "@types/node" "*" +"@types/cookie-parser@^1.4.1": + version "1.4.1" + resolved "https://registry.yarnpkg.com/@types/cookie-parser/-/cookie-parser-1.4.1.tgz#e88a39c41960f31549b4c52dd8620aa4a2feb4bb" + integrity sha512-iJY6B3ZGufLiDf2OCAgiAAQuj1sMKC/wz/7XCEjZ+/MDuultfFJuSwrBKcLSmJ5iYApLzCCYBYJZs0Ws8GPmwA== + dependencies: + "@types/express" "*" + "@types/cors@^2.8.4": version "2.8.4" resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.4.tgz#50991a759a29c0b89492751008c6af7a7c8267b0" @@ -829,6 +836,14 @@ content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" +cookie-parser@^1.4.4: + version "1.4.4" + resolved "https://registry.yarnpkg.com/cookie-parser/-/cookie-parser-1.4.4.tgz#e6363de4ea98c3def9697b93421c09f30cf5d188" + integrity sha512-lo13tqF3JEtFO7FyA49CqbhaFkskRJ0u/UAiINgrIXeRCY41c88/zxtrECl8AKH3B0hj9q10+h3Kt8I7KlW4tw== + dependencies: + cookie "0.3.1" + cookie-signature "1.0.6" + cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"