All files / src auth.js

52.46% Statements 32/61
40.63% Branches 13/32
73.68% Functions 14/19
52.46% Lines 32/61
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149            1x                                               16x 12x 25x 4x         11x 11x     11x                             11x             14x 14x 14x 14x               25x 25x 13x   12x         12x       10x 10x 10x       13x       2x 2x 2x       7x       7x                 6x 6x   6x       6x                                           4x    
import queryParse from './util/query-parse'
import { Observable } from 'rxjs/Observable'
import 'rxjs/add/operator/do'
import 'rxjs/add/operator/map'
import 'rxjs/add/observable/dom/ajax'
 
const HORIZON_JWT = 'horizon-jwt'
 
/** @this Horizon **/
export function authEndpoint(name) {
  const endpointForName = methods => {
    if (methods.hasOwnProperty(name)) {
      return this._root + methods[name]
    } else {
      throw new Error(`Unconfigured auth type: ${name}`)
    }
  }
  if (!this._authMethods) {
    return Observable.ajax(`${this._horizonPath}/auth_methods`)
      .map(ajax => ajax.response)
      .do(authMethods => {
        this._authMethods = authMethods
      }).map(endpointForName)
  } else {
    return Observable.of(this._authMethods).map(endpointForName)
  }
}
 
// Simple shim to make a Map look like local/session storage
export class FakeStorage {
  constructor() { this._storage = new Map() }
  setItem(a, b) { return this._storage.set(a, b) }
  getItem(a) { return this._storage.get(a) }
  removeItem(a) { return this._storage.delete(a) }
}
 
function getStorage(storeLocally = true) {
  let storage
  try {
    Eif (!storeLocally ||
        typeof window !== 'object' ||
        window.localStorage === undefined) {
      storage = new FakeStorage()
    } else {
      // Mobile safari in private browsing has a localStorage, but it
      // has a size limit of 0
      window.localStorage.setItem('$$fake', 1)
      window.localStorage.removeItem('$$fake')
      storage = window.localStorage
    }
  } catch (error) {
    if (window.sessionStorage === undefined) {
      storage = new FakeStorage()
    } else {
      storage = window.sessionStorage
    }
  }
  return storage
}
 
export class TokenStorage {
  constructor({ authType = 'token',
                storage = getStorage(authType.storeLocally),
                path = 'horizon' } = {}) {
    this._storage = storage
    this._path = path
    Eif (typeof authType === 'string') {
      this._authType = authType
    } else {
      this._authType = 'token'
      this.set(authType.token)
    }
  }
 
  _getHash() {
    const val = this._storage.getItem(HORIZON_JWT)
    if (val == null) {
      return {}
    } else {
      return JSON.parse(val)
    }
  }
 
  _setHash(hash) {
    this._storage.setItem(HORIZON_JWT, JSON.stringify(hash))
  }
 
  set(jwt) {
    const current = this._getHash()
    current[this._path] = jwt
    this._setHash(current)
  }
 
  get() {
    return this._getHash()[this._path]
  }
 
  remove() {
    const current = this._getHash()
    delete current[this._path]
    this._setHash(current)
  }
 
  setAuthFromQueryParams() {
    const parsed = typeof window !== 'undefined' &&
      typeof window.location !== 'undefined' ?
            queryParse(window.location.search) : {}
 
    Iif (parsed.horizon_token != null) {
      this.set(parsed.horizon_token)
    }
  }
 
  // Handshake types are implemented here
  handshake() {
    // If we have a token, we should send it rather than requesting a
    // new one
    const token = this.get()
    Iif (token != null) {
      return { method: 'token', token }
    } else Iif (this._authType === 'token') {
      throw new Error(
        'Attempting to authenticate with a token, but no token is present')
    } else {
      return { method: this._authType }
    }
  }
 
  // Whether there is an auth token for the provided authType
  hasAuthToken() {
    const token = this.get()
    if (!token) {
      return false
    }
    try {
      const meta = JSON.parse(atob(token.split('.')[1]))
      const exp = meta.exp
      const now = new Date().getTime() / 1000
      return (now < exp)
    } catch (e) {
      return false
    }
  }
}
 
export function clearAuthTokens() {
  return getStorage().removeItem(HORIZON_JWT)
}