Skip to content

Convert from sessions to JWT. #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
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
25 changes: 14 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,37 @@
"author": "Nick Mandylas",
"license": "MIT",
"dependencies": {
"@autotelic/apollo-server-fastify": "^4.0.0",
"@autotelic/apollo-server-fastify": "^4.0.2",
"apollo-server-fastify": "^2.19.0",
"argon2": "^0.27.0",
"class-validator": "^0.12.2",
"connect-redis": "^5.0.0",
"dotenv": "^8.2.0",
"fastify": "^3.6.0",
"fastify": "^3.8.0",
"fastify-cookie": "^4.1.0",
"fastify-cors": "^4.1.0",
"fastify-session": "^5.2.0",
"graphql": "^15.3.0",
"ioredis": "^4.17.3",
"pg": "^8.4.1",
"fastify-jwt": "^2.1.3",
"fastify-session": "^5.2.1",
"graphql": "^15.4.0",
"ioredis": "^4.19.2",
"pg": "^8.5.0",
"redis": "^3.0.2",
"type-graphql": "^1.1.0",
"typeorm": "^0.2.28",
"tsconfig-paths": "^3.9.0",
"type-graphql": "^1.1.1",
"typeorm": "^0.2.29",
"uuid": "^8.3.1"
},
"devDependencies": {
"@types/connect-redis": "^0.0.14",
"@types/connect-redis": "^0.0.15",
"@types/uuid": "^8.3.0",
"gen-env-types": "^1.0.4",
"ts-node": "^9.0.0",
"ts-node-dev": "^1.0.0-pre.65",
"typescript": "^4.0.3"
"typescript": "^4.0.5"
},
"scripts": {
"gen-env": "gen-env-types .env -o env.d.ts -e .",
"dev": "NODE_ENV=development ts-node-dev --respawn src/index.ts",
"dev": "NODE_ENV=development ts-node-dev --respawn -r tsconfig-paths/register src/index.ts",
"build": "tsc",
"start": "NODE_ENV=production node dist/src/index.js"
}
Expand Down
74 changes: 37 additions & 37 deletions src/entities/User.ts
Original file line number Diff line number Diff line change
@@ -1,46 +1,46 @@
import { Field, ID, ObjectType } from "type-graphql";
import {
BaseEntity,
BeforeInsert,
Column,
Entity,
PrimaryColumn,
BaseEntity,
BeforeInsert,
Column,
Entity,
PrimaryColumn,
} from "typeorm";
import { v4 as uuidv4 } from "uuid";

@ObjectType()
@Entity({ name: "user" })
export class User extends BaseEntity {
@Field(() => ID)
@PrimaryColumn()
id: string;

@Field()
@Column()
firstName: string;

@Field()
@Column()
lastName: string;

@Field()
@Column("text", { unique: true })
email: string;

@Field()
@Column("bigint")
joinDate: number;

@Column()
password: string;

@BeforeInsert()
addId() {
this.id = uuidv4();
}

@BeforeInsert()
setJoinDate() {
this.joinDate = Date.now();
}
@Field(() => ID)
@PrimaryColumn()
id: string;

@Field()
@Column()
firstName: string;

@Field()
@Column()
lastName: string;

@Field()
@Column("text", { unique: true })
email: string;

@Field()
@Column("bigint")
joinDate: number;

@Column()
password: string;

@BeforeInsert()
addId() {
this.id = uuidv4();
}

@BeforeInsert()
setJoinDate() {
this.joinDate = Date.now();
}
}
86 changes: 46 additions & 40 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import dotenv from "dotenv";
import fastify from "fastify";
import fastifyCookie from "fastify-cookie";
import fastifyCors from "fastify-cors";
import jwt from "fastify-jwt";
import fastifySession from "fastify-session";
import { FastifyContext } from "./types/Context";
import { connection } from "./utils/connection";
Expand All @@ -13,46 +14,51 @@ import { sessionOptions } from "./utils/session";
dotenv.config();

const main = async () => {
const PORT: number = Number(process.env.PORT) || 4000;

await connection;
const schema = await createSchema();

const server = new ApolloServer({
schema,
context: ({ request, reply }: FastifyContext) => ({
request,
reply,
}),
});

const app = fastify({
logger: true,
trustProxy: process.env.NODE_ENV === "production",
});

app.register(server.createHandler({ cors: false }));
app.register(fastifyCors, {
origin: process.env.NODE_ENV === "production" ? "" : "localhost:4000",
credentials: true,
});
app.register(fastifyCookie);
app.register(fastifySession, sessionOptions);

app.listen(PORT, (err, address) => {
if (err) {
app.log.error(err);
process.exit(1);
}

app.log.info(
`🚀 Server launched on address ${address} in ${process.env.NODE_ENV?.toUpperCase()}`
);
});

app.get("/", async (_, reply) => {
reply.redirect(302, "http://github.com/NickMandylas");
});
const PORT: number = Number(process.env.PORT) || 4000;

await connection;
const schema = await createSchema();

const server = new ApolloServer({
schema,
context: ({ request, reply }: FastifyContext) => ({
request,
reply,
}),
});

const app = fastify({
logger: true,
trustProxy: process.env.NODE_ENV === "production",
});

app.register(server.createHandler({ cors: false }));
app.register(fastifyCors, {
origin:
process.env.NODE_ENV === "production" ? "example.com" : "localhost:4000",
credentials: true,
});
app.register(fastifyCookie);
app.register(jwt, {
secret: "boilerplate-secret",
cookie: { cookieName: "jid" },
});
app.register(fastifySession, sessionOptions);

app.listen(PORT, (err, address) => {
if (err) {
app.log.error(err.message);
process.exit(1);
}

app.log.info(
`🚀 Server launched on address ${address} in ${process.env.NODE_ENV?.toUpperCase()}`,
);
});

app.get("/", async (_, reply) => {
reply.redirect(302, "http://github.com/NickMandylas");
});
};

main();
21 changes: 21 additions & 0 deletions src/modules/middleware/Authentication.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { createMethodDecorator } from "type-graphql";
import { FastifyContext } from "types/Context";

export function Authentication(): MethodDecorator {
return createMethodDecorator<FastifyContext>(async ({ context }, next) => {
return context.request.jwtVerify((err: any, decode: any) => {
if (err || decode.userId === undefined) {
return {
errors: [
{
field: "authentication",
message: "Not authenticated or doesn't have valid credentials.",
},
],
};
}
context.payload = decode;
return next();
});
});
}
28 changes: 14 additions & 14 deletions src/modules/resolvers/Status.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { Query, Resolver, ObjectType, Field } from "type-graphql";

@ObjectType()
class StatusMessage {
@Field()
status: string;
@Field()
status: string;

@Field()
environment: string;
@Field()
environment: string;

@Field()
time: number;
@Field()
time: number;
}

/**
Expand All @@ -19,13 +19,13 @@ class StatusMessage {

@Resolver()
export class StatusResolver {
@Query(() => StatusMessage)
async Status(): Promise<StatusMessage> {
const res = new StatusMessage();
res.status = "up";
res.environment = process.env.NODE_ENV!;
res.time = Date.now();
@Query(() => StatusMessage)
async status(): Promise<StatusMessage> {
const res = new StatusMessage();
res.status = "up";
res.environment = process.env.NODE_ENV!;
res.time = Date.now();

return res;
}
return res;
}
}
38 changes: 20 additions & 18 deletions src/modules/resolvers/user/Me.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { User } from "entities/User";
import { Authentication } from "modules/middleware/Authentication";
import { UserResponse } from "modules/shared/responses/User";
import { Ctx, Query, Resolver } from "type-graphql";
import { User } from "../../../entities/User";
import { FastifyContext } from "../../../types/Context";
import { UserResponse } from "../../shared/responses/User";
import { FastifyContext } from "types/Context";

/**
* Query to recognise whether session ID is valid.
Expand All @@ -11,21 +12,22 @@ import { UserResponse } from "../../shared/responses/User";

@Resolver()
export class MeResolver {
@Query(() => UserResponse, { nullable: true })
async Me(@Ctx() ctx: FastifyContext): Promise<UserResponse> {
if (!ctx.request.session.userId) {
return {
errors: [
{
field: "session",
message: "User data could not be found for this session.",
},
],
};
}
@Authentication()
@Query(() => UserResponse, { nullable: true })
async me(@Ctx() ctx: FastifyContext): Promise<UserResponse> {
const user = await User.findOne({ where: { id: ctx.payload.userId } });

const user = await User.findOne(ctx.request.session!.userId);
if (!user) {
return {
errors: [
{
field: "member",
message: "Session exists but member entity no longer in database.",
},
],
};
}

return { user };
}
return { user };
}
}
Loading