Authentication
The session middleware provides a mechanismfor adding session logic to your service, using e.g. a collection orJSON Web Tokens to store the sessions between requests.
With these building blocks you can implement your own session-basedauthentication.
In this example we’ll use two collections: a collection to store theuser objects with names and credentials, and a sessions
collection to storethe session data. We’ll also make sure usernames are uniqueby adding a hash index:
Next you should create a sessions middleware that uses the sessions
collection and the “cookie” transport in a separate file, and add itto the service router:
// in util/sessions.js
"use strict";
const sessionsMiddleware = require("@arangodb/foxx/sessions");
const sessions = sessionsMiddleware({
storage: module.context.collection("sessions"),
transport: "cookie"
});
module.exports = sessions;
// in your main file
// ...
const sessions = require("./util/sessions");
module.context.use(sessions);
If you want, you can now use the authenticator to help create an initial userin the setup script. Note we’re hardcoding the password here but you couldmake it configurable via a:
// ...
const auth = require("./util/auth");
const users = module.context.collection("users");
if (!users.firstExample({ username: "admin" })) {
users.save({
username: "admin",
password: auth.create("hunter2")
});
}
We can now put the two together to create a login route:
// ...
const auth = require("./util/auth");
const users = module.context.collection("users");
const joi = require("joi");
const createRouter = require("@arangodb/foxx/router");
const router = createRouter();
router
.post("/login", function(req, res) {
const user = users.firstExample({
username: req.body.username
});
user ? user.authData : {},
req.body.password
);
if (!valid) res.throw("unauthorized");
// Log the user in using the key
// because usernames might change
req.session.uid = user._key;
req.sessionStorage.save(req.session);
res.send({ username: user.username });
})
.body(
joi
.object({
username: joi.string().required(),
password: joi.string().required()
})
.required()
);
To provide information about the authenticated user we can look upthe session user:
To log a user out we can remove the user from the session:
router.post("/logout", function(req, res) {
if (req.session.uid) {
req.session.uid = null;
req.sessionStorage.save(req.session);
}
res.status("no content");
});
"use strict";
const sessions = require("./util/sessions");
module.exports = sessions.storage.prune();
When using HTTP Basic authentication, ArangoDB will set the arangoUser
attribute of the request object if thecredentials match a valid ArangoDB user for the database.
Note: Although the presence and value of this attribute can be used toimplement a low-level authentication mechanism this is only useful if yourservice is only intended to be used by developers who already have access tothe HTTP API or the administrative web interface.
Example:
If you need more control than the sessions middleware provides,you can also create a basic session system with a few lines of code yourself:
"use strict";
const sessions = module.context.collection("sessions");
// This is the secret string used to sign cookies
// you probably don't want to hardcode this.
const secret = "some secret string";
let sid = req.cookie("sid", { secret });
if (sid) {
try {
// Try to find a matching session
req.session = sessions.document(sid);
} catch (e) {
// No session found, cookie is invalid
sid = null;
// Clear the cookie so it will be discarded
res.cookie("sid", "", { ttl: -1, secret });
}
}
try {
// Continue handling the request
next();
} finally {
// Do this even if the request threw
if (req.session) {
if (sid) {
// Sync the session's changes to the db
sessions.update(sid, req.session);
} else {
// Create a new session with a new key
sid = sessions.save(req.session)._key;
}
// Set or update the session cookie
res.cookie("sid", sid, { ttl: 24 * 60 * 60, secret });
} else if (sid) {
// The request handler explicitly cleared
// the session, so we need to delete it
sessions.remove(sid);
// And clear the cookie too
res.cookie("sid", "", { ttl: -1, secret });
}
}