chore(i8n,learn): processed translations

This commit is contained in:
Crowdin Bot
2021-02-06 04:42:36 +00:00
committed by Mrugesh Mohapatra
parent 15047f2d90
commit e5c44a3ae5
3274 changed files with 172122 additions and 14164 deletions

View File

@ -0,0 +1,90 @@
---
id: 589fc832f9fc0f352b528e78
title: Announce New Users
challengeType: 2
forumTopicId: 301546
dashedName: announce-new-users
---
# --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 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 `'user'`, and pass an object along containing the fields 'name', 'currentUsers', and 'connected' (to be `true` in case of connection, or `false` for disconnection of the user sent). Be sure to change both 'user count' events and set the disconnect one to send `false` for the field 'connected' instead of `true` like the event emitted on connect.
```js
io.emit('user', {
name: socket.request.user.name,
currentUsers,
connected: true
});
```
Now your client will have all the necessary information to correctly display the current user count and announce when a user connects or disconnects! To handle this event on the client side we should listen for `'user'`, then update the current user count by using jQuery to change the text of `#num-users` to `'{NUMBER} users online'`, as well as append a `<li>` to the unordered list with id `messages` with `'{NAME} has {joined/left} the chat.'`.
An implementation of this could look like the following:
```js
socket.on('user', data => {
$('#num-users').text(data.currentUsers + ' users online');
let message =
data.name +
(data.connected ? ' has joined the chat.' : ' has left the chat.');
$('#messages').append($('<li>').html('<b>' + message + '</b>'));
});
```
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 [here](https://gist.github.com/camperbot/bf95a0f74b756cf0771cd62c087b8286).
# --hints--
Event `'user'` should be emitted with name, currentUsers, and connected.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/io.emit.*('|")user\1.*name.*currentUsers.*connected/gis,
'You should have an event emitted named user sending name, currentUsers, and connected'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Client should properly handle and display the new data from event `'user'`.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/public/client.js').then(
(data) => {
assert.match(
data,
/socket.on.*('|")user\1[^]*num-users/gi,
'You should change the text of "#num-users" within on your client within the "user" event listener to show the current users connected'
);
assert.match(
data,
/socket.on.*('|")user\1[^]*messages.*li/gi,
'You should append a list item to "#messages" on your client within the "user" event listener to announce a user came or went'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,96 @@
---
id: 5895f70df9fc0f352b528e68
title: Authentication Strategies
challengeType: 2
forumTopicId: 301547
dashedName: authentication-strategies
---
# --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 hundreds of strategies, visit Passport's site [here](http://passportjs.org/).
Add `passport-local` as a dependency and add it to your server as follows: `const LocalStrategy = require('passport-local');`
Now you will have to tell passport to **use** 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
passport.use(new LocalStrategy(
function(username, password, done) {
myDataBase.findOne({ username: username }, function (err, user) {
console.log('User '+ username +' attempted to log in.');
if (err) { return done(err); }
if (!user) { return done(null, false); }
if (password !== user.password) { return done(null, false); }
return done(null, user);
});
}
));
```
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 `user`'s object is returned and they are authenticated.
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!
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 [here](https://gist.github.com/camperbot/53b495c02b92adeee0aa1bd3f3be8a4b).
# --hints--
Passport-local should be a dependency.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'passport-local',
'Your project should list "passport-local " as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Passport-local should be correctly required and setup.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/require.*("|')passport-local("|')/gi,
'You should have required passport-local'
);
assert.match(
data,
/new LocalStrategy/gi,
'You should have told passport to use a new strategy'
);
assert.match(
data,
/findOne/gi,
'Your new local strategy should use the findOne query to find a username based on the inputs'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,150 @@
---
id: 589fc831f9fc0f352b528e77
title: Authentication with Socket.IO
challengeType: 2
forumTopicId: 301548
dashedName: authentication-with-socket-io
---
# --description--
Currently, you cannot determine who is connected to your web socket. While `req.user` contains the user object, that's only when your user interacts with the web server, and with web sockets you have no `req` (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 `passport.socketio`, `connect-mongo`, and `cookie-parser` as dependencies and require them as `passportSocketIo`, `MongoStore`, and `cookieParser` respectively. Also, we need to initialize a new memory store, from `express-session` which we previously required. It should look like this:
```js
const MongoStore = require('connect-mongo')(session);
const URI = process.env.MONGO_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 like this:
```js
io.use(
passportSocketIo.authorize({
cookieParser: cookieParser,
key: 'express.sid',
secret: process.env.SESSION_SECRET,
store: store,
success: onAuthorizeSuccess,
fail: onAuthorizeFail
})
);
```
Be sure to add the `key` and `store` to the `session` middleware mounted on the app. This is necessary to tell *SocketIO* which session to relate to.
<hr />
Now, define the `success`, and `fail` callback functions:
```js
function onAuthorizeSuccess(data, accept) {
console.log('successful connection to socket.io');
accept(null, true);
}
function onAuthorizeFail(data, message, error, accept) {
if (error) throw new Error(message);
console.log('failed connection to socket.io:', message);
accept(null, false);
}
```
The user object is now accessible on your socket object as `socket.request.user`. For example, now you can add the following:
```js
console.log('user ' + socket.request.user.name + ' connected');
```
It will log to the server console who has connected!
Submit your page when you think you've got it right. If you're running into errors, you can check out the project up to this point [here](https://gist.github.com/camperbot/1414cc9433044e306dd7fd0caa1c6254).
# --hints--
`passport.socketio` should be a dependency.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'passport.socketio',
'Your project should list "passport.socketio" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
`cookie-parser` should be a dependency.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'cookie-parser',
'Your project should list "cookie-parser" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
passportSocketIo should be properly required.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/require\((['"])passport\.socketio\1\)/gi,
'You should correctly require and instantiate "passport.socketio"'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
passportSocketIo should be properly setup.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/io\.use\(\s*\w+\.authorize\(/,
'You should register "passport.socketio" as socket.io middleware and provide it correct options'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,64 @@
---
id: 589690e6f9fc0f352b528e6e
title: Clean Up Your Project with Modules
challengeType: 2
forumTopicId: 301549
dashedName: clean-up-your-project-with-modules
---
# --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. Create 2 new files: `routes.js` and `auth.js`
Both should start with the following code:
```js
module.exports = function (app, myDataBase) {
}
```
Now, in the top of your server file, require these files like so: `const routes = require('./routes.js');` Right after you establish a successful connection with the database, instantiate each of them like so: `routes(app, myDataBase)`
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` function, since it was specifically created for routing. Now, you will have to correctly add the dependencies in which are used, such as `const passport = require('passport');`, at the very top, above the export line in your `routes.js` 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**)!
Now do the same thing in your auth.js file with all of the things related to authentication such as the serialization and the setting up of the local strategy and erase them from your server file. Be sure to add the dependencies in and call `auth(app, myDataBase)` in the server in the same spot.
Submit your page when you think you've got it right. If you're running into errors, you can check out an example of the completed project [here](https://gist.github.com/camperbot/2d06ac5c7d850d8cf073d2c2c794cc92).
# --hints--
Modules should be present.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/require\s*\(('|")\.\/routes(\.js)?\1\)/gi,
'You should have required your new files'
);
assert.match(
data,
/client.db[^]*routes/gi,
'Your new modules should be called after your connection to the database'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,107 @@
---
id: 589fc831f9fc0f352b528e75
title: Communicate by Emitting
challengeType: 2
forumTopicId: 301550
dashedName: communicate-by-emitting
---
# --description--
<dfn>Emit</dfn> is the most common way of communicating you will use. When you emit something from the server to 'io', you send an event's name and data to all the connected sockets. A good example of this concept would be emitting the current count of connected users each time a new user connects!
Start by adding a variable to keep track of the users, just before where you are currently listening for connections.
```js
let currentUsers = 0;
```
Now, when someone connects, you should increment the count before emitting the count. So, you will want to add the incrementer within the connection listener.
```js
++currentUsers;
```
Finally, after incrementing the count, you should emit the event (still within the connection listener). The event should be named 'user count', and the data should just be the `currentUsers`.
```js
io.emit('user count', currentUsers);
```
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 `on` keyword.
```js
socket.on('user count', function(data) {
console.log(data);
});
```
Now, try loading up your app, authenticate, and you should see in your client console '1' representing the current user count! Try loading more clients up, and authenticating to see the number go up.
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 [here](https://gist.github.com/camperbot/28ef7f1078f56eb48c7b1aeea35ba1f5).
# --hints--
currentUsers should be defined.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/currentUsers/gi,
'You should have variable currentUsers defined'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Server should emit the current user count at each new connection.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/io.emit.*('|")user count('|").*currentUsers/gi,
'You should emit "user count" with data currentUsers'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Your client should be listening for 'user count' event.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/public/client.js').then(
(data) => {
assert.match(
data,
/socket.on.*('|")user count('|")/gi,
'Your client should be connection to server with the connection defined as socket'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,87 @@
---
id: 5895f70df9fc0f352b528e6a
title: Create New Middleware
challengeType: 2
forumTopicId: 301551
dashedName: create-new-middleware
---
# --description--
As is, any user can just go to `/profile` 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 `ensureAuthenticated(req, res, next)`, which will check if a user is authenticated by calling passport's `isAuthenticated` method on the `request` which, in turn, checks if `req.user` is defined. If it is, then `next()` 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
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
res.redirect('/');
};
```
Now add *ensureAuthenticated* as a middleware to the request for the profile page before the argument to the get request containing the function that renders the page.
```js
app
.route('/profile')
.get(ensureAuthenticated, (req,res) => {
res.render(process.cwd() + '/views/pug/profile');
});
```
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 [here](https://gist.github.com/camperbot/ae49b8778cab87e93284a91343da0959).
# --hints--
Middleware ensureAuthenticated should be implemented and on our /profile route.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/ensureAuthenticated[^]*req.isAuthenticated/gi,
'Your ensureAuthenticated middleware should be defined and utilize the req.isAuthenticated function'
);
assert.match(
data,
/profile[^]*get[^]*ensureAuthenticated/gi,
'Your ensureAuthenticated middleware should be attached to the /profile route'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
A Get request to /profile should correctly redirect to / since we are not authenticated.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/profile').then(
(data) => {
assert.match(
data,
/Home page/gi,
'An attempt to go to the profile at this point should redirect to the homepage since we are not logged in'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,69 @@
---
id: 589fc831f9fc0f352b528e76
title: Handle a Disconnect
challengeType: 2
forumTopicId: 301552
dashedName: handle-a-disconnect
---
# --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 you have to listen for it on each socket instead of on the whole server.
To do this, add another listener inside the existing `'connect'` listener that listens for `'disconnect'` on the socket with no data passed through. You can test this functionality by just logging that a user has disconnected to the console.
```js
socket.on('disconnect', () => {
/*anything you want to do on disconnect*/
});
```
To make sure clients continuously have the updated count of current users, you should decrease the currentUsers by 1 when the disconnect happens then emit the 'user count' event with the updated count!
**Note:** Just like `'disconnect'`, all other events that a socket can emit to the server should be handled within the connecting listener where we have 'socket' defined.
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 [here](https://gist.github.com/camperbot/ab1007b76069884fb45b215d3c4496fa).
# --hints--
Server should handle the event disconnect from a socket.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(data, /socket.on.*('|")disconnect('|")/gi, '');
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Your client should be listening for 'user count' event.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/public/client.js').then(
(data) => {
assert.match(
data,
/socket.on.*('|")user count('|")/gi,
'Your client should be connection to server with the connection defined as socket'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,86 @@
---
id: 58a25c98f9fc0f352b528e7f
title: Hashing Your Passwords
challengeType: 2
forumTopicId: 301553
dashedName: hashing-your-passwords
---
# --description--
Going back to the information security section, you may remember that storing plaintext passwords is *never* 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.
Currently on our registration route, you insert a user's password into the database like so: `password: req.body.password`. An easy way to implement saving a hash instead is to add the following before your database logic `const hash = bcrypt.hashSync(req.body.password, 12);`, and replacing the `req.body.password` in the database saving with just `password: hash`.
Finally, on our authentication strategy, we check for the following in our code before completing the process: `if (password !== user.password) { return done(null, false); }`. After making the previous changes, now `user.password` 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:
```js
if (!bcrypt.compareSync(password, user.password)) {
return done(null, false);
}
```
That is all it takes to implement one of the most important security features when you have to store passwords!
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 [here](https://gist.github.com/camperbot/dc16cca09daea4d4151a9c36a1fab564).
# --hints--
BCrypt should be a dependency.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'bcrypt',
'Your project should list "bcrypt" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
BCrypt should be correctly required and implemented.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/require.*("|')bcrypt\1/gi,
'You should have required bcrypt'
);
assert.match(
data,
/bcrypt.hashSync/gi,
'You should use hash the password in the registration'
);
assert.match(
data,
/bcrypt.compareSync/gi,
'You should compare the password to the hash in your strategy'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,57 @@
---
id: 5895f70ef9fc0f352b528e6b
title: How to Put a Profile Together
challengeType: 2
forumTopicId: 301554
dashedName: how-to-put-a-profile-together
---
# --description--
Now that we can ensure the user accessing the `/profile` is authenticated, we can use the information contained in `req.user` on our page!
Pass an object containing the property `username` and value of `req.user.username` as the second argument for the render method of the profile view. Then, go to your `profile.pug` view, and add the following line below the existing `h1` element, and at the same level of indentation:
```pug
h2.center#welcome Welcome, #{username}!
```
This creates an `h2` element with the class '`center`' and id '`welcome`' containing the text '`Welcome,`' followed by the username.
Also, in `profile.pug`, add a link referring to the `/logout` route, which will host the logic to unauthenticate a user.
```pug
a(href='/logout') Logout
```
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 [here](https://gist.github.com/camperbot/136b3ad611cc80b41cab6f74bb460f6a).
# --hints--
You should correctly add a Pug render variable to /profile.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/username:( |)req.user.username/gi,
'You should be passing the variable username with req.user.username into the render function of the profile page'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,79 @@
---
id: 5895f70df9fc0f352b528e69
title: How to Use Passport Strategies
challengeType: 2
forumTopicId: 301555
dashedName: how-to-use-passport-strategies
---
# --description--
In the `index.pug` file supplied, there is actually a login form. It has previously been hidden because of the inline JavaScript `if showLogin` with the form indented after it. Before `showLogin` as a variable was never defined, so it never rendered the code block containing the form. Go ahead and on the `res.render` for that page add a new variable to the object `showLogin: true`. When you refresh your page, you should then see the form! This form is set up to **POST** on `/login`, 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 `function(req,res)` with your response! The middleware to use is `passport.authenticate('local')`.
`passport.authenticate` can also take some options as an argument such as: `{ failureRedirect: '/' }` 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 `/profile` and that route should render the view `profile.pug`.
If the authentication was successful, the user object will be saved in `req.user`.
At this point, if you enter a username and password in the form, it should redirect to the home page `/`, and the console of your server should display `'User {USERNAME} attempted to log in.'`, 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 [here](https://gist.github.com/camperbot/7ad011ac54612ad53188b500c5e99cb9).
# --hints--
All steps should be correctly implemented in the server.js.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/showLogin:( |)true/gi,
'You should be passing the variable "showLogin" as true to your render function for the homepage'
);
assert.match(
data,
/failureRedirect:( |)('|")\/('|")/gi,
'Your code should include a failureRedirect to the "/" route'
);
assert.match(
data,
/login[^]*post[^]*local/gi,
'You should have a route for login which accepts a POST and passport.authenticates local'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
A POST request to /login should correctly redirect to /.
```js
(getUserInput) =>
$.post(getUserInput('url') + '/login').then(
(data) => {
assert.match(
data,
/Looks like this page is being rendered from Pug into HTML!/gi,
'A login attempt at this point should redirect to the homepage since we do not have any registered users'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,91 @@
---
id: 5895f70cf9fc0f352b528e67
title: Implement the Serialization of a Passport User
challengeType: 2
forumTopicId: 301556
dashedName: implement-the-serialization-of-a-passport-user
---
# --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. To do this, add your database's connection string (for example: `mongodb+srv://:@cluster0-jvwxi.mongodb.net/?retryWrites=true&w=majority`) to the environment variable `MONGO_URI`. This is used in the `connection.js` file.
*You can set up a free database on [MongoDB Atlas](https://www.mongodb.com/cloud/atlas).*
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
myDB(async client => {
const myDataBase = await client.db('database').collection('users');
// Be sure to change the title
app.route('/').get((req, res) => {
//Change the response to render the Pug template
res.render('pug', {
title: 'Connected to Database',
message: 'Please login'
});
});
// Serialization and deserialization here...
// Be sure to add this...
}).catch(e => {
app.route('/').get((req, res) => {
res.render('pug', { title: e, message: 'Unable to login' });
});
});
// app.listen out here...
```
Be sure to uncomment the `myDataBase` code in `deserializeUser`, and edit your `done(null, null)` to include the `doc`.
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 [here](https://gist.github.com/camperbot/175f2f585a2d8034044c7e8857d5add7).
# --hints--
Database connection should be present.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/').then(
(data) => {
assert.match(
data,
/Connected to Database/gi,
'You successfully connected to the database!'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Deserialization should now be correctly using the DB and `done(null, null)` should be called with the `doc`.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/null,\s*doc/gi,
'The callback in deserializeUser of (null, null) should be altered to (null, doc)'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,114 @@
---
id: 589a69f5f9fc0f352b528e71
title: Implementation of Social Authentication II
challengeType: 2
forumTopicId: 301557
dashedName: implementation-of-social-authentication-ii
---
# --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 `auth.js` as `GithubStrategy` like this: `const GitHubStrategy = require('passport-github').Strategy;`. Do not forget to require and configure `dotenv` to use your environment variables.
To set up the GitHub strategy, you have to tell Passport to use an instantiated `GitHubStrategy`, which accepts 2 arguments: an object (containing `clientID`, `clientSecret`, and `callbackURL`) 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 *scope* 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 [here](https://github.com/jaredhanson/passport-github/), but we're going through it all right here on freeCodeCamp!
Here's how your new strategy should look at this point:
```js
passport.use(new GitHubStrategy({
clientID: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
callbackURL: /*INSERT CALLBACK URL ENTERED INTO GITHUB HERE*/
},
function(accessToken, refreshToken, profile, cb) {
console.log(profile);
//Database logic here with callback containing our user object
}
));
```
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 [here](https://gist.github.com/camperbot/ff3a1166684c1b184709ac0bee30dee6).
# --hints--
passport-github dependency should be added.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'passport-github',
'Your project should list "passport-github" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
passport-github should be required.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/auth.js').then(
(data) => {
assert.match(
data,
/require.*("|')passport-github("|')/gi,
'You should have required passport-github'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
GitHub strategy should be setup correctly thus far.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/auth.js').then(
(data) => {
assert.match(
data,
/passport\.use.*new GitHubStrategy/gi,
'Passport should use a new GitHubStrategy'
);
assert.match(
data,
/callbackURL:\s*("|').*("|')/gi,
'You should have a callbackURL'
);
assert.match(
data,
/process.env.GITHUB_CLIENT_SECRET/g,
'You should use process.env.GITHUB_CLIENT_SECRET'
);
assert.match(
data,
/process.env.GITHUB_CLIENT_ID/g,
'You should use process.env.GITHUB_CLIENT_ID'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,80 @@
---
id: 589a8eb3f9fc0f352b528e72
title: Implementation of Social Authentication III
challengeType: 2
forumTopicId: 301558
dashedName: implementation-of-social-authentication-iii
---
# --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 *id* 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 `console.log(profile);` currently is:
```js
myDataBase.findOneAndUpdate(
{ id: profile.id },
{
$setOnInsert: {
id: profile.id,
name: profile.displayName || 'John Doe',
photo: profile.photos[0].value || '',
email: Array.isArray(profile.emails)
? profile.emails[0].value
: 'No public email',
created_on: new Date(),
provider: profile.provider || ''
},
$set: {
last_login: new Date()
},
$inc: {
login_count: 1
}
},
{ upsert: true, new: true },
(err, doc) => {
return cb(null, doc.value);
}
);
```
`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!
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 [here](https://gist.github.com/camperbot/183e968f0e01d81dde015d45ba9d2745).
# --hints--
GitHub strategy setup should be complete.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/auth.js').then(
(data) => {
assert.match(
data,
/GitHubStrategy[^]*myDataBase/gi,
'Strategy should use now use the database to search for the user'
);
assert.match(
data,
/GitHubStrategy[^]*return cb/gi,
'Strategy should return the callback function "cb"'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,82 @@
---
id: 589a69f5f9fc0f352b528e70
title: Implementation of Social Authentication
challengeType: 2
forumTopicId: 301559
dashedName: implementation-of-social-authentication
---
# --description--
The basic path this kind of authentication will follow in your app is:
1. User clicks a button or link sending them to our route to authenticate using a specific strategy (e.g. GitHub).
2. Your route calls `passport.authenticate('github')` which redirects them to GitHub.
3. 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.
4. The user is then returned to our app at a specific callback url with their profile if they are approved.
5. 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.
Strategies with OAuth require you to have at least a *Client ID* and a *Client Secret* 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--**THEY ARE NOT TO BE SHARED** and should never be uploaded to a public repository or written directly in your code. A common practice is to put them in your `.env` file and reference them like so: `process.env.GITHUB_CLIENT_ID`. For this challenge we're going to use the GitHub strategy.
Obtaining your *Client ID and Secret* from GitHub is done in your account profile settings under 'developer settings', then '[OAuth applications](https://github.com/settings/developers)'. Click 'Register a new application', name your app, paste in the url to your Repl.it homepage (**Not the project code's url**), and lastly, for the callback url, paste in the same url as the homepage but with `/auth/github/callback` added on. This is where users will be redirected for us to handle after authenticating on GitHub. Save the returned information as `'GITHUB_CLIENT_ID'` and `'GITHUB_CLIENT_SECRET'` in your `.env` file.
In your `routes.js` file, add `showSocialAuth: true` to the homepage route, after `showRegistration: true`. Now, create 2 routes accepting GET requests: `/auth/github` and `/auth/github/callback`. The first should only call passport to authenticate `'github'`. 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).
An example of how `/auth/github/callback` should look is similar to how we handled a normal login:
```js
app.route('/login')
.post(passport.authenticate('local', { failureRedirect: '/' }), (req,res) => {
res.redirect('/profile');
});
```
Submit your page when you think you've got it right. If you're running into errors, you can check out the project up to this point [here](https://gist.github.com/camperbot/1f7f6f76adb178680246989612bea21e).
# --hints--
Route /auth/github should be correct.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/routes.js').then(
(data) => {
assert.match(
data.replace(/\s/g, ''),
/('|")\/auth\/github\/?\1[^]*?get.*?passport.authenticate.*?github/gi,
'Route auth/github should only call passport.authenticate with github'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Route /auth/github/callback should be correct.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/routes.js').then(
(data) => {
assert.match(
data.replace(/\s/g, ''),
/('|")\/auth\/github\/callback\/?\1[^]*?get.*?passport.authenticate.*?github.*?failureRedirect:("|')\/\2/gi,
'Route auth/github/callback should accept a get request and call passport.authenticate for github with a failure redirect to home'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,81 @@
---
id: 58965611f9fc0f352b528e6c
title: Logging a User Out
challengeType: 2
forumTopicId: 301560
dashedName: logging-a-user-out
---
# --description--
Creating the logout logic is easy. The route should just unauthenticate the user and redirect to the home page instead of rendering any view.
In passport, unauthenticating a user is as easy as just calling `req.logout();` before redirecting.
```js
app.route('/logout')
.get((req, res) => {
req.logout();
res.redirect('/');
});
```
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
app.use((req, res, next) => {
res.status(404)
.type('text')
.send('Not Found');
});
```
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 [here](https://gist.github.com/camperbot/c3eeb8a3ebf855e021fd0c044095a23b).
# --hints--
`req.Logout` should be called in your `/logout` route.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/req.logout/gi,
'You should be calling req.logout() in your /logout route'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Logout should redirect to the home page.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/logout').then(
(data) => {
assert.match(
data,
/Home page/gi,
'When a user logs out they should be redirected to the homepage'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,205 @@
---
id: 58966a17f9fc0f352b528e6d
title: Registration of New Users
challengeType: 2
forumTopicId: 301561
dashedName: registration-of-new-users
---
# --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--`showRegistration: true`. 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 **POST** on `/register`, so this is where we should set up to accept the **POST** 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 step 1, registering the new user, should be as follows: Query database with a findOne command > if user is returned then it exists and redirect back to home *OR* if user is undefined and no error occurs then 'insertOne' into the database with the username and password, and, as long as no errors occur, call *next* to go to step 2, authenticating the new user, which we've already written the logic for in our POST */login* route.
```js
app.route('/register')
.post((req, res, next) => {
myDataBase.findOne({ username: req.body.username }, function(err, user) {
if (err) {
next(err);
} else if (user) {
res.redirect('/');
} else {
myDataBase.insertOne({
username: req.body.username,
password: req.body.password
},
(err, doc) => {
if (err) {
res.redirect('/');
} else {
// The inserted document is held within
// the ops property of the doc
next(null, doc.ops[0]);
}
}
)
}
})
},
passport.authenticate('local', { failureRedirect: '/' }),
(req, res, next) => {
res.redirect('/profile');
}
);
```
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 [here](https://gist.github.com/camperbot/b230a5b3bbc89b1fa0ce32a2aa7b083e).
**NOTE:** From this point onwards, issues can arise relating to the use of the *picture-in-picture* browser. If you are using an online IDE which offers a preview of the app within the editor, it is recommended to open this preview in a new tab.
# --hints--
You should register route and display on home.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/showRegistration:( |)true/gi,
'You should be passing the variable showRegistration as true to your render function for the homepage'
);
assert.match(
data,
/register[^]*post[^]*findOne[^]*username:( |)req.body.username/gi,
'You should have a route accepted a post request on register that querys the db with findone and the query being username: req.body.username'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Registering should work.
```js
async (getUserInput) => {
const url = getUserInput('url');
const user = `freeCodeCampTester${Date.now()}`;
const xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
test(this);
} else {
throw new Error(`${this.status} ${this.statusText}`);
}
};
xhttp.open('POST', url + '/register', true);
xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhttp.send(`username=${user}&password=${user}`);
function test(xhttpRes) {
const data = xhttpRes.responseText;
assert.match(
data,
/Profile/gi,
'Register should work, and redirect successfully to the profile.'
);
}
};
```
Login should work.
```js
async (getUserInput) => {
const url = getUserInput('url');
const user = `freeCodeCampTester${Date.now()}`;
const xhttpReg = new XMLHttpRequest();
xhttpReg.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
login();
} else {
throw new Error(`${this.status} ${this.statusText}`);
}
};
xhttpReg.open('POST', url + '/register', true);
xhttpReg.setRequestHeader(
'Content-type',
'application/x-www-form-urlencoded'
);
xhttpReg.send(`username=${user}&password=${user}`);
function login() {
const xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status == 200) {
test(this);
} else {
throw new Error(`${this.status} ${this.statusText}`);
}
};
xhttp.open('POST', url + '/login', true);
xhttp.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhttp.send(`username=${user}&password=${user}`);
}
function test(xhttpRes) {
const data = xhttpRes.responseText;
assert.match(
data,
/Profile/gi,
'Login should work if previous test was done successfully and redirect successfully to the profile.'
);
assert.match(
data,
new RegExp(user, 'g'),
'The profile should properly display the welcome to the user logged in'
);
}
};
```
Logout should work.
```js
(getUserInput) =>
$.ajax({
url: getUserInput('url') + '/logout',
type: 'GET',
xhrFields: { withCredentials: true }
}).then(
(data) => {
assert.match(data, /Home/gi, 'Logout should redirect to home');
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Profile should no longer work after logout.
```js
(getUserInput) =>
$.ajax({
url: getUserInput('url') + '/profile',
type: 'GET',
crossDomain: true,
xhrFields: { withCredentials: true }
}).then(
(data) => {
assert.match(
data,
/Home/gi,
'Profile should redirect to home when we are logged out now again'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,79 @@
---
id: 589fc832f9fc0f352b528e79
title: Send and Display Chat Messages
challengeType: 2
forumTopicId: 301562
dashedName: send-and-display-chat-messages
---
# --description--
It's time you start allowing clients to send a chat message to the server to emit to all the clients! In your `client.js` file, you should see there is already a block of code handling when the message form is submitted.
```js
$('form').submit(function() {
/*logic*/
});
```
Within the form submit code, you should emit an event after you define `messageToSend` but before you clear the text box `#m`. The event should be named `'chat message'` and the data should just be `messageToSend`.
```js
socket.emit('chat message', messageToSend);
```
Now, on your server, you should be listening to the socket for the event `'chat message'` with the data being named `message`. Once the event is received, it should emit the event `'chat message'` to all sockets `io.emit` with the data being an object containing `name` and `message`.
In `client.js`, you should now listen for event `'chat message'` and, when received, append a list item to `#messages` with the name, a colon, and the message!
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 [here](https://gist.github.com/camperbot/d7af9864375207e254f73262976d2016).
# --hints--
Server should listen for `'chat message'` and then emit it properly.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/socket.on.*('|")chat message('|")[^]*io.emit.*('|")chat message('|").*name.*message/gis,
'Your server should listen to the socket for "chat message" then emit to all users "chat message" with name and message in the data object'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Client should properly handle and display the new data from event `'chat message'`.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/public/client.js').then(
(data) => {
assert.match(
data,
/socket.on.*('|")chat message('|")[^]*messages.*li/gis,
'You should append a list item to #messages on your client within the "chat message" event listener to display the new message'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,131 @@
---
id: 5895f70cf9fc0f352b528e66
title: Serialization of a User Object
challengeType: 2
forumTopicId: 301563
dashedName: serialization-of-a-user-object
---
# --description--
Serialization and deserialization are important concepts in regards to authentication. To serialize an object means to convert its contents into a small *key* 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 `passport.serializeUser( OURFUNCTION )` and `passport.deserializeUser( OURFUNCTION )`
The `serializeUser` 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 `_id` in the object. It should be unique as it generated by MongoDB. Similarly, `deserializeUser` 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 `_id`, you will have to create `const ObjectID = require('mongodb').ObjectID;`, and then to use it you call `new ObjectID(THE_ID)`. Be sure to add MongoDB as a dependency. You can see this in the examples below:
```js
passport.serializeUser((user, done) => {
done(null, user._id);
});
passport.deserializeUser((id, done) => {
myDataBase.findOne({ _id: new ObjectID(id) }, (err, doc) => {
done(null, null);
});
});
```
NOTE: This `deserializeUser` 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 `done(null, null)` in the function `deserializeUser`.
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 [here](https://gist.github.com/camperbot/7068a0d09e61ec7424572b366751f048).
# --hints--
You should serialize user function correctly.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/passport.serializeUser/gi,
'You should have created your passport.serializeUser function'
);
assert.match(
data,
/null,\s*user._id/gi,
'There should be a callback in your serializeUser with (null, user._id)'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
You should deserialize user function correctly.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/passport.deserializeUser/gi,
'You should have created your passport.deserializeUser function'
);
assert.match(
data,
/null,\s*null/gi,
'There should be a callback in your deserializeUser with (null, null) for now'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
MongoDB should be a dependency.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'mongodb',
'Your project should list "mongodb" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Mongodb should be properly required including the ObjectId.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/require.*("|')mongodb\1/gi,
'You should have required mongodb'
);
assert.match(
data,
/new ObjectID.*id/gi,
'Even though the block is commented out, you should use new ObjectID(id) for when we add the database'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,110 @@
---
id: 5895f700f9fc0f352b528e63
title: Set up a Template Engine
challengeType: 2
forumTopicId: 301564
dashedName: set-up-a-template-engine
---
# --description--
As a reminder, this project is built upon the following starter project on [Repl.it](https://repl.it/github/freeCodeCamp/boilerplate-advancednode), or clone from [GitHub](https://github.com/freeCodeCamp/boilerplate-advancednode/).
A template engine enables you to use static template files (such as those written in *Pug*) in your app. At runtime, the template engine replaces variables in a template file with actual values which can be supplied by your server. Then it transforms the template into a static HTML file that is sent to the client. This approach makes it easier to design an HTML page and allows for displaying variables on the page without needing to make an API call from the client.
Add `pug@~3.0.0` as a dependency in your `package.json` file.
Express needs to know which template engine you are using. We will use the `set` method to assign `pug` as the `view engine` property's value: `app.set('view engine', 'pug')`
Your page will not load until you correctly render the index file in the `views/pug` directory.
Change the argument of the `res.render()` declaration in the `/` route to be the file path to the `views/pug` directory. The path can be a relative path (relative to views), or an absolute path, and does not require a file extension.
If all went as planned, your app home page will stop showing the message "`Pug template is not defined.`" and will now display a message indicating you've successfully rendered the Pug template!
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 [here](https://gist.github.com/camperbot/3515cd676ea4dfceab4e322f59a37791).
# --hints--
Pug should be a dependency.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'pug',
'Your project should list "pug" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
View engine should be Pug.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/('|")view engine('|"),( |)('|")pug('|")/gi,
'Your project should set Pug as a view engine'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Use the correct ExpressJS method to render the index page from the response.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/').then(
(data) => {
assert.match(
data,
/FCC Advanced Node and Express/gi,
'You successfully rendered the Pug template!'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Pug should be working.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/').then(
(data) => {
assert.match(
data,
/pug-success-message/gi,
'Your projects home page should now be rendered by pug with the projects .pug file unaltered'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,132 @@
---
id: 5895f70cf9fc0f352b528e65
title: Set up Passport
challengeType: 2
forumTopicId: 301565
dashedName: set-up-passport
---
# --description--
It's time to set up *Passport* so we can finally start allowing a user to register or login to an account! In addition to Passport, we will use Express-session to handle sessions. Using this middleware saves the session id as a cookie in the client and allows us to access the session data using that id on the server. This way we keep personal account information out of the cookie used by the client to verify to our server they are authenticated and just keep the *key* to access the data stored on the server.
To set up Passport for use in your project, you will need to add it as a dependency first in your package.json. `"passport": "^0.3.2"`
In addition, add Express-session as a dependency now as well. Express-session has a ton of advanced features you can use but for now we're just going to use the basics! `"express-session": "^1.15.0"`
You will need to set up the session settings now and initialize Passport. Be sure to first create the variables 'session' and 'passport' to require 'express-session' and 'passport' respectively.
To set up your express app to use the session we'll define just a few basic options. Be sure to add 'SESSION_SECRET' to your .env file and give it a random value. This is used to compute the hash used to encrypt your cookie!
```js
app.use(session({
secret: process.env.SESSION_SECRET,
resave: true,
saveUninitialized: true,
cookie: { secure: false }
}));
```
As well you can go ahead and tell your express app to **use** 'passport.initialize()' and 'passport.session()'. (For example, `app.use(passport.initialize());`)
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 [here](https://gist.github.com/camperbot/4068a7662a2f9f5d5011074397d6788c).
# --hints--
Passport and Express-session should be dependencies.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'passport',
'Your project should list "passport" as a dependency'
);
assert.property(
packJson.dependencies,
'express-session',
'Your project should list "express-session" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Dependencies should be correctly required.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/require.*("|')passport("|')/gi,
'You should have required passport'
);
assert.match(
data,
/require.*("|')express-session("|')/gi,
'You should have required express-session'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Express app should use new dependencies.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/passport.initialize/gi,
'Your express app should use "passport.initialize()"'
);
assert.match(
data,
/passport.session/gi,
'Your express app should use "passport.session()"'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Session and session secret should be correctly set up.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/secret:( |)process.env.SESSION_SECRET/gi,
'Your express app should have express-session set up with your secret as process.env.SESSION_SECRET'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,148 @@
---
id: 589fc830f9fc0f352b528e74
title: Set up the Environment
challengeType: 2
forumTopicId: 301566
dashedName: set-up-the-environment
---
# --description--
The following challenges will make use of the `chat.pug` file. So, in your `routes.js` file, add a GET route pointing to `/chat` which makes use of `ensureAuthenticated`, and renders `chat.pug`, with `{ user: req.user }` passed as an argument to the response. Now, alter your existing `/auth/github/callback` route to set the `req.session.user_id = req.user.id`, and redirect to `/chat`.
Add `http` and `socket.io` as a dependency and require/instantiate them in your server defined as follows:
```javascript
const http = require('http').createServer(app);
const io = require('socket.io')(http);
```
Now that the *http* server is mounted on the *express app*, you need to listen from the *http* server. Change the line with `app.listen` to `http.listen`.
The first thing needing to be handled is listening for a new connection from the client. The <dfn>on</dfn> keyword does just that- listen for a specific event. It requires 2 arguments: a string containing the title of the event thats emitted, and a function with which the data is passed though. In the case of our connection listener, we use *socket* to define the data in the second argument. A socket is an individual client who is connected.
To listen for connections to your server, add the following within your database connection:
```javascript
io.on('connection', socket => {
console.log('A user has connected');
});
```
Now for the client to connect, you just need to add the following to your `client.js` which is loaded by the page after you've authenticated:
```js
/*global io*/
let socket = io();
```
The comment suppresses the error you would normally see since 'io' is not defined in the file. We've already added a reliable CDN to the Socket.IO library on the page in chat.pug.
Now try loading up your app and authenticate and you should see in your server console 'A user has connected'!
**Note:**`io()` works only when connecting to a socket hosted on the same url/server. For connecting to an external socket hosted elsewhere, you would use `io.connect('URL');`.
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 [here](https://gist.github.com/camperbot/aae41cf59debc1a4755c9a00ee3859d1).
# --hints--
`socket.io` should be a dependency.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/package.json').then(
(data) => {
var packJson = JSON.parse(data);
assert.property(
packJson.dependencies,
'socket.io',
'Your project should list "socket.io" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
You should correctly require and instantiate `http` as `http`.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/http.*=.*require.*('|")http\1/gi,
'Your project should list "http" as a dependency'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
You should correctly require and instantiate `socket.io` as `io`.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/io.*=.*require.*('|")socket.io\1.*http/gi,
'You should correctly require and instantiate socket.io as io.'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Socket.IO should be listening for connections.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/_api/server.js').then(
(data) => {
assert.match(
data,
/io.on.*('|")connection\1.*socket/gi,
'io should listen for "connection" and socket should be the 2nd arguments variable'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
Your client should connect to your server.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/public/client.js').then(
(data) => {
assert.match(
data,
/socket.*=.*io/gi,
'Your client should be connection to server with the connection defined as socket'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```

View File

@ -0,0 +1,53 @@
---
id: 5895f70bf9fc0f352b528e64
title: Use a Template Engine's Powers
challengeType: 2
forumTopicId: 301567
dashedName: use-a-template-engines-powers
---
# --description--
One of the greatest features of using a template engine is being able to pass variables from the server to the template file before rendering it to HTML.
In your Pug file, you're able to use a variable by referencing the variable name as `#{variable_name}` inline with other text on an element or by using an equal sign on the element without a space such as `p=variable_name` which assigns the variable's value to the p element's text.
We strongly recommend looking at the syntax and structure of Pug [here](https://github.com/pugjs/pug) on GitHub's README. Pug is all about using whitespace and tabs to show nested elements and cutting down on the amount of code needed to make a beautiful site.
Looking at our pug file 'index.pug' included in your project, we used the variables *title* and *message*.
To pass those along from our server, you will need to add an object as a second argument to your *res.render* with the variables and their values. For example, pass this object along setting the variables for your index view: `{title: 'Hello', message: 'Please login'}`
It should look like: `res.render(process.cwd() + '/views/pug/index', {title: 'Hello', message: 'Please login'});` Now refresh your page and you should see those values rendered in your view in the correct spot as laid out in your index.pug file!
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 [here](https://gist.github.com/camperbot/4af125119ed36e6e6a8bb920db0c0871).
# --hints--
Pug should correctly render variables.
```js
(getUserInput) =>
$.get(getUserInput('url') + '/').then(
(data) => {
assert.match(
data,
/pug-variable("|')>Please login/gi,
'Your projects home page should now be rendered by pug with the projects .pug file unaltered'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
);
```
# --solutions--
```js
/**
Backend challenges don't need solutions,
because they would need to be tested against a full working project.
Please check our contributing guidelines to learn more.
*/
```