├── .gitignore
├── README.md
├── nest-cli.json
├── package-lock.json
├── package.json
├── src
├── app.module.ts
├── authz
│ ├── authz.module.ts
│ └── jwt.strategy.ts
├── item.ts
├── items.ts
├── items
│ ├── items.controller.ts
│ ├── items.module.ts
│ └── items.service.ts
├── main.ts
├── permissions.decorator.ts
└── permissions.guard.ts
├── tsconfig.build.json
├── tsconfig.json
└── tslint.json
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/node,linux,macos,windows,intellij,webstorm,jetbrains,visualstudio,visualstudiocode
3 | # Edit at https://www.gitignore.io/?templates=node,linux,macos,windows,intellij,webstorm,jetbrains,visualstudio,visualstudiocode
4 |
5 | ### Intellij ###
6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
8 |
9 | # User-specific stuff
10 | .idea/**/workspace.xml
11 | .idea/**/tasks.xml
12 | .idea/**/usage.statistics.xml
13 | .idea/**/dictionaries
14 | .idea/**/shelf
15 |
16 | # Generated files
17 | .idea/**/contentModel.xml
18 |
19 | # Sensitive or high-churn files
20 | .idea/**/dataSources/
21 | .idea/**/dataSources.ids
22 | .idea/**/dataSources.local.xml
23 | .idea/**/sqlDataSources.xml
24 | .idea/**/dynamic.xml
25 | .idea/**/uiDesigner.xml
26 | .idea/**/dbnavigator.xml
27 |
28 | # Gradle
29 | .idea/**/gradle.xml
30 | .idea/**/libraries
31 |
32 | # Gradle and Maven with auto-import
33 | # When using Gradle or Maven with auto-import, you should exclude module files,
34 | # since they will be recreated, and may cause churn. Uncomment if using
35 | # auto-import.
36 | # .idea/modules.xml
37 | # .idea/*.iml
38 | # .idea/modules
39 |
40 | # CMake
41 | cmake-build-*/
42 |
43 | # Mongo Explorer plugin
44 | .idea/**/mongoSettings.xml
45 |
46 | # File-based project format
47 | *.iws
48 |
49 | # IntelliJ
50 | out/
51 |
52 | # mpeltonen/sbt-idea plugin
53 | .idea_modules/
54 |
55 | # JIRA plugin
56 | atlassian-ide-plugin.xml
57 |
58 | # Cursive Clojure plugin
59 | .idea/replstate.xml
60 |
61 | # Crashlytics plugin (for Android Studio and IntelliJ)
62 | com_crashlytics_export_strings.xml
63 | crashlytics.properties
64 | crashlytics-build.properties
65 | fabric.properties
66 |
67 | # Editor-based Rest Client
68 | .idea/httpRequests
69 |
70 | # Android studio 3.1+ serialized cache file
71 | .idea/caches/build_file_checksums.ser
72 |
73 | # JetBrains templates
74 | **___jb_tmp___
75 |
76 | ### Intellij Patch ###
77 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
78 |
79 | # *.iml
80 | # modules.xml
81 | # .idea/misc.xml
82 | # *.ipr
83 |
84 | # Sonarlint plugin
85 | .idea/sonarlint
86 |
87 | ### JetBrains ###
88 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
89 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
90 |
91 | # User-specific stuff
92 |
93 | # Generated files
94 |
95 | # Sensitive or high-churn files
96 |
97 | # Gradle
98 |
99 | # Gradle and Maven with auto-import
100 | # When using Gradle or Maven with auto-import, you should exclude module files,
101 | # since they will be recreated, and may cause churn. Uncomment if using
102 | # auto-import.
103 | # .idea/modules.xml
104 | # .idea/*.iml
105 | # .idea/modules
106 |
107 | # CMake
108 |
109 | # Mongo Explorer plugin
110 |
111 | # File-based project format
112 |
113 | # IntelliJ
114 |
115 | # mpeltonen/sbt-idea plugin
116 |
117 | # JIRA plugin
118 |
119 | # Cursive Clojure plugin
120 |
121 | # Crashlytics plugin (for Android Studio and IntelliJ)
122 |
123 | # Editor-based Rest Client
124 |
125 | # Android studio 3.1+ serialized cache file
126 |
127 | # JetBrains templates
128 |
129 | ### JetBrains Patch ###
130 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
131 |
132 | # *.iml
133 | # modules.xml
134 | # .idea/misc.xml
135 | # *.ipr
136 |
137 | # Sonarlint plugin
138 |
139 | ### Linux ###
140 | *~
141 |
142 | # temporary files which can be created if a process still has a handle open of a deleted file
143 | .fuse_hidden*
144 |
145 | # KDE directory preferences
146 | .directory
147 |
148 | # Linux trash folder which might appear on any partition or disk
149 | .Trash-*
150 |
151 | # .nfs files are created when an open file is removed but is still being accessed
152 | .nfs*
153 |
154 | ### macOS ###
155 | # General
156 | .DS_Store
157 | .AppleDouble
158 | .LSOverride
159 |
160 | # Icon must end with two \r
161 | Icon
162 |
163 | # Thumbnails
164 | ._*
165 |
166 | # Files that might appear in the root of a volume
167 | .DocumentRevisions-V100
168 | .fseventsd
169 | .Spotlight-V100
170 | .TemporaryItems
171 | .Trashes
172 | .VolumeIcon.icns
173 | .com.apple.timemachine.donotpresent
174 |
175 | # Directories potentially created on remote AFP share
176 | .AppleDB
177 | .AppleDesktop
178 | Network Trash Folder
179 | Temporary Items
180 | .apdisk
181 |
182 | ### Node ###
183 | # Logs
184 | logs
185 | *.log
186 | npm-debug.log*
187 | yarn-debug.log*
188 | yarn-error.log*
189 | lerna-debug.log*
190 |
191 | # Diagnostic reports (https://nodejs.org/api/report.html)
192 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
193 |
194 | # Runtime data
195 | pids
196 | *.pid
197 | *.seed
198 | *.pid.lock
199 |
200 | # Directory for instrumented libs generated by jscoverage/JSCover
201 | lib-cov
202 |
203 | # Coverage directory used by tools like istanbul
204 | coverage
205 |
206 | # nyc test coverage
207 | .nyc_output
208 |
209 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
210 | .grunt
211 |
212 | # Bower dependency directory (https://bower.io/)
213 | bower_components
214 |
215 | # node-waf configuration
216 | .lock-wscript
217 |
218 | # Compiled binary addons (https://nodejs.org/api/addons.html)
219 | build/Release
220 |
221 | # Dependency directories
222 | node_modules/
223 | jspm_packages/
224 |
225 | # TypeScript v1 declaration files
226 | typings/
227 |
228 | # Optional npm cache directory
229 | .npm
230 |
231 | # Optional eslint cache
232 | .eslintcache
233 |
234 | # Optional REPL history
235 | .node_repl_history
236 |
237 | # Output of 'npm pack'
238 | *.tgz
239 |
240 | # Yarn Integrity file
241 | .yarn-integrity
242 |
243 | # dotenv environment variables file
244 | .env
245 | .env.test
246 |
247 | # parcel-bundler cache (https://parceljs.org/)
248 | .cache
249 |
250 | # next.js build output
251 | .next
252 |
253 | # nuxt.js build output
254 | .nuxt
255 |
256 | # vuepress build output
257 | .vuepress/dist
258 |
259 | # Serverless directories
260 | .serverless/
261 |
262 | # FuseBox cache
263 | .fusebox/
264 |
265 | # DynamoDB Local files
266 | .dynamodb/
267 |
268 | ### VisualStudioCode ###
269 | .vscode/*
270 | !.vscode/settings.json
271 | !.vscode/tasks.json
272 | !.vscode/launch.json
273 | !.vscode/extensions.json
274 |
275 | ### VisualStudioCode Patch ###
276 | # Ignore all local history of files
277 | .history
278 |
279 | ### WebStorm ###
280 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
281 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
282 |
283 | # User-specific stuff
284 |
285 | # Generated files
286 |
287 | # Sensitive or high-churn files
288 |
289 | # Gradle
290 |
291 | # Gradle and Maven with auto-import
292 | # When using Gradle or Maven with auto-import, you should exclude module files,
293 | # since they will be recreated, and may cause churn. Uncomment if using
294 | # auto-import.
295 | # .idea/modules.xml
296 | # .idea/*.iml
297 | # .idea/modules
298 |
299 | # CMake
300 |
301 | # Mongo Explorer plugin
302 |
303 | # File-based project format
304 |
305 | # IntelliJ
306 |
307 | # mpeltonen/sbt-idea plugin
308 |
309 | # JIRA plugin
310 |
311 | # Cursive Clojure plugin
312 |
313 | # Crashlytics plugin (for Android Studio and IntelliJ)
314 |
315 | # Editor-based Rest Client
316 |
317 | # Android studio 3.1+ serialized cache file
318 |
319 | # JetBrains templates
320 |
321 | ### WebStorm Patch ###
322 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
323 |
324 | # *.iml
325 | # modules.xml
326 | # .idea/misc.xml
327 | # *.ipr
328 |
329 | # Sonarlint plugin
330 |
331 | ### Windows ###
332 | # Windows thumbnail cache files
333 | Thumbs.db
334 | ehthumbs.db
335 | ehthumbs_vista.db
336 |
337 | # Dump file
338 | *.stackdump
339 |
340 | # Folder config file
341 | [Dd]esktop.ini
342 |
343 | # Recycle Bin used on file shares
344 | $RECYCLE.BIN/
345 |
346 | # Windows Installer files
347 | *.cab
348 | *.msi
349 | *.msix
350 | *.msm
351 | *.msp
352 |
353 | # Windows shortcuts
354 | *.lnk
355 |
356 | ### VisualStudio ###
357 | ## Ignore Visual Studio temporary files, build results, and
358 | ## files generated by popular Visual Studio add-ons.
359 | ##
360 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
361 |
362 | # User-specific files
363 | *.rsuser
364 | *.suo
365 | *.user
366 | *.userosscache
367 | *.sln.docstates
368 |
369 | # User-specific files (MonoDevelop/Xamarin Studio)
370 | *.userprefs
371 |
372 | # Mono auto generated files
373 | mono_crash.*
374 |
375 | # Build results
376 | [Dd]ebug/
377 | [Dd]ebugPublic/
378 | [Rr]elease/
379 | [Rr]eleases/
380 | x64/
381 | x86/
382 | [Aa][Rr][Mm]/
383 | [Aa][Rr][Mm]64/
384 | bld/
385 | [Bb]in/
386 | [Oo]bj/
387 | [Ll]og/
388 |
389 | # Visual Studio 2015/2017 cache/options directory
390 | .vs/
391 | # Uncomment if you have tasks that create the project's static files in wwwroot
392 | #wwwroot/
393 |
394 | # Visual Studio 2017 auto generated files
395 | Generated\ Files/
396 |
397 | # MSTest test Results
398 | [Tt]est[Rr]esult*/
399 | [Bb]uild[Ll]og.*
400 |
401 | # NUNIT
402 | *.VisualState.xml
403 | TestResult.xml
404 |
405 | # Build Results of an ATL Project
406 | [Dd]ebugPS/
407 | [Rr]eleasePS/
408 | dlldata.c
409 |
410 | # Benchmark Results
411 | BenchmarkDotNet.Artifacts/
412 |
413 | # .NET Core
414 | project.lock.json
415 | project.fragment.lock.json
416 | artifacts/
417 |
418 | # StyleCop
419 | StyleCopReport.xml
420 |
421 | # Files built by Visual Studio
422 | *_i.c
423 | *_p.c
424 | *_h.h
425 | *.ilk
426 | *.meta
427 | *.obj
428 | *.iobj
429 | *.pch
430 | *.pdb
431 | *.ipdb
432 | *.pgc
433 | *.pgd
434 | *.rsp
435 | *.sbr
436 | *.tlb
437 | *.tli
438 | *.tlh
439 | *.tmp
440 | *.tmp_proj
441 | *_wpftmp.csproj
442 | *.vspscc
443 | *.vssscc
444 | .builds
445 | *.pidb
446 | *.svclog
447 | *.scc
448 |
449 | # Chutzpah Test files
450 | _Chutzpah*
451 |
452 | # Visual C++ cache files
453 | ipch/
454 | *.aps
455 | *.ncb
456 | *.opendb
457 | *.opensdf
458 | *.sdf
459 | *.cachefile
460 | *.VC.db
461 | *.VC.VC.opendb
462 |
463 | # Visual Studio profiler
464 | *.psess
465 | *.vsp
466 | *.vspx
467 | *.sap
468 |
469 | # Visual Studio Trace Files
470 | *.e2e
471 |
472 | # TFS 2012 Local Workspace
473 | $tf/
474 |
475 | # Guidance Automation Toolkit
476 | *.gpState
477 |
478 | # ReSharper is a .NET coding add-in
479 | _ReSharper*/
480 | *.[Rr]e[Ss]harper
481 | *.DotSettings.user
482 |
483 | # JustCode is a .NET coding add-in
484 | .JustCode
485 |
486 | # TeamCity is a build add-in
487 | _TeamCity*
488 |
489 | # DotCover is a Code Coverage Tool
490 | *.dotCover
491 |
492 | # AxoCover is a Code Coverage Tool
493 | .axoCover/*
494 | !.axoCover/settings.json
495 |
496 | # Visual Studio code coverage results
497 | *.coverage
498 | *.coveragexml
499 |
500 | # NCrunch
501 | _NCrunch_*
502 | .*crunch*.local.xml
503 | nCrunchTemp_*
504 |
505 | # MightyMoose
506 | *.mm.*
507 | AutoTest.Net/
508 |
509 | # Web workbench (sass)
510 | .sass-cache/
511 |
512 | # Installshield output folder
513 | [Ee]xpress/
514 |
515 | # DocProject is a documentation generator add-in
516 | DocProject/buildhelp/
517 | DocProject/Help/*.HxT
518 | DocProject/Help/*.HxC
519 | DocProject/Help/*.hhc
520 | DocProject/Help/*.hhk
521 | DocProject/Help/*.hhp
522 | DocProject/Help/Html2
523 | DocProject/Help/html
524 |
525 | # Click-Once directory
526 | publish/
527 |
528 | # Publish Web Output
529 | *.[Pp]ublish.xml
530 | *.azurePubxml
531 | # Note: Comment the next line if you want to checkin your web deploy settings,
532 | # but database connection strings (with potential passwords) will be unencrypted
533 | *.pubxml
534 | *.publishproj
535 |
536 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
537 | # checkin your Azure Web App publish settings, but sensitive information contained
538 | # in these scripts will be unencrypted
539 | PublishScripts/
540 |
541 | # NuGet Packages
542 | *.nupkg
543 | # The packages folder can be ignored because of Package Restore
544 | **/[Pp]ackages/*
545 | # except build/, which is used as an MSBuild target.
546 | !**/[Pp]ackages/build/
547 | # Uncomment if necessary however generally it will be regenerated when needed
548 | #!**/[Pp]ackages/repositories.config
549 | # NuGet v3's project.json files produces more ignorable files
550 | *.nuget.props
551 | *.nuget.targets
552 |
553 | # Microsoft Azure Build Output
554 | csx/
555 | *.build.csdef
556 |
557 | # Microsoft Azure Emulator
558 | ecf/
559 | rcf/
560 |
561 | # Windows Store app package directories and files
562 | AppPackages/
563 | BundleArtifacts/
564 | Package.StoreAssociation.xml
565 | _pkginfo.txt
566 | *.appx
567 | *.appxbundle
568 | *.appxupload
569 |
570 | # Visual Studio cache files
571 | # files ending in .cache can be ignored
572 | *.[Cc]ache
573 | # but keep track of directories ending in .cache
574 | !?*.[Cc]ache/
575 |
576 | # Others
577 | ClientBin/
578 | ~$*
579 | *.dbmdl
580 | *.dbproj.schemaview
581 | *.jfm
582 | *.pfx
583 | *.publishsettings
584 | orleans.codegen.cs
585 |
586 | # Including strong name files can present a security risk
587 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
588 | #*.snk
589 |
590 | # Since there are multiple workflows, uncomment next line to ignore bower_components
591 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
592 | #bower_components/
593 |
594 | # RIA/Silverlight projects
595 | Generated_Code/
596 |
597 | # Backup & report files from converting an old project file
598 | # to a newer Visual Studio version. Backup files are not needed,
599 | # because we have git ;-)
600 | _UpgradeReport_Files/
601 | Backup*/
602 | UpgradeLog*.XML
603 | UpgradeLog*.htm
604 | ServiceFabricBackup/
605 | *.rptproj.bak
606 |
607 | # SQL Server files
608 | *.mdf
609 | *.ldf
610 | *.ndf
611 |
612 | # Business Intelligence projects
613 | *.rdl.data
614 | *.bim.layout
615 | *.bim_*.settings
616 | *.rptproj.rsuser
617 | *- Backup*.rdl
618 |
619 | # Microsoft Fakes
620 | FakesAssemblies/
621 |
622 | # GhostDoc plugin setting file
623 | *.GhostDoc.xml
624 |
625 | # Node.js Tools for Visual Studio
626 | .ntvs_analysis.dat
627 |
628 | # Visual Studio 6 build log
629 | *.plg
630 |
631 | # Visual Studio 6 workspace options file
632 | *.opt
633 |
634 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
635 | *.vbw
636 |
637 | # Visual Studio LightSwitch build output
638 | **/*.HTMLClient/GeneratedArtifacts
639 | **/*.DesktopClient/GeneratedArtifacts
640 | **/*.DesktopClient/ModelManifest.xml
641 | **/*.Server/GeneratedArtifacts
642 | **/*.Server/ModelManifest.xml
643 | _Pvt_Extensions
644 |
645 | # Paket dependency manager
646 | .paket/paket.exe
647 | paket-files/
648 |
649 | # FAKE - F# Make
650 | .fake/
651 |
652 | # CodeRush personal settings
653 | .cr/personal
654 |
655 | # Python Tools for Visual Studio (PTVS)
656 | __pycache__/
657 | *.pyc
658 |
659 | # Cake - Uncomment if you are using it
660 | # tools/**
661 | # !tools/packages.config
662 |
663 | # Tabs Studio
664 | *.tss
665 |
666 | # Telerik's JustMock configuration file
667 | *.jmconfig
668 |
669 | # BizTalk build output
670 | *.btp.cs
671 | *.btm.cs
672 | *.odx.cs
673 | *.xsd.cs
674 |
675 | # OpenCover UI analysis results
676 | OpenCover/
677 |
678 | # Azure Stream Analytics local run output
679 | ASALocalRun/
680 |
681 | # MSBuild Binary and Structured Log
682 | *.binlog
683 |
684 | # NVidia Nsight GPU debugger configuration file
685 | *.nvuser
686 |
687 | # MFractors (Xamarin productivity tool) working folder
688 | .mfractor/
689 |
690 | # Local History for Visual Studio
691 | .localhistory/
692 |
693 | # BeatPulse healthcheck temp database
694 | healthchecksdb
695 |
696 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
697 | MigrationBackup/
698 |
699 | # End of https://www.gitignore.io/api/node,linux,macos,windows,intellij,webstorm,jetbrains,visualstudio,visualstudiocode
700 |
701 | .idea/
702 |
703 | dist/
704 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Developing a Secure API with NestJS
2 |
3 | [Click here to read the full tutorial](https://auth0.com/blog/developing-a-secure-api-with-nestjs-adding-authorization/)
4 |
5 | Learn how to **build a feature-complete API using NestJS** that lets clients perform data operations on resources that describe a restaurant menu. Using TypeScript with Node.js gives you access to optional static type-checking along with robust tooling for large apps and the latest ECMAScript features.
6 |
7 | Learn also how to **define data models, create a data service, and quickly build modular endpoints**. As an option, you can also learn how to **secure the API using Auth0**. To see your API in action, you’ll use a production client called "WHATABYTE Dashboard", which is inspired by the [sleek web player from Spotify](https://open.spotify.com/search).
8 |
9 | 
10 |
11 | ## Setup
12 |
13 | - Clone repository:
14 |
15 | ```bash
16 | git clone git@github.com:auth0-blog/wab-menu-api-nestjs.git \
17 | nest-restaurant-api \
18 | ```
19 |
20 | - Make the project folder your current directory:
21 |
22 | ```bash
23 | cd nest-restaurant-api
24 | ```
25 |
26 | - Install the project dependencies:
27 |
28 | ```bash
29 | npm i
30 | ```
31 |
32 | - If you haven't set up any Auth0 applications, follow the steps from these tutorial sections to create them:
33 |
34 | Set Up API Authorization
35 |
36 | Register a Client Application with Auth0
37 |
38 | Connect a Client Application With Auth0
39 |
40 | - Create a `.env` hidden file:
41 |
42 | ```bash
43 | touch .env
44 | ```
45 |
46 | - Populate `.env` with this:
47 |
48 | ```bash
49 | PORT=7000
50 | AUTH0_DOMAIN="Your Auth0 domain"
51 | AUTH0_AUDIENCE="Your Auth0 audience"
52 | ```
53 |
54 | - Start the Express server:
55 |
56 | ```bash
57 | npm run start:dev
58 | ```
59 |
60 | [Read on the complete tutorial](https://auth0.com/blog/developing-a-secure-api-with-nestjs-adding-authorization/)
61 |
62 | **If you have any questions or feedback, please [contact us through our Community Site for this tutorial](https://community.auth0.com/t/developing-a-secure-api-with-nestjs/33026).**
63 |
64 | ## About Auth0
65 |
66 | Auth0, the identity platform for application builders, provides thousands of enterprise customers with a Universal Identity Platform for their web, mobile, IoT, and internal applications. Its extensible platform seamlessly authenticates and secures more than 2.5B logins per month, making it loved by developers and trusted by global enterprises. The company's U.S. headquarters in Bellevue, WA, and additional offices in Buenos Aires, London, Tokyo, Sydney, and Singapore, support its customers that are located in 70+ countries.
67 |
68 | For more information, visit [https://auth0.com](https://auth0.com/) or follow [@auth0 on Twitter](https://twitter.com/auth0).
69 |
--------------------------------------------------------------------------------
/nest-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "collection": "@nestjs/schematics",
3 | "sourceRoot": "src"
4 | }
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nest-restaurant-api",
3 | "version": "0.0.1",
4 | "description": "",
5 | "author": "",
6 | "license": "MIT",
7 | "scripts": {
8 | "prebuild": "rimraf dist",
9 | "build": "nest build",
10 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
11 | "start": "nest start",
12 | "start:dev": "nest start --watch",
13 | "start:debug": "nest start --debug --watch",
14 | "start:prod": "node dist/main",
15 | "lint": "tslint -p tsconfig.json -c tslint.json",
16 | "test": "jest",
17 | "test:watch": "jest --watch",
18 | "test:cov": "jest --coverage",
19 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
20 | "test:e2e": "jest --config ./test/jest-e2e.json"
21 | },
22 | "dependencies": {
23 | "@nestjs/common": "^6.7.2",
24 | "@nestjs/core": "^6.7.2",
25 | "@nestjs/passport": "^6.1.1",
26 | "@nestjs/platform-express": "^6.7.2",
27 | "class-transformer": "^0.2.3",
28 | "class-validator": "^0.11.0",
29 | "dotenv": "^8.2.0",
30 | "jwks-rsa": "^1.6.0",
31 | "passport": "^0.4.1",
32 | "passport-jwt": "^4.0.0",
33 | "reflect-metadata": "^0.1.13",
34 | "rimraf": "^3.0.0",
35 | "rxjs": "^6.5.3"
36 | },
37 | "devDependencies": {
38 | "@nestjs/cli": "^6.13.2",
39 | "@nestjs/schematics": "^6.7.0",
40 | "@nestjs/testing": "^6.7.1",
41 | "@types/express": "^4.17.1",
42 | "@types/jest": "^24.0.18",
43 | "@types/node": "^12.7.5",
44 | "@types/supertest": "^2.0.8",
45 | "jest": "^24.9.0",
46 | "prettier": "^1.18.2",
47 | "supertest": "^4.0.2",
48 | "ts-jest": "^24.1.0",
49 | "ts-loader": "^6.1.1",
50 | "ts-node": "^8.4.1",
51 | "tsconfig-paths": "^3.9.0",
52 | "tslint": "^5.20.0",
53 | "typescript": "^3.6.3"
54 | },
55 | "jest": {
56 | "moduleFileExtensions": [
57 | "js",
58 | "json",
59 | "ts"
60 | ],
61 | "rootDir": "src",
62 | "testRegex": ".spec.ts$",
63 | "transform": {
64 | "^.+\\.(t|j)s$": "ts-jest"
65 | },
66 | "coverageDirectory": "../coverage",
67 | "testEnvironment": "node"
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/app.module.ts:
--------------------------------------------------------------------------------
1 | // src/app.module.ts
2 |
3 | import { Module } from '@nestjs/common';
4 | import { ItemsModule } from './items/items.module';
5 | import { AuthzModule } from './authz/authz.module';
6 |
7 | @Module({
8 | imports: [ItemsModule, AuthzModule],
9 | controllers: [],
10 | providers: [],
11 | })
12 | export class AppModule {}
13 |
--------------------------------------------------------------------------------
/src/authz/authz.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { PassportModule } from '@nestjs/passport';
3 | import { JwtStrategy } from './jwt.strategy';
4 |
5 | @Module({
6 | imports: [PassportModule.register({ defaultStrategy: 'jwt' })],
7 | providers: [JwtStrategy],
8 | exports: [PassportModule],
9 | })
10 | export class AuthzModule {}
11 |
--------------------------------------------------------------------------------
/src/authz/jwt.strategy.ts:
--------------------------------------------------------------------------------
1 | // src/authz/jwt.strategy.ts
2 |
3 | import { Injectable } from '@nestjs/common';
4 | import { PassportStrategy } from '@nestjs/passport';
5 | import { ExtractJwt, Strategy } from 'passport-jwt';
6 | import { passportJwtSecret } from 'jwks-rsa';
7 | import * as dotenv from 'dotenv';
8 |
9 | dotenv.config();
10 |
11 | @Injectable()
12 | export class JwtStrategy extends PassportStrategy(Strategy) {
13 | constructor() {
14 | super({
15 | secretOrKeyProvider: passportJwtSecret({
16 | cache: true,
17 | rateLimit: true,
18 | jwksRequestsPerMinute: 5,
19 | jwksUri: `${process.env.AUTH0_DOMAIN}.well-known/jwks.json`,
20 | }),
21 |
22 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
23 | audience: process.env.AUTH0_AUDIENCE,
24 | issuer: `${process.env.AUTH0_DOMAIN}`,
25 | algorithms: ['RS256'],
26 | });
27 | }
28 |
29 | validate(payload: any) {
30 | return payload;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/item.ts:
--------------------------------------------------------------------------------
1 | // src/item.ts
2 |
3 | import { IsString, IsNumber, IsOptional } from 'class-validator';
4 |
5 | export class Item {
6 | @IsNumber() @IsOptional() readonly id: number;
7 | @IsString() readonly name: string;
8 | @IsNumber() readonly price: number;
9 | @IsString() readonly description: string;
10 | @IsString() readonly image: string;
11 | }
12 |
--------------------------------------------------------------------------------
/src/items.ts:
--------------------------------------------------------------------------------
1 | // src/items.ts
2 |
3 | import { Item } from './item';
4 |
5 | export class Items {
6 | [key: number]: Item;
7 | }
8 |
--------------------------------------------------------------------------------
/src/items/items.controller.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Body,
3 | Controller,
4 | Delete,
5 | Get,
6 | Param,
7 | Post,
8 | Put,
9 | UseGuards,
10 | } from '@nestjs/common';
11 | import { ItemsService } from './items.service';
12 | import { Items } from '../items';
13 | import { Item } from '../item';
14 | import { AuthGuard } from '@nestjs/passport';
15 | import { Permissions } from '../permissions.decorator';
16 | import { PermissionsGuard } from '../permissions.guard';
17 |
18 | @Controller('items')
19 | export class ItemsController {
20 | constructor(private readonly itemsService: ItemsService) {}
21 |
22 | @Get()
23 | async findAll(): Promise {
24 | return this.itemsService.findAll();
25 | }
26 |
27 | @Get(':id')
28 | async find(@Param('id') id: number): Promise- {
29 | return this.itemsService.find(id);
30 | }
31 |
32 | @UseGuards(AuthGuard('jwt'), PermissionsGuard)
33 | @Post()
34 | @Permissions('create:items')
35 | create(@Body('item') item: Item) {
36 | this.itemsService.create(item);
37 | }
38 |
39 | @UseGuards(AuthGuard('jwt'), PermissionsGuard)
40 | @Put()
41 | @Permissions('update:items')
42 | update(@Body('item') item: Item) {
43 | this.itemsService.update(item);
44 | }
45 |
46 | @UseGuards(AuthGuard('jwt'), PermissionsGuard)
47 | @Delete(':id')
48 | @Permissions('delete:items')
49 | delete(@Param('id') id: number) {
50 | this.itemsService.delete(id);
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/items/items.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ItemsService } from './items.service';
3 | import { ItemsController } from './items.controller';
4 |
5 | @Module({
6 | providers: [ItemsService],
7 | controllers: [ItemsController],
8 | })
9 | export class ItemsModule {}
10 |
--------------------------------------------------------------------------------
/src/items/items.service.ts:
--------------------------------------------------------------------------------
1 | // src/items/items.service.ts
2 |
3 | import { Injectable } from '@nestjs/common';
4 | import { Item } from '../item';
5 | import { Items } from '../items';
6 |
7 | @Injectable()
8 | export class ItemsService {
9 | private readonly items: Items = {
10 | 1: {
11 | id: 1,
12 | name: 'Burger',
13 | price: 5.99,
14 | description: 'Tasty',
15 | image: 'https://cdn.auth0.com/blog/whatabyte/burger-sm.png',
16 | },
17 | 2: {
18 | id: 2,
19 | name: 'Pizza',
20 | price: 2.99,
21 | description: 'Cheesy',
22 | image: 'https://cdn.auth0.com/blog/whatabyte/pizza-sm.png',
23 | },
24 | 3: {
25 | id: 3,
26 | name: 'Tea',
27 | price: 1.99,
28 | description: 'Informative',
29 | image: 'https://cdn.auth0.com/blog/whatabyte/tea-sm.png',
30 | },
31 | };
32 |
33 | findAll(): Items {
34 | return this.items;
35 | }
36 |
37 | create(newItem: Item) {
38 | const id = new Date().valueOf();
39 | this.items[id] = {
40 | ...newItem,
41 | id,
42 | };
43 | }
44 |
45 | find(id: number): Item {
46 | const record: Item = this.items[id];
47 |
48 | if (record) {
49 | return record;
50 | }
51 |
52 | throw new Error('No record found');
53 | }
54 |
55 | update(updatedItem: Item) {
56 | if (this.items[updatedItem.id]) {
57 | this.items[updatedItem.id] = updatedItem;
58 | return;
59 | }
60 |
61 | throw new Error('No record found to update');
62 | }
63 |
64 | delete(id: number) {
65 | const record: Item = this.items[id];
66 |
67 | if (record) {
68 | delete this.items[id];
69 | return;
70 | }
71 |
72 | throw new Error('No record found to delete');
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | // src/main.ts
2 |
3 | import { NestFactory } from '@nestjs/core';
4 | import { AppModule } from './app.module';
5 | import * as dotenv from 'dotenv';
6 | import { ValidationPipe } from '@nestjs/common';
7 |
8 | dotenv.config();
9 |
10 | async function bootstrap() {
11 | const app = await NestFactory.create(AppModule);
12 |
13 | app.enableCors();
14 | app.useGlobalPipes(
15 | new ValidationPipe({
16 | disableErrorMessages: true,
17 | }),
18 | );
19 |
20 | await app.listen(process.env.PORT);
21 | }
22 | bootstrap();
23 |
--------------------------------------------------------------------------------
/src/permissions.decorator.ts:
--------------------------------------------------------------------------------
1 | // src/permissions.decorator.ts
2 |
3 | import { SetMetadata } from '@nestjs/common';
4 |
5 | export const Permissions = (...permissions: string[]) =>
6 | SetMetadata('permissions', permissions);
7 |
--------------------------------------------------------------------------------
/src/permissions.guard.ts:
--------------------------------------------------------------------------------
1 | // src/permissions.guard.ts
2 |
3 | import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
4 | import { Observable } from 'rxjs';
5 | import { Reflector } from '@nestjs/core';
6 |
7 | @Injectable()
8 | export class PermissionsGuard implements CanActivate {
9 | constructor(private readonly reflector: Reflector) {}
10 |
11 | canActivate(
12 | context: ExecutionContext,
13 | ): boolean | Promise | Observable {
14 | const routePermissions = this.reflector.get(
15 | 'permissions',
16 | context.getHandler(),
17 | );
18 |
19 | const userPermissions = context.getArgs()[0].user.permissions;
20 |
21 | if (!routePermissions) {
22 | return true;
23 | }
24 |
25 | const hasPermission = () =>
26 | routePermissions.every(routePermission =>
27 | userPermissions.includes(routePermission),
28 | );
29 |
30 | return hasPermission();
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "target": "es2017",
9 | "sourceMap": true,
10 | "outDir": "./dist",
11 | "baseUrl": "./",
12 | "incremental": true
13 | },
14 | "exclude": ["node_modules", "dist"]
15 | }
16 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "defaultSeverity": "error",
3 | "extends": ["tslint:recommended"],
4 | "jsRules": {
5 | "no-unused-expression": true
6 | },
7 | "rules": {
8 | "quotemark": [true, "single"],
9 | "member-access": [false],
10 | "ordered-imports": [false],
11 | "max-line-length": [true, 150],
12 | "member-ordering": [false],
13 | "interface-name": [false],
14 | "arrow-parens": false,
15 | "object-literal-sort-keys": false
16 | },
17 | "rulesDirectory": []
18 | }
19 |
--------------------------------------------------------------------------------