All files / src/auth/resolvers OnlineAddinOnly.ts

21.05% Statements 8/38
0% Branches 0/6
0% Functions 0/10
21.62% Lines 8/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                                                                                                                                                                                
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++) {
            if (data.endpoints[i].protocol === 'OAuth2') {
              resolve(data.endpoints[i].location);
              return undefined;
            }
          }
        });
    });
  }
 
  private getRealm(siteUrl: string): Promise<string> {
 
    if (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>;
  }
}