Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(learn): advanced-node-and-express username field #47349

Expand Up @@ -8,13 +8,13 @@ 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.
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 username of the user who connected/disconnected, the current user count, and if that username 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.
Change the event name to `'user'`, and pass an object along containing the fields `username`, `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,
username: socket.request.user.username,
currentUsers,
connected: true
});
Expand All @@ -28,7 +28,7 @@ An implementation of this could look like the following:
socket.on('user', data => {
$('#num-users').text(data.currentUsers + ' users online');
let message =
data.name +
data.username +
(data.connected ? ' has joined the chat.' : ' has left the chat.');
$('#messages').append($('<li>').html('<b>' + message + '</b>'));
});
Expand All @@ -38,45 +38,39 @@ Submit your page when you think you've got it right. If you're running into erro

# --hints--

Event `'user'` should be emitted with name, currentUsers, and connected.
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);
}
async (getUserInput) => {
const url = new URL("/_api/server.js", getUserInput("url"));
const res = await fetch(url);
const data = await res.text();
assert.match(
data,
/io.emit.*('|")user\1.*name.*currentUsers.*connected/s,
'You should have an event emitted named user sending name, currentUsers, and connected'
);
}
```

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);
}
async (getUserInput) => {
const url = new URL("/public/client.js", getUserInput("url"));
const res = await fetch(url);
const data = await res.text();
assert.match(
data,
/socket.on.*('|")user\1[^]*num-users/s,
'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/s,
'You should append a list item to "#messages" on your client within the "user" event listener to announce a user came or went'
);
}
```

# --solutions--
Expand Down
Expand Up @@ -10,29 +10,31 @@ dashedName: authentication-strategies

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 use Passport middleware. Passport provides a comprehensive set of strategies that support authentication using a username and password, GitHub, Google, and more.

`passport-local@~1.0.0` has already been added as a dependency, so add it to your server as follows: `const LocalStrategy = require('passport-local');`
`passport-local@~1.0.0` has already been added as a dependency. Add it to your server as follows:

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!
```javascript
const LocalStrategy = require('passport-local');
```

```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);
});
}
));
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!:

```javascript
passport.use(new LocalStrategy((username, password, done) => {
myDataBase.findOne({ username: username }, (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.
This is defining the process to use when you try to authenticate someone locally. First, it tries to find a user in your database with the username entered. Then, it checks for the password to match. Finally, if no errors have popped up that you checked for (e.g. an incorrect password), the `user` 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.
Many strategies are set up using different settings. 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 you 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 you 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, you 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 <a href="https://gist.github.com/camperbot/53b495c02b92adeee0aa1bd3f3be8a4b" target="_blank" rel="noopener noreferrer nofollow">check out the project completed up to this point</a>.

Expand All @@ -41,48 +43,41 @@ Submit your page when you think you've got it right. If you're running into erro
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);
}
async (getUserInput) => {
const url = new URL("/_api/package.json", getUserInput("url"));
const res = await fetch(url);
const packJson = await res.json();
assert.property(
packJson.dependencies,
'passport-local',
'Your project should list "passport-local " as a dependency'
);
}
```

Passport-local should be correctly required and setup.
Passport-local should be correctly required and set up.

```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);
}
async (getUserInput) => {
const url = new URL("/_api/server.js", getUserInput("url"));
const res = await fetch(url);
const data = await res.text();
assert.match(
data,
/require.*("|')passport-local("|')/,
'You should have required passport-local'
);
assert.match(
data,
/new LocalStrategy/,
'You should have told passport to use a new strategy'
);
assert.match(
data,
/findOne/,
'Your new local strategy should use the findOne query to find a username based on the inputs'
);
}
```

# --solutions--
Expand Down
Expand Up @@ -60,7 +60,7 @@ function onAuthorizeFail(data, message, error, accept) {
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');
console.log('user ' + socket.request.user.username + ' connected');
```

It will log to the server console who has connected!
Expand All @@ -72,75 +72,61 @@ Submit your page when you think you've got it right. If you're running into erro
`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);
}
async (getUserInput) => {
const url = new URL("/_api/package.json", getUserInput("url"));
const res = await fetch(url);
const packJson = await res.json();
assert.property(
packJson.dependencies,
'passport.socketio',
'Your project should list "passport.socketio" as a dependency'
);
}
```

`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);
}
async (getUserInput) => {
const url = new URL("/_api/package.json", getUserInput("url"));
const res = await fetch(url);
const packJson = await res.json();
assert.property(
packJson.dependencies,
'cookie-parser',
'Your project should list "cookie-parser" as a dependency'
);
}
```

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);
}
async (getUserInput) => {
const url = new URL("/_api/server.js", getUserInput("url"));
const res = await fetch(url);
const data = await res.text();
assert.match(
data,
/require\((['"])passport\.socketio\1\)/gi,
'You should correctly require and instantiate "passport.socketio"'
);
}
```

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);
}
async (getUserInput) => {
const url = new URL("/_api/server.js", getUserInput("url"));
const res = await fetch(url);
const data = await res.text();
assert.match(
data,
/io\.use\(\s*\w+\.authorize\(/,
'You should register "passport.socketio" as socket.io middleware and provide it correct options'
);
}
```

# --solutions--
Expand Down
Expand Up @@ -24,7 +24,7 @@ Finally, take all of the routes in your server and paste them into your new 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.
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 <a href="https://gist.github.com/camperbot/2d06ac5c7d850d8cf073d2c2c794cc92" target="_blank" rel="noopener noreferrer nofollow">check out an example of the completed project</a>.

Expand All @@ -33,24 +33,21 @@ Submit your page when you think you've got it right. If you're running into erro
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\s*\.db[^]*routes/gi,
'Your new modules should be called after your connection to the database'
);
},
(xhr) => {
throw new Error(xhr.statusText);
}
async (getUserInput) => {
const url = new URL("/_api/server.js", getUserInput("url"));
const res = await fetch(url);
const data = await res.text();
assert.match(
data,
/require\s*\(('|")\.\/routes(\.js)?\1\)/gi,
'You should have required your new files'
);
assert.match(
data,
/client\s*\.db[^]*routes/gi,
'Your new modules should be called after your connection to the database'
);
}
```

# --solutions--
Expand Down