MitocGroup/deep-framework

View on GitHub
src/deep-security/lib/IdentityProvider.js

Summary

Maintainability
B
6 hrs
Test Coverage
/**
 * Created by mgoria on 11/12/15.
 */

'use strict';

import {MissingLoginProviderException} from './Exception/MissingLoginProviderException';
import {IdentityProviderMismatchException} from './Exception/IdentityProviderMismatchException';
import {InvalidProviderIdentityException} from './Exception/InvalidProviderIdentityException';
import {MissingIdentityImplementationException} from './Exception/MissingIdentityImplementationException';
import {UserPoolImplementation} from './IdentityImplementation/UserPoolImplementation';
import {MissingRefreshTokenException} from './Exception/MissingRefreshTokenException';

/**
 * @todo: split identity providers implementations
 * 
 * 3rd Party identity provider (Amazon, Facebook, Google, etc.)
 */
export class IdentityProvider {
  /**
   * @param {Object} providers
   * @param {String} providerName
   * @param {Object} identityMetadata
   */
  constructor(providers, providerName, identityMetadata) {
    let providerDomain = this.getProviderDomain(providerName, providers);

    if (!providerDomain) {
      throw new MissingLoginProviderException(providerName);
    }

    if (identityMetadata.provider
      && identityMetadata.provider !== providerName
      && providerName !== IdentityProvider.SNAPSHOT_PROVIDER) {

      throw new IdentityProviderMismatchException(providerName, identityMetadata.provider);
    }

    let normalizedMetadata = this._normalizeIdentityMetadata(providerName, identityMetadata);

    this._metadata = identityMetadata;
    this._userToken = normalizedMetadata.token;
    this._tokenExpTime = new Date(normalizedMetadata.expireTime);
    this._userId = normalizedMetadata.userId;
    this._providers = providers;
    this._refreshToken = normalizedMetadata.refreshToken;
    this._domain = providerDomain;
    this._clientName = normalizedMetadata.clientName;
    this._name = providerName;
  }

  /**
   * @param {Object} metadata
   * @returns {IdentityProvider}
   */
  static createFromSnapshot(metadata) {
    let provider = new IdentityProvider(null, IdentityProvider.SNAPSHOT_PROVIDER, metadata);

    provider.fillFromSnapshot(metadata);

    return provider;
  }

  /**
   * @param {String} name
   */
  set name(name) {
    this._name = name;
  }

  /**
   * @param {String} providerName
   * @param {Object} providers
   * @returns {*}
   */
  getProviderDomain(providerName, providers) {
    let domainRegexp;

    switch(providerName) {
      case IdentityProvider.AMAZON_PROVIDER:
        domainRegexp = /^www\.amazon\.com$/;
        break;
      case IdentityProvider.FACEBOOK_PROVIDER:
        domainRegexp = /^graph\.facebook\.com$/;
        break;
      case IdentityProvider.GOOGLE_PROVIDER:
        domainRegexp = /^accounts\.google\.com$/;
        break;
      case IdentityProvider.AUTH0_PROVIDER:
        domainRegexp = /^.+\.auth0\.com$/;
        break;
      case IdentityProvider.COGNITO_USER_POOL_PROVIDER:
        domainRegexp = /^cognito\-idp\.[\w\d\-]+\.amazonaws\.com\/[\w\d\-]+$/;
        break;
      case IdentityProvider.SNAPSHOT_PROVIDER:
        return IdentityProvider.SNAPSHOT_PROVIDER;
    }

    if (!domainRegexp) {
      return null;
    }

    for (let providerDomain in providers) {
      if (!providers.hasOwnProperty(providerDomain)) {
        continue;
      }

      if (domainRegexp.test(providerDomain)) {
        return providerDomain;
      }
    }

    return null;
  }

  /**
   * @todo: Implement other identity providers
   * @param {String} providerName
   * @param {Object} identityMetadata
   * @returns {*}
   * @private
   */
  _normalizeIdentityMetadata(providerName, identityMetadata) {
    let token = null;
    let expiresIn  = null;
    let expireTime = null;
    let userId = null;
    let refreshToken = null;
    let clientName = null;

    switch(providerName) {
      case IdentityProvider.FACEBOOK_PROVIDER:
        token = identityMetadata.accessToken;
        expiresIn = identityMetadata.expiresIn;
        userId = identityMetadata.userID;
        break;

      case IdentityProvider.COGNITO_USER_POOL_PROVIDER:
        let userSession = identityMetadata.getSignInUserSession();
        let idTokenInstance = userSession.getIdToken();
        refreshToken = userSession.getRefreshToken().getToken();
        token = idTokenInstance.getJwtToken();
        expireTime = idTokenInstance.getExpiration() * 1000;
        clientName = identityMetadata.pool.getClientId();
        break;

      case IdentityProvider.AMAZON_PROVIDER:
        token = identityMetadata.access_token;
        userId = identityMetadata.user_id;
        expiresIn = identityMetadata.expires_in || 3600;
        break;

      case IdentityProvider.AUTH0_PROVIDER:
        expireTime = identityMetadata.tokenExpirationTime;
        token = identityMetadata.access_token;
        userId = identityMetadata.user_id;
        break;

      // backend identity provider has the same structure as normalized metadata. see `toJSON` method
      case IdentityProvider.SNAPSHOT_PROVIDER:
        return identityMetadata;
    }

    userId = userId || null;
    expireTime = expireTime ||
      (expiresIn ?
        (Date.now() + expiresIn * 1000) :
        null);

    if (!(token && expireTime)) {
      throw new InvalidProviderIdentityException(providerName);
    }

    return {token, userId, expireTime, refreshToken, clientName};
  }

  /**
   * @returns {Object}
   */
  get providers() {
    return this._providers;
  }

  /**
   * @returns {String}
   */
  get name() {
    return this._name;
  }

  /**
   * @returns {String}
   */
  get domain() {
    return this._domain;
  }

  /**
   * @returns {String}
   */
  get userToken() {
    return this._userToken;
  }

  /**
   * @param {String} userToken
   */
  set userToken(userToken) {
    this._userToken = userToken;
  }

  /**
   * @returns {Date}
   */
  get tokenExpirationTime() {
    return this._tokenExpTime;
  }

  /**
   * @param {Date} tokenExpirationTime
   */
  set tokenExpirationTime(tokenExpirationTime) {
    this._tokenExpTime = tokenExpirationTime instanceof Date ? tokenExpirationTime : new Date(tokenExpirationTime);
  }

  /**
   * @returns {String}
   */
  get refreshToken() {
    return this._refreshToken;
  }

  /**
   * @returns {String}
   */
  get clientName() {
    return this._clientName;
  }

  /**
   * @returns {boolean}
   */
  isTokenValid() {
    if (this.userToken && this.tokenExpirationTime) {
      return this.tokenExpirationTime > new Date();
    }

    return false;
  }

  /**
   * @param {Object} idpSnapshot
   * 
   * @returns {IdentityProvider}
   */
  fillFromSnapshot(idpSnapshot) {
    this._name = idpSnapshot.name;
    this._domain = idpSnapshot.domain;
    this._refreshToken = idpSnapshot.refreshToken;
    this._clientName = idpSnapshot.clientName;
    this._userToken = idpSnapshot.token;
    this._tokenExpTime = new Date(idpSnapshot.expireTime);

    return this;
  }

  /**
   * return normalizedMetadata compatible structure, see `_normalizeIdentityMetadata` method
   * @returns {*}
   */
  toJSON() {
    return {
      token: this._userToken,
      expireTime: this._tokenExpTime.getTime(),
      userId: this._userId,
      refreshToken: this._refreshToken,
      name: this._name,
      domain: this._domain,
      clientName: this._clientName,
    };
  }

  /**
   * @returns {Promise}
   */
  refresh() {
    if (!this._refreshToken) {
      return Promise.reject(new MissingRefreshTokenException());
    }

    let implementation = null;

    switch (this._name) {
      case IdentityProvider.COGNITO_USER_POOL_PROVIDER:
        implementation = new UserPoolImplementation(this);
        break;
      default:
        throw new MissingIdentityImplementationException(this._name);
    }

    return implementation.refreshIdentity();
  }

  /**
   * @returns {String}
   */
  get userId() {
    return this._userId;
  }

  /**
   * @param {String} name
   * @returns {Object}
   */
  config(name) {
    if (!this.providers.hasOwnProperty(name)) {
      throw new MissingLoginProviderException(name);
    }

    return this.providers[name];
  }

  /**
   * @returns {String}
   */
  static get COGNITO_USER_POOL_PROVIDER() {
    return 'cognito-user-pool';
  }

  /**
   * @returns {String}
   */
  static get FACEBOOK_PROVIDER() {
    return 'facebook';
  }

  /**
   * @returns {String}
   */
  static get AMAZON_PROVIDER() {
    return 'amazon';
  }

  /**
   * @returns {String}
   */
  static get GOOGLE_PROVIDER() {
    return 'google';
  }

  /**
   * @returns {String}
   */
  static get AUTH0_PROVIDER() {
    return 'auth0';
  }

  /**
   * @returns {String}
   */
  static get SNAPSHOT_PROVIDER() {
    return 'snapshot';
  }
}