All files / src/auth/resolvers OnlineAddinOnly.ts

97.37% Statements 37/38
66.67% Branches 4/6
100% Functions 10/10
97.3% Lines 36/37
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 1031x 1x   1x         1x 1x 1x   1x   1x   3x     3x 3x   3x   3x 2x             1x   1x     1x 1x   1x 1x   1x                     1x 1x   1x                 1x 1x   1x   1x 1x 1x 1x                 1x       1x                 1x 1x 1x        
import * as Promise from 'bluebird';
import * as request from 'request-promise';
import { IncomingMessage } from 'http';
import * as url from 'url';
 
import { IAuthResolver } from './../IAuthResolver';
import { IOnlineAddinCredentials } from './../IAuthOptions';
import { IAuthResponse } from './../IAuthResponse';
import { Cache } from './../../utils/Cache';
import { UrlHelper } from './../../utils/UrlHelper';
import * as consts from './../../Consts';
 
export class OnlineAddinOnly implements IAuthResolver {
 
  private static TokenCache: Cache = new Cache();
 
  constructor(private _siteUrl: string, private _authOptions: IOnlineAddinCredentials) { }
 
  public getAuth(): Promise<IAuthResponse> {
    let sharepointhostname: string = url.parse(this._siteUrl).hostname;
    let cacheKey: string = this._authOptions.clientSecret;
 
    let cachedToken: string = OnlineAddinOnly.TokenCache.get<string>(cacheKey);
 
    if (cachedToken) {
      return Promise.resolve({
        headers: {
          'Authorization': `Bearer ${cachedToken}`
        }
      });
    }
 
    return this.getRealm(this._siteUrl)
      .then(realm => {
        return Promise.all([realm, this.getAuthUrl(realm)]);
      })
      .then(data => {
        let realm: string = data[0];
        let authUrl: string = data[1];
 
        let resource = `${consts.SharePointServicePrincipal}/${sharepointhostname}@${realm}`;
        let fullClientId = `${this._authOptions.clientId}@${realm}`;
 
        return request.post(authUrl, {
          json: true,
          form: {
            'grant_type': 'client_credentials',
            'client_id': fullClientId,
            'client_secret': this._authOptions.clientSecret,
            'resource': resource
          }
        });
      })
      .then(data => {
        let expiration: number = parseInt(data.expires_in, 10);
        OnlineAddinOnly.TokenCache.set(cacheKey, data.access_token, expiration - 60);
 
        return {
          headers: {
            'Authorization': `Bearer ${data.access_token}`
          }
        };
      });
  };
 
  private getAuthUrl(realm: string): Promise<string> {
    return new Promise<string>((resolve, reject) => {
      let url = `https://accounts.accesscontrol.windows.net/metadata/json/1?realm=${realm}`;
 
      request.get(url, { json: true })
        .then((data: { endpoints: { protocol: string, location: string }[] }) => {
          for (let i = 0; i < data.endpoints.length; i++) {
            Eif (data.endpoints[i].protocol === 'OAuth2') {
              resolve(data.endpoints[i].location);
              return undefined;
            }
          }
        });
    });
  }
 
  private getRealm(siteUrl: string): Promise<string> {
 
    Iif (this._authOptions.realm) {
      return Promise.resolve(this._authOptions.realm);
    }
 
    return request.post(`${UrlHelper.removeTrailingSlash(siteUrl)}/vti_bin/client.svc`, {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer '
      },
      resolveWithFullResponse: true,
      simple: false
    })
      .then((data: IncomingMessage) => {
        let header: string = data.headers['www-authenticate'];
        let index: number = header.indexOf('Bearer realm="');
        return header.substring(index + 14, index + 50);
      }) as Promise<string>;
  }
}