Copyediting of descriptions for Advanced Node & Express challenges (#39606)
* Copyediting of challenge descriptions * Copyedited descriptions of Chai challenges * Update curriculum/challenges/english/06-quality-assurance/advanced-node-and-express/clean-up-your-project-with-modules.english.md Co-authored-by: Shaun Hamilton <51722130+Sky020@users.noreply.github.com> * Update curriculum/challenges/english/06-quality-assurance/advanced-node-and-express/create-new-middleware.english.md Co-authored-by: Shaun Hamilton <51722130+Sky020@users.noreply.github.com> * Update curriculum/challenges/english/06-quality-assurance/advanced-node-and-express/implement-the-serialization-of-a-passport-user.english.md * Update curriculum/challenges/english/06-quality-assurance/advanced-node-and-express/create-new-middleware.english.md Co-authored-by: Shaun Hamilton <51722130+Sky020@users.noreply.github.com> * Update curriculum/challenges/english/06-quality-assurance/advanced-node-and-express/implementation-of-social-authentication.english.md Co-authored-by: Shaun Hamilton <51722130+Sky020@users.noreply.github.com> * Update curriculum/challenges/english/06-quality-assurance/advanced-node-and-express/implementation-of-social-authentication.english.md Co-authored-by: Shaun Hamilton <51722130+Sky020@users.noreply.github.com> * Update curriculum/challenges/english/06-quality-assurance/advanced-node-and-express/registration-of-new-users.english.md Co-authored-by: Shaun Hamilton <51722130+Sky020@users.noreply.github.com> * Update curriculum/challenges/english/06-quality-assurance/quality-assurance-and-testing-with-chai/test-for-truthiness.english.md Co-authored-by: Shaun Hamilton <51722130+Sky020@users.noreply.github.com> * Update curriculum/challenges/english/06-quality-assurance/quality-assurance-and-testing-with-chai/test-for-truthiness.english.md Co-authored-by: Shaun Hamilton <51722130+Sky020@users.noreply.github.com> * Update curriculum/challenges/english/06-quality-assurance/advanced-node-and-express/how-to-use-passport-strategies.english.md Co-authored-by: Shaun Hamilton <51722130+Sky020@users.noreply.github.com> * Update curriculum/challenges/english/06-quality-assurance/advanced-node-and-express/implement-the-serialization-of-a-passport-user.english.md Co-authored-by: Shaun Hamilton <51722130+Sky020@users.noreply.github.com> * Update curriculum/challenges/english/06-quality-assurance/advanced-node-and-express/implementation-of-social-authentication-ii.english.md Co-authored-by: Shaun Hamilton <51722130+Sky020@users.noreply.github.com> * Update curriculum/challenges/english/06-quality-assurance/advanced-node-and-express/clean-up-your-project-with-modules.english.md Co-authored-by: Manish Giri <manish.giri.me@gmail.com> * fix: added code tags Co-authored-by: Manish Giri <manish.giri.me@gmail.com> * fix: add code tag * fix: change to code tag * fix: change to code tags * fix: added code tags * fix: capitalize Passport * fix: added code tags * fix: added code tags * fix: added code tags * fix: added code tags * fix: added code tags * fix: added code tags * fix: added code tags * fix: added code tags * fix: added code tags Co-authored-by: Shaun Hamilton <51722130+Sky020@users.noreply.github.com> Co-authored-by: Mrugesh Mohapatra <1884376+raisedadead@users.noreply.github.com> Co-authored-by: Manish Giri <manish.giri.me@gmail.com> Co-authored-by: Randell Dawson <5313213+RandellDawson@users.noreply.github.com>
This commit is contained in:
@ -9,9 +9,9 @@ forumTopicId: 301546
|
|||||||
|
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
|
|
||||||
Many chat rooms are able to announce when a user connects or disconnects and then display that to all of the connected users in the chat. Seeing as though you already are emitting an event on connect and disconnect, you will just have to modify this event to support such feature. The most logical way of doing so is sending 3 pieces of data with the event: name of the user connected/disconnected, the current user count, and if that name connected or disconnected.
|
Many chat rooms are able to announce when a user connects or disconnects and then display that to all of the connected users in the chat. Seeing as though you already are emitting an event on connect and disconnect, you will just have to modify this event to support such a feature. The most logical way of doing so is sending 3 pieces of data with the event: the name of the user who connected/disconnected, the current user count, and if that name connected or disconnected.
|
||||||
|
|
||||||
Change the event name to <code>'user'</code>, and pass an object along containing fields 'name', 'currentUsers', and 'connected' (to be <code>true</code> if connection, or <code>false</code> for disconnection of the user sent). Be sure to change both 'user count' events and set the disconnect one to send <code>false</code> for field 'connected' instead of <code>true</code> like the event emitted on connect.
|
Change the event name to <code>'user'</code>, and pass an object along containing the fields 'name', 'currentUsers', and 'connected' (to be <code>true</code> in case of connection, or <code>false</code> for disconnection of the user sent). Be sure to change both 'user count' events and set the disconnect one to send <code>false</code> for the field 'connected' instead of <code>true</code> like the event emitted on connect.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
io.emit('user', {
|
io.emit('user', {
|
||||||
|
@ -8,11 +8,11 @@ forumTopicId: 301547
|
|||||||
## Description
|
## Description
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
|
|
||||||
A strategy is a way of authenticating a user. You can use a strategy for allowing users to authenticate based on locally saved information (if you have them register first) or from a variety of providers such as Google or GitHub. For this project we will set up a local strategy. To see a list of the 100's of strategies, visit Passports site <a href='http://passportjs.org/' target='_blank'>here</a>.
|
A strategy is a way of authenticating a user. You can use a strategy for allowing users to authenticate based on locally saved information (if you have them register first) or from a variety of providers such as Google or GitHub. For this project, we will set up a local strategy. To see a list of the hundreds of strategies, visit Passport's site <a href='http://passportjs.org/' target='_blank'>here</a>.
|
||||||
|
|
||||||
Add <em>passport-local</em> as a dependency and add it to your server as follows: <code>const LocalStrategy = require('passport-local');</code>
|
Add <code>passport-local</code> as a dependency and add it to your server as follows: <code>const LocalStrategy = require('passport-local');</code>
|
||||||
|
|
||||||
Now you will have to tell passport to <b>use</b> an instantiated LocalStrategy object with a few settings defined. Make sure this as well as everything from this point on is encapsulated in the database connection since it relies on it!
|
Now you will have to tell passport to <b>use</b> an instantiated LocalStrategy object with a few settings defined. Make sure this (as well as everything from this point on) is encapsulated in the database connection since it relies on it!
|
||||||
|
|
||||||
```js
|
```js
|
||||||
passport.use(new LocalStrategy(
|
passport.use(new LocalStrategy(
|
||||||
@ -28,11 +28,11 @@ passport.use(new LocalStrategy(
|
|||||||
));
|
));
|
||||||
```
|
```
|
||||||
|
|
||||||
This is defining the process to take when we try to authenticate someone locally. First it tries to find a user in our database with the username entered, then it checks for the password to match, then finally if no errors have popped up that we checked for, like an incorrect password, the users object is returned and they are authenticated.
|
This is defining the process to use when we try to authenticate someone locally. First, it tries to find a user in our database with the username entered, then it checks for the password to match, then finally, if no errors have popped up that we checked for, like an incorrect password, the <code>user</code>'s object is returned and they are authenticated.
|
||||||
|
|
||||||
Many strategies are set up using different settings, general it is easy to set it up based on the README in that strategies repository though. A good example of this is the GitHub strategy where we don't need to worry about a username or password because the user will be sent to GitHub's auth page to authenticate and as long as they are logged in and agree then GitHub returns their profile for us to use.
|
Many strategies are set up using different settings, but generally it is easy to set it up based on the README in that strategy's repository. A good example of this is the GitHub strategy where we don't need to worry about a username or password because the user will be sent to GitHub's auth page to authenticate. As long as they are logged in and agree then GitHub returns their profile for us to use.
|
||||||
|
|
||||||
In the next step we will set up how to actually call the authentication strategy to validate a user based on form data!
|
In the next step, we will set up how to actually call the authentication strategy to validate a user based on form data!
|
||||||
|
|
||||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point <a href='https://gist.github.com/camperbot/53b495c02b92adeee0aa1bd3f3be8a4b' target='_blank'>here</a>.
|
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point <a href='https://gist.github.com/camperbot/53b495c02b92adeee0aa1bd3f3be8a4b' target='_blank'>here</a>.
|
||||||
|
|
||||||
|
@ -9,9 +9,9 @@ forumTopicId: 301548
|
|||||||
|
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
|
|
||||||
Currently, you cannot determine who is connected to your web socket. While <code>req.user</code> contains the user object, that's only when your user interacts with the web server and with web sockets you have no <code>req</code> (request) and therefore no user data. One way to solve the problem of knowing who is connected to your web socket is by parsing and decoding the cookie that contains the passport session then deserializing it to obtain the user object. Luckily, there is a package on NPM just for this that turns a once complex task into something simple!
|
Currently, you cannot determine who is connected to your web socket. While <code>req.user</code> contains the user object, that's only when your user interacts with the web server, and with web sockets you have no <code>req</code> (request) and therefore no user data. One way to solve the problem of knowing who is connected to your web socket is by parsing and decoding the cookie that contains the passport session then deserializing it to obtain the user object. Luckily, there is a package on NPM just for this that turns a once complex task into something simple!
|
||||||
|
|
||||||
Add <code>passport.socketio</code>, <code>connect-mongo</code>, and <code>cookie-parser</code> as dependencies and require them as <code>passportSocketIo</code>, <code>MongoStore</code>, and <code>cookieParser</code> respectfully. Also, we need to initialize a new memory store, from <code>express-session</code> which we previously required. It should look as follows:
|
Add <code>passport.socketio</code>, <code>connect-mongo</code>, and <code>cookie-parser</code> as dependencies and require them as <code>passportSocketIo</code>, <code>MongoStore</code>, and <code>cookieParser</code> respectively. Also, we need to initialize a new memory store, from <code>express-session</code> which we previously required. It should look like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const MongoStore = require('connect-mongo')(session);
|
const MongoStore = require('connect-mongo')(session);
|
||||||
@ -19,7 +19,7 @@ const URI = process.env.MONGO_URI;
|
|||||||
const store = new MongoStore({ url: URI });
|
const store = new MongoStore({ url: URI });
|
||||||
```
|
```
|
||||||
|
|
||||||
Now we just have to tell Socket.IO to use it and set the options. Be sure this is added before the existing socket code and not in the existing connection listener. For your server it should look as follows:
|
Now we just have to tell Socket.IO to use it and set the options. Be sure this is added before the existing socket code and not in the existing connection listener. For your server, it should look like this:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
io.use(
|
io.use(
|
||||||
|
@ -8,7 +8,7 @@ forumTopicId: 301549
|
|||||||
## Description
|
## Description
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
|
|
||||||
Right now everything you have is in your server.js file. This can lead to hard to manage code that isn't very expandable.
|
Right now, everything you have is in your <code>server.js</code> file. This can lead to hard to manage code that isn't very expandable.
|
||||||
Create 2 new files: <code>routes.js</code> and <code>auth.js</code>
|
Create 2 new files: <code>routes.js</code> and <code>auth.js</code>
|
||||||
|
|
||||||
Both should start with the following code:
|
Both should start with the following code:
|
||||||
@ -19,10 +19,10 @@ module.exports = function (app, myDataBase) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Now in the top of your server file, require these files like such: <code>const routes = require('./routes.js');</code>
|
Now, in the top of your server file, require these files like so: <code>const routes = require('./routes.js');</code>
|
||||||
Right after you establish a successful connect with the database instantiate each of them like such: <code>routes(app, myDataBase)</code>
|
Right after you establish a successful connection with the database, instantiate each of them like so: <code>routes(app, myDataBase)</code>
|
||||||
|
|
||||||
Finally, take all of the routes in your server and paste them into your new files and remove them from your server file. Also take the ensureAuthenticated since we created that middleware function for routing specifically. You will have to now correctly add the dependencies in that are used, such as <code>const passport = require('passport');</code>, at the very top above the export line in your routes.js file.
|
Finally, take all of the routes in your server and paste them into your new files, and remove them from your server file. Also take the <code>ensureAuthenticated</code> function, since it was specifically created for routing. Now, you will have to correctly add the dependencies in which are used, such as <code>const passport = require('passport');</code>, at the very top, above the export line in your <code>routes.js</code> file.
|
||||||
|
|
||||||
Keep adding them until no more errors exist, and your server file no longer has any routing (**except for the route in the catch block**)!
|
Keep adding them until no more errors exist, and your server file no longer has any routing (**except for the route in the catch block**)!
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ Finally, after incrementing the count, you should emit the event (still within t
|
|||||||
io.emit('user count', currentUsers);
|
io.emit('user count', currentUsers);
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, you can implement a way for your client to listen for this event! Similarly to listening for a connection on the server, you will use the <em>on</em> keyword.
|
Now, you can implement a way for your client to listen for this event! Similar to listening for a connection on the server, you will use the <code>on</code> keyword.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
socket.on('user count', function(data) {
|
socket.on('user count', function(data) {
|
||||||
|
@ -8,9 +8,9 @@ forumTopicId: 301551
|
|||||||
## Description
|
## Description
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
|
|
||||||
As in, any user can just go to /profile whether they authenticated or not by typing in the url. We want to prevent this by checking if the user is authenticated first before rendering the profile page. This is the perfect example of when to create a middleware.
|
As is, any user can just go to <code>/profile</code> whether they have authenticated or not, by typing in the url. We want to prevent this, by checking if the user is authenticated first before rendering the profile page. This is the perfect example of when to create a middleware.
|
||||||
|
|
||||||
The challenge here is creating the middleware function <code>ensureAuthenticated(req, res, next)</code>, which will check if a user is authenticated by calling passports isAuthenticated on the <em>request</em> which in turn checks for <em>req.user</em> is to be defined. If it is then <em>next()</em> should be called, otherwise we can just respond to the request with a redirect to our homepage to login. An implementation of this middleware is:
|
The challenge here is creating the middleware function <code>ensureAuthenticated(req, res, next)</code>, which will check if a user is authenticated by calling passport's <code>isAuthenticated</code> method on the <code>request</code> which, in turn, checks if <code>req.user</code> is defined. If it is, then <code>next()</code> should be called, otherwise, we can just respond to the request with a redirect to our homepage to login. An implementation of this middleware is:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
function ensureAuthenticated(req, res, next) {
|
function ensureAuthenticated(req, res, next) {
|
||||||
|
@ -9,7 +9,7 @@ forumTopicId: 301552
|
|||||||
|
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
|
|
||||||
You may notice that up to now you have only been increasing the user count. Handling a user disconnecting is just as easy as handling the initial connect except the difference is you have to listen for it on each socket versus on the whole server.
|
You may notice that up to now you have only been increasing the user count. Handling a user disconnecting is just as easy as handling the initial connect, except you have to listen for it on each socket instead of on the whole server.
|
||||||
|
|
||||||
To do this, add another listener inside the existing <code>'connect'</code> listener that listens for <code>'disconnect'</code> on the socket with no data passed through. You can test this functionality by just logging that a user has disconnected to the console.
|
To do this, add another listener inside the existing <code>'connect'</code> listener that listens for <code>'disconnect'</code> on the socket with no data passed through. You can test this functionality by just logging that a user has disconnected to the console.
|
||||||
|
|
||||||
|
@ -8,11 +8,11 @@ forumTopicId: 301553
|
|||||||
## Description
|
## Description
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
|
|
||||||
Going back to the information security section you may remember that storing plaintext passwords is <em>never</em> okay. Now it is time to implement BCrypt to solve this issue.
|
Going back to the information security section, you may remember that storing plaintext passwords is <em>never</em> okay. Now it is time to implement BCrypt to solve this issue.
|
||||||
|
|
||||||
Add BCrypt as a dependency, and require it in your server. You will need to handle hashing in 2 key areas: where you handle registering/saving a new account, and when you check to see that a password is correct on login.
|
Add BCrypt as a dependency, and require it in your server. You will need to handle hashing in 2 key areas: where you handle registering/saving a new account, and when you check to see that a password is correct on login.
|
||||||
|
|
||||||
Currently on our registration route, you insert a user's password into the database like the following: <code>password: req.body.password</code>. An easy way to implement saving a hash instead is to add the following before your database logic <code>const hash = bcrypt.hashSync(req.body.password, 12);</code>, and replacing the <code>req.body.password</code> in the database saving with just <code>password: hash</code>.
|
Currently on our registration route, you insert a user's password into the database like so: <code>password: req.body.password</code>. An easy way to implement saving a hash instead is to add the following before your database logic <code>const hash = bcrypt.hashSync(req.body.password, 12);</code>, and replacing the <code>req.body.password</code> in the database saving with just <code>password: hash</code>.
|
||||||
|
|
||||||
Finally, on our authentication strategy, we check for the following in our code before completing the process: <code>if (password !== user.password) { return done(null, false); }</code>. After making the previous changes, now <code>user.password</code> is a hash. Before making a change to the existing code, notice how the statement is checking if the password is **not** equal then return non-authenticated. With this in mind, your code could look as follows to properly check the password entered against the hash:
|
Finally, on our authentication strategy, we check for the following in our code before completing the process: <code>if (password !== user.password) { return done(null, false); }</code>. After making the previous changes, now <code>user.password</code> is a hash. Before making a change to the existing code, notice how the statement is checking if the password is **not** equal then return non-authenticated. With this in mind, your code could look as follows to properly check the password entered against the hash:
|
||||||
|
|
||||||
|
@ -8,15 +8,15 @@ forumTopicId: 301555
|
|||||||
## Description
|
## Description
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
|
|
||||||
In the index.pug file supplied there is actually a login form. It has previously been hidden because of the inline JavaScript <code>if showLogin</code> with the form indented after it. Before <code>showLogin</code> as a variable was never defined, it never rendered the code block containing the form. Go ahead and on the <code>res.render</code> for that page add a new variable to the object <code>showLogin: true</code>. When you refresh your page, you should then see the form! This form is set up to <b>POST</b> on <em>/login</em> so this is where we should set up to accept the POST and authenticate the user.
|
In the <code>index.pug</code> file supplied, there is actually a login form. It has previously been hidden because of the inline JavaScript <code>if showLogin</code> with the form indented after it. Before <code>showLogin</code> as a variable was never defined, so it never rendered the code block containing the form. Go ahead and on the <code>res.render</code> for that page add a new variable to the object <code>showLogin: true</code>. When you refresh your page, you should then see the form! This form is set up to <b>POST</b> on <code>/login</code>, so this is where we should set up to accept the POST and authenticate the user.
|
||||||
|
|
||||||
For this challenge you should add the route /login to accept a POST request. To authenticate on this route you need to add a middleware to do so before then sending a response. This is done by just passing another argument with the middleware before your <code>function(req,res)</code> with your response! The middleware to use is <code>passport.authenticate('local')</code>.
|
For this challenge you should add the route <code>/login</code> to accept a POST request. To authenticate on this route, you need to add a middleware to do so before then sending a response. This is done by just passing another argument with the middleware before your <code>function(req,res)</code> with your response! The middleware to use is <code>passport.authenticate('local')</code>.
|
||||||
|
|
||||||
<em>passport.authenticate</em> can also take some options as an argument such as: <code>{ failureRedirect: '/' }</code> which is incredibly useful so be sure to add that in as well. As a response after using the middleware (which will only be called if the authentication middleware passes) should be to redirect the user to <em>/profile</em> and that route should render the view 'profile.pug'.
|
<code>passport.authenticate</code> can also take some options as an argument such as: <code>{ failureRedirect: '/' }</code> which is incredibly useful, so be sure to add that in as well. The response after using the middleware (which will only be called if the authentication middleware passes) should be to redirect the user to <code>/profile</code> and that route should render the view <code>profile.pug</code>.
|
||||||
|
|
||||||
If the authentication was successful, the user object will be saved in <em>req.user</em>.
|
If the authentication was successful, the user object will be saved in <code>req.user</code>.
|
||||||
|
|
||||||
Now at this point if you enter a username and password in the form, it should redirect to the home page <code>/</code> and in the console of your server should be 'User {USERNAME} attempted to log in.' since we currently cannot login a user who isn't registered.
|
At this point, if you enter a username and password in the form, it should redirect to the home page <code>/</code>, and the console of your server should display <code>'User {USERNAME} attempted to log in.'</code>, since we currently cannot login a user who isn't registered.
|
||||||
|
|
||||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point <a href='https://gist.github.com/camperbot/7ad011ac54612ad53188b500c5e99cb9' target='_blank'>here</a>.
|
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point <a href='https://gist.github.com/camperbot/7ad011ac54612ad53188b500c5e99cb9' target='_blank'>here</a>.
|
||||||
|
|
||||||
|
@ -9,12 +9,12 @@ forumTopicId: 301556
|
|||||||
|
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
|
|
||||||
Right now we're not loading an actual user object since we haven't set up our database. This can be done many different ways, but for our project we will connect to the database once when we start the server and keep a persistent connection for the full life-cycle of the app.
|
Right now, we're not loading an actual user object since we haven't set up our database. This can be done many different ways, but for our project we will connect to the database once when we start the server and keep a persistent connection for the full life-cycle of the app.
|
||||||
To do this, add your database's connection string (for example: <code>mongodb+srv://:@cluster0-jvwxi.mongodb.net/?retryWrites=true&w=majority</code>) to the environment variable <code>MONGO_URI</code>. This is used in the <em>connection.js</em> file.
|
To do this, add your database's connection string (for example: <code>mongodb+srv://:@cluster0-jvwxi.mongodb.net/?retryWrites=true&w=majority</code>) to the environment variable <code>MONGO_URI</code>. This is used in the <code>connection.js</code> file.
|
||||||
|
|
||||||
_You can set up a free database on <a href='https://www.mongodb.com/cloud/atlas' target='_blank'>MongoDB Atlas</a>._
|
_You can set up a free database on <a href='https://www.mongodb.com/cloud/atlas' target='_blank'>MongoDB Atlas</a>._
|
||||||
|
|
||||||
Now we want to the connect to our database then start listening for requests. The purpose of this is to not allow requests before our database is connected or if there is a database error. To accomplish you will want to encompass your serialization and your app routes in the following:
|
Now we want to connect to our database then start listening for requests. The purpose of this is to not allow requests before our database is connected or if there is a database error. To accomplish this, you will want to encompass your serialization and your app routes in the following code:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
myDB(async client => {
|
myDB(async client => {
|
||||||
@ -40,7 +40,7 @@ myDB(async client => {
|
|||||||
// app.listen out here...
|
// app.listen out here...
|
||||||
```
|
```
|
||||||
|
|
||||||
Be sure to uncomment the <code>myDataBase</code> code in deserializeUser, and edit your <code>done(null, null)</code> to include the <code>doc</code>.
|
Be sure to uncomment the <code>myDataBase</code> code in <code>deserializeUser</code>, and edit your <code>done(null, null)</code> to include the <code>doc</code>.
|
||||||
|
|
||||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point <a href='https://gist.github.com/camperbot/175f2f585a2d8034044c7e8857d5add7' target='_blank'>here</a>.
|
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point <a href='https://gist.github.com/camperbot/175f2f585a2d8034044c7e8857d5add7' target='_blank'>here</a>.
|
||||||
|
|
||||||
|
@ -8,9 +8,9 @@ forumTopicId: 301557
|
|||||||
## Description
|
## Description
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
|
|
||||||
The last part of setting up your GitHub authentication is to create the strategy itself. For this, you will need to add the dependency of 'passport-github' to your project and require it in your <code>auth.js</code> as GithubStrategy like <code>const GitHubStrategy = require('passport-github').Strategy;</code>. Do not forget to require and configure <code>dotenv</code> to use your environment variables.
|
The last part of setting up your GitHub authentication is to create the strategy itself. For this, you will need to add the dependency of 'passport-github' to your project and require it in your <code>auth.js</code> as <code>GithubStrategy</code> like this: <code>const GitHubStrategy = require('passport-github').Strategy;</code>. Do not forget to require and configure <code>dotenv</code> to use your environment variables.
|
||||||
|
|
||||||
To set up the GitHub strategy, you have to tell <b>passport</b> to <b>use</b> an instantiated <b>GitHubStrategy</b>, which accepts 2 arguments: An object (containing <em>clientID</em>, <em>clientSecret</em>, and <em>callbackURL</em>) and a function to be called when a user is successfully authenticated which we will determine if the user is new and what fields to save initially in the user's database object. This is common across many strategies but some may require more information as outlined in that specific strategy's github README; for example, Google requires a <em>scope</em> as well which determines what kind of information your request is asking returned and asks the user to approve such access. The current strategy we are implementing has its usage outlined <a href='https://github.com/jaredhanson/passport-github/' target='_blank'>here</a>, but we're going through it all right here on freeCodeCamp!
|
To set up the GitHub strategy, you have to tell Passport to use an instantiated <code>GitHubStrategy</code>, which accepts 2 arguments: an object (containing <code>clientID</code>, <code>clientSecret</code>, and <code>callbackURL</code>) and a function to be called when a user is successfully authenticated, which will determine if the user is new and what fields to save initially in the user's database object. This is common across many strategies, but some may require more information as outlined in that specific strategy's GitHub README. For example, Google requires a <em>scope</em> as well which determines what kind of information your request is asking to be returned and asks the user to approve such access. The current strategy we are implementing has its usage outlined <a href='https://github.com/jaredhanson/passport-github/' target='_blank'>here</a>, but we're going through it all right here on freeCodeCamp!
|
||||||
|
|
||||||
Here's how your new strategy should look at this point:
|
Here's how your new strategy should look at this point:
|
||||||
|
|
||||||
@ -27,7 +27,7 @@ passport.use(new GitHubStrategy({
|
|||||||
));
|
));
|
||||||
```
|
```
|
||||||
|
|
||||||
Your authentication won't be successful yet, and actually throw an error, without the database logic and callback, but it should log to your console your GitHub profile if you try it!
|
Your authentication won't be successful yet, and it will actually throw an error without the database logic and callback, but it should log your GitHub profile to your console if you try it!
|
||||||
|
|
||||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point <a href='https://gist.github.com/camperbot/ff3a1166684c1b184709ac0bee30dee6' target='_blank'>here</a>.
|
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point <a href='https://gist.github.com/camperbot/ff3a1166684c1b184709ac0bee30dee6' target='_blank'>here</a>.
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ forumTopicId: 301558
|
|||||||
|
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
|
|
||||||
The final part of the strategy is handling the profile returned from GitHub. We need to load the user's database object if it exists, or create one if it doesn't, and populate the fields from the profile, then return the user's object. GitHub supplies us a unique <em>id</em> within each profile which we can use to search with to serialize the user with (already implemented). Below is an example implementation you can use in your project- it goes within the function that is the second argument for the new strategy, right below the <code>console.log(profile);</code> currently is:
|
The final part of the strategy is handling the profile returned from GitHub. We need to load the user's database object if it exists, or create one if it doesn't, and populate the fields from the profile, then return the user's object. GitHub supplies us a unique <em>id</em> within each profile which we can use to search with to serialize the user with (already implemented). Below is an example implementation you can use in your project--it goes within the function that is the second argument for the new strategy, right below where <code>console.log(profile);</code> currently is:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
myDataBase.findOneAndUpdate(
|
myDataBase.findOneAndUpdate(
|
||||||
@ -41,7 +41,7 @@ myDataBase.findOneAndUpdate(
|
|||||||
|
|
||||||
`findOneAndUpdate` allows you to search for an object and update it. If the object doesn't exist, it will be inserted and made available to the callback function. In this example, we always set `last_login`, increment the `login_count` by `1`, and only populate the majority of the fields when a new object (new user) is inserted. Notice the use of default values. Sometimes a profile returned won't have all the information filled out or the user will keep it private. In this case, you handle it to prevent an error.
|
`findOneAndUpdate` allows you to search for an object and update it. If the object doesn't exist, it will be inserted and made available to the callback function. In this example, we always set `last_login`, increment the `login_count` by `1`, and only populate the majority of the fields when a new object (new user) is inserted. Notice the use of default values. Sometimes a profile returned won't have all the information filled out or the user will keep it private. In this case, you handle it to prevent an error.
|
||||||
|
|
||||||
You should be able to login to your app now- try it!
|
You should be able to login to your app now--try it!
|
||||||
|
|
||||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point <a href='https://gist.github.com/camperbot/183e968f0e01d81dde015d45ba9d2745' target='_blank'>here</a>.
|
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point <a href='https://gist.github.com/camperbot/183e968f0e01d81dde015d45ba9d2745' target='_blank'>here</a>.
|
||||||
|
|
||||||
|
@ -8,15 +8,15 @@ forumTopicId: 301559
|
|||||||
## Description
|
## Description
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
|
|
||||||
The basic path this kind of authentication will follow in your app is: <ol><li>User clicks a button or link sending them to our route to authenticate using a specific strategy (EG. GitHub)</li><li>Your route calls <code>passport.authenticate('github')</code> which redirects them to GitHub.</li><li>The page the user lands on, on GitHub, allows them to login if they aren't already. It then asks them to approve access to their profile from our app.</li><li>The user is then returned to our app at a specific callback url with their profile if they are approved.</li><li>They are now authenticated and your app should check if it is a returning profile, or save it in your database if it is not.</li></ol>
|
The basic path this kind of authentication will follow in your app is: <ol><li>User clicks a button or link sending them to our route to authenticate using a specific strategy (e.g. GitHub).</li><li>Your route calls <code>passport.authenticate('github')</code> which redirects them to GitHub.</li><li>The page the user lands on, on GitHub, allows them to login if they aren't already. It then asks them to approve access to their profile from our app.</li><li>The user is then returned to our app at a specific callback url with their profile if they are approved.</li><li>They are now authenticated, and your app should check if it is a returning profile, or save it in your database if it is not.</li></ol>
|
||||||
|
|
||||||
Strategies with OAuth require you to have at least a <em>Client ID</em> and a <em>Client Secret</em> which is a way for them to verify who the authentication request is coming from and if it is valid. These are obtained from the site you are trying to implement authentication with, such as GitHub, and are unique to your app- <b>THEY ARE NOT TO BE SHARED</b> and should never be uploaded to a public repository or written directly in your code. A common practice is to put them in your <em>.env</em> file and reference them like: <code>process.env.GITHUB_CLIENT_ID</code>. For this challenge we're going to use the GitHub strategy.
|
Strategies with OAuth require you to have at least a <em>Client ID</em> and a <em>Client Secret</em> which is a way for the service to verify who the authentication request is coming from and if it is valid. These are obtained from the site you are trying to implement authentication with, such as GitHub, and are unique to your app--<b>THEY ARE NOT TO BE SHARED</b> and should never be uploaded to a public repository or written directly in your code. A common practice is to put them in your <code>.env</code> file and reference them like so: <code>process.env.GITHUB_CLIENT_ID</code>. For this challenge we're going to use the GitHub strategy.
|
||||||
|
|
||||||
Obtaining your <em>Client ID and Secret</em> from GitHub is done in your account profile settings under 'developer settings', then '<a href='https://github.com/settings/developers' target='_blank'>OAuth applications</a>'. Click 'Register a new application', name your app, paste in the url to your Repl.it homepage (<b>Not the project code's url</b>), and lastly for the callback url, paste in the same url as the homepage but with <code>/auth/github/callback</code> added on. This is where users will be redirected to for us to handle after authenticating on GitHub. Save the returned information as 'GITHUB_CLIENT_ID' and 'GITHUB_CLIENT_SECRET' in your .env file.
|
Obtaining your <em>Client ID and Secret</em> from GitHub is done in your account profile settings under 'developer settings', then '<a href='https://github.com/settings/developers' target='_blank'>OAuth applications</a>'. Click 'Register a new application', name your app, paste in the url to your Repl.it homepage (<b>Not the project code's url</b>), and lastly, for the callback url, paste in the same url as the homepage but with <code>/auth/github/callback</code> added on. This is where users will be redirected for us to handle after authenticating on GitHub. Save the returned information as <code>'GITHUB_CLIENT_ID'</code> and <code>'GITHUB_CLIENT_SECRET'</code> in your <code>.env</code> file.
|
||||||
|
|
||||||
In your <code>routes.js</code> file, add <code>showSocialAuth: true</code> to the homepage route, after <code>showRegistration: true</code>. Now, create 2 routes accepting GET requests: <code>/auth/github</code> and <code>/auth/github/callback</code>. The first should only call passport to authenticate 'github' and the second should call passport to authenticate 'github' with a failure redirect to '/' and then if that is successful redirect to '/profile' (similar to our last project).
|
In your <code>routes.js</code> file, add <code>showSocialAuth: true</code> to the homepage route, after <code>showRegistration: true</code>. Now, create 2 routes accepting GET requests: <code>/auth/github</code> and <code>/auth/github/callback</code>. The first should only call passport to authenticate <code>'github'</code>. The second should call passport to authenticate <code>'github'</code> with a failure redirect to <code>/</code>, and then if that is successful redirect to <code>/profile</code> (similar to our last project).
|
||||||
|
|
||||||
An example of how '/auth/github/callback' should look is similar to how we handled a normal login:
|
An example of how <code>/auth/github/callback</code> should look is similar to how we handled a normal login:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
app.route('/login')
|
app.route('/login')
|
||||||
|
@ -20,7 +20,7 @@ app.route('/logout')
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
You may have noticed that we're not handling missing pages (404), the common way to handle this in Node is with the following middleware. Go ahead and add this in after all your other routes:
|
You may have noticed that we're not handling missing pages (404). The common way to handle this in Node is with the following middleware. Go ahead and add this in after all your other routes:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
|
@ -8,7 +8,7 @@ forumTopicId: 301561
|
|||||||
## Description
|
## Description
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
|
|
||||||
Now we need to allow a new user on our site to register an account. On the res.render for the home page add a new variable to the object passed along- <code>showRegistration: true</code>. When you refresh your page, you should then see the registration form that was already created in your index.pug file! This form is set up to <b>POST</b> on <em>/register</em> so this is where we should set up to accept the POST and create the user object in the database.
|
Now we need to allow a new user on our site to register an account. On the <code>res.render</code> for the home page add a new variable to the object passed along--<code>showRegistration: true</code>. When you refresh your page, you should then see the registration form that was already created in your <code>index.pug</code> file! This form is set up to <b>POST</b> on <code>/register</code>, so this is where we should set up to accept the <b>POST</b> and create the user object in the database.
|
||||||
|
|
||||||
The logic of the registration route should be as follows: Register the new user > Authenticate the new user > Redirect to /profile
|
The logic of the registration route should be as follows: Register the new user > Authenticate the new user > Redirect to /profile
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ forumTopicId: 301562
|
|||||||
|
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
|
|
||||||
It's time you start allowing clients to send a chat message to the server to emit to all the clients! Already in your client.js file you should see there is already a block of code handling when the message form is submitted.
|
It's time you start allowing clients to send a chat message to the server to emit to all the clients! In your <code>client.js</code> file, you should see there is already a block of code handling when the message form is submitted.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
$('form').submit(function() {
|
$('form').submit(function() {
|
||||||
@ -23,11 +23,11 @@ Within the form submit code, you should emit an event after you define <code>mes
|
|||||||
socket.emit('chat message', messageToSend);
|
socket.emit('chat message', messageToSend);
|
||||||
```
|
```
|
||||||
|
|
||||||
Now on your server you should be listening to the socket for the event <code>'chat message'</code> with the data being named <code>message</code>. Once the event is received it should then emit the event <code>'chat message'</code> to all sockets <code>io.emit</code> with the data being an object containing <code>name</code> and <code>message</code>.
|
Now, on your server, you should be listening to the socket for the event <code>'chat message'</code> with the data being named <code>message</code>. Once the event is received, it should emit the event <code>'chat message'</code> to all sockets <code>io.emit</code> with the data being an object containing <code>name</code> and <code>message</code>.
|
||||||
|
|
||||||
In <code>client.js</code>, you should now listen for event <code>'chat message'</code> and, when received, append a list item to <code>#messages</code> with the name a colon and the message!
|
In <code>client.js</code>, you should now listen for event <code>'chat message'</code> and, when received, append a list item to <code>#messages</code> with the name, a colon, and the message!
|
||||||
|
|
||||||
At this point the chat should be fully functional and sending messages across all clients!
|
At this point, the chat should be fully functional and sending messages across all clients!
|
||||||
|
|
||||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point <a href='https://gist.github.com/camperbot/d7af9864375207e254f73262976d2016' target='_blank'>here</a>.
|
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point <a href='https://gist.github.com/camperbot/d7af9864375207e254f73262976d2016' target='_blank'>here</a>.
|
||||||
|
|
||||||
|
@ -9,11 +9,11 @@ forumTopicId: 301563
|
|||||||
|
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
|
|
||||||
Serialization and deserialization are important concepts in regards to authentication. To serialize an object means to convert its contents into a small <em>key</em> essentially that can then be deserialized into the original object. This is what allows us to know who has communicated with the server without having to send the authentication data like username and password at each request for a new page.
|
Serialization and deserialization are important concepts in regards to authentication. To serialize an object means to convert its contents into a small <em>key</em> that can then be deserialized into the original object. This is what allows us to know who has communicated with the server without having to send the authentication data, like the username and password, at each request for a new page.
|
||||||
|
|
||||||
To set this up properly, we need to have a serialize function and a deserialize function. In passport we create these with <code>passport.serializeUser( OURFUNCTION )</code> and <code>passport.deserializeUser( OURFUNCTION )</code>
|
To set this up properly, we need to have a serialize function and a deserialize function. In Passport, we create these with <code>passport.serializeUser( OURFUNCTION )</code> and <code>passport.deserializeUser( OURFUNCTION )</code>
|
||||||
|
|
||||||
The <code>serializeUser</code> is called with 2 arguments, the full user object and a callback used by passport. Returned in the callback should be a unique key to identify that user - the easiest one to use being the user's <code>_id</code> in the object as it should be unique as it generated by MongoDB. Similarly, <code>deserializeUser</code> is called with that key and a callback function for passport as well, but, this time, we have to take that key and return the full user object to the callback. To make a query search for a Mongo <code>_id</code>, you will have to create <code>const ObjectID = require('mongodb').ObjectID;</code>, and then to use it you call <code>new ObjectID(THE_ID)</code>. Be sure to add MongoDB as a dependency. You can see this in the examples below:
|
The <code>serializeUser</code> is called with 2 arguments, the full user object and a callback used by passport. A unique key to identify that user should be returned in the callback, the easiest one to use being the user's <code>_id</code> in the object. It should be unique as it generated by MongoDB. Similarly, <code>deserializeUser</code> is called with that key and a callback function for passport as well, but, this time, we have to take that key and return the full user object to the callback. To make a query search for a Mongo <code>_id</code>, you will have to create <code>const ObjectID = require('mongodb').ObjectID;</code>, and then to use it you call <code>new ObjectID(THE_ID)</code>. Be sure to add MongoDB as a dependency. You can see this in the examples below:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
passport.serializeUser((user, done) => {
|
passport.serializeUser((user, done) => {
|
||||||
@ -27,7 +27,7 @@ passport.deserializeUser((id, done) => {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
NOTE: This <code>deserializeUser</code> will throw an error until we set up the DB in the next step so comment out the whole block and just call <code>done(null, null)</code> in the function <code>deserializeUser</code>.
|
NOTE: This <code>deserializeUser</code> will throw an error until we set up the DB in the next step, so for now comment out the whole block and just call <code>done(null, null)</code> in the function <code>deserializeUser</code>.
|
||||||
|
|
||||||
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point <a href='https://gist.github.com/camperbot/7068a0d09e61ec7424572b366751f048' target='_blank'>here</a>.
|
Submit your page when you think you've got it right. If you're running into errors, you can check out the project completed up to this point <a href='https://gist.github.com/camperbot/7068a0d09e61ec7424572b366751f048' target='_blank'>here</a>.
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ forumTopicId: 301591
|
|||||||
## Description
|
## Description
|
||||||
<section id='description'>
|
<section id='description'>
|
||||||
As a reminder, this project is being built upon the following starter project on <a href="https://repl.it/github/freeCodeCamp/boilerplate-mochachai">Repl.it</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.
|
As a reminder, this project is being built upon the following starter project on <a href="https://repl.it/github/freeCodeCamp/boilerplate-mochachai">Repl.it</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.
|
||||||
This exercise is similar to the preceding. Look at it for the details.
|
This exercise is similar to the preceding one. Look at it for the details.
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@ -24,7 +24,7 @@ Send
|
|||||||
|
|
||||||
Replace <code>assert.fail()</code> and make the test pass.
|
Replace <code>assert.fail()</code> and make the test pass.
|
||||||
Check for 1) <code>status</code>, 2) <code>type</code>, 3) <code>body.name</code>, 4) <code>body.surname</code>.
|
Check for 1) <code>status</code>, 2) <code>type</code>, 3) <code>body.name</code>, 4) <code>body.surname</code>.
|
||||||
Follow the assertion order above, We rely on it.
|
Follow the assertion order above: we rely on it.
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
@ -14,7 +14,7 @@ As a reminder, this project is being built upon the following starter project on
|
|||||||
## Instructions
|
## Instructions
|
||||||
<section id='instructions'>
|
<section id='instructions'>
|
||||||
Replace <code>assert.fail()</code>. Test the status and the <code>text.response</code>. Make the test pass.
|
Replace <code>assert.fail()</code>. Test the status and the <code>text.response</code>. Make the test pass.
|
||||||
Don't send a name in the query, the endpoint responds with "hello Guest".
|
Don't send a name in the query; the endpoint responds with <code>hello Guest</code>.
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
@ -13,8 +13,8 @@ As a reminder, this project is being built upon the following starter project on
|
|||||||
|
|
||||||
## Instructions
|
## Instructions
|
||||||
<section id='instructions'>
|
<section id='instructions'>
|
||||||
This exercise is similar to the preceding.
|
This exercise is similar to the preceding one.
|
||||||
Look at the code for directions. Follow the assertions order, We rely on it.
|
Look at the code for directions. Follow the assertions order: we rely on it.
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
## Tests
|
## Tests
|
||||||
|
@ -9,7 +9,7 @@ forumTopicId: 301595
|
|||||||
<section id='description'>
|
<section id='description'>
|
||||||
As a reminder, this project is being built upon the following starter project on <a href="https://repl.it/github/freeCodeCamp/boilerplate-mochachai">Repl.it</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.
|
As a reminder, this project is being built upon the following starter project on <a href="https://repl.it/github/freeCodeCamp/boilerplate-mochachai">Repl.it</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.
|
||||||
In the next challenges we are going to simulate the human interaction with a page using a device called 'Headless Browser'.
|
In the next challenges we are going to simulate the human interaction with a page using a device called 'Headless Browser'.
|
||||||
A headless browser is a web browser without a graphical user interface. These kind of tools are particularly useful for testing web pages as they are able to render and understand HTML, CSS, and JavaScript the same way a browser would.
|
A headless browser is a web browser without a graphical user interface. This kind of tool is particularly useful for testing web pages, as it is able to render and understand HTML, CSS, and JavaScript the same way a browser would.
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ forumTopicId: 301596
|
|||||||
<section id='description'>
|
<section id='description'>
|
||||||
As a reminder, this project is being built upon the following starter project on <a href="https://repl.it/github/freeCodeCamp/boilerplate-mochachai">Repl.it</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.
|
As a reminder, this project is being built upon the following starter project on <a href="https://repl.it/github/freeCodeCamp/boilerplate-mochachai">Repl.it</a>, or cloned from <a href='https://github.com/freeCodeCamp/boilerplate-mochachai/'>GitHub</a>.
|
||||||
|
|
||||||
<code>isTrue()</code> will test for the boolean value true and <code>isNotTrue()</code> will pass when given anything but the boolean value of true.
|
<code>isTrue()</code> will test for the boolean value <code>true</code> and <code>isNotTrue()</code> will pass when given anything but the boolean value of <code>true</code>.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
assert.isTrue(true, 'this will pass with the boolean value true');
|
assert.isTrue(true, 'this will pass with the boolean value true');
|
||||||
@ -18,7 +18,7 @@ assert.isTrue(1, 'this will NOT pass with the number value 1');
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
<code>isFalse()</code> and <code>isNotFalse()</code> also exist and behave similary to their true counterparts except they look for the boolean value of false.
|
<code>isFalse()</code> and <code>isNotFalse()</code> also exist and behave similarly to their true counterparts except they look for the boolean value of <code>false</code>.
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
## Instructions
|
## Instructions
|
||||||
|
Reference in New Issue
Block a user