Skip to content

Commit

Permalink
Merge pull request #31 from feathersjs/migrate-docs-to-book
Browse files Browse the repository at this point in the history
Migrate docs to book
  • Loading branch information
marshallswain authored and daffl committed Aug 29, 2018
1 parent b854980 commit cd57a12
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 231 deletions.
232 changes: 9 additions & 223 deletions packages/authentication/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,20 @@

> Add Authentication to your FeathersJS app.
`feathers-authentication` adds shared [PassportJS](http://passportjs.org/) authentication for Feathers HTTP REST and websockets services using [JSON Web Tokens](http://jwt.io/).
`feathers-authentication` adds shared [PassportJS](http://passportjs.org/) authentication for Feathers HTTP REST and WebSockets services using [JSON Web Tokens](http://jwt.io/).

## Usage
If you are using the default options, setting up JWT auth for your Feathers app is as simple as the below example. Note: You must set up the `body-parser` module before setting up `feathers-authentication`.

```js
var app = feathers()
.configure(feathers.rest())
.configure(feathers.socketio())
.configure(hooks())
.use(bodyParser.json())
.use(bodyParser.urlencoded({ extended: true }))
## Documentation

// Configure feathers-authentication
.configure(feathersAuth({
secret: 'feathers-rocks'
}));
```
Please refer to the [Authentication documentation](http://docs.feathersjs.com/authentication/readme.html) for more details:

- [Local Auth Tutorial](http://docs.feathersjs.com/authentication/local.html) - How to implement a username and password-based authentication.
- [Use Hooks for Authorization](http://docs.feathersjs.com/authorization/readme.html) - Learn about the bundled hooks.

## Getting Started Tutorial

Here's a more complete example that you can use to get started.
## Complete Example

Here's an example of a Feathers server that uses `feathers-authentication` for local auth. It includes a `users` service that uses `feathers-mongoose`. *Note that it does NOT implement any authorization.*

```js
/* * * Import Feathers and Plugins * * */
Expand Down Expand Up @@ -79,213 +71,7 @@ server.on('listening', function() {
});
```

Please note that the above User service does not include any kind of authorization or access control. That will require setting up additional hooks, later. For now, leaving out the access control will allow us to easily create a user. Here's an example request to create a user (make sure your server is running):
```js
// Create User (POST http://localhost:3030/api/users)
jQuery.ajax({
url: 'http://localhost:3030/api/users',
type: 'POST',
headers: {
'Content-Type': 'application/json',
},
contentType: 'application/json',
data: JSON.stringify({
'username': 'feathersuser',
'password': 'fowlplay'
})
})
.done(function(data, textStatus, jqXHR) {
console.log('HTTP Request Succeeded: ' + jqXHR.status);
console.log(data);
})
.fail(function(jqXHR, textStatus, errorThrown) {
console.log('HTTP Request Failed', arguments);
});
```
Once you've created a user, logging in is as simple as `POST`ing a request to the `loginEndpoint`, which is `/api/login` by default. Here's an example request for logging in:
```js
// Login by email (POST http://localhost:3030/api/login)
jQuery.ajax({
url: 'http://localhost:3030/api/login',
type: 'POST',
headers: {
'Content-Type': 'application/json',
},
contentType: 'application/json',
data: JSON.stringify({
'username': 'feathersuser',
'password': 'fowlplay'
})
})
.done(function(data, textStatus, jqXHR) {
console.log('HTTP Request Succeeded: ' + jqXHR.status);
console.log(data);
})
.fail(function(jqXHR, textStatus, errorThrown) {
console.log('HTTP Request Failed', arguments);
});
```
The server will respond with an object that contains two properties, `user` and `token`. The `user` property contains an object with whatever data was returned from your user service. You'll notice that it currently includes the password. You really don't want it exposed, so when you're ready to secure the service, you'll need an additional feathers-hook to remove the password property from the response.
The `token` property contains a JWT token that you can use to authenticate REST requests or for socket connections. You can learn more about how JWT tokens work on [jwt.io](http://jwt.io/).
### Authenticating REST Requests
Authenticated REST requests must have an `Authorization` header in the format `'Bearer <token>'`, where the <token> is the JWT token. The header should have the following format:
```
Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IklseWEgRmFkZWV2IiwiYWRtaW4iOnRydWV9.YiG9JdVVm6Pvpqj8jDT5bMxsm0gwoQTOaZOLI-QfSNc
```
Assuming you've set up a todos service, here is full request example you can try out. Be sure to replace `<token>` with a token you've retrieved from your `loginEndpoint`:
```js
// List Todos (GET http://localhost:3030/api/todos)
jQuery.ajax({
url: 'http://localhost:3030/api/todos',
type: 'GET',
headers: {
"Authorization": "Bearer <token>",
"Accept": "application/json",
},
})
.done(function(data, textStatus, jqXHR) {
console.log("HTTP Request Succeeded: " + jqXHR.status);
console.log(data);
})
.fail(function(jqXHR, textStatus, errorThrown) {
console.log("HTTP Request Failed", arguments);
});
```
### Authenticating Socket.io Connections
In order to authenticate a Websocket connection, you must first obtain a token using an Ajax request to your `loginEndpoint`. You then include that token in the request. The example below is for Socket.io, but the same `query` key can be passed to Primus.
```js
socket = io('', {
// Assuming you've already saved a token to localStorage.
query: 'token=' + localStorage.getItem('featherstoken'),
transports: ['websocket'], // optional, see below
forceNew:true, // optional, see below
});
```
In the above example, the `transports` key is only needed if you, for some reason, need to force the browser to only use websockets. The `forceNew` key is only needed if you have previously connected an *unauthenticated* Websocket connection and you now want to start an *authenticated* request.
## Options
The following options are available:
- __secret__ *required* - The secret used to create encrypted tokens.
- __userEndpoint__ - The api endpoint used to look up the user service. The default is `'/api/users`.
- __loginEndpoint__ - The url for posting the username and password during login. The default is `/api/login`. You can also post a valid token here to receive a new one. You might use this when the current auth token is about to expire to stay logged in on the client.
- __usernameField__ The database field containing the username on the user service. The default is `username`.
- __passwordField__ The database field containing the password on the user service. The default is `password`.
- __loginError__ - The message to return for invalid login. Default is 'Invalid login.'
- __jwtOptions__ - Used to customize the configuration for the jsonwebtoken library. [See the API](https://github.com/auth0/node-jsonwebtoken)
- __jwtOptions.expiresIn__ - The number of **seconds** until the token expires. Default is 36000 (10 hours).
- __strategy__ - Allows you to pass a custom strategy to use for local auth. The default strategy should fit most projects.
- __passport__ (default: `require('passport')`) - The passport module
## Bundled Hooks
The `feathers-authentication` plugin automatically handles auth. Keep in mind that access control is not automatic, but is easy to set up with the included hooks. See the [feathers-hooks](https://github.com/feathersjs/feathers-hooks) plugin and the [FeathersJS website](http://feathersjs.com/learn) for more information about hooks.
#### hashPassword('password')
This is intended to be used on the user service on the `create` method. It will automatically hash the data coming in on the `password` field. You can specify another field by providing another string.
#### requireAuth
## Example
The following shows a commented example for an application using local authentication with a Feathers user service:
```js
var feathers = require('feathers');
var passport = require('passport');
var hooks = require('feathers-hooks');
var memory = require('feathers-memory');
var bodyParser = require('body-parser');
var feathersAuth = require('feathers-authentication').default;
var authHooks = require('feathers-authentication').hooks;
// Initialize the application
var app = feathers()
.configure(feathers.rest())
.configure(feathers.socketio())
.configure(hooks())
// Needed for parsing bodies (login)
.use(bodyParser.urlencoded({ extended: true }))
// Configure feathers-authentication
.configure(feathersAuth({
secret: 'feathers-rocks'
}))
// Initialize a user service
.use('/api/users', memory())
// A simple Todos service that we can used for testing
.use('/todos', {
get: function(id, params, callback) {
callback(null, {
id: id,
text: 'You have to do ' + id + '!',
user: params.user
});
}
})
.use('/', feathers.static(__dirname));
var userService = app.service('/api/users');
// Add a hook to the user service that automatically replaces
// the password with a hash of the password before saving it.
userService.before({
create: authHooks.hashPassword('password')
});
// Create a user that we can use to log in
userService.create({
username: 'feathers',
password: 'secret'
}, {}, function(error, user) {
console.log('Created default user', user);
});
app.listen(4000);
```
Add a `login.html` with an HTML form that allows to log our user in:
```html
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form action="/login" method="post">
<div>
<label>Username:</label>
<input type="text" name="username"/>
</div>
<div>
<label>Password:</label>
<input type="password" name="password"/>
</div>
<div>
<input type="submit" value="Log In"/>
</div>
</form>
</body>
</html>
```

## About


## Changelog
Expand Down
2 changes: 1 addition & 1 deletion packages/authentication/src/hooks/query-with-user-id.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* find, get, update
*/
export default function queryWithUserId(idOnResource = 'userId', id = '_id') {
export default function queryWithUserId(id = '_id', idOnResource = 'userId') {
return function(hook) {
if (hook.params.user) {
hook.params.query[idOnResource] = hook.params.user[id];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
* unauthorized users from setting other users up as administrators.
* This typically would be used on a user-type service.
*
* create, update
* create, update, patch
*/
export default function requireAdminToSetAdmin(){
export default function requireAdminToSetAdmin(adminField = 'admin'){
return function(hook){
if (hook.params.user && !hook.params.user.admin) {
delete hook.data.admin;
if (hook.params.user && !hook.params.user[adminField]) {
delete hook.data[adminField];
}
};
}
9 changes: 6 additions & 3 deletions packages/authentication/src/hooks/to-lower-case.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
* looks for the key on the data object for before hooks and the result object for
* the after hooks.
*/
export default function toLowercase(fieldname) {
export default function toLowercase(fieldName) {
if (!fieldName) {
throw new Error('You must provide the name of the field to use in the toLowerCase hook.');
}
return function(hook){
let location = hook.type === 'before' ? 'data' : 'params';
// Allow user to view records without a userId.
if (hook[location][fieldname] && hook[location][fieldname].toLowercase) {
hook[location][fieldname] = hook[location][fieldname].toLowerCase();
if (hook[location][fieldName] && hook[location][fieldName].toLowercase) {
hook[location][fieldName] = hook[location][fieldName].toLowerCase();
}
};
}

0 comments on commit cd57a12

Please sign in to comment.