Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
"express": "^4.17.1",
"graphql": "^14.5.6",
"jsonwebtoken": "^8.5.1",
"pg": "^7.3.0",
"pg": "^8.7.1",
"reflect-metadata": "^0.1.10",
"type-graphql": "^0.17.5",
"typeorm": "0.2.19"
Expand Down
3 changes: 2 additions & 1 deletion server/src/MyContext.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Request, Response } from "express";
import { User } from "./entity/User";

export interface MyContext {
req: Request;
res: Response;
payload?: { userId: string };
payload?: User;
}
50 changes: 14 additions & 36 deletions server/src/UserResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ import {
Query,
Mutation,
Arg,
ObjectType,
Field,
Ctx,
UseMiddleware,
Int
Expand All @@ -16,15 +14,6 @@ import { createRefreshToken, createAccessToken } from "./auth";
import { isAuth } from "./isAuth";
import { sendRefreshToken } from "./sendRefreshToken";
import { getConnection } from "typeorm";
import { verify } from "jsonwebtoken";

@ObjectType()
class LoginResponse {
@Field()
accessToken: string;
@Field(() => User)
user: User;
}

@Resolver()
export class UserResolver {
Expand All @@ -37,7 +26,7 @@ export class UserResolver {
@UseMiddleware(isAuth)
bye(@Ctx() { payload }: MyContext) {
console.log(payload);
return `your user id is: ${payload!.userId}`;
return `your user id is: ${payload!.id}`;
}

@Query(() => [User])
Expand All @@ -46,26 +35,18 @@ export class UserResolver {
}

@Query(() => User, { nullable: true })
@UseMiddleware(isAuth)
me(@Ctx() context: MyContext) {
const authorization = context.req.headers["authorization"];

if (!authorization) {
return null;
}

try {
const token = authorization.split(" ")[1];
const payload: any = verify(token, process.env.ACCESS_TOKEN_SECRET!);
return User.findOne(payload.userId);
} catch (err) {
console.log(err);
return null;
}
return context.payload!;
}

@Mutation(() => Boolean)
async logout(@Ctx() { res }: MyContext) {
sendRefreshToken(res, "");
@UseMiddleware(isAuth)
async logout(@Ctx() { res, payload }: MyContext) {
res.clearCookie("jid");
await getConnection()
.getRepository(User)
.increment({ id: payload!.id }, "tokenVersion", 1);

return true;
}
Expand All @@ -79,12 +60,13 @@ export class UserResolver {
return true;
}

@Mutation(() => LoginResponse)
@Mutation(() => User)
async login(
@Arg("email") email: string,
@Arg("password") password: string,
@Ctx() { res }: MyContext
): Promise<LoginResponse> {
): Promise<User> {
console.log(email);
const user = await User.findOne({ where: { email } });

if (!user) {
Expand All @@ -100,11 +82,8 @@ export class UserResolver {
// login successful

sendRefreshToken(res, createRefreshToken(user));

return {
accessToken: createAccessToken(user),
user
};
res.setHeader("access-token", createAccessToken(user));
return user;
}

@Mutation(() => Boolean)
Expand All @@ -123,7 +102,6 @@ export class UserResolver {
console.log(err);
return false;
}

return true;
}
}
39 changes: 4 additions & 35 deletions server/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,52 +6,21 @@ import { buildSchema } from "type-graphql";
import { UserResolver } from "./UserResolver";
import { createConnection } from "typeorm";
import cookieParser from "cookie-parser";
import { verify } from "jsonwebtoken";
import cors from "cors";
import { User } from "./entity/User";
import { sendRefreshToken } from "./sendRefreshToken";
import { createAccessToken, createRefreshToken } from "./auth";

(async () => {
const app = express();
app.use(
cors({
origin: "http://localhost:3000",
credentials: true
credentials: true,
exposedHeaders: [
"access-token"
]
})
);
app.use(cookieParser());
app.get("/", (_req, res) => res.send("hello"));
app.post("/refresh_token", async (req, res) => {
const token = req.cookies.jid;
if (!token) {
return res.send({ ok: false, accessToken: "" });
}

let payload: any = null;
try {
payload = verify(token, process.env.REFRESH_TOKEN_SECRET!);
} catch (err) {
console.log(err);
return res.send({ ok: false, accessToken: "" });
}

// token is valid and
// we can send back an access token
const user = await User.findOne({ id: payload.userId });

if (!user) {
return res.send({ ok: false, accessToken: "" });
}

if (user.tokenVersion !== payload.tokenVersion) {
return res.send({ ok: false, accessToken: "" });
}

sendRefreshToken(res, createRefreshToken(user));

return res.send({ ok: true, accessToken: createAccessToken(user) });
});

await createConnection();

Expand Down
53 changes: 37 additions & 16 deletions server/src/isAuth.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,45 @@
import { MiddlewareFn } from "type-graphql";
import { verify } from "jsonwebtoken";
import { MyContext } from "./MyContext";
import { createAccessToken } from "./auth";
import { User } from "./entity/User";

// bearer 102930ajslkdaoq01

export const isAuth: MiddlewareFn<MyContext> = ({ context }, next) => {
const authorization = context.req.headers["authorization"];
export const isAuth: MiddlewareFn<MyContext> = async ({ context }, next) => {
if (context.req.cookies.jid) {
const authorization = context.req.headers["authorization"];
if (!authorization) {
context.res.clearCookie("jid");
throw new Error("not authenticated");
}

if (!authorization) {
throw new Error("not authenticated");
}

try {
const token = authorization.split(" ")[1];
const payload = verify(token, process.env.ACCESS_TOKEN_SECRET!);
context.payload = payload as any;
} catch (err) {
console.log(err);
throw new Error("not authenticated");
}

return next();
try {
const token = authorization.split(" ")[1];
const { userId } = verify(token, process.env.ACCESS_TOKEN_SECRET!) as { userId: number };
const user = await User.findOne({ where: { id: userId } });
if (!user) {
context.res.clearCookie("jid");
throw new Error("not authenticated");
}
context.payload = user;
} catch (err) {
try {
const { userId, tokenVersion } = verify(context.req.cookies.jid, process.env.REFRESH_TOKEN_SECRET!) as { userId: number; tokenVersion: number; };
const user = await User.findOne({ where: { id: userId } });
if (!user || user.tokenVersion !== tokenVersion) {
context.res.clearCookie("jid");
throw new Error("not authenticated");
}
context.res.setHeader("access-token", createAccessToken(user));
context.payload = user;
} catch (error) {
console.log(error);
context.res.clearCookie("jid");
throw new Error("not authenticated");
}
}
return next();
};
throw new Error("not authenticated");
};
3 changes: 2 additions & 1 deletion server/src/sendRefreshToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Response } from "express";
export const sendRefreshToken = (res: Response, token: string) => {
res.cookie("jid", token, {
httpOnly: true,
path: "/refresh_token"
secure: true,
maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days token expiration time
});
};
Loading