Source: service/authentication-service.js

const User = require('../auth/user');
const AuthenticationFactory = require('../security/authentication-factory');

/**
 * Service dealing with user authentication in a secured server.
 *
 * @author Mihail Radkov
 * @author Svilen Velikov
 * @author Teodossi Dossev
 */
class AuthenticationService {
  /**
   * Instantiates the service with the provided HTTP request executor.
   *
   * @param {HttpClient} [httpClient] used to execute HTTP requests
   */
  constructor(httpClient) {
    this.httpClient = httpClient;
    this.authenticationFactory = new AuthenticationFactory();
  }

  /**
   * Performs a login request against secured server with provided username and
   * password. Upon successful authentication a {@link User} instance is created
   * with the user data and the auth token and returned to the client.
   *
   * @param {ClientConfig} clientConfig concrete client configuration
   * @param {User} user logged in user
   *
   * @return {Promise<User>} a promise resolving to an authenticated
   * {@link User} instance.
   */
  login(clientConfig, user) {
    if (!clientConfig.shouldAuthenticate() ||
      !this.isAlreadyAuthenticated(clientConfig, user)) {
      return Promise.resolve(user);
    }

    const authentication = this.getAuthentication(clientConfig);
    return this.httpClient.request(this.getLoginRequest(clientConfig))
      .then((response) => {
        const token = authentication.getResponseAuthToken(response);
        return new User(token, clientConfig.getPass(), response.data);
      });
  }

  /**
   * Performs a logout of logged in user. This effectively removes the stored in
   * the client user. Every consecutive call against secured server will result
   * in <code>Unauthorized</code> error with status code <code>401</code>.
   *
   * @param {User} user logged in user
   *
   * @return {Promise} returns a promise which resolves with undefined.
   */
  logout(user) {
    user.clearToken();
    return Promise.resolve();
  }

  /**
   * Return an effective valid token as string which is going to be send as a
   * request header <code>Authorization: token</code>. If there is no logged in
   * user, then this method returns <code>undefined</code>.
   *
   * @param {User} user logged in user
   * @return {string|undefined} authentication token
   */
  getAuthenticationToken(user) {
    return user && user.getToken();
  }

  /**
   * Checks if user credentials are provided and there isn't authenticated user
   * yet. If that's the case, authentication should be made.
   *
   * @private
   * @param {ClientConfig} clientConfig concrete client config
   * @param {User} user logged in user
   *
   * @return {boolean} true if authentication should be made
   */
  isAlreadyAuthenticated(clientConfig, user) {
    const hasCredentials = clientConfig.getUsername() && clientConfig.getPass();
    const isAuthenticated = user && user.getToken();
    return hasCredentials && !isAuthenticated;
  }

  /**
   * Returns authentication type related {@link HttpRequestBuilder}
   * login request builder
   *
   * @param {ClientConfig} clientConfig concrete client configuration
   * @return {HttpRequestBuilder} request builder
   */
  getLoginRequest(clientConfig) {
    return this.getAuthentication(clientConfig).getLoginRequestBuilder();
  }

  /**
   * Authentication type getter
   * @param {ClientConfig} clientConfig concrete client configuration
   * @return {BasicAuthentication|GdbTokenAuthentication} concrete
   * authentication type
   */
  getAuthentication(clientConfig) {
    return this.authenticationFactory.getAuthenticationType(clientConfig);
  }
}

module.exports = AuthenticationService;