Skip to content

Commit

Permalink
Merge pull request #73 from Rocketseat/feature/authentication
Browse files Browse the repository at this point in the history
feat: add support for authentication using JWT
  • Loading branch information
gabrielbuzziv authored Jan 18, 2023
2 parents 70aa91a + 9ab83ce commit 3360c28
Show file tree
Hide file tree
Showing 9 changed files with 252 additions and 3 deletions.
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ DATABASE_PASS=docker
DATABASE_NAME=app

DATABASE_URL="postgresql://${DATABASE_USER}:${DATABASE_PASS}@${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_NAME}"

JWT_SECRET_KEY=
JWT_PUBLIC_KEY=
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@nestjs/config": "2.0.0",
"@nestjs/core": "8.4.3",
"@nestjs/graphql": "10.0.8",
"@nestjs/passport": "9.0.0",
"@nestjs/platform-express": "8.4.3",
"@prisma/client": "3.11.1",
"apollo-server-express": "3.6.7",
Expand All @@ -52,6 +53,8 @@
"graphql-query-complexity": "0.11.0",
"lightship": "7.0.2",
"nestjs-pino": "2.5.2",
"passport": "0.6.0",
"passport-jwt": "4.0.1",
"pg": "8.7.3",
"pino-http": "6.6.0",
"reflect-metadata": "0.1.13",
Expand All @@ -71,6 +74,7 @@
"@types/express": "4.17.13",
"@types/jest": "27.4.1",
"@types/node": "16.11.26",
"@types/passport-jwt": "3.0.8",
"@types/pg": "8.6.5",
"@types/supertest": "2.0.12",
"@types/uuid": "8.3.4",
Expand Down
3 changes: 3 additions & 0 deletions src/infra/http/auth/auth-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export type AuthUser = {
atlasUserId: string;
};
9 changes: 9 additions & 0 deletions src/infra/http/auth/current-user.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

import { AuthUser } from '@infra/http/auth/auth-user';

export const CurrentUser = createParamDecorator(
(data: unknown, context: ExecutionContext): AuthUser => {
return context.switchToHttp().getRequest().user;
},
);
57 changes: 57 additions & 0 deletions src/infra/http/auth/jwt-auth-guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { ContextType, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { GqlExecutionContext } from '@nestjs/graphql';
import { AuthGuard } from '@nestjs/passport';

import { AuthUser } from '@infra/http/auth/auth-user';
import { IS_PUBLIC_KEY } from '@infra/http/auth/public';

@Injectable()
export class JWTAuthGuard extends AuthGuard('jwt') {
constructor(private reflector: Reflector) {
super();
}
canActivate(context: ExecutionContext) {
if (process.env.NODE_ENV === 'test') {
if (context.getType<ContextType | 'graphql'>() === 'graphql') {
const ctx = GqlExecutionContext.create(context);

const atlasUserId =
ctx.getContext().req.headers['request-atlas-user-id'];

ctx.getContext().req.user = {
atlasUserId,
} as AuthUser;

return true;
}

const request = context.switchToHttp().getRequest();

request.user = {
atlasUserId: request.headers['request-atlas-user-id'],
} as AuthUser;

return true;
}

const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);

if (isPublic) {
return true;
}

return super.canActivate(context);
}

getRequest(context: ExecutionContext) {
if (context.getType<ContextType | 'graphql'>() === 'graphql') {
return GqlExecutionContext.create(context).getContext().req;
}

return context.switchToHttp().getRequest();
}
}
27 changes: 27 additions & 0 deletions src/infra/http/auth/jwt.strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';

import { AuthUser } from './auth-user';

type Payload = {
uid: string;
};

@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: process.env.JWT_PUBLIC_KEY
? Buffer.from(process.env.JWT_PUBLIC_KEY, 'base64').toString()
: '',
algorithms: ['RS256'],
});
}

async validate(payload: Payload): Promise<AuthUser> {
return { atlasUserId: payload.uid };
}
}
7 changes: 7 additions & 0 deletions src/infra/http/auth/public.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { SetMetadata } from '@nestjs/common';

export const IS_PUBLIC_KEY = 'isPublic';
/**
* Disable authentication for specific GraphQL resolvers or REST endpoints
*/
export const Public = () => SetMetadata(IS_PUBLIC_KEY, true);
13 changes: 12 additions & 1 deletion src/infra/http/http.module.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { ApolloDriver } from '@nestjs/apollo';
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { GraphQLModule } from '@nestjs/graphql';
import path from 'node:path';

// import { DatabaseModule } from '@infra/database/database.module';
import { ComplexityPlugin } from '@infra/http/graphql/complexity-plugin';
import { ExampleResolver } from '@infra/http/graphql/resolvers/example.resolver';

import { JWTAuthGuard } from './auth/jwt-auth-guard';
import { JwtStrategy } from './auth/jwt.strategy';

@Module({
imports: [
// DatabaseModule,
Expand All @@ -21,6 +25,13 @@ import { ExampleResolver } from '@infra/http/graphql/resolvers/example.resolver'
// },
}),
],
providers: [ExampleResolver],
providers: [
ExampleResolver,
JwtStrategy,
{
provide: APP_GUARD,
useClass: JWTAuthGuard,
},
],
})
export class HttpModule {}
132 changes: 130 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1228,6 +1228,11 @@
resolved "https://registry.yarnpkg.com/@nestjs/mapped-types/-/mapped-types-1.0.1.tgz#78b62041c7a407db4a90eb140567321602bed18e"
integrity sha512-NFvofzSinp00j5rzUd4tf+xi9od6383iY0JP7o0Bnu1fuItAUkWBgc4EKuIQ3D+c2QI3i9pG1kDWAeY27EMGtg==

"@nestjs/[email protected]":
version "9.0.0"
resolved "https://registry.yarnpkg.com/@nestjs/passport/-/passport-9.0.0.tgz#0571bb08f8043456bc6df44cd4f59ca5f10c9b9f"
integrity sha512-Gnh8n1wzFPOLSS/94X1sUP4IRAoXTgG4odl7/AO5h+uwscEGXxJFercrZfqdAwkWhqkKWbsntM3j5mRy/6ZQDA==

"@nestjs/[email protected]":
version "8.4.3"
resolved "https://registry.yarnpkg.com/@nestjs/platform-express/-/platform-express-8.4.3.tgz#eac13147bf9cb1221f574ec5c40eac6a9d777750"
Expand Down Expand Up @@ -1679,6 +1684,25 @@
"@types/qs" "*"
"@types/range-parser" "*"

"@types/express-serve-static-core@^4.17.31":
version "4.17.32"
resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.32.tgz#93dda387f5516af616d8d3f05f2c4c79d81e1b82"
integrity sha512-aI5h/VOkxOF2Z1saPy0Zsxs5avets/iaiAJYznQFm5By/pamU31xWKL//epiF4OfUA2qTOc9PV6tCUjhO8wlZA==
dependencies:
"@types/node" "*"
"@types/qs" "*"
"@types/range-parser" "*"

"@types/express@*":
version "4.17.15"
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.15.tgz#9290e983ec8b054b65a5abccb610411953d417ff"
integrity sha512-Yv0k4bXGOH+8a+7bELd2PqHQsuiANB+A8a4gnQrkRWzrkKlb6KHaVvyXhqs04sVW/OWlbPyYxRgYlIXLfrufMQ==
dependencies:
"@types/body-parser" "*"
"@types/express-serve-static-core" "^4.17.31"
"@types/qs" "*"
"@types/serve-static" "*"

"@types/[email protected]":
version "4.17.13"
resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034"
Expand Down Expand Up @@ -1738,6 +1762,13 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=

"@types/jsonwebtoken@*":
version "9.0.1"
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.1.tgz#29b1369c4774200d6d6f63135bf3d1ba3ef997a4"
integrity sha512-c5ltxazpWabia/4UzhIoaDcIza4KViOQhdbjRlfcIGVnsE3c3brkz9Z+F/EeJIECOQP7W7US2hNE930cWWkPiw==
dependencies:
"@types/node" "*"

"@types/long@^4.0.0", "@types/long@^4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9"
Expand Down Expand Up @@ -1783,6 +1814,30 @@
resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==

"@types/[email protected]":
version "3.0.8"
resolved "https://registry.yarnpkg.com/@types/passport-jwt/-/passport-jwt-3.0.8.tgz#c8a95bf7d8f330f2560f1b3d07605e23ac01469a"
integrity sha512-VKJZDJUAHFhPHHYvxdqFcc5vlDht8Q2pL1/ePvKAgqRThDaCc84lSYOTQmnx3+JIkDlN+2KfhFhXIzlcVT+Pcw==
dependencies:
"@types/express" "*"
"@types/jsonwebtoken" "*"
"@types/passport-strategy" "*"

"@types/passport-strategy@*":
version "0.2.35"
resolved "https://registry.yarnpkg.com/@types/passport-strategy/-/passport-strategy-0.2.35.tgz#e52f5212279ea73f02d9b06af67efe9cefce2d0c"
integrity sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==
dependencies:
"@types/express" "*"
"@types/passport" "*"

"@types/passport@*":
version "1.0.11"
resolved "https://registry.yarnpkg.com/@types/passport/-/passport-1.0.11.tgz#d046b41e28b280f4e7994614fb976e9b449cb7c6"
integrity sha512-pz1cx9ptZvozyGKKKIPLcVDVHwae4hrH5d6g5J+DkMRRjR3cVETb4jMabhXAUbg3Ov7T22nFHEgaK2jj+5CBpw==
dependencies:
"@types/express" "*"

"@types/[email protected]":
version "8.6.5"
resolved "https://registry.yarnpkg.com/@types/pg/-/pg-8.6.5.tgz#2dce9cb468a6a5e0f1296a59aea3ac75dd27b702"
Expand Down Expand Up @@ -2622,6 +2677,11 @@ [email protected]:
dependencies:
node-int64 "^0.4.0"

[email protected]:
version "1.0.1"
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819"
integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==

buffer-from@^1.0.0:
version "1.1.2"
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
Expand Down Expand Up @@ -3294,6 +3354,13 @@ duplexify@^4.1.2:
readable-stream "^3.1.1"
stream-shift "^1.0.0"

[email protected]:
version "1.0.11"
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf"
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==
dependencies:
safe-buffer "^5.0.1"

[email protected]:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
Expand Down Expand Up @@ -5066,6 +5133,33 @@ jsonparse@^1.2.0:
resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280"
integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=

jsonwebtoken@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz#d0faf9ba1cc3a56255fe49c0961a67e520c1926d"
integrity sha512-tuGfYXxkQGDPnLJ7SibiQgVgeDgfbPq2k2ICcbgqW8WxWLBAxKQM/ZCu/IT8SOSwmaYl4dpTFCW5xZv7YbbWUw==
dependencies:
jws "^3.2.2"
lodash "^4.17.21"
ms "^2.1.1"
semver "^7.3.8"

jwa@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a"
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==
dependencies:
buffer-equal-constant-time "1.0.1"
ecdsa-sig-formatter "1.0.11"
safe-buffer "^5.0.1"

jws@^3.2.2:
version "3.2.2"
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304"
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==
dependencies:
jwa "^1.4.1"
safe-buffer "^5.0.1"

kind-of@^6.0.3:
version "6.0.3"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
Expand Down Expand Up @@ -5462,7 +5556,7 @@ [email protected]:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==

[email protected]:
[email protected], ms@^2.1.1:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
Expand Down Expand Up @@ -5765,6 +5859,28 @@ parseurl@^1.3.3, parseurl@~1.3.3:
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==

[email protected]:
version "4.0.1"
resolved "https://registry.yarnpkg.com/passport-jwt/-/passport-jwt-4.0.1.tgz#c443795eff322c38d173faa0a3c481479646ec3d"
integrity sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==
dependencies:
jsonwebtoken "^9.0.0"
passport-strategy "^1.0.0"

[email protected], passport-strategy@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4"
integrity sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==

[email protected]:
version "0.6.0"
resolved "https://registry.yarnpkg.com/passport/-/passport-0.6.0.tgz#e869579fab465b5c0b291e841e6cc95c005fac9d"
integrity sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==
dependencies:
passport-strategy "1.x.x"
pause "0.0.1"
utils-merge "^1.0.1"

path-exists@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
Expand Down Expand Up @@ -5800,6 +5916,11 @@ path-type@^4.0.0:
resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==

[email protected]:
version "0.0.1"
resolved "https://registry.yarnpkg.com/pause/-/pause-0.0.1.tgz#1d408b3fdb76923b9543d96fb4c9dfd535d9cb5d"
integrity sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==

performance-now@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b"
Expand Down Expand Up @@ -6489,6 +6610,13 @@ semver@^6.0.0, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==

semver@^7.3.8:
version "7.3.8"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798"
integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==
dependencies:
lru-cache "^6.0.0"

[email protected]:
version "0.17.2"
resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820"
Expand Down Expand Up @@ -7268,7 +7396,7 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=

[email protected]:
[email protected], utils-merge@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
Expand Down

0 comments on commit 3360c28

Please sign in to comment.