Browse Source

introduce the vatsim oauth logic

Sven Czarnian 2 years ago
parent
commit
566762a36c
7 changed files with 188 additions and 139 deletions
  1. 7 1
      config/config.yaml
  2. 67 138
      package-lock.json
  3. 1 0
      package.json
  4. 8 0
      src/app.module.ts
  5. 18 0
      src/auth/auth.controller.spec.ts
  6. 78 0
      src/auth/auth.controller.ts
  7. 9 0
      src/auth/auth.module.ts

+ 7 - 1
config/config.yaml

@@ -1,4 +1,10 @@
 database:
   host: 'localhost'
   port: 27017
-  name: 'aman'
+  name: 'aman'
+vatsim-auth:
+  base-url: 'https://auth-dev.vatsim.net'
+  token-endpoint: 'oauth/token'
+  user-endpoint: 'api/user'
+  client-id: '461'
+  client-secret: 'kMAuQeIa4TvfZOIZw0t5AA5G5PA0UKTehtSASYdI'

+ 67 - 138
package-lock.json

@@ -9,6 +9,7 @@
       "version": "0.0.1",
       "license": "MIT",
       "dependencies": {
+        "@nestjs/axios": "^0.1.0",
         "@nestjs/common": "^9.0.0",
         "@nestjs/config": "^2.2.0",
         "@nestjs/core": "^9.0.0",
@@ -1432,6 +1433,19 @@
         "@jridgewell/sourcemap-codec": "1.4.14"
       }
     },
+    "node_modules/@nestjs/axios": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-0.1.0.tgz",
+      "integrity": "sha512-b2TT2X6BFbnNoeteiaxCIiHaFcSbVW+S5yygYqiIq5i6H77yIU3IVuLdpQkHq8/EqOWFwMopLN8jdkUT71Am9w==",
+      "dependencies": {
+        "axios": "0.27.2"
+      },
+      "peerDependencies": {
+        "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0",
+        "reflect-metadata": "^0.1.12",
+        "rxjs": "^6.0.0 || ^7.0.0"
+      }
+    },
     "node_modules/@nestjs/cli": {
       "version": "9.1.4",
       "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.1.4.tgz",
@@ -1606,15 +1620,6 @@
         "rxjs": "^7.0.0"
       }
     },
-    "node_modules/@nestjs/passport": {
-      "version": "9.0.0",
-      "resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-9.0.0.tgz",
-      "integrity": "sha512-Gnh8n1wzFPOLSS/94X1sUP4IRAoXTgG4odl7/AO5h+uwscEGXxJFercrZfqdAwkWhqkKWbsntM3j5mRy/6ZQDA==",
-      "peerDependencies": {
-        "@nestjs/common": "^8.0.0 || ^9.0.0",
-        "passport": "^0.4.0 || ^0.5.0 || ^0.6.0"
-      }
-    },
     "node_modules/@nestjs/platform-express": {
       "version": "9.1.5",
       "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.1.5.tgz",
@@ -2710,8 +2715,16 @@
     "node_modules/asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
-      "dev": true
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "node_modules/axios": {
+      "version": "0.27.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+      "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+      "dependencies": {
+        "follow-redirects": "^1.14.9",
+        "form-data": "^4.0.0"
+      }
     },
     "node_modules/babel-jest": {
       "version": "28.1.3",
@@ -2844,14 +2857,6 @@
         }
       ]
     },
-    "node_modules/base64url": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz",
-      "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==",
-      "engines": {
-        "node": ">=6.0.0"
-      }
-    },
     "node_modules/binary-extensions": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -3293,7 +3298,6 @@
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
       "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
       "dependencies": {
         "delayed-stream": "~1.0.0"
       },
@@ -3496,7 +3500,6 @@
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
       "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
-      "dev": true,
       "engines": {
         "node": ">=0.4.0"
       }
@@ -4317,6 +4320,25 @@
       "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
       "dev": true
     },
+    "node_modules/follow-redirects": {
+      "version": "1.15.2",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+      "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/fork-ts-checker-webpack-plugin": {
       "version": "7.2.13",
       "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.13.tgz",
@@ -4371,7 +4393,6 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
       "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
-      "dev": true,
       "dependencies": {
         "asynckit": "^0.4.0",
         "combined-stream": "^1.0.8",
@@ -6496,11 +6517,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/oauth": {
-      "version": "0.9.15",
-      "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
-      "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
-    },
     "node_modules/object-assign": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -6717,50 +6733,6 @@
         "node": ">= 0.8"
       }
     },
-    "node_modules/passport": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz",
-      "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==",
-      "dependencies": {
-        "passport-strategy": "1.x.x",
-        "pause": "0.0.1",
-        "utils-merge": "^1.0.1"
-      },
-      "engines": {
-        "node": ">= 0.4.0"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/jaredhanson"
-      }
-    },
-    "node_modules/passport-oauth2": {
-      "version": "1.6.1",
-      "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz",
-      "integrity": "sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ==",
-      "dependencies": {
-        "base64url": "3.x.x",
-        "oauth": "0.9.x",
-        "passport-strategy": "1.x.x",
-        "uid2": "0.0.x",
-        "utils-merge": "1.x.x"
-      },
-      "engines": {
-        "node": ">= 0.4.0"
-      },
-      "funding": {
-        "type": "github",
-        "url": "https://github.com/sponsors/jaredhanson"
-      }
-    },
-    "node_modules/passport-strategy": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
-      "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==",
-      "engines": {
-        "node": ">= 0.4.0"
-      }
-    },
     "node_modules/path-exists": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -6807,11 +6779,6 @@
         "node": ">=8"
       }
     },
-    "node_modules/pause": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
-      "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
-    },
     "node_modules/picocolors": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -8300,11 +8267,6 @@
         "node": ">=4.2.0"
       }
     },
-    "node_modules/uid2": {
-      "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz",
-      "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA=="
-    },
     "node_modules/universalify": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",
@@ -9798,6 +9760,14 @@
         "@jridgewell/sourcemap-codec": "1.4.14"
       }
     },
+    "@nestjs/axios": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/@nestjs/axios/-/axios-0.1.0.tgz",
+      "integrity": "sha512-b2TT2X6BFbnNoeteiaxCIiHaFcSbVW+S5yygYqiIq5i6H77yIU3IVuLdpQkHq8/EqOWFwMopLN8jdkUT71Am9w==",
+      "requires": {
+        "axios": "0.27.2"
+      }
+    },
     "@nestjs/cli": {
       "version": "9.1.4",
       "resolved": "https://registry.npmjs.org/@nestjs/cli/-/cli-9.1.4.tgz",
@@ -9890,11 +9860,6 @@
       "integrity": "sha512-fC33b8nwR7C3y6hlTyKjiHQVU57SFOd3Dwa6alWj3BOhK5bHt8Ld6qOK2G35/XXPHjtSIk3LIKEqwyK5xGcmBA==",
       "requires": {}
     },
-    "@nestjs/passport": {
-      "version": "https://registry.npmjs.org/@nestjs/passport/-/passport-9.0.0.tgz",
-      "integrity": "sha512-Gnh8n1wzFPOLSS/94X1sUP4IRAoXTgG4odl7/AO5h+uwscEGXxJFercrZfqdAwkWhqkKWbsntM3j5mRy/6ZQDA==",
-      "requires": {}
-    },
     "@nestjs/platform-express": {
       "version": "9.1.5",
       "resolved": "https://registry.npmjs.org/@nestjs/platform-express/-/platform-express-9.1.5.tgz",
@@ -10770,8 +10735,16 @@
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
-      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
-      "dev": true
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "axios": {
+      "version": "0.27.2",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
+      "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
+      "requires": {
+        "follow-redirects": "^1.14.9",
+        "form-data": "^4.0.0"
+      }
     },
     "babel-jest": {
       "version": "28.1.3",
@@ -10865,11 +10838,6 @@
       "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
       "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
     },
-    "base64url": {
-      "version": "3.0.1",
-      "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz",
-      "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A=="
-    },
     "binary-extensions": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -11191,7 +11159,6 @@
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
       "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
-      "dev": true,
       "requires": {
         "delayed-stream": "~1.0.0"
       }
@@ -11351,8 +11318,7 @@
     "delayed-stream": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
-      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
-      "dev": true
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
     },
     "denque": {
       "version": "2.1.0",
@@ -11984,6 +11950,11 @@
       "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
       "dev": true
     },
+    "follow-redirects": {
+      "version": "1.15.2",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+      "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
+    },
     "fork-ts-checker-webpack-plugin": {
       "version": "7.2.13",
       "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-7.2.13.tgz",
@@ -12020,7 +11991,6 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
       "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
-      "dev": true,
       "requires": {
         "asynckit": "^0.4.0",
         "combined-stream": "^1.0.8",
@@ -13611,11 +13581,6 @@
         "path-key": "^3.0.0"
       }
     },
-    "oauth": {
-      "version": "0.9.15",
-      "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
-      "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA=="
-    },
     "object-assign": {
       "version": "4.1.1",
       "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -13765,32 +13730,6 @@
       "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
       "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
     },
-    "passport": {
-      "version": "0.6.0",
-      "resolved": "https://registry.npmjs.org/passport/-/passport-0.6.0.tgz",
-      "integrity": "sha512-0fe+p3ZnrWRW74fe8+SvCyf4a3Pb2/h7gFkQ8yTJpAO50gDzlfjZUZTO1k5Eg9kUct22OxHLqDZoKUWRHOh9ug==",
-      "requires": {
-        "passport-strategy": "1.x.x",
-        "pause": "0.0.1",
-        "utils-merge": "^1.0.1"
-      }
-    },
-    "passport-oauth2": {
-      "version": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz",
-      "integrity": "sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ==",
-      "requires": {
-        "base64url": "3.x.x",
-        "oauth": "0.9.x",
-        "passport-strategy": "1.x.x",
-        "uid2": "0.0.x",
-        "utils-merge": "1.x.x"
-      }
-    },
-    "passport-strategy": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
-      "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA=="
-    },
     "path-exists": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -13825,11 +13764,6 @@
       "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
       "dev": true
     },
-    "pause": {
-      "version": "0.0.1",
-      "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
-      "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
-    },
     "picocolors": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -14894,11 +14828,6 @@
       "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==",
       "dev": true
     },
-    "uid2": {
-      "version": "0.0.4",
-      "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz",
-      "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA=="
-    },
     "universalify": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz",

+ 1 - 0
package.json

@@ -21,6 +21,7 @@
     "test:e2e": "jest --config ./test/jest-e2e.json"
   },
   "dependencies": {
+    "@nestjs/axios": "^0.1.0",
     "@nestjs/common": "^9.0.0",
     "@nestjs/config": "^2.2.0",
     "@nestjs/core": "^9.0.0",

+ 8 - 0
src/app.module.ts

@@ -1,3 +1,4 @@
+import { HttpModule } from '@nestjs/axios';
 import { Module } from '@nestjs/common';
 import { ConfigModule, ConfigService } from '@nestjs/config';
 import { MongooseModule } from '@nestjs/mongoose';
@@ -8,6 +9,8 @@ import { AirportModule } from './airport/airport.module';
 import { LoggingModule } from './logging/logging.module';
 import { InboundModule } from './inbound/inbound.module';
 import { WeatherModule } from './weather/weather.module';
+import { AuthController } from './auth/auth.controller';
+import { AuthModule } from './auth/auth.module';
 
 @Module({
   imports: [
@@ -26,12 +29,17 @@ import { WeatherModule } from './weather/weather.module';
         )}`,
       }),
     }),
+    HttpModule.register({
+      timeout: 5000,
+      maxRedirects: 3,
+    }),
     VersioningModule,
     PerformanceModule,
     AirportModule,
     LoggingModule,
     InboundModule,
     WeatherModule,
+    AuthModule,
   ],
 })
 export class AppModule {}

+ 18 - 0
src/auth/auth.controller.spec.ts

@@ -0,0 +1,18 @@
+import { Test, TestingModule } from '@nestjs/testing';
+import { AuthController } from './auth.controller';
+
+describe('AuthController', () => {
+  let controller: AuthController;
+
+  beforeEach(async () => {
+    const module: TestingModule = await Test.createTestingModule({
+      controllers: [AuthController],
+    }).compile();
+
+    controller = module.get<AuthController>(AuthController);
+  });
+
+  it('should be defined', () => {
+    expect(controller).toBeDefined();
+  });
+});

+ 78 - 0
src/auth/auth.controller.ts

@@ -0,0 +1,78 @@
+import { HttpService } from '@nestjs/axios';
+import {
+  Controller,
+  Get,
+  HttpException,
+  HttpStatus,
+  Query,
+} from '@nestjs/common';
+import { ApiQuery } from '@nestjs/swagger';
+import { ConfigService } from '@nestjs/config';
+import { catchError, lastValueFrom, map } from 'rxjs';
+
+@Controller('auth')
+export class AuthController {
+  constructor(
+    private config: ConfigService,
+    private httpService: HttpService,
+  ) {}
+
+  @Get('/vatsim')
+  @ApiQuery({
+    name: 'code',
+    description: 'The authorization code',
+    type: String,
+  })
+  async vatsim(@Query('code') code): Promise<void> {
+    if (!code) {
+      throw new HttpException(
+        'Code not found in the query',
+        HttpStatus.NOT_ACCEPTABLE,
+      );
+    }
+
+    const token = await lastValueFrom(
+      this.httpService
+        .post(
+          `${this.config.get<string>(
+            'vatsim-auth.base-url',
+          )}/${this.config.get<string>('vatsim-auth.token-endpoint')}`,
+          {
+            grant_type: 'authorization_code',
+            client_id: this.config.get<string>('vatsim-auth.client-id'),
+            client_secret: this.config.get<string>('vatsim-auth.client-secret'),
+            redirect_uri: 'http://localhost:3000/auth/vatsim',
+            code,
+          },
+        )
+        .pipe(
+          map((response) => response.data.access_token),
+          catchError((err) => {
+            throw new HttpException(err.response.data, err.response.status);
+          }),
+        ),
+    );
+
+    const user = await lastValueFrom(
+      this.httpService
+        .get(
+          `${this.config.get<string>(
+            'vatsim-auth.base-url',
+          )}/${this.config.get<string>('vatsim-auth.user-endpoint')}`,
+          {
+            headers: {
+              Authorization: `Bearer ${token}`,
+              Accept: 'application/json',
+            },
+          },
+        )
+        .pipe(
+          map((response) => response.data.data),
+          catchError((err) => {
+            throw new HttpException(err.response.data, err.response.status);
+          }),
+        ),
+    );
+    console.log(user);
+  }
+}

+ 9 - 0
src/auth/auth.module.ts

@@ -0,0 +1,9 @@
+import { HttpModule } from '@nestjs/axios';
+import { Module } from '@nestjs/common';
+import { AuthController } from './auth.controller';
+
+@Module({
+  imports: [HttpModule],
+  controllers: [AuthController],
+})
+export class AuthModule {}