diff --git a/README.md b/README.md
index cd68f917..c05907cc 100644
--- a/README.md
+++ b/README.md
@@ -16,9 +16,9 @@
| Method | URL | Description |
| -------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| [GET] | /profile/ | Returns an array of all existing profiles. |
-| [GET] | /profile/:okta_id/ | Returns the profile object with the specified `okta_id`. |
-| [GET] | /profiles/users/:profile_id (BUG: /profiles/users route does not exist; app.js only connects to /profiles/user) | Returns an array filled with event objects that contains information based on profile_id and role_id. |
-| [GET] | /profile/role/:role_id (BUG: does not return any data) | Returns an array filled with event objects that contain information based on role_id for all profiles of a role_id type. |
+| [GET] | /profile/:profile_id/ (BUG: does not complete request) | Returns the profile object with the specified `profile_id`. |
+| [GET] | /profiles/users/:profile_id | Returns an array filled with event objects that contains information based on profile_id and role_id. |
+| [GET] | /profile/role/:role_id | Returns an array filled with event objects that contain information based on role_id for all profiles of a role_id type. |
| [POST] | /profile/ | Requires a name, password, and email. Registers a new user. |
| [PUT] | /profile/ | Returns an event object with the specified `okta`. Updates specific profile. |
| [DELETE] | /profile/:okta_id/ | Returns an event object with the specified `okta`. Deletes specific profile. |
diff --git a/api/app.js b/api/app.js
index 2ce94ebc..66c4504c 100644
--- a/api/app.js
+++ b/api/app.js
@@ -64,7 +64,7 @@ app.use('/', indexRouter);
app.use(['/profile', '/profiles'], profileRouter);
app.use(['/parent', '/parents'], parentRouter);
app.use(['/instructor', '/instructors'], instructorRouter);
-app.use(['/user'], userRouter);
+app.use(['/user', '/users'], userRouter);
app.use(['/conversation', '/conversations'], inboxRouter);
app.use(
['/program-type', '/program-types', '/program', '/programs'],
diff --git a/api/courses/coursesRouter.js b/api/courses/coursesRouter.js
index a66c8348..46c22712 100644
--- a/api/courses/coursesRouter.js
+++ b/api/courses/coursesRouter.js
@@ -402,9 +402,9 @@ router.post(
router.put(
'/:course_id',
+ authRequired,
validateCourseObject,
checkInstructorExists,
- authRequired,
checkCourseExists,
(req, res, next) => {
const course_id = parseInt(req.params.course_id);
diff --git a/api/instructor/instructorRouter.js b/api/instructor/instructorRouter.js
index ae07d1d4..85eb3451 100644
--- a/api/instructor/instructorRouter.js
+++ b/api/instructor/instructorRouter.js
@@ -2,7 +2,7 @@ const express = require('express');
const authRequired = require('../middleware/authRequired');
const Instructors = require('./instructorModel');
const router = express.Router();
-const Profiles = require('../profile/profileModel');
+// const Profiles = require('../profile/profileModel');
const {
getInstructorId,
checkInstructorExist,
@@ -11,37 +11,36 @@ const {
/* Create a new Instructor profile */
router.post('/register', (req, res) => {
if (!req.body) return res.sendStatus(400);
- const newInstructor = {
- profile: {
- firstName: req.body.firstName,
- lastName: req.body.lastName,
- email: req.body.email,
- login: req.body.email,
- },
- }; // now sending to Okta
-
- // TO-DO: Implement Auth0 -> create new user
- // oktaClient
- // .createUser(newInstructor)
- // .then((instructor) => {
- // return Profiles.create({
- // email: instructor.profile.email,
- // name: instructor.profile.firstName + ' ' + instructor.profile.lastName,
- // okta_id: instructor.id,
- // role_id: 3,
- // pending: true,
- // }).then(() => instructor);
- // })
- // .then((instructor) => {
- // res.status(201);
- // res.send(instructor);
- // })
- // .catch((err) => {
- // res.status(400);
- // res.send(err);
- // });
+ // const newInstructor = {
+ // profile: {
+ // firstName: req.body.firstName,
+ // lastName: req.body.lastName,
+ // email: req.body.email,
+ // login: req.body.email,
+ // },
});
+// TO-DO: Implement Auth0 -> create new user
+// oktaClient
+// .createUser(newInstructor)
+// .then((instructor) => {
+// return Profiles.create({
+// email: instructor.profile.email,
+// name: instructor.profile.firstName + ' ' + instructor.profile.lastName,
+// okta_id: instructor.id,
+// role_id: 3,
+// pending: true,
+// }).then(() => instructor);
+// })
+// .then((instructor) => {
+// res.status(201);
+// res.send(instructor);
+// })
+// .catch((err) => {
+// res.status(400);
+// res.send(err);
+// });
+
router.get('/courses', authRequired, getInstructorId, (req, res, next) => {
Instructors.findInstructorCourses(req.instructor_id)
.then((courses) => {
diff --git a/api/middleware/auth0Middleware.js b/api/middleware/auth0Middleware.js
new file mode 100644
index 00000000..b7002c72
--- /dev/null
+++ b/api/middleware/auth0Middleware.js
@@ -0,0 +1,43 @@
+const { expressjwt: jwt } = require('express-jwt');
+const jwksRsa = require('jwks-rsa');
+const createError = require('http-errors');
+const config = require('../../config/auth0');
+
+// writing the config for expressJwt method to be invoked with product environment variables
+const verifyJwt = jwt({
+ secret: jwksRsa.expressJwtSecret({
+ cache: true,
+ rateLimit: true,
+ jwksRequestsPerMinute: 5,
+ jwksUri: config.jwksUri,
+ }),
+ audience: config.audience,
+ issuer: config.issuer,
+ algorithms: ['RS256'],
+ requestProperty: 'auth0User',
+}).unless({ path: ['/'] });
+//************ */
+//in the above line add all public endpoints (routes with no need for auth) as a string inside the path array [] <=
+// **** IMPORTANT note: if your public endpoint has descendent end points (ex.: ./yourRouterEndPoint/:user_id) you need ot use regex as in the following example:
+// ({ path: ['/', /^\/\/.*/] }); => example: ({ path: ['/', /^\/application\/.*/, /^\/roles\/.*/] });
+//************ */
+
+//exporting the verifyjwt method as a middleware to be used in app.js server file against all routes except what is in the exception array in above method
+const authRequired = (req, res, next) => {
+ verifyJwt(req, res, next);
+};
+
+//method to grab the current authenticated user from the req.auth0User and save it to req.body.user to be used globally in the server
+const authProfile = async (req, res, next) => {
+ try {
+ const profile = await req.auth0User;
+ if (profile) {
+ req.body.profile = profile;
+ }
+ next();
+ } catch (err) {
+ next(createError(401, err.message));
+ }
+};
+
+module.exports = { authRequired, authProfile };
diff --git a/api/middleware/authRequired.js b/api/middleware/authRequired.js
index 264fd399..07732b74 100644
--- a/api/middleware/authRequired.js
+++ b/api/middleware/authRequired.js
@@ -1,23 +1,32 @@
-// const createError = require('http-errors');
-// const Profiles = require('../profile/profileModel');
+const Profiles = require('../profile/profileModel');
/**
- * TO-DO: Implement Auth0
- * If token verification fails
- * catch(err) => next(createError(401, err.message));
- */
-
-/**
- * A simple middleware that asserts valid Okta idToken and sends 401 responses
+ * A simple middleware that asserts valid Auth0 idToken and sends 401 responses
* if the token is not present or fails validation. If the token is valid its
* contents are attached to req.profile
*/
const authRequired = async (req, res, next) => {
- // TO-DO: Implement Auth0 -> assure authorized through Header Token
+ try {
+ // Verify that the token is valid
+ const profile = Profiles.findOrCreateProfile;
+ console.log("I'm in authRequired");
+
+ if (profile) {
+ req.profile = profile;
+ } else {
+ next({
+ status: 401,
+ message: 'Unable to process idToken',
+ });
+ }
- // const match = authHeader.match(/Bearer (.+)/);
- // if (!match) throw new Error('Missing idToken');
- // req.profile = authorizedProfile;
- next();
+ // Proceed with request if token is valid
+ next();
+ } catch (err) {
+ next({
+ status: err.status || 500,
+ message: err.message || 'Internal Server Error',
+ });
+ }
};
module.exports = authRequired;
diff --git a/api/middleware/getUserfromAuth0.js b/api/middleware/getUserfromAuth0.js
new file mode 100644
index 00000000..ed41bdbb
--- /dev/null
+++ b/api/middleware/getUserfromAuth0.js
@@ -0,0 +1,41 @@
+const axios = require('axios');
+const config = require('../../config/auth0');
+
+const getUserAuth0 = async (req, res, next) => {
+ try {
+ // Check if there's a token in the auth header
+ const authHeader = req.headers.authorization || '';
+ const token = authHeader.match(/Bearer (.+)/);
+ if (!token) {
+ next({
+ status: 401,
+ message: 'Missing Token',
+ });
+ }
+ // Check against Auth0 if token is valid and grab user data
+ await axios
+ .get(`${config.issuer}/userinfo`, {
+ headers: {
+ authorization: authHeader,
+ },
+ })
+ .then((res) => {
+ const profile = res.data;
+ req.profile = profile;
+ next();
+ })
+ .catch((err) => {
+ next({
+ status: 404,
+ message: err,
+ });
+ });
+ } catch (err) {
+ next({
+ status: err.status || 500,
+ message: err.message || 'Internal Server Error',
+ });
+ }
+};
+
+module.exports = { getUserAuth0 };
diff --git a/api/middleware/ownerAuthorization.js b/api/middleware/ownerAuthorization.js
index 03348912..3f6ac283 100644
--- a/api/middleware/ownerAuthorization.js
+++ b/api/middleware/ownerAuthorization.js
@@ -1,16 +1,16 @@
-// pass in the key to the profile that is the owner of what's being affected
+// pass in the key parameter to the profile that is the owner of what's being affected
const ownerAuthorization = (key) => (req, res, next) => {
// TO-DO: Implement Auth0 - check that the role_id matches the key passed
- // if (
- // req.profile.profile_id === req[key].profile_id ||
- // (req.profile.role_id < 3 && req.profile.role_id < (req[key].role_id || 3))
- // )
- // next();
- // else
- // res
- // .status(401)
- // .json({ message: 'You are not authorized to take this action' });
+ if (
+ req.profile.profile_id === req[key].profile_id ||
+ (req.profile.role_id < 3 && req.profile.role_id < (req[key].role_id || 3))
+ )
+ next();
+ else
+ res
+ .status(401)
+ .json({ message: 'You are not authorized to take this action' });
next();
};
diff --git a/api/profile/profileMiddleware.js b/api/profile/profileMiddleware.js
index a940165b..44d7f12a 100644
--- a/api/profile/profileMiddleware.js
+++ b/api/profile/profileMiddleware.js
@@ -2,9 +2,11 @@ const Profiles = require('./profileModel');
const checkProfileObject = (req, res, next) => {
const { email, name, role_id, avatarUrl } = req.body;
- if (!role_id) return next({ status: 400, message: 'role_id is required' });
- if (!email) return next({ status: 400, message: 'email is required' });
- if (!name) return next({ status: 400, message: 'name is required' });
+ console.log(req.body);
+ console.log("I'm in the obj middleware");
+ // if (!role_id) return next({ status: 400, message: 'role_id is required' });
+ // if (!email) return next({ status: 400, message: 'email is required' });
+ // if (!name) return next({ status: 400, message: 'name is required' });
if (
typeof role_id !== 'number' ||
typeof email !== 'string' ||
@@ -15,6 +17,7 @@ const checkProfileObject = (req, res, next) => {
message:
'variables in the request body must all be of type string except role_id must be of type number',
});
+ console.log('I made it to the other side!');
if (email.length > 255 || name.length > 255)
return next({
status: 400,
diff --git a/api/profile/profileModel.js b/api/profile/profileModel.js
index e184f57a..c6f5ac62 100644
--- a/api/profile/profileModel.js
+++ b/api/profile/profileModel.js
@@ -25,9 +25,7 @@ const remove = async (id) => {
};
const findOrCreateProfile = async (profileObj) => {
- const foundProfile = await findById(profileObj.okta_id).then(
- (profile) => profile
- );
+ const foundProfile = await findById(profileObj.id).then((profile) => profile);
if (foundProfile) {
return foundProfile;
} else {
diff --git a/api/profile/profileRouter.js b/api/profile/profileRouter.js
index 1441c5ad..4a7f03c0 100644
--- a/api/profile/profileRouter.js
+++ b/api/profile/profileRouter.js
@@ -9,17 +9,22 @@ const {
checkProfileExist,
} = require('./profileMiddleware');
-router.get('/role/:role_id', authRequired, checkRoleExist, function (req, res) {
- const role_id = req.params.role_id;
- Profiles.findByRoleId(role_id)
- .then((roleList) => {
- res.status(200).json(roleList);
- })
- .catch((err) => {
- console.log(err);
- res.status(500).json({ message: err.message });
- });
-});
+router.get(
+ '/:role/:role_id',
+ authRequired,
+ checkRoleExist,
+ function (req, res) {
+ const role_id = req.params.role_id;
+ Profiles.findByRoleId(role_id)
+ .then((roleList) => {
+ res.status(200).json(roleList);
+ })
+ .catch((err) => {
+ console.log(err);
+ res.status(500).json({ message: err.message });
+ });
+ }
+);
router.get(
'/users/:profile_id',
@@ -194,9 +199,9 @@ router.get(
* profile:
* $ref: '#/components/schemas/Profile'
*/
-router.post('/', checkProfileObject, async (req, res) => {
+router.post('/', authRequired, checkProfileObject, async (req, res) => {
const profile = req.body;
-
+ console.log("I'm back to the router!");
// TO-DO: Implement Auth0 - check DB if specific Auth0 ID already exists
// changed verification from findById(okta_id) to findBy(email)
try {
diff --git a/api/programs/programTypesRouter.js b/api/programs/programTypesRouter.js
index f5857503..49416478 100644
--- a/api/programs/programTypesRouter.js
+++ b/api/programs/programTypesRouter.js
@@ -151,8 +151,8 @@ router.get('/:id', authRequired, checkProgramExists, async function (req, res) {
router.post(
'/',
- validateProgramObject,
authRequired,
+ validateProgramObject,
checkIfProgramIsUnique,
async (req, res, next) => {
try {
diff --git a/api/user/userRouter.js b/api/user/userRouter.js
index 52b88a33..15a8ad82 100644
--- a/api/user/userRouter.js
+++ b/api/user/userRouter.js
@@ -32,7 +32,7 @@ router.post('/register', (req, res) => {
router.get('/', authRequired, function (req, res) {
// TO-DO: Implement Auth0 to check logged in (middleware) then use req.profile from what is received back
- // const { role_id, profile_id } = req.profile;
+ const { role_id, profile_id } = req.profile;
User.findUserData(role_id, profile_id)
.then((user) => {
diff --git a/config/auth0.js b/config/auth0.js
new file mode 100644
index 00000000..cc7555ad
--- /dev/null
+++ b/config/auth0.js
@@ -0,0 +1,7 @@
+module.exports = {
+ jwksUri: process.env.AUTH0_JWKS_URI,
+ audience: process.env.AUTH0_AUDIENCE,
+ issuer: process.env.AUTH0_ISSUER,
+ connection: process.env.AUTH0_CONNECTION,
+ token: process.env.AUTH0_TOKEN,
+};
diff --git a/data/seeds/001_profiles.js b/data/seeds/001_profiles.js
index 74ce7f57..6652399c 100644
--- a/data/seeds/001_profiles.js
+++ b/data/seeds/001_profiles.js
@@ -1,8 +1,7 @@
const faker = require('faker');
let profiles = [...new Array(20)].map((i, idx) => ({
- // TO-DO: Implement Auth0 ID
- // okta_id: idx === 0 ? '00ulthapbErVUwVJy4x6' : faker.random.alphaNumeric(20),
+ auth0_id: idx === 0 ? '00ulthapbErVUwVJy4x6' : faker.random.alphaNumeric(20),
avatarUrl: faker.image.avatar(),
email: idx === 0 ? 'llama001@maildrop.cc"' : faker.internet.email(),
name:
@@ -13,9 +12,9 @@ let profiles = [...new Array(20)].map((i, idx) => ({
}));
/*
- Manually setting the `id` for each profile to the Okta provided ID. Adding
+ Manually setting the `id` for each profile to the Auth0 provided ID. Adding
profiles was not in scope for this iteration, but adding profiles in the
- future will require the okta-id to be set as the `id` for each profile.
+ future will require the auth0-id to be set as the `id` for each profile.
*/
// TO-DO: Implement dummy Auth0 IDs
diff --git a/package-lock.json b/package-lock.json
index 367eb7bf..869f1a28 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -15,6 +15,7 @@
"debug": "~2.6.9",
"dotenv": "^8.2.0",
"express": "~4.16.1",
+ "express-oauth2-jwt-bearer": "^1.3.0",
"faker": "^4.1.0",
"helmet": "^3.23.1",
"http-errors": "~1.6.3",
@@ -3794,6 +3795,17 @@
"node": ">= 0.10.0"
}
},
+ "node_modules/express-oauth2-jwt-bearer": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/express-oauth2-jwt-bearer/-/express-oauth2-jwt-bearer-1.3.0.tgz",
+ "integrity": "sha512-m8UyAxL9eHpDDmSxWEaKLEPlE+6lfRCT/z3i2Cm0MYajUP4L/WFaZ66ch5KrPPiHEy91op6fhzZ0RTN8Ldap1Q==",
+ "dependencies": {
+ "jose": "^4.9.2"
+ },
+ "engines": {
+ "node": "^12.19.0 || ^14.15.0 || ^16.13.0 || ^18.12.0"
+ }
+ },
"node_modules/express/node_modules/cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
@@ -6287,6 +6299,14 @@
"node": ">= 8.3"
}
},
+ "node_modules/jose": {
+ "version": "4.11.2",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.2.tgz",
+ "integrity": "sha512-njj0VL2TsIxCtgzhO+9RRobBvws4oYyCM8TpvoUQwl/MbIM3NFJRR9+e6x0sS5xXaP1t6OCBkaBME98OV9zU5A==",
+ "funding": {
+ "url": "https://github.com/sponsors/panva"
+ }
+ },
"node_modules/js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -13776,6 +13796,14 @@
}
}
},
+ "express-oauth2-jwt-bearer": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/express-oauth2-jwt-bearer/-/express-oauth2-jwt-bearer-1.3.0.tgz",
+ "integrity": "sha512-m8UyAxL9eHpDDmSxWEaKLEPlE+6lfRCT/z3i2Cm0MYajUP4L/WFaZ66ch5KrPPiHEy91op6fhzZ0RTN8Ldap1Q==",
+ "requires": {
+ "jose": "^4.9.2"
+ }
+ },
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -15678,6 +15706,11 @@
"supports-color": "^7.0.0"
}
},
+ "jose": {
+ "version": "4.11.2",
+ "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.2.tgz",
+ "integrity": "sha512-njj0VL2TsIxCtgzhO+9RRobBvws4oYyCM8TpvoUQwl/MbIM3NFJRR9+e6x0sS5xXaP1t6OCBkaBME98OV9zU5A=="
+ },
"js-tokens": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
diff --git a/package.json b/package.json
index 88a72c1c..bf8f183a 100644
--- a/package.json
+++ b/package.json
@@ -68,6 +68,7 @@
"debug": "~2.6.9",
"dotenv": "^8.2.0",
"express": "~4.16.1",
+ "express-oauth2-jwt-bearer": "^1.3.0",
"faker": "^4.1.0",
"helmet": "^3.23.1",
"http-errors": "~1.6.3",
diff --git a/server.js b/server.js
index fd7133c7..0056a321 100644
--- a/server.js
+++ b/server.js
@@ -1,6 +1,38 @@
require('dotenv').config();
-
const app = require('./api/app.js');
+const { auth, requiredScopes } = require('express-oauth2-jwt-bearer');
+
+// Authorization middleware. When used, the Access Token must
+// exist and be verified against the Auth0 JSON Web Key Set.
+const checkJwt = auth({
+ audience: 'https://a.coderheroes.dev/',
+ issuerBaseURL: `https://dev-35n2stap.auth0.com/`,
+});
+
+// This route doesn't need authentication
+app.get('/api/public', function (req, res) {
+ res.json({
+ message:
+ "Hello from a public endpoint! You don't need to be authenticated to see this.",
+ });
+});
+
+// This route needs authentication
+app.get('/api/private', checkJwt, function (req, res) {
+ res.json({
+ message:
+ 'Hello from a private endpoint! You need to be authenticated to see this.',
+ });
+});
+
+const checkScopes = requiredScopes('read:messages');
+
+app.get('/api/private-scoped', checkJwt, checkScopes, function (req, res) {
+ res.json({
+ message:
+ 'Hello from a private endpoint! You need to be authenticated and have a scope of read:messages to see this.',
+ });
+});
const port = process.env.PORT || 8000;
app.listen(port, () => console.log(`\n** Running on port ${port} **\n`));
diff --git a/temp.js b/temp.js
deleted file mode 100644
index e69de29b..00000000