Cell API
    Preparing search index...

    Module @celljs/security - v4.1.0

    @celljs/security

    Authentication and authorization for Cell backends: form login, HTTP Basic, logout, @Authenticated() / @Anonymous() / @PreAuthorize() on controllers, and password hashing. Install it and it is active; no registration is needed.

    npm install @celljs/security
    

    There is no built-in user. Give the login a user source in one of two ways:

    1. One user in cell.yml. password is a hash made by the PasswordEncoder, never the plain password:

      backend:
      cell:
      security:
      username: admin
      password: pbkdf2-sha256$600000$<salt>$<key>

      Make a hash with the encoder of the version you deploy, for example:

      // hash.mjs — node hash.mjs 'the password'
      import 'reflect-metadata';
      import { Pbkdf2PasswordEncoder } from '@celljs/security/lib/node/crypto/password/password-encoder';
      console.log(await new Pbkdf2PasswordEncoder().encode(process.argv[2]));

      If you set cell.security.passwordEncoder.secret (a pepper), new hashes depend on it: make them inside the application (inject PasswordEncoder), not with the script above.

    2. Your own UserService, for users stored in a database:

      import { Component } from '@celljs/core';
      import { User, UserType } from '@celljs/security/lib/common';
      import { UserService, UsernameNotFoundError } from '@celljs/security/lib/node';

      @Component({ id: UserService, rebind: true })
      export class DbUserService implements UserService<string, User> {
      async load(username: string): Promise<User> {
      const row = await findUser(username); // your data access
      if (!row) {
      throw new UsernameNotFoundError(`Could not find: ${username}`);
      }
      return {
      type: UserType.Database, username, password: row.passwordHash, policies: [],
      accountNonExpired: true, accountNonLocked: true, credentialsNonExpired: true, enabled: true
      };
      }

      // Optional: lets a login replace an outdated hash (see "Password hashes").
      async updatePassword(user: User, encodedPassword: string): Promise<void> {
      await saveHash(user.username, encodedPassword);
      }
      }

    With neither, the application starts and logs a warning that no user can log in (an application may log in with OAuth2 only, or use this package for authorization alone).

    Way Request Result
    Form POST /login with username and password, as JSON or as a form (cell.security.loginUrl, loginMethod, usernameKey, passwordKey) 302 to loginSuccessUrl (or the redirect query parameter) with a session cookie; wrong credentials 401
    Basic Authorization: Basic … on any request, e.g. curl -u admin:secret http://localhost:3000/users The request itself is authenticated and continues to the resource; wrong credentials 401
    Logout POST /logout 302 to logoutSuccessUrl

    Basic is off by default; turn it on with cell.security.basic.enabled: true. It checks the password on every request, and one check is one PBKDF2 derivation with 600000 iterations (about 45 ms of CPU). Use Basic only for low-frequency machine-to-machine calls; for frequent calls log in once and use the session, or use JWT (@celljs/jwt).

    import { Controller, Get } from '@celljs/mvc/lib/node';
    import { Anonymous, Authenticated } from '@celljs/security/lib/node';

    @Controller('users')
    @Authenticated() // requires a logged-in user; anonymous requests get 401
    export class UserController {
    @Get()
    list() { return []; }
    }

    @Controller()
    @Anonymous() // reachable without logging in
    export class HomeController {}

    @PreAuthorize(el) / @PostAuthorize(el) check an expression before or after the method; @Authorize, @Resource and @Action work with policies.

    PasswordEncoder (Pbkdf2PasswordEncoder) stores pbkdf2-sha256$<iterations>$<salt>$<key>: PBKDF2-HMAC-SHA256, 600000 iterations, a random 16-byte salt, compared in constant time.

    Hashes stored by 4.0.x keep matching. After a successful login (form or Basic), such a hash is replaced by a new one when your UserService implements updatePassword(). The upgrade is one-way: once a user has logged in, an application rolled back to 4.0.x can no longer check that user's password. Hashes made with crypto-js 4.1 or older are not supported.

    Keys under cell.security (defaults in this package's cell.yml):

    Key Default
    username, password — The single user of the default UserService; password is a hash
    loginUrl, loginMethod /login, POST Form login endpoint
    usernameKey, passwordKey username, password Field names of the form login
    loginSuccessUrl / Where a form login redirects
    logoutUrl, logoutMethod, logoutSuccessUrl /logout, POST, the login page Logout
    basic.enabled, basic.realm false, realm HTTP Basic
    passwordEncoder.secret — Optional pepper mixed into new hashes
    browser
    common
    node