1
0
Fork 0
arangodb/Documentation/Books/Manual/Foxx/Users.md

135 lines
3.7 KiB
Markdown

User management
===============
Foxx does not provide any user management out of the box but it is very easy to roll your own solution:
The [session middleware](Sessions/README.md) provides mechanisms for adding session logic to your service, using e.g. a collection or JSON Web Tokens to store the sessions between requests.
The [auth module](Auth.md) provides utilities for basic password verification and hashing.
The following example service demonstrates how user management can be implemented using these basic building blocks.
Setting up the collections
--------------------------
Let's say we want to store sessions and users in collections. We can use the setup script to make sure these collections are created before the service is mounted.
First add a setup script to your manifest if it isn't already defined:
```json
"scripts": {
"setup": "scripts/setup.js"
}
```
Then create the setup script with the following content:
```js
'use strict';
const db = require('@arangodb').db;
const sessions = module.context.collectionName('sessions');
const users = module.context.collectionName('users');
if (!db._collection(sessions)) {
db._createDocumentCollection(sessions);
}
if (!db._collection(users)) {
db._createDocumentCollection(users);
}
db._collection(users).ensureIndex({
type: 'hash',
fields: ['username'],
unique: true
});
```
Creating the router
-------------------
The following main file demonstrates basic user management:
```js
'use strict';
const joi = require('joi');
const createAuth = require('@arangodb/foxx/auth');
const createRouter = require('@arangodb/foxx/router');
const sessionsMiddleware = require('@arangodb/foxx/sessions');
const auth = createAuth();
const router = createRouter();
const users = module.context.collection('users');
const sessions = sessionsMiddleware({
storage: module.context.collection('sessions'),
transport: 'cookie'
});
module.context.use(sessions);
module.context.use(router);
router.get('/whoami', function (req, res) {
try {
const user = users.document(req.session.uid);
res.send({username: user.username});
} catch (e) {
res.send({username: null});
}
})
.description('Returns the currently active username.');
router.post('/login', function (req, res) {
// This may return a user object or null
const user = users.firstExample({
username: req.body.username
});
const valid = auth.verify(
// Pretend to validate even if no user was found
user ? user.authData : {},
req.body.password
);
if (!valid) res.throw('unauthorized');
// Log the user in
req.session.uid = user._key;
req.sessionStorage.save(req.session);
res.send({sucess: true});
})
.body(joi.object({
username: joi.string().required(),
password: joi.string().required()
}).required(), 'Credentials')
.description('Logs a registered user in.');
router.post('/logout', function (req, res) {
if (req.session.uid) {
req.session.uid = null;
req.sessionStorage.save(req.session);
}
res.send({success: true});
})
.description('Logs the current user out.');
router.post('/signup', function (req, res) {
const user = req.body;
try {
// Create an authentication hash
user.authData = auth.create(user.password);
delete user.password;
const meta = users.save(user);
Object.assign(user, meta);
} catch (e) {
// Failed to save the user
// We'll assume the UniqueConstraint has been violated
res.throw('bad request', 'Username already taken', e);
}
// Log the user in
req.session.uid = user._key;
req.sessionStorage.save(req.session);
res.send({success: true});
})
.body(joi.object({
username: joi.string().required(),
password: joi.string().required()
}).required(), 'Credentials')
.description('Creates a new user and logs them in.');
```