Authentication

    The session middleware provides a mechanism for adding session logic to your service, using e.g. a collection or JSON Web Tokens to store the sessions between requests.

    With these building blocks you can implement your own session-based authentication.

    In this example we’ll use two collections: a collection to store the user objects with names and credentials, and a sessions collection to store the session data. We’ll also make sure usernames are unique by 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 it to the service router:

    1. // in util/sessions.js
    2. "use strict";
    3. const sessionsMiddleware = require("@arangodb/foxx/sessions");
    4. const sessions = sessionsMiddleware({
    5. storage: module.context.collection("sessions"),
    6. transport: "cookie"
    7. });
    8. module.exports = sessions;
    1. // in your main file
    2. // ...
    3. const sessions = require("./util/sessions");
    4. module.context.use(sessions);

    If you want, you can now use the authenticator to help create an initial user in the setup script. Note we’re hardcoding the password here but you could make it configurable via a :

    1. // ...
    2. const auth = require("./util/auth");
    3. const users = module.context.collection("users");
    4. if (!users.firstExample({ username: "admin" })) {
    5. users.save({
    6. username: "admin",
    7. password: auth.create("hunter2")
    8. });
    9. }

    We can now put the two together to create a login route:

    1. // ...
    2. const auth = require("./util/auth");
    3. const users = module.context.collection("users");
    4. const joi = require("joi");
    5. const createRouter = require("@arangodb/foxx/router");
    6. const router = createRouter();
    7. router
    8. .post("/login", function(req, res) {
    9. const user = users.firstExample({
    10. username: req.body.username
    11. });
    12. user ? user.password : {},
    13. req.body.password
    14. );
    15. if (!valid) res.throw("unauthorized");
    16. // Log the user in using the key
    17. // because usernames might change
    18. req.session.uid = user._key;
    19. req.sessionStorage.save(req.session);
    20. res.send({ username: user.username });
    21. })
    22. .body(
    23. joi
    24. .object({
    25. username: joi.string().required(),
    26. password: joi.string().required()
    27. })
    28. .required()
    29. );

    To provide information about the authenticated user we can look up the session user:

    To log a user out we can remove the user from the session:

    1. router.post("/logout", function(req, res) {
    2. if (req.session.uid) {
    3. req.session.uid = null;
    4. req.sessionStorage.save(req.session);
    5. }
    6. res.status("no content");
    7. });
    1. "use strict";
    2. const sessions = require("./util/sessions");
    3. module.exports = sessions.storage.prune();

    When using HTTP Basic authentication, ArangoDB will set the arangoUser attribute of the request object if the credentials match a valid ArangoDB user for the database.

    Note: Although the presence and value of this attribute can be used to implement a low-level authentication mechanism this is only useful if your service is only intended to be used by developers who already have access to the 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:

    1. "use strict";
    2. const sessions = module.context.collection("sessions");
    3. // This is the secret string used to sign cookies
    4. // you probably don't want to hardcode this.
    5. const secret = "some secret string";
    6. let sid = req.cookie("sid", { secret });
    7. if (sid) {
    8. try {
    9. // Try to find a matching session
    10. req.session = sessions.document(sid);
    11. } catch (e) {
    12. // No session found, cookie is invalid
    13. sid = null;
    14. // Clear the cookie so it will be discarded
    15. res.cookie("sid", "", { ttl: -1, secret });
    16. }
    17. }
    18. try {
    19. // Continue handling the request
    20. next();
    21. } finally {
    22. // Do this even if the request threw
    23. if (req.session) {
    24. if (sid) {
    25. // Sync the session's changes to the db
    26. sessions.update(sid, req.session);
    27. } else {
    28. // Create a new session with a new key
    29. sid = sessions.save(req.session)._key;
    30. }
    31. // Set or update the session cookie
    32. res.cookie("sid", sid, { ttl: 24 * 60 * 60, secret });
    33. } else if (sid) {
    34. // The request handler explicitly cleared
    35. // the session, so we need to delete it
    36. sessions.remove(sid);
    37. // And clear the cookie too
    38. res.cookie("sid", "", { ttl: -1, secret });
    39. }
    40. }