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 sessionscollection and the “cookie” transport in a separate file, and add itto 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 userin the setup script. Note we’re hardcoding the password here but you couldmake 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.authData : {},
    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 upthe 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 arangoUserattribute 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:

    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. }