UNPKG

@glamtime/oauth-oidc-client

Version:

Secure your Angular app using the latest standards for OpenID Connect & OAuth2. Provides support for token refresh, all modern OIDC Identity Providers and more.

174 lines 29.1 kB
import { Inject, Injectable, PLATFORM_ID } from '@angular/core'; import { HttpHeaders, HttpParams } from '@angular/common/http'; import { BehaviorSubject, map, of, switchMap, tap } from 'rxjs'; import { DOCUMENT } from '@angular/common'; import { OAUTH_CONFIG, OAuthConfig } from './models/options.model'; import * as i0 from "@angular/core"; import * as i1 from "@angular/common/http"; import * as i2 from "./storages/oauth-storage.service"; import * as i3 from "./crypto/oauth-crypto.service"; export const OAUTH_WELL_KNOWN_SUFFIX = `/.well-known/openid-configuration`; export class OAuthService { constructor(_zone, _httpClient, _storageService, _cryptoService, _document, _platformId, config) { this._zone = _zone; this._httpClient = _httpClient; this._storageService = _storageService; this._cryptoService = _cryptoService; this._document = _document; this._platformId = _platformId; this._config = new OAuthConfig(); this._openIDConfiguration = null; this._jwtKeys = { keys: [] }; this._userInfo = null; this.authenticated$ = new BehaviorSubject(false); this._config = { ...(new OAuthConfig()), ...config }; } get accessToken() { return this._storageService.accessToken; } get resourceServers() { return this._config.resourceServers; } checkAuth() { return this._fetchOpenIdConfiguration().pipe(map(() => !!(this._storageService.refreshToken && this._storageService.accessToken)), switchMap((result) => { if (result) { this.authenticated$.next(true); return this._fetchRefreshToken().pipe(switchMap(() => this._fetchUserProfile())); } else { const currentUrl = this._document.defaultView.location.toString(); const parsedUrl = new URL(currentUrl); const urlParams = new URLSearchParams(parsedUrl.search); if (urlParams.has('code') && urlParams.has('state')) { const authorizationCode = urlParams.get('code'); const state = urlParams.get('state'); if (state === this._storageService.state) { return this._fetchToken(authorizationCode).pipe(switchMap(() => this._fetchUserProfile()), tap(() => { this.authenticated$.next(true); })); } else { throw new Error(''); } } } this.authenticated$.next(false); return of(false); })); } login() { this._storageService.resetAuthState(); return this._fetchOpenIdConfiguration().pipe(map(() => { this._storageService.state = this._cryptoService.generateState(); this._storageService.codeVerifier = this._cryptoService.generatePKCECodeVerifier(); return this._storageService.codeVerifier; }), switchMap((codeVerifier) => this._cryptoService.generatePKCECodeChallenge(codeVerifier)), map((codeChallenge) => { this._redirectTo(this._createLoginUrl(codeChallenge)); return true; })); } logout() { this._storageService.resetAuthState(); return this._fetchOpenIdConfiguration().pipe(switchMap(() => { const url = this._openIDConfiguration?.revocation_endpoint; let headers = new HttpHeaders(); headers = headers.set('Content-Type', 'application/x-www-form-urlencoded'); headers = headers.set('Authorization', this._encodeClientCredentials()); let params = new HttpParams() .set('token', this._storageService.accessToken); return this._httpClient.post(url, params, { headers: headers }); }), tap(() => this.authenticated$.next(false))); } // Fetch the metadata from openid-configuration _fetchOpenIdConfiguration() { if (this._openIDConfiguration) { return of(this._openIDConfiguration); } const url = this._config.issuer + OAUTH_WELL_KNOWN_SUFFIX; return this._httpClient.get(url).pipe(tap((data) => { this._openIDConfiguration = data; }), switchMap(_ => { return this._httpClient.get(this._openIDConfiguration.jwks_uri).pipe(tap((data) => { this._jwtKeys = data; })); }), map(_ => { return this._openIDConfiguration; })); } _fetchToken(authorizationCode) { const url = this._openIDConfiguration.token_endpoint; let headers = new HttpHeaders(); headers = headers.set('Content-Type', 'application/x-www-form-urlencoded'); headers = headers.set('Authorization', this._encodeClientCredentials()); let params = new HttpParams() .set('grant_type', 'authorization_code') .set('code', authorizationCode) .set('redirect_uri', this._config.redirectUri); if (this._storageService.codeVerifier) { params = params.set('code_verifier', this._storageService.codeVerifier); } return this._httpClient.post(url, params, { headers: headers }).pipe(tap((value) => { this._storageService.accessToken = value.access_token; this._storageService.refreshToken = value.refresh_token; this._storageService.idToken = value.id_token; })); } _fetchRefreshToken() { const url = this._openIDConfiguration.token_endpoint; let headers = new HttpHeaders(); headers = headers.set('Content-Type', 'application/x-www-form-urlencoded'); headers = headers.set('Authorization', this._encodeClientCredentials()); let params = new HttpParams() .set('grant_type', 'refresh_token') .set('refresh_token', this._storageService.refreshToken); return this._httpClient.post(url, params, { headers: headers }).pipe(tap((value) => { this._storageService.accessToken = value.access_token; this._storageService.refreshToken = value.refresh_token; this._storageService.idToken = value.id_token; })); } _fetchUserProfile() { const headers = new HttpHeaders().set('Authorization', 'Bearer ' + this._storageService.accessToken); return this._httpClient.get(this._openIDConfiguration.userinfo_endpoint, { headers: headers }).pipe(tap((value) => { this._userInfo = value; })); } _createLoginUrl(codeChallenge) { return this._openIDConfiguration.authorization_endpoint + '?' + 'response_type=' + encodeURIComponent('code') + '&client_id=' + encodeURIComponent(this._config.clientId) + '&state=' + encodeURIComponent(this._storageService.state) + '&scope=' + encodeURIComponent(this._config.scopes.join(' ')) + '&code_challenge=' + codeChallenge + '&code_challenge_method=S256' + '&redirect_uri=' + encodeURIComponent(this._config.redirectUri); } _redirectTo(url) { this._document.location.href = url; } _encodeClientCredentials() { return 'Basic ' + self.btoa(`${this._config.clientId}:${this._config.clientSecret}`); } } OAuthService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.8", ngImport: i0, type: OAuthService, deps: [{ token: i0.NgZone }, { token: i1.HttpClient }, { token: i2.OAuthStorageService }, { token: i3.OAuthCryptoService }, { token: DOCUMENT }, { token: PLATFORM_ID }, { token: OAUTH_CONFIG }], target: i0.ɵɵFactoryTarget.Injectable }); OAuthService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.8", ngImport: i0, type: OAuthService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.8", ngImport: i0, type: OAuthService, decorators: [{ type: Injectable }], ctorParameters: function () { return [{ type: i0.NgZone }, { type: i1.HttpClient }, { type: i2.OAuthStorageService }, { type: i3.OAuthCryptoService }, { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT] }] }, { type: undefined, decorators: [{ type: Inject, args: [PLATFORM_ID] }] }, { type: undefined, decorators: [{ type: Inject, args: [OAUTH_CONFIG] }] }]; } }); //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2F1dGguc2VydmljZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3Byb2plY3RzL29hdXRoLW9pZGMtY2xpZW50L3NyYy9saWIvb2F1dGguc2VydmljZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUMsTUFBTSxFQUFFLFVBQVUsRUFBVSxXQUFXLEVBQUMsTUFBTSxlQUFlLENBQUM7QUFDdEUsT0FBTyxFQUFhLFdBQVcsRUFBRSxVQUFVLEVBQUMsTUFBTSxzQkFBc0IsQ0FBQztBQUN6RSxPQUFPLEVBQUMsZUFBZSxFQUFFLEdBQUcsRUFBYyxFQUFFLEVBQUUsU0FBUyxFQUFFLEdBQUcsRUFBQyxNQUFNLE1BQU0sQ0FBQztBQUMxRSxPQUFPLEVBQUMsUUFBUSxFQUFDLE1BQU0saUJBQWlCLENBQUM7QUFFekMsT0FBTyxFQUFDLFlBQVksRUFBRSxXQUFXLEVBQWUsTUFBTSx3QkFBd0IsQ0FBQzs7Ozs7QUFPL0UsTUFBTSxDQUFDLE1BQU0sdUJBQXVCLEdBQUcsbUNBQW1DLENBQUM7QUFHM0UsTUFBTSxPQUFPLFlBQVk7SUFTdkIsWUFBc0IsS0FBYSxFQUNiLFdBQXVCLEVBQ3pCLGVBQW9DLEVBQ3BDLGNBQWtDLEVBQ2hCLFNBQWMsRUFDRixXQUFtQixFQUNuQyxNQUFvQjtRQU5oQyxVQUFLLEdBQUwsS0FBSyxDQUFRO1FBQ2IsZ0JBQVcsR0FBWCxXQUFXLENBQVk7UUFDekIsb0JBQWUsR0FBZixlQUFlLENBQXFCO1FBQ3BDLG1CQUFjLEdBQWQsY0FBYyxDQUFvQjtRQUNoQixjQUFTLEdBQVQsU0FBUyxDQUFLO1FBQ0YsZ0JBQVcsR0FBWCxXQUFXLENBQVE7UUFacEQsWUFBTyxHQUFnQixJQUFJLFdBQVcsRUFBRSxDQUFDO1FBQ2xELHlCQUFvQixHQUErQixJQUFJLENBQUM7UUFDeEQsYUFBUSxHQUFZLEVBQUMsSUFBSSxFQUFFLEVBQUUsRUFBQyxDQUFDO1FBQy9CLGNBQVMsR0FBMEIsSUFBSSxDQUFD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QUFDLG9CQUFxQixDQUFDLHNCQUFzQjtZQUN0RCxHQUFHO1lBQ0gsZ0JBQWdCO1lBQ2hCLGtCQUFrQixDQUFDLE1BQU0sQ0FBQztZQUMxQixhQUFhO1lBQ2Isa0JBQWtCLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLENBQUM7WUFDekMsU0FBUztZQUNULGtCQUFrQixDQUFDLElBQUksQ0FBQyxlQUFlLENBQUMsS0FBTSxDQUFDO1lBQy9DLFNBQVM7WUFDVCxrQkFBa0IsQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7WUFDakQsa0JBQWtCO1lBQ2xCLGFBQWE7WUFDYiw2QkFBNkI7WUFDN0IsZ0JBQWdCO1lBQ2hCLGtCQUFrQixDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDakQsQ0FBQztJQUVPLFdBQVcsQ0FBQyxHQUFXO1FBQzdCLElBQUksQ0FBQyxTQUFTLENBQUMsUUFBUSxDQUFDLElBQUksR0FBRyxHQUFHLENBQUM7SUFDckMsQ0FBQztJQUVTLHdCQUF3QjtRQUNoQyxPQUFPLFFBQVEsR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxRQUFRLElBQUksSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLEVBQUUsQ0FBQyxDQUFDO0lBQ3ZGLENBQUM7O3lHQTdNVSxZQUFZLHVJQWFILFFBQVEsYUFDUixXQUFXLGFBQ1gsWUFBWTs2R0FmckIsWUFBWTsyRkFBWixZQUFZO2tCQUR4QixVQUFVOzswQkFjSSxNQUFNOzJCQUFDLFFBQVE7OzBCQUNmLE1BQU07MkJBQUMsV0FBVzs7MEJBQ2xCLE1BQU07MkJBQUMsWUFBWSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7SW5qZWN0LCBJbmplY3RhYmxlLCBOZ1pvbmUsIFBMQVRGT1JNX0lEfSBmcm9tICdAYW5ndWxhci9jb3JlJztcbmltcG9ydCB7SHR0cENsaWVudCwgSHR0cEhlYWRlcnMsIEh0dHBQYXJhbXN9IGZyb20gJ0Bhbmd1bGFyL2NvbW1vbi9odHRwJztcbmltcG9ydCB7QmVoYXZpb3JTdWJqZWN0LCBtYXAsIE9ic2VydmFibGUsIG9mLCBzd2l0Y2hNYXAsIHRhcH0gZnJvbSAncnhqcyc7XG5pbXBvcnQge0RPQ1VNRU5UfSBmcm9tICdAYW5ndWxhci9jb21tb24nO1xuaW1wb3J0IHtPcGVuSURDb25maWd1cmF0aW9ufSBmcm9tICcuL21vZGVscy9vcGVuaWQtY29uZmlndXJhdGlvbi5tb2RlbCc7XG5pbXBvcnQge09BVVRIX0NPTkZJRywgT0F1dGhDb25maWcsIE9BdXRoT3B0aW9uc30gZnJvbSAnLi9tb2RlbHMvb3B0aW9ucy5tb2RlbCc7XG5pbXBvcnQge0p3dEtleXN9IGZyb20gJy4vbW9kZWxzL2p3ay5tb2RlbCc7XG5pbXBvcnQge1Rva2VuUmVzcG9uc2V9IGZyb20gJy4vbW9kZWxzL3Rva2VuLXJlc3BvbnNlLm1vZGVsJztcbmltcG9ydCB7T0F1dGhTdG9yYWdlU2VydmljZX0gZnJvbSAnLi9zdG9yYWdlcy9vYXV0aC1zdG9yYWdlLnNlcnZpY2UnO1xuaW1wb3J0IHtPQXV0aENyeXB0b1NlcnZpY2V9IGZyb20gXCIuL2NyeXB0by9vYXV0aC1jcnlwdG8uc2VydmljZVwiO1xuaW1wb3J0IHtPcGVuSURVc2VySW5mb30gZnJvbSAnLi9tb2RlbHMvb3BlbmlkLXVzZXItaW5mbyc7XG5cbmV4cG9ydCBjb25zdCBPQVVUSF9XRUxMX0tOT1dOX1NVRkZJWCA9IGAvLndlbGwta25vd24vb3BlbmlkLWNvbmZpZ3VyYXRpb25gO1xuXG5ASW5qZWN0YWJsZSgpXG5leHBvcnQgY2xhc3MgT0F1dGhTZXJ2aWNlIHtcblxuICBwcml2YXRlIHJlYWRvbmx5IF9jb25maWc6IE9BdXRoQ29uZmlnID0gbmV3IE9BdXRoQ29uZmlnKCk7XG4gIHByaXZhdGUgX29wZW5JRENvbmZpZ3VyYXRpb246IE9wZW5JRENvbmZpZ3VyYXRpb24gfCBudWxsID0gbnVsbDtcbiAgcHJpdmF0ZSBfand0S2V5czogSnd0S2V5cyA9IHtrZXlzOiBbXX07XG4gIHByaXZhdGUgX3VzZXJJbmZvOiBPcGVuSURVc2VySW5mbyB8IG51bGwgPSBudWxsO1xuXG4gIHB1YmxpYyByZWFkb25seSBhdXRoZW50aWNhdGVkJCA9IG5ldyBCZWhhdmlvclN1YmplY3Q8Ym9vbGVhbj4oZmFsc2UpO1xuXG4gIGNvbnN0cnVjdG9yKHByb3RlY3RlZCBfem9uZTogTmdab25lLFxuICAgICAgICAgICAgICBwcm90ZWN0ZWQgX2h0dHBDbGllbnQ6IEh0dHBDbGllbnQsXG4gICAgICAgICAgICAgIHByaXZhdGUgX3N0b3JhZ2VTZXJ2aWNlOiBPQXV0aFN0b3JhZ2VTZXJ2aWNlLFxuICAgICAgICAgICAgICBwcml2YXRlIF9jcnlwdG9TZXJ2aWNlOiBPQXV0aENyeXB0b1NlcnZpY2UsXG4gICAgICAgICAgICAgIEBJbmplY3QoRE9DVU1FTlQpIHByaXZhdGUgX2RvY3VtZW50OiBhbnksXG4gICAgICAgICAgICAgIEBJbmplY3QoUExBVEZPUk1fSUQpIHByaXZhdGUgcmVhZG9ubHkgX3BsYXRmb3JtSWQ6IHN0cmluZyxcbiAgICAgICAgICAgICAgQEluamVjdChPQVVUSF9DT05GSUcpIGNvbmZpZzogT0F1dGhPcHRpb25zKSB7XG5cbiAgICB0aGlzLl9jb25maWcgPSB7Li4uKG5ldyBPQXV0aENvbmZpZygpKSwgLi4uY29uZmlnfTtcbiAgfVxuXG4gIHB1YmxpYyBnZXQgYWNjZXNzVG9rZW4oKTogc3RyaW5nIHwgbnVsbCB7XG4gICAgcmV0dXJuIHRoaXMuX3N0b3JhZ2VTZXJ2aWNlLmFjY2Vzc1Rva2VuO1xuICB9XG5cbiAgZ2V0IHJlc291cmNlU2VydmVycygpOiBzdHJpbmdbXSB7XG4gICAgcmV0dXJuIHRoaXMuX2NvbmZpZy5yZXNvdXJjZVNlcnZlcnM7XG4gIH1cblxuICBjaGVja0F1dGgoKTogT2JzZXJ2YWJsZTxhbnk+IHtcbiAgICByZXR1cm4gdGhpcy5fZmV0Y2hPcGVuSWRDb25maWd1cmF0aW9uKCkucGlwZShcbiAgICAgIG1hcCgoKSA9PiAhISh0aGlzLl9zdG9yYWdlU2VydmljZS5yZWZyZXNoVG9rZW4gJiYgdGhpcy5fc3RvcmFnZVNlcnZpY2UuYWNjZXNzVG9rZW4pKSxcbiAgICAgIHN3aXRjaE1hcCgocmVzdWx0KSA9PiB7XG4gICAgICAgIGlmIChyZXN1bHQpIHtcbiAgICAgICAgICB0aGlzLmF1dGhlbnRpY2F0ZWQkLm5leHQodHJ1ZSk7XG4gICAgICAgICAgcmV0dXJuIHRoaXMuX2ZldGNoUmVmcmVzaFRva2VuKCkucGlwZShcbiAgICAgICAgICAgIHN3aXRjaE1hcCgoKSA9PiB0aGlzLl9mZXRjaFVzZXJQcm9maWxlKCkpXG4gICAgICAgICAgKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBjb25zdCBjdXJyZW50VXJsID0gdGhpcy5fZG9jdW1lbnQuZGVmYXVsdFZpZXcubG9jYXRpb24udG9TdHJpbmcoKTtcbiAgICAgICAgICBjb25zdCBwYXJzZWRVcmwgPSBuZXcgVVJMKGN1cnJlbnRVcmwpO1xuICAgICAgICAgIGNvbnN0IHVybFBhcmFtcyA9IG5ldyBVUkxTZWFyY2hQYXJhbXMocGFyc2VkVXJsLnNlYXJjaCk7XG4gICAgICAgICAgaWYgKHVybFBhcmFtcy5oYXMoJ2NvZGUnKSAmJiB1cmxQYXJhbXMuaGFzKCdzdGF0ZScpKSB7XG4gICAgICAgICAgICBjb25zdCBhdXRob3JpemF0aW9uQ29kZSA9IHVybFBhcmFtcy5nZXQoJ2NvZGUnKTtcbiAgICAgICAgICAgIGNvbnN0IHN0YXRlID0gdXJsUGFyYW1zLmdldCgnc3RhdGUnKTtcbiAgICAgICAgICAgIGlmIChzdGF0ZSA9PT0gdGhpcy5fc3RvcmFnZVNlcnZpY2Uuc3RhdGUpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuX2ZldGNoVG9rZW4oYXV0aG9yaXphdGlvbkNvZGUhKS5waXBlKFxuICAgICAgICAgICAgICAgIHN3aXRjaE1hcCgoKSA9PiB0aGlzLl9mZXRjaFVzZXJQcm9maWxlKCkpLFxuICAgICAgICAgICAgICAgIHRhcCgoKSA9PiB7XG4gICAgICAgICAgICAgICAgICB0aGlzLmF1dGhlbnRpY2F0ZWQkLm5leHQodHJ1ZSk7XG4gICAgICAgICAgICAgICAgfSlcbiAgICAgICAgICAgICAgKTtcbiAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcignJyk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICAgIHRoaXMuYXV0aGVudGljYXRlZCQubmV4dChmYWxzZSk7XG4gICAgICAgIHJldHVybiBvZihmYWxzZSk7XG4gICAgICB9KVxuICAgICk7XG4gIH1cblxuICBsb2dpbigpOiBPYnNlcnZhYmxlPGJvb2xlYW4+IHtcbiAgICB0aGlzLl9zdG9yYWdlU2VydmljZS5yZXNldEF1dGhTdGF0ZSgpO1xuICAgIHJldHVybiB0aGlzLl9mZXRjaE9wZW5JZENvbmZpZ3VyYXRpb24oKS5waXBlKFxuICAgICAgbWFwKCgpID0+IHtcbiAgICAgICAgdGhpcy5fc3RvcmFnZVNlcnZpY2Uuc3RhdGUgPSB0aGlzLl9jcnlwdG9TZXJ2aWNlLmdlbmVyYXRlU3RhdGUoKTtcbiAgICAgICAgdGhpcy5fc3RvcmFnZVNlcnZpY2UuY29kZVZlcmlmaWVyID0gdGhpcy5fY3J5cHRvU2VydmljZS5nZW5lcmF0ZVBLQ0VDb2RlVmVyaWZpZXIoKTtcbiAgICAgICAgcmV0dXJuIHRoaXMuX3N0b3JhZ2VTZXJ2aWNlLmNvZGVWZXJpZmllcjtcbiAgICAgIH0pLFxuICAgICAgc3dpdGNoTWFwKChjb2RlVmVyaWZpZXIpID0+XG4gICAgICAgIHRoaXMuX2NyeXB0b1NlcnZpY2UuZ2VuZXJhdGVQS0NFQ29kZUNoYWxsZW5nZShjb2RlVmVyaWZpZXIpXG4gICAgICApLFxuICAgICAgbWFwKChjb2RlQ2hhbGxlbmdlKSA9PiB7XG4gICAgICAgIHRoaXMuX3JlZGlyZWN0VG8odGhpcy5fY3JlYXRlTG9naW5VcmwoY29kZUNoYWxsZW5nZSkpO1xuICAgICAgICByZXR1cm4gdHJ1ZTtcbiAgICAgIH0pXG4gICAgKTtcbiAgfVxuXG4gIGxvZ291dCgpOiBPYnNlcnZhYmxlPGFueT4ge1xuICAgIHRoaXMuX3N0b3JhZ2VTZXJ2aWNlLnJlc2V0QXV0aFN0YXRlKCk7XG4gICAgcmV0dXJuIHRoaXMuX2ZldGNoT3BlbklkQ29uZmlndXJhdGlvbigpLnBpcGUoXG4gICAgICBzd2l0Y2hNYXAoKCkgPT4ge1xuICAgICAgICBjb25zdCB1cmwgPSB0aGlzLl9vcGVuSURDb25maWd1cmF0aW9uPy5yZXZvY2F0aW9uX2VuZHBvaW50ITtcblxuICAgICAgICBsZXQgaGVhZGVyczogSHR0cEhlYWRlcnMgPSBuZXcgSHR0cEhlYWRlcnMoKTtcbiAgICAgICAgaGVhZGVycyA9IGhlYWRlcnMuc2V0KCdDb250ZW50LVR5cGUnLCAnYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkJyk7XG4gICAgICAgIGhlYWRlcnMgPSBoZWFkZXJzLnNldCgnQXV0aG9yaXphdGlvbicsIHRoaXMuX2VuY29kZUNsaWVudENyZWRlbnRpYWxzKCkpO1xuXG4gICAgICAgIGxldCBwYXJhbXMgPSBuZXcgSHR0cFBhcmFtcygpXG4gICAgICAgICAgLnNldCgndG9rZW4nLCB0aGlzLl9zdG9yYWdlU2VydmljZS5hY2Nlc3NUb2tlbiEpO1xuXG4gICAgICAgIHJldHVybiB0aGlzLl9odHRwQ2xpZW50LnBvc3Q8VG9rZW5SZXNwb25zZT4odXJsLCBwYXJhbXMsIHtoZWFkZXJzOiBoZWFkZXJzfSk7XG4gICAgICB9KSxcbiAgICAgIHRhcCgoKSA9PiB0aGlzLmF1dGhlbnRpY2F0ZWQkLm5leHQoZmFsc2UpKVxuICAgICk7XG4gIH1cblxuICAvLyBGZXRjaCB0aGUgbWV0YWRhdGEgZnJvbSBvcGVuaWQtY29uZmlndXJhdGlvblxuICBwcm90ZWN0ZWQgX2ZldGNoT3BlbklkQ29uZmlndXJhdGlvbigpOiBPYnNlcnZhYmxlPE9wZW5JRENvbmZpZ3VyYXRpb24+IHtcbiAgICBpZiAodGhpcy5fb3BlbklEQ29uZmlndXJhdGlvbikge1xuICAgICAgcmV0dXJuIG9mKHRoaXMuX29wZW5JRENvbmZpZ3VyYXRpb24pO1xuICAgIH1cblxuICAgIGNvbnN0IHVybDogc3RyaW5nID0gdGhpcy5fY29uZmlnLmlzc3VlciArIE9BVVRIX1dFTExfS05PV05fU1VGRklYO1xuICAgIHJldHVybiB0aGlzLl9odHRwQ2xpZW50LmdldDxPcGVuSURDb25maWd1cmF0aW9uPih1cmwpLnBpcGUoXG4gICAgICB0YXAoKGRhdGEpID0+IHtcbiAgICAgICAgdGhpcy5fb3BlbklEQ29uZmlndXJhdGlvbiA9IGRhdGE7XG4gICAgICB9KSxcbiAgICAgIHN3aXRjaE1hcChfID0+IHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2h0dHBDbGllbnQuZ2V0PEp3dEtleXM+KHRoaXMuX29wZW5JRENvbmZpZ3VyYXRpb24hLmp3a3NfdXJpKS5waXBlKFxuICAgICAgICAgIHRhcCgoZGF0YSkgPT4ge1xuICAgICAgICAgICAgdGhpcy5fand0S2V5cyA9IGRhdGE7XG4gICAgICAgICAgfSlcbiAgICAgICAgKTtcbiAgICAgIH0pLFxuICAgICAgbWFwKF8gPT4ge1xuICAgICAgICByZXR1cm4gdGhpcy5fb3BlbklEQ29uZmlndXJhdGlvbiE7XG4gICAgICB9KVxuICAgICk7XG4gIH1cblxuICBwcm90ZWN0ZWQgX2ZldGNoVG9rZW4oYXV0aG9yaXphdGlvbkNvZGU6IHN0cmluZykge1xuICAgIGNvbnN0IHVybDogc3RyaW5nID0gdGhpcy5fb3BlbklEQ29uZmlndXJhdGlvbiEudG9rZW5fZW5kcG9pbnQ7XG5cbiAgICBsZXQgaGVhZGVyczogSHR0cEhlYWRlcnMgPSBuZXcgSHR0cEhlYWRlcnMoKTtcbiAgICBoZWFkZXJzID0gaGVhZGVycy5zZXQoJ0NvbnRlbnQtVHlwZScsICdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQnKTtcbiAgICBoZWFkZXJzID0gaGVhZGVycy5zZXQoJ0F1dGhvcml6YXRpb24nLCB0aGlzLl9lbmNvZGVDbGllbnRDcmVkZW50aWFscygpKTtcblxuICAgIGxldCBwYXJhbXMgPSBuZXcgSHR0cFBhcmFtcygpXG4gICAgICAuc2V0KCdncmFudF90eXBlJywgJ2F1dGhvcml6YXRpb25fY29kZScpXG4gICAgICAuc2V0KCdjb2RlJywgYXV0aG9yaXphdGlvbkNvZGUpXG4gICAgICAuc2V0KCdyZWRpcmVjdF91cmknLCB0aGlzLl9jb25maWcucmVkaXJlY3RVcmkpO1xuXG4gICAgaWYgKHRoaXMuX3N0b3JhZ2VTZXJ2aWNlLmNvZGVWZXJpZmllcikge1xuICAgICAgcGFyYW1zID0gcGFyYW1zLnNldCgnY29kZV92ZXJpZmllcicsIHRoaXMuX3N0b3JhZ2VTZXJ2aWNlLmNvZGVWZXJpZmllcik7XG4gICAgfVxuXG4gICAgcmV0dXJuIHRoaXMuX2h0dHBDbGllbnQucG9zdDxUb2tlblJlc3BvbnNlPih1cmwsIHBhcmFtcywge2hlYWRlcnM6IGhlYWRlcnN9KS5waXBlKFxuICAgICAgdGFwKCh2YWx1ZSkgPT4ge1xuICAgICAgICB0aGlzLl9zdG9yYWdlU2VydmljZS5hY2Nlc3NUb2tlbiA9IHZhbHVlLmFjY2Vzc190b2tlbjtcbiAgICAgICAgdGhpcy5fc3RvcmFnZVNlcnZpY2UucmVmcmVzaFRva2VuID0gdmFsdWUucmVmcmVzaF90b2tlbjtcbiAgICAgICAgdGhpcy5fc3RvcmFnZVNlcnZpY2UuaWRUb2tlbiA9IHZhbHVlLmlkX3Rva2VuO1xuICAgICAgfSlcbiAgICApO1xuICB9XG5cbiAgcHJvdGVjdGVkIF9mZXRjaFJlZnJlc2hUb2tlbigpOiBPYnNlcnZhYmxlPGFueT4ge1xuICAgIGNvbnN0IHVybDogc3RyaW5nID0gdGhpcy5fb3BlbklEQ29uZmlndXJhdGlvbiEudG9rZW5fZW5kcG9pbnQ7XG5cbiAgICBsZXQgaGVhZGVyczogSHR0cEhlYWRlcnMgPSBuZXcgSHR0cEhlYWRlcnMoKTtcbiAgICBoZWFkZXJzID0gaGVhZGVycy5zZXQoJ0NvbnRlbnQtVHlwZScsICdhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQnKTtcbiAgICBoZWFkZXJzID0gaGVhZGVycy5zZXQoJ0F1dGhvcml6YXRpb24nLCB0aGlzLl9lbmNvZGVDbGllbnRDcmVkZW50aWFscygpKTtcblxuICAgIGxldCBwYXJhbXMgPSBuZXcgSHR0cFBhcmFtcygpXG4gICAgICAuc2V0KCdncmFudF90eXBlJywgJ3JlZnJlc2hfdG9rZW4nKVxuICAgICAgLnNldCgncmVmcmVzaF90b2tlbicsIHRoaXMuX3N0b3JhZ2VTZXJ2aWNlLnJlZnJlc2hUb2tlbiEpO1xuXG4gICAgcmV0dXJuIHRoaXMuX2h0dHBDbGllbnQucG9zdDxUb2tlblJlc3BvbnNlPih1cmwsIHBhcmFtcywge2hlYWRlcnM6IGhlYWRlcnN9KS5waXBlKFxuICAgICAgdGFwKCh2YWx1ZSkgPT4ge1xuICAgICAgICB0aGlzLl9zdG9yYWdlU2VydmljZS5hY2Nlc3NUb2tlbiA9IHZhbHVlLmFjY2Vzc190b2tlbjtcbiAgICAgICAgdGhpcy5fc3RvcmFnZVNlcnZpY2UucmVmcmVzaFRva2VuID0gdmFsdWUucmVmcmVzaF90b2tlbjtcbiAgICAgICAgdGhpcy5fc3RvcmFnZVNlcnZpY2UuaWRUb2tlbiA9IHZhbHVlLmlkX3Rva2VuO1xuICAgICAgfSlcbiAgICApO1xuICB9XG5cbiAgcHJpdmF0ZSBfZmV0Y2hVc2VyUHJvZmlsZSgpOiBPYnNlcnZhYmxlPE9wZW5JRFVzZXJJbmZvPiB7XG4gICAgY29uc3QgaGVhZGVycyA9IG5ldyBIdHRwSGVhZGVycygpLnNldChcbiAgICAgICdBdXRob3JpemF0aW9uJyxcbiAgICAgICdCZWFyZXIgJyArIHRoaXMuX3N0b3JhZ2VTZXJ2aWNlLmFjY2Vzc1Rva2VuXG4gICAgKTtcblxuICAgIHJldHVybiB0aGlzLl9odHRwQ2xpZW50LmdldDxPcGVuSURVc2VySW5mbz4odGhpcy5fb3BlbklEQ29uZmlndXJhdGlvbiEudXNlcmluZm9fZW5kcG9pbnQsIHtoZWFkZXJzOiBoZWFkZXJzfSkucGlwZShcbiAgICAgIHRhcCgodmFsdWUpID0+IHtcbiAgICAgICAgdGhpcy5fdXNlckluZm8gPSB2YWx1ZTtcbiAgICAgIH0pXG4gICAgKTtcbiAgfVxuXG4gIHByb3RlY3RlZCBfY3JlYXRlTG9naW5VcmwoY29kZUNoYWxsZW5nZTogc3RyaW5nKTogc3RyaW5nIHtcbiAgICByZXR1cm4gdGhpcy5fb3BlbklEQ29uZmlndXJhdGlvbiEuYXV0aG9yaXphdGlvbl9lbmRwb2ludCArXG4gICAgICAnPycgK1xuICAgICAgJ3Jlc3BvbnNlX3R5cGU9JyArXG4gICAgICBlbmNvZGVVUklDb21wb25lbnQoJ2NvZGUnKSArXG4gICAgICAnJmNsaWVudF9pZD0nICtcbiAgICAgIGVuY29kZVVSSUNvbXBvbmVudCh0aGlzLl9jb25maWcuY2xpZW50SWQpICtcbiAgICAgICcmc3RhdGU9JyArXG4gICAgICBlbmNvZGVVUklDb21wb25lbnQodGhpcy5fc3RvcmFnZVNlcnZpY2Uuc3RhdGUhKSArXG4gICAgICAnJnNjb3BlPScgK1xuICAgICAgZW5jb2RlVVJJQ29tcG9uZW50KHRoaXMuX2NvbmZpZy5zY29wZXMuam9pbignICcpKSArXG4gICAgICAnJmNvZGVfY2hhbGxlbmdlPScgK1xuICAgICAgY29kZUNoYWxsZW5nZSArXG4gICAgICAnJmNvZGVfY2hhbGxlbmdlX21ldGhvZD1TMjU2JyArXG4gICAgICAnJnJlZGlyZWN0X3VyaT0nICtcbiAgICAgIGVuY29kZVVSSUNvbXBvbmVudCh0aGlzLl9jb25maWcucmVkaXJlY3RVcmkpO1xuICB9XG5cbiAgcHJpdmF0ZSBfcmVkaXJlY3RUbyh1cmw6IHN0cmluZyk6IHZvaWQge1xuICAgIHRoaXMuX2RvY3VtZW50LmxvY2F0aW9uLmhyZWYgPSB1cmw7XG4gIH1cblxuICBwcm90ZWN0ZWQgX2VuY29kZUNsaWVudENyZWRlbnRpYWxzKCk6IHN0cmluZyB7XG4gICAgcmV0dXJuICdCYXNpYyAnICsgc2VsZi5idG9hKGAke3RoaXMuX2NvbmZpZy5jbGllbnRJZH06JHt0aGlzLl9jb25maWcuY2xpZW50U2VjcmV0fWApO1xuICB9XG5cbn1cbiJdfQ==