@webex/plugin-authorization-browser-first-party 3.0.0-beta.13 → 3.0.0-beta.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -379,7 +379,7 @@ var Authorization = _webexCore.WebexPlugin.extend((_dec = (0, _common.whileInFli
379
379
  throw new Error("CSRF token ".concat(token, " does not match stored token ").concat(sessionToken));
380
380
  }
381
381
  },
382
- version: "3.0.0-beta.13"
382
+ version: "3.0.0-beta.15"
383
383
  }, ((0, _applyDecoratedDescriptor2.default)(_obj, "initiateAuthorizationCodeGrant", [_dec], (0, _getOwnPropertyDescriptor.default)(_obj, "initiateAuthorizationCodeGrant"), _obj), (0, _applyDecoratedDescriptor2.default)(_obj, "requestAuthorizationCodeGrant", [_dec2, _common.oneFlight], (0, _getOwnPropertyDescriptor.default)(_obj, "requestAuthorizationCodeGrant"), _obj)), _obj)));
384
384
 
385
385
  var _default = Authorization;
@@ -1 +1 @@
1
- {"version":3,"names":["lodash","require","OAUTH2_CSRF_TOKEN","OAUTH2_CODE_VERIFIER","Authorization","WebexPlugin","extend","whileInFlight","derived","isAuthenticating","deps","fn","isAuthorizing","session","default","type","ready","namespace","initialize","attrs","ret","prototype","location","url","parse","webex","getWindow","href","_checkForErrors","code","query","state","JSON","base64","decode","codeVerifier","sessionStorage","getItem","removeItem","emailhash","_verifySecurityToken","_cleanUrl","process","nextTick","internal","services","collectPreauthCatalog","catch","resolve","then","requestAuthorizationCodeGrant","initiateLogin","options","email","emailHash","CryptoJS","SHA256","toString","csrf_token","_generateSecurityToken","code_challenge","_generateCodeChallenge","code_challenge_method","initiateAuthorizationCodeGrant","logger","info","credentials","buildLoginUrl","response_type","logout","noRedirect","buildLogoutUrl","reject","Error","form","grant_type","redirect_uri","config","self_contained_token","code_verifier","request","method","uri","tokenUrl","auth","user","client_id","pass","client_secret","sendImmediately","shouldRefreshAccessToken","res","set","supertoken","body","statusCode","ErrorConstructor","grantErrors","select","error","_res","history","replaceState","encode","search","querystring","stringify","format","safeCharacterMap","base64url","_safe_map","times","random","length","join","codeChallenge","setItem","token","uuid","v4","sessionToken","oneFlight"],"sources":["authorization.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\n/* eslint camelcase: [0] */\n\nimport querystring from 'querystring';\nimport url from 'url';\n\nimport {base64, oneFlight, whileInFlight} from '@webex/common';\nimport {grantErrors, WebexPlugin} from '@webex/webex-core';\nimport {cloneDeep, isEmpty, omit} from 'lodash';\nimport uuid from 'uuid';\nimport base64url from 'crypto-js/enc-base64url';\nimport CryptoJS from 'crypto-js';\n\n// Necessary to require lodash this way in order to stub\n// methods in the unit test\nconst lodash = require('lodash');\n\nconst OAUTH2_CSRF_TOKEN = 'oauth2-csrf-token';\nconst OAUTH2_CODE_VERIFIER = 'oauth2-code-verifier';\n\n/**\n * Browser support for OAuth2. Automatically parses the URL query for an\n * authorization code\n *\n * Use of this plugin for anything other than the Webex Web Client is strongly\n * discouraged and may be broken at any time\n * @class\n * @name AuthorizationBrowserFirstParty\n * @private\n */\nconst Authorization = WebexPlugin.extend({\n derived: {\n /**\n * Alias of {@link AuthorizationBrowserFirstParty#isAuthorizing}\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @type {boolean}\n */\n isAuthenticating: {\n deps: ['isAuthorizing'],\n fn() {\n return this.isAuthorizing;\n }\n }\n },\n\n session: {\n /**\n * Indicates if an Authorization Code exchange is inflight\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @type {boolean}\n */\n isAuthorizing: {\n default: false,\n type: 'boolean'\n },\n ready: {\n default: false,\n type: 'boolean'\n }\n },\n\n namespace: 'Credentials',\n\n /**\n * Initializer\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @private\n * @returns {Authorization}\n */\n // eslint-disable-next-line complexity\n initialize(...attrs) {\n const ret = Reflect.apply(WebexPlugin.prototype.initialize, this, attrs);\n const location = url.parse(this.webex.getWindow().location.href, true);\n\n this._checkForErrors(location);\n\n const {code} = location.query;\n\n if (!code) {\n this.ready = true;\n\n return ret;\n }\n\n if (location.query.state) {\n location.query.state = JSON.parse(base64.decode(location.query.state));\n }\n else {\n location.query.state = {};\n }\n\n const codeVerifier = this.webex.getWindow().sessionStorage.getItem(OAUTH2_CODE_VERIFIER);\n\n this.webex.getWindow().sessionStorage.removeItem(OAUTH2_CODE_VERIFIER);\n\n\n const {emailhash} = location.query.state;\n\n this._verifySecurityToken(location.query);\n this._cleanUrl(location);\n\n // Wait until nextTick in case `credentials` hasn't initialized yet\n process.nextTick(() => {\n this.webex.internal.services.collectPreauthCatalog({emailhash})\n .catch(() => Promise.resolve())\n .then(() => this.requestAuthorizationCodeGrant({code, codeVerifier}))\n .then(() => {\n this.ready = true;\n });\n });\n\n\n return ret;\n },\n\n /**\n * Kicks off an oauth flow\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @returns {Promise}\n */\n initiateLogin(options = {}) {\n options = cloneDeep(options);\n if (options.email) {\n options.emailHash = CryptoJS.SHA256(options.email).toString();\n }\n delete options.email;\n options.state = options.state || {};\n options.state.csrf_token = this._generateSecurityToken();\n // catalog uses emailhash and redirectCI uses emailHash\n options.state.emailhash = options.emailHash;\n\n options.code_challenge = this._generateCodeChallenge();\n options.code_challenge_method = 'S256';\n\n\n return this.initiateAuthorizationCodeGrant(options);\n },\n\n @whileInFlight('isAuthorizing')\n /**\n * Kicks off the Implicit Code grant flow. Typically called via\n * {@link AuthorizationBrowserFirstParty#initiateLogin}\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @returns {Promise}\n */\n initiateAuthorizationCodeGrant(options) {\n this.logger.info('authorization: initiating authorization code grant flow');\n this.webex.getWindow().location = this.webex.credentials.buildLoginUrl(Object.assign({response_type: 'code'}, options));\n\n return Promise.resolve();\n },\n\n /**\n * Called by {@link WebexCore#logout()}. Redirects to the logout page\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @param {boolean} options.noRedirect if true, does not redirect\n * @returns {Promise}\n */\n logout(options = {}) {\n if (!options.noRedirect) {\n this.webex.getWindow().location = this.webex.credentials.buildLogoutUrl(options);\n }\n },\n\n\n @whileInFlight('isAuthorizing')\n @oneFlight\n /**\n * Exchanges an authorization code for an access token\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @param {Object} options.code\n * @returns {Promise}\n */\n requestAuthorizationCodeGrant(options = {}) {\n this.logger.info('credentials: requesting authorization code grant');\n\n if (!options.code) {\n return Promise.reject(new Error('`options.code` is required'));\n }\n\n const form = {\n grant_type: 'authorization_code',\n redirect_uri: this.config.redirect_uri,\n code: options.code,\n self_contained_token: true\n };\n\n if (options.codeVerifier) {\n form.code_verifier = options.codeVerifier;\n }\n\n return this.webex.request({\n method: 'POST',\n uri: this.config.tokenUrl,\n form,\n auth: {\n user: this.config.client_id,\n pass: this.config.client_secret,\n sendImmediately: true\n },\n shouldRefreshAccessToken: false\n })\n .then((res) => {\n this.webex.credentials.set({supertoken: res.body});\n })\n .catch((res) => {\n if (res.statusCode !== 400) {\n return Promise.reject(res);\n }\n\n const ErrorConstructor = grantErrors.select(res.body.error);\n\n return Promise.reject(new ErrorConstructor(res._res || res));\n });\n },\n\n /**\n * Checks if the result of the login redirect contains an error string\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} location\n * @private\n * @returns {Promise}\n */\n _checkForErrors(location) {\n const {query} = location;\n\n if (query && query.error) {\n const ErrorConstructor = grantErrors.select(query.error);\n\n throw new ErrorConstructor(query);\n }\n },\n\n /**\n * Removes no-longer needed values from the url (access token, csrf token, etc)\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} location\n * @private\n * @returns {Promise}\n */\n _cleanUrl(location) {\n location = cloneDeep(location);\n if (this.webex.getWindow().history && this.webex.getWindow().history.replaceState) {\n Reflect.deleteProperty(location.query, 'code');\n if (isEmpty(omit(location.query.state, 'csrf_token'))) {\n Reflect.deleteProperty(location.query, 'state');\n }\n else {\n location.query.state = base64.encode(JSON.stringify(omit(location.query.state, 'csrf_token')));\n }\n location.search = querystring.stringify(location.query);\n Reflect.deleteProperty(location, 'query');\n this.webex.getWindow().history.replaceState({}, null, url.format(location));\n }\n },\n\n /**\n * Generates PKCE code verifier and code challenge and sets the the code verifier in sessionStorage\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @private\n * @returns {string}\n */\n _generateCodeChallenge() {\n this.logger.info('authorization: generating PKCE code challenge');\n\n // eslint-disable-next-line no-underscore-dangle\n const safeCharacterMap = base64url._safe_map;\n\n const codeVerifier = lodash.times(\n 128,\n () => safeCharacterMap[lodash.random(0, safeCharacterMap.length - 1)]\n ).join('');\n\n const codeChallenge = CryptoJS.SHA256(codeVerifier).toString(base64url);\n\n this.webex.getWindow().sessionStorage.setItem(\n OAUTH2_CODE_VERIFIER, codeVerifier\n );\n\n return codeChallenge;\n },\n\n /**\n * Generates a CSRF token and sticks in in sessionStorage\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @private\n * @returns {Promise}\n */\n _generateSecurityToken() {\n this.logger.info('authorization: generating csrf token');\n\n const token = uuid.v4();\n\n this.webex.getWindow().sessionStorage.setItem('oauth2-csrf-token', token);\n\n return token;\n },\n\n /**\n * Checks if the CSRF token in sessionStorage is the same as the one returned\n * in the url.\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} query\n * @private\n * @returns {Promise}\n */\n _verifySecurityToken(query) {\n const sessionToken = this.webex.getWindow().sessionStorage.getItem(OAUTH2_CSRF_TOKEN);\n\n this.webex.getWindow().sessionStorage.removeItem(OAUTH2_CSRF_TOKEN);\n if (!sessionToken) {\n return;\n }\n\n if (!query.state) {\n throw new Error(`Expected CSRF token ${sessionToken}, but not found in redirect query`);\n }\n\n if (!query.state.csrf_token) {\n throw new Error(`Expected CSRF token ${sessionToken}, but not found in redirect query`);\n }\n\n const token = query.state.csrf_token;\n\n if (token !== sessionToken) {\n throw new Error(`CSRF token ${token} does not match stored token ${sessionToken}`);\n }\n }\n});\n\nexport default Authorization;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAMA;;AACA;;AAEA;;AACA;;AAEA;;AACA;;AACA;;;;AAEA;AACA;AACA,IAAMA,MAAM,GAAGC,OAAO,CAAC,QAAD,CAAtB;;AAEA,IAAMC,iBAAiB,GAAG,mBAA1B;AACA,IAAMC,oBAAoB,GAAG,sBAA7B;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AACA,IAAMC,aAAa,GAAGC,sBAAA,CAAYC,MAAZ,SAiHnB,IAAAC,qBAAA,EAAc,eAAd,CAjHmB,UAgJnB,IAAAA,qBAAA,EAAc,eAAd,CAhJmB,UAAmB;EACvCC,OAAO,EAAE;IACP;AACJ;AACA;AACA;AACA;AACA;IACIC,gBAAgB,EAAE;MAChBC,IAAI,EAAE,CAAC,eAAD,CADU;MAEhBC,EAFgB,gBAEX;QACH,OAAO,KAAKC,aAAZ;MACD;IAJe;EAPX,CAD8B;EAgBvCC,OAAO,EAAE;IACP;AACJ;AACA;AACA;AACA;AACA;IACID,aAAa,EAAE;MACbE,OAAO,EAAE,KADI;MAEbC,IAAI,EAAE;IAFO,CAPR;IAWPC,KAAK,EAAE;MACLF,OAAO,EAAE,KADJ;MAELC,IAAI,EAAE;IAFD;EAXA,CAhB8B;EAiCvCE,SAAS,EAAE,aAjC4B;;EAmCvC;AACF;AACA;AACA;AACA;AACA;AACA;EACE;EACAC,UA3CuC,wBA2ClB;IAAA;;IAAA,kCAAPC,KAAO;MAAPA,KAAO;IAAA;;IACnB,IAAMC,GAAG,GAAG,oBAAcf,sBAAA,CAAYgB,SAAZ,CAAsBH,UAApC,EAAgD,IAAhD,EAAsDC,KAAtD,CAAZ;;IACA,IAAMG,QAAQ,GAAGC,YAAA,CAAIC,KAAJ,CAAU,KAAKC,KAAL,CAAWC,SAAX,GAAuBJ,QAAvB,CAAgCK,IAA1C,EAAgD,IAAhD,CAAjB;;IAEA,KAAKC,eAAL,CAAqBN,QAArB;;IAEA,IAAOO,IAAP,GAAeP,QAAQ,CAACQ,KAAxB,CAAOD,IAAP;;IAEA,IAAI,CAACA,IAAL,EAAW;MACT,KAAKb,KAAL,GAAa,IAAb;MAEA,OAAOI,GAAP;IACD;;IAED,IAAIE,QAAQ,CAACQ,KAAT,CAAeC,KAAnB,EAA0B;MACxBT,QAAQ,CAACQ,KAAT,CAAeC,KAAf,GAAuBC,IAAI,CAACR,KAAL,CAAWS,cAAA,CAAOC,MAAP,CAAcZ,QAAQ,CAACQ,KAAT,CAAeC,KAA7B,CAAX,CAAvB;IACD,CAFD,MAGK;MACHT,QAAQ,CAACQ,KAAT,CAAeC,KAAf,GAAuB,EAAvB;IACD;;IAED,IAAMI,YAAY,GAAG,KAAKV,KAAL,CAAWC,SAAX,GAAuBU,cAAvB,CAAsCC,OAAtC,CAA8ClC,oBAA9C,CAArB;IAEA,KAAKsB,KAAL,CAAWC,SAAX,GAAuBU,cAAvB,CAAsCE,UAAtC,CAAiDnC,oBAAjD;IAGA,IAAOoC,SAAP,GAAoBjB,QAAQ,CAACQ,KAAT,CAAeC,KAAnC,CAAOQ,SAAP;;IAEA,KAAKC,oBAAL,CAA0BlB,QAAQ,CAACQ,KAAnC;;IACA,KAAKW,SAAL,CAAenB,QAAf,EA7BmB,CA+BnB;;;IACAoB,OAAO,CAACC,QAAR,CAAiB,YAAM;MACrB,KAAI,CAAClB,KAAL,CAAWmB,QAAX,CAAoBC,QAApB,CAA6BC,qBAA7B,CAAmD;QAACP,SAAS,EAATA;MAAD,CAAnD,EACGQ,KADH,CACS;QAAA,OAAM,iBAAQC,OAAR,EAAN;MAAA,CADT,EAEGC,IAFH,CAEQ;QAAA,OAAM,KAAI,CAACC,6BAAL,CAAmC;UAACrB,IAAI,EAAJA,IAAD;UAAOM,YAAY,EAAZA;QAAP,CAAnC,CAAN;MAAA,CAFR,EAGGc,IAHH,CAGQ,YAAM;QACV,KAAI,CAACjC,KAAL,GAAa,IAAb;MACD,CALH;IAMD,CAPD;IAUA,OAAOI,GAAP;EACD,CAtFsC;;EAwFvC;AACF;AACA;AACA;AACA;AACA;AACA;EACE+B,aA/FuC,2BA+FX;IAAA,IAAdC,OAAc,uEAAJ,EAAI;IAC1BA,OAAO,GAAG,yBAAUA,OAAV,CAAV;;IACA,IAAIA,OAAO,CAACC,KAAZ,EAAmB;MACjBD,OAAO,CAACE,SAAR,GAAoBC,iBAAA,CAASC,MAAT,CAAgBJ,OAAO,CAACC,KAAxB,EAA+BI,QAA/B,EAApB;IACD;;IACD,OAAOL,OAAO,CAACC,KAAf;IACAD,OAAO,CAACrB,KAAR,GAAgBqB,OAAO,CAACrB,KAAR,IAAiB,EAAjC;IACAqB,OAAO,CAACrB,KAAR,CAAc2B,UAAd,GAA2B,KAAKC,sBAAL,EAA3B,CAP0B,CAQ1B;;IACAP,OAAO,CAACrB,KAAR,CAAcQ,SAAd,GAA0Ba,OAAO,CAACE,SAAlC;IAEAF,OAAO,CAACQ,cAAR,GAAyB,KAAKC,sBAAL,EAAzB;IACAT,OAAO,CAACU,qBAAR,GAAgC,MAAhC;IAGA,OAAO,KAAKC,8BAAL,CAAoCX,OAApC,CAAP;EACD,CA/GsC;;EAkHvC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEW,8BA1HuC,0CA0HRX,OA1HQ,EA0HC;IACtC,KAAKY,MAAL,CAAYC,IAAZ,CAAiB,yDAAjB;IACA,KAAKxC,KAAL,CAAWC,SAAX,GAAuBJ,QAAvB,GAAkC,KAAKG,KAAL,CAAWyC,WAAX,CAAuBC,aAAvB,CAAqC,qBAAc;MAACC,aAAa,EAAE;IAAhB,CAAd,EAAuChB,OAAvC,CAArC,CAAlC;IAEA,OAAO,iBAAQJ,OAAR,EAAP;EACD,CA/HsC;;EAiIvC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEqB,MAzIuC,oBAyIlB;IAAA,IAAdjB,OAAc,uEAAJ,EAAI;;IACnB,IAAI,CAACA,OAAO,CAACkB,UAAb,EAAyB;MACvB,KAAK7C,KAAL,CAAWC,SAAX,GAAuBJ,QAAvB,GAAkC,KAAKG,KAAL,CAAWyC,WAAX,CAAuBK,cAAvB,CAAsCnB,OAAtC,CAAlC;IACD;EACF,CA7IsC;;EAkJvC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEF,6BA1JuC,2CA0JK;IAAA;;IAAA,IAAdE,OAAc,uEAAJ,EAAI;IAC1C,KAAKY,MAAL,CAAYC,IAAZ,CAAiB,kDAAjB;;IAEA,IAAI,CAACb,OAAO,CAACvB,IAAb,EAAmB;MACjB,OAAO,iBAAQ2C,MAAR,CAAe,IAAIC,KAAJ,CAAU,4BAAV,CAAf,CAAP;IACD;;IAED,IAAMC,IAAI,GAAG;MACXC,UAAU,EAAE,oBADD;MAEXC,YAAY,EAAE,KAAKC,MAAL,CAAYD,YAFf;MAGX/C,IAAI,EAAEuB,OAAO,CAACvB,IAHH;MAIXiD,oBAAoB,EAAE;IAJX,CAAb;;IAOA,IAAI1B,OAAO,CAACjB,YAAZ,EAA0B;MACxBuC,IAAI,CAACK,aAAL,GAAqB3B,OAAO,CAACjB,YAA7B;IACD;;IAED,OAAO,KAAKV,KAAL,CAAWuD,OAAX,CAAmB;MACxBC,MAAM,EAAE,MADgB;MAExBC,GAAG,EAAE,KAAKL,MAAL,CAAYM,QAFO;MAGxBT,IAAI,EAAJA,IAHwB;MAIxBU,IAAI,EAAE;QACJC,IAAI,EAAE,KAAKR,MAAL,CAAYS,SADd;QAEJC,IAAI,EAAE,KAAKV,MAAL,CAAYW,aAFd;QAGJC,eAAe,EAAE;MAHb,CAJkB;MASxBC,wBAAwB,EAAE;IATF,CAAnB,EAWJzC,IAXI,CAWC,UAAC0C,GAAD,EAAS;MACb,MAAI,CAAClE,KAAL,CAAWyC,WAAX,CAAuB0B,GAAvB,CAA2B;QAACC,UAAU,EAAEF,GAAG,CAACG;MAAjB,CAA3B;IACD,CAbI,EAcJ/C,KAdI,CAcE,UAAC4C,GAAD,EAAS;MACd,IAAIA,GAAG,CAACI,UAAJ,KAAmB,GAAvB,EAA4B;QAC1B,OAAO,iBAAQvB,MAAR,CAAemB,GAAf,CAAP;MACD;;MAED,IAAMK,gBAAgB,GAAGC,sBAAA,CAAYC,MAAZ,CAAmBP,GAAG,CAACG,IAAJ,CAASK,KAA5B,CAAzB;;MAEA,OAAO,iBAAQ3B,MAAR,CAAe,IAAIwB,gBAAJ,CAAqBL,GAAG,CAACS,IAAJ,IAAYT,GAAjC,CAAf,CAAP;IACD,CAtBI,CAAP;EAuBD,CAnMsC;;EAqMvC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACE/D,eA7MuC,2BA6MvBN,QA7MuB,EA6Mb;IACxB,IAAOQ,KAAP,GAAgBR,QAAhB,CAAOQ,KAAP;;IAEA,IAAIA,KAAK,IAAIA,KAAK,CAACqE,KAAnB,EAA0B;MACxB,IAAMH,gBAAgB,GAAGC,sBAAA,CAAYC,MAAZ,CAAmBpE,KAAK,CAACqE,KAAzB,CAAzB;;MAEA,MAAM,IAAIH,gBAAJ,CAAqBlE,KAArB,CAAN;IACD;EACF,CArNsC;;EAuNvC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEW,SA/NuC,qBA+N7BnB,QA/N6B,EA+NnB;IAClBA,QAAQ,GAAG,yBAAUA,QAAV,CAAX;;IACA,IAAI,KAAKG,KAAL,CAAWC,SAAX,GAAuB2E,OAAvB,IAAkC,KAAK5E,KAAL,CAAWC,SAAX,GAAuB2E,OAAvB,CAA+BC,YAArE,EAAmF;MACjF,6BAAuBhF,QAAQ,CAACQ,KAAhC,EAAuC,MAAvC;;MACA,IAAI,uBAAQ,oBAAKR,QAAQ,CAACQ,KAAT,CAAeC,KAApB,EAA2B,YAA3B,CAAR,CAAJ,EAAuD;QACrD,6BAAuBT,QAAQ,CAACQ,KAAhC,EAAuC,OAAvC;MACD,CAFD,MAGK;QACHR,QAAQ,CAACQ,KAAT,CAAeC,KAAf,GAAuBE,cAAA,CAAOsE,MAAP,CAAc,wBAAe,oBAAKjF,QAAQ,CAACQ,KAAT,CAAeC,KAApB,EAA2B,YAA3B,CAAf,CAAd,CAAvB;MACD;;MACDT,QAAQ,CAACkF,MAAT,GAAkBC,oBAAA,CAAYC,SAAZ,CAAsBpF,QAAQ,CAACQ,KAA/B,CAAlB;MACA,6BAAuBR,QAAvB,EAAiC,OAAjC;MACA,KAAKG,KAAL,CAAWC,SAAX,GAAuB2E,OAAvB,CAA+BC,YAA/B,CAA4C,EAA5C,EAAgD,IAAhD,EAAsD/E,YAAA,CAAIoF,MAAJ,CAAWrF,QAAX,CAAtD;IACD;EACF,CA7OsC;;EA+OvC;AACF;AACA;AACA;AACA;AACA;AACA;EACEuC,sBAtPuC,oCAsPd;IACvB,KAAKG,MAAL,CAAYC,IAAZ,CAAiB,+CAAjB,EADuB,CAGvB;;IACA,IAAM2C,gBAAgB,GAAGC,qBAAA,CAAUC,SAAnC;IAEA,IAAM3E,YAAY,GAAGnC,MAAM,CAAC+G,KAAP,CACnB,GADmB,EAEnB;MAAA,OAAMH,gBAAgB,CAAC5G,MAAM,CAACgH,MAAP,CAAc,CAAd,EAAiBJ,gBAAgB,CAACK,MAAjB,GAA0B,CAA3C,CAAD,CAAtB;IAAA,CAFmB,EAGnBC,IAHmB,CAGd,EAHc,CAArB;;IAKA,IAAMC,aAAa,GAAG5D,iBAAA,CAASC,MAAT,CAAgBrB,YAAhB,EAA8BsB,QAA9B,CAAuCoD,qBAAvC,CAAtB;;IAEA,KAAKpF,KAAL,CAAWC,SAAX,GAAuBU,cAAvB,CAAsCgF,OAAtC,CACEjH,oBADF,EACwBgC,YADxB;IAIA,OAAOgF,aAAP;EACD,CAxQsC;;EA0QvC;AACF;AACA;AACA;AACA;AACA;AACA;EACExD,sBAjRuC,oCAiRd;IACvB,KAAKK,MAAL,CAAYC,IAAZ,CAAiB,sCAAjB;;IAEA,IAAMoD,KAAK,GAAGC,aAAA,CAAKC,EAAL,EAAd;;IAEA,KAAK9F,KAAL,CAAWC,SAAX,GAAuBU,cAAvB,CAAsCgF,OAAtC,CAA8C,mBAA9C,EAAmEC,KAAnE;IAEA,OAAOA,KAAP;EACD,CAzRsC;;EA2RvC;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACE7E,oBApSuC,gCAoSlBV,KApSkB,EAoSX;IAC1B,IAAM0F,YAAY,GAAG,KAAK/F,KAAL,CAAWC,SAAX,GAAuBU,cAAvB,CAAsCC,OAAtC,CAA8CnC,iBAA9C,CAArB;IAEA,KAAKuB,KAAL,CAAWC,SAAX,GAAuBU,cAAvB,CAAsCE,UAAtC,CAAiDpC,iBAAjD;;IACA,IAAI,CAACsH,YAAL,EAAmB;MACjB;IACD;;IAED,IAAI,CAAC1F,KAAK,CAACC,KAAX,EAAkB;MAChB,MAAM,IAAI0C,KAAJ,+BAAiC+C,YAAjC,uCAAN;IACD;;IAED,IAAI,CAAC1F,KAAK,CAACC,KAAN,CAAY2B,UAAjB,EAA6B;MAC3B,MAAM,IAAIe,KAAJ,+BAAiC+C,YAAjC,uCAAN;IACD;;IAED,IAAMH,KAAK,GAAGvF,KAAK,CAACC,KAAN,CAAY2B,UAA1B;;IAEA,IAAI2D,KAAK,KAAKG,YAAd,EAA4B;MAC1B,MAAM,IAAI/C,KAAJ,sBAAwB4C,KAAxB,0CAA6DG,YAA7D,EAAN;IACD;EACF,CAzTsC;EAAA;AAAA,CAAnB,yQAiJnBC,iBAjJmB,iGAAtB;;eA4TerH,a"}
1
+ {"version":3,"names":["lodash","require","OAUTH2_CSRF_TOKEN","OAUTH2_CODE_VERIFIER","Authorization","WebexPlugin","extend","whileInFlight","derived","isAuthenticating","deps","fn","isAuthorizing","session","default","type","ready","namespace","initialize","attrs","ret","prototype","location","url","parse","webex","getWindow","href","_checkForErrors","code","query","state","JSON","base64","decode","codeVerifier","sessionStorage","getItem","removeItem","emailhash","_verifySecurityToken","_cleanUrl","process","nextTick","internal","services","collectPreauthCatalog","catch","resolve","then","requestAuthorizationCodeGrant","initiateLogin","options","email","emailHash","CryptoJS","SHA256","toString","csrf_token","_generateSecurityToken","code_challenge","_generateCodeChallenge","code_challenge_method","initiateAuthorizationCodeGrant","logger","info","credentials","buildLoginUrl","response_type","logout","noRedirect","buildLogoutUrl","reject","Error","form","grant_type","redirect_uri","config","self_contained_token","code_verifier","request","method","uri","tokenUrl","auth","user","client_id","pass","client_secret","sendImmediately","shouldRefreshAccessToken","res","set","supertoken","body","statusCode","ErrorConstructor","grantErrors","select","error","_res","history","replaceState","encode","search","querystring","stringify","format","safeCharacterMap","base64url","_safe_map","times","random","length","join","codeChallenge","setItem","token","uuid","v4","sessionToken","oneFlight"],"sources":["authorization.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\n/* eslint camelcase: [0] */\n\nimport querystring from 'querystring';\nimport url from 'url';\n\nimport {base64, oneFlight, whileInFlight} from '@webex/common';\nimport {grantErrors, WebexPlugin} from '@webex/webex-core';\nimport {cloneDeep, isEmpty, omit} from 'lodash';\nimport uuid from 'uuid';\nimport base64url from 'crypto-js/enc-base64url';\nimport CryptoJS from 'crypto-js';\n\n// Necessary to require lodash this way in order to stub\n// methods in the unit test\nconst lodash = require('lodash');\n\nconst OAUTH2_CSRF_TOKEN = 'oauth2-csrf-token';\nconst OAUTH2_CODE_VERIFIER = 'oauth2-code-verifier';\n\n/**\n * Browser support for OAuth2. Automatically parses the URL query for an\n * authorization code\n *\n * Use of this plugin for anything other than the Webex Web Client is strongly\n * discouraged and may be broken at any time\n * @class\n * @name AuthorizationBrowserFirstParty\n * @private\n */\nconst Authorization = WebexPlugin.extend({\n derived: {\n /**\n * Alias of {@link AuthorizationBrowserFirstParty#isAuthorizing}\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @type {boolean}\n */\n isAuthenticating: {\n deps: ['isAuthorizing'],\n fn() {\n return this.isAuthorizing;\n },\n },\n },\n\n session: {\n /**\n * Indicates if an Authorization Code exchange is inflight\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @type {boolean}\n */\n isAuthorizing: {\n default: false,\n type: 'boolean',\n },\n ready: {\n default: false,\n type: 'boolean',\n },\n },\n\n namespace: 'Credentials',\n\n /**\n * Initializer\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @private\n * @returns {Authorization}\n */\n // eslint-disable-next-line complexity\n initialize(...attrs) {\n const ret = Reflect.apply(WebexPlugin.prototype.initialize, this, attrs);\n const location = url.parse(this.webex.getWindow().location.href, true);\n\n this._checkForErrors(location);\n\n const {code} = location.query;\n\n if (!code) {\n this.ready = true;\n\n return ret;\n }\n\n if (location.query.state) {\n location.query.state = JSON.parse(base64.decode(location.query.state));\n } else {\n location.query.state = {};\n }\n\n const codeVerifier = this.webex.getWindow().sessionStorage.getItem(OAUTH2_CODE_VERIFIER);\n\n this.webex.getWindow().sessionStorage.removeItem(OAUTH2_CODE_VERIFIER);\n\n const {emailhash} = location.query.state;\n\n this._verifySecurityToken(location.query);\n this._cleanUrl(location);\n\n // Wait until nextTick in case `credentials` hasn't initialized yet\n process.nextTick(() => {\n this.webex.internal.services\n .collectPreauthCatalog({emailhash})\n .catch(() => Promise.resolve())\n .then(() => this.requestAuthorizationCodeGrant({code, codeVerifier}))\n .then(() => {\n this.ready = true;\n });\n });\n\n return ret;\n },\n\n /**\n * Kicks off an oauth flow\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @returns {Promise}\n */\n initiateLogin(options = {}) {\n options = cloneDeep(options);\n if (options.email) {\n options.emailHash = CryptoJS.SHA256(options.email).toString();\n }\n delete options.email;\n options.state = options.state || {};\n options.state.csrf_token = this._generateSecurityToken();\n // catalog uses emailhash and redirectCI uses emailHash\n options.state.emailhash = options.emailHash;\n\n options.code_challenge = this._generateCodeChallenge();\n options.code_challenge_method = 'S256';\n\n return this.initiateAuthorizationCodeGrant(options);\n },\n\n @whileInFlight('isAuthorizing')\n /**\n * Kicks off the Implicit Code grant flow. Typically called via\n * {@link AuthorizationBrowserFirstParty#initiateLogin}\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @returns {Promise}\n */\n initiateAuthorizationCodeGrant(options) {\n this.logger.info('authorization: initiating authorization code grant flow');\n this.webex.getWindow().location = this.webex.credentials.buildLoginUrl(\n Object.assign({response_type: 'code'}, options)\n );\n\n return Promise.resolve();\n },\n\n /**\n * Called by {@link WebexCore#logout()}. Redirects to the logout page\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @param {boolean} options.noRedirect if true, does not redirect\n * @returns {Promise}\n */\n logout(options = {}) {\n if (!options.noRedirect) {\n this.webex.getWindow().location = this.webex.credentials.buildLogoutUrl(options);\n }\n },\n\n @whileInFlight('isAuthorizing')\n @oneFlight\n /**\n * Exchanges an authorization code for an access token\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} options\n * @param {Object} options.code\n * @returns {Promise}\n */\n requestAuthorizationCodeGrant(options = {}) {\n this.logger.info('credentials: requesting authorization code grant');\n\n if (!options.code) {\n return Promise.reject(new Error('`options.code` is required'));\n }\n\n const form = {\n grant_type: 'authorization_code',\n redirect_uri: this.config.redirect_uri,\n code: options.code,\n self_contained_token: true,\n };\n\n if (options.codeVerifier) {\n form.code_verifier = options.codeVerifier;\n }\n\n return this.webex\n .request({\n method: 'POST',\n uri: this.config.tokenUrl,\n form,\n auth: {\n user: this.config.client_id,\n pass: this.config.client_secret,\n sendImmediately: true,\n },\n shouldRefreshAccessToken: false,\n })\n .then((res) => {\n this.webex.credentials.set({supertoken: res.body});\n })\n .catch((res) => {\n if (res.statusCode !== 400) {\n return Promise.reject(res);\n }\n\n const ErrorConstructor = grantErrors.select(res.body.error);\n\n return Promise.reject(new ErrorConstructor(res._res || res));\n });\n },\n\n /**\n * Checks if the result of the login redirect contains an error string\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} location\n * @private\n * @returns {Promise}\n */\n _checkForErrors(location) {\n const {query} = location;\n\n if (query && query.error) {\n const ErrorConstructor = grantErrors.select(query.error);\n\n throw new ErrorConstructor(query);\n }\n },\n\n /**\n * Removes no-longer needed values from the url (access token, csrf token, etc)\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} location\n * @private\n * @returns {Promise}\n */\n _cleanUrl(location) {\n location = cloneDeep(location);\n if (this.webex.getWindow().history && this.webex.getWindow().history.replaceState) {\n Reflect.deleteProperty(location.query, 'code');\n if (isEmpty(omit(location.query.state, 'csrf_token'))) {\n Reflect.deleteProperty(location.query, 'state');\n } else {\n location.query.state = base64.encode(\n JSON.stringify(omit(location.query.state, 'csrf_token'))\n );\n }\n location.search = querystring.stringify(location.query);\n Reflect.deleteProperty(location, 'query');\n this.webex.getWindow().history.replaceState({}, null, url.format(location));\n }\n },\n\n /**\n * Generates PKCE code verifier and code challenge and sets the the code verifier in sessionStorage\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @private\n * @returns {string}\n */\n _generateCodeChallenge() {\n this.logger.info('authorization: generating PKCE code challenge');\n\n // eslint-disable-next-line no-underscore-dangle\n const safeCharacterMap = base64url._safe_map;\n\n const codeVerifier = lodash\n .times(128, () => safeCharacterMap[lodash.random(0, safeCharacterMap.length - 1)])\n .join('');\n\n const codeChallenge = CryptoJS.SHA256(codeVerifier).toString(base64url);\n\n this.webex.getWindow().sessionStorage.setItem(OAUTH2_CODE_VERIFIER, codeVerifier);\n\n return codeChallenge;\n },\n\n /**\n * Generates a CSRF token and sticks in in sessionStorage\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @private\n * @returns {Promise}\n */\n _generateSecurityToken() {\n this.logger.info('authorization: generating csrf token');\n\n const token = uuid.v4();\n\n this.webex.getWindow().sessionStorage.setItem('oauth2-csrf-token', token);\n\n return token;\n },\n\n /**\n * Checks if the CSRF token in sessionStorage is the same as the one returned\n * in the url.\n * @instance\n * @memberof AuthorizationBrowserFirstParty\n * @param {Object} query\n * @private\n * @returns {Promise}\n */\n _verifySecurityToken(query) {\n const sessionToken = this.webex.getWindow().sessionStorage.getItem(OAUTH2_CSRF_TOKEN);\n\n this.webex.getWindow().sessionStorage.removeItem(OAUTH2_CSRF_TOKEN);\n if (!sessionToken) {\n return;\n }\n\n if (!query.state) {\n throw new Error(`Expected CSRF token ${sessionToken}, but not found in redirect query`);\n }\n\n if (!query.state.csrf_token) {\n throw new Error(`Expected CSRF token ${sessionToken}, but not found in redirect query`);\n }\n\n const token = query.state.csrf_token;\n\n if (token !== sessionToken) {\n throw new Error(`CSRF token ${token} does not match stored token ${sessionToken}`);\n }\n },\n});\n\nexport default Authorization;\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"names":["credentials","clientType","refreshCallback","webex","token","request","method","uri","config","tokenUrl","form","grant_type","redirect_uri","refresh_token","auth","user","client_id","pass","client_secret","sendImmediately","shouldRefreshAccessToken","then","res","body"],"sources":["config.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\nexport default {\n credentials: {\n /**\n * Controls whether {@link Authorization#initiateLogin()} requests a token\n * or an auth code. Anything other than 'confidential' will be treated as\n * 'public'\n * @private\n * @type {string}\n */\n clientType: 'public',\n\n refreshCallback(webex, token) {\n /* eslint-disable camelcase */\n return webex.request({\n method: 'POST',\n uri: token.config.tokenUrl,\n form: {\n grant_type: 'refresh_token',\n redirect_uri: token.config.redirect_uri,\n refresh_token: token.refresh_token\n },\n auth: {\n user: token.config.client_id,\n pass: token.config.client_secret,\n sendImmediately: true\n },\n shouldRefreshAccessToken: false\n })\n .then((res) => res.body);\n /* eslint-enable camelcase */\n }\n }\n};\n"],"mappings":";;;;;;;;;;AAAA;AACA;AACA;eAEe;EACbA,WAAW,EAAE;IACX;AACJ;AACA;AACA;AACA;AACA;AACA;IACIC,UAAU,EAAE,QARD;IAUXC,eAVW,2BAUKC,KAVL,EAUYC,KAVZ,EAUmB;MAC5B;MACA,OAAOD,KAAK,CAACE,OAAN,CAAc;QACnBC,MAAM,EAAE,MADW;QAEnBC,GAAG,EAAEH,KAAK,CAACI,MAAN,CAAaC,QAFC;QAGnBC,IAAI,EAAE;UACJC,UAAU,EAAE,eADR;UAEJC,YAAY,EAAER,KAAK,CAACI,MAAN,CAAaI,YAFvB;UAGJC,aAAa,EAAET,KAAK,CAACS;QAHjB,CAHa;QAQnBC,IAAI,EAAE;UACJC,IAAI,EAAEX,KAAK,CAACI,MAAN,CAAaQ,SADf;UAEJC,IAAI,EAAEb,KAAK,CAACI,MAAN,CAAaU,aAFf;UAGJC,eAAe,EAAE;QAHb,CARa;QAanBC,wBAAwB,EAAE;MAbP,CAAd,EAeJC,IAfI,CAeC,UAACC,GAAD;QAAA,OAASA,GAAG,CAACC,IAAb;MAAA,CAfD,CAAP;MAgBA;IACD;EA7BU;AADA,C"}
1
+ {"version":3,"names":["credentials","clientType","refreshCallback","webex","token","request","method","uri","config","tokenUrl","form","grant_type","redirect_uri","refresh_token","auth","user","client_id","pass","client_secret","sendImmediately","shouldRefreshAccessToken","then","res","body"],"sources":["config.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\nexport default {\n credentials: {\n /**\n * Controls whether {@link Authorization#initiateLogin()} requests a token\n * or an auth code. Anything other than 'confidential' will be treated as\n * 'public'\n * @private\n * @type {string}\n */\n clientType: 'public',\n\n refreshCallback(webex, token) {\n /* eslint-disable camelcase */\n return webex\n .request({\n method: 'POST',\n uri: token.config.tokenUrl,\n form: {\n grant_type: 'refresh_token',\n redirect_uri: token.config.redirect_uri,\n refresh_token: token.refresh_token,\n },\n auth: {\n user: token.config.client_id,\n pass: token.config.client_secret,\n sendImmediately: true,\n },\n shouldRefreshAccessToken: false,\n })\n .then((res) => res.body);\n /* eslint-enable camelcase */\n },\n },\n};\n"],"mappings":";;;;;;;;;;AAAA;AACA;AACA;eAEe;EACbA,WAAW,EAAE;IACX;AACJ;AACA;AACA;AACA;AACA;AACA;IACIC,UAAU,EAAE,QARD;IAUXC,eAVW,2BAUKC,KAVL,EAUYC,KAVZ,EAUmB;MAC5B;MACA,OAAOD,KAAK,CACTE,OADI,CACI;QACPC,MAAM,EAAE,MADD;QAEPC,GAAG,EAAEH,KAAK,CAACI,MAAN,CAAaC,QAFX;QAGPC,IAAI,EAAE;UACJC,UAAU,EAAE,eADR;UAEJC,YAAY,EAAER,KAAK,CAACI,MAAN,CAAaI,YAFvB;UAGJC,aAAa,EAAET,KAAK,CAACS;QAHjB,CAHC;QAQPC,IAAI,EAAE;UACJC,IAAI,EAAEX,KAAK,CAACI,MAAN,CAAaQ,SADf;UAEJC,IAAI,EAAEb,KAAK,CAACI,MAAN,CAAaU,aAFf;UAGJC,eAAe,EAAE;QAHb,CARC;QAaPC,wBAAwB,EAAE;MAbnB,CADJ,EAgBJC,IAhBI,CAgBC,UAACC,GAAD;QAAA,OAASA,GAAG,CAACC,IAAb;MAAA,CAhBD,CAAP;MAiBA;IACD;EA9BU;AADA,C"}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"names":["proxies","registerPlugin","Authorization","config"],"sources":["index.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\nimport {registerPlugin} from '@webex/webex-core';\n\nimport Authorization from './authorization';\nimport config from './config';\n\nconst proxies = [\n 'isAuthorizing',\n 'isAuthenticating'\n];\n\nregisterPlugin('authorization', Authorization, {\n config,\n proxies\n});\n\nexport {default} from './authorization';\nexport {default as config} from './config';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAIA;;AAEA;;AACA;;AAPA;AACA;AACA;AAOA,IAAMA,OAAO,GAAG,CACd,eADc,EAEd,kBAFc,CAAhB;AAKA,IAAAC,yBAAA,EAAe,eAAf,EAAgCC,sBAAhC,EAA+C;EAC7CC,MAAM,EAANA,eAD6C;EAE7CH,OAAO,EAAPA;AAF6C,CAA/C"}
1
+ {"version":3,"names":["proxies","registerPlugin","Authorization","config"],"sources":["index.js"],"sourcesContent":["/*!\n * Copyright (c) 2015-2020 Cisco Systems, Inc. See LICENSE file.\n */\n\nimport {registerPlugin} from '@webex/webex-core';\n\nimport Authorization from './authorization';\nimport config from './config';\n\nconst proxies = ['isAuthorizing', 'isAuthenticating'];\n\nregisterPlugin('authorization', Authorization, {\n config,\n proxies,\n});\n\nexport {default} from './authorization';\nexport {default as config} from './config';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAIA;;AAEA;;AACA;;AAPA;AACA;AACA;AAOA,IAAMA,OAAO,GAAG,CAAC,eAAD,EAAkB,kBAAlB,CAAhB;AAEA,IAAAC,yBAAA,EAAe,eAAf,EAAgCC,sBAAhC,EAA+C;EAC7CC,MAAM,EAANA,eAD6C;EAE7CH,OAAO,EAAPA;AAF6C,CAA/C"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webex/plugin-authorization-browser-first-party",
3
- "version": "3.0.0-beta.13",
3
+ "version": "3.0.0-beta.15",
4
4
  "description": "",
5
5
  "license": "MIT",
6
6
  "author": "Ian W. Remmel <iremmel@cisco.com>",
@@ -25,15 +25,15 @@
25
25
  "sinon": "^9.2.4"
26
26
  },
27
27
  "dependencies": {
28
- "@webex/common": "3.0.0-beta.13",
29
- "@webex/plugin-authorization-browser-first-party": "3.0.0-beta.13",
30
- "@webex/storage-adapter-local-storage": "3.0.0-beta.13",
31
- "@webex/test-helper-automation": "3.0.0-beta.13",
32
- "@webex/test-helper-chai": "3.0.0-beta.13",
33
- "@webex/test-helper-mocha": "3.0.0-beta.13",
34
- "@webex/test-helper-mock-webex": "3.0.0-beta.13",
35
- "@webex/test-helper-test-users": "3.0.0-beta.13",
36
- "@webex/webex-core": "3.0.0-beta.13",
28
+ "@webex/common": "3.0.0-beta.15",
29
+ "@webex/plugin-authorization-browser-first-party": "3.0.0-beta.15",
30
+ "@webex/storage-adapter-local-storage": "3.0.0-beta.15",
31
+ "@webex/test-helper-automation": "3.0.0-beta.15",
32
+ "@webex/test-helper-chai": "3.0.0-beta.15",
33
+ "@webex/test-helper-mocha": "3.0.0-beta.15",
34
+ "@webex/test-helper-mock-webex": "3.0.0-beta.15",
35
+ "@webex/test-helper-test-users": "3.0.0-beta.15",
36
+ "@webex/webex-core": "3.0.0-beta.15",
37
37
  "crypto-js": "^4.1.1",
38
38
  "lodash": "^4.17.21",
39
39
  "uuid": "^3.3.2"
@@ -43,8 +43,8 @@ const Authorization = WebexPlugin.extend({
43
43
  deps: ['isAuthorizing'],
44
44
  fn() {
45
45
  return this.isAuthorizing;
46
- }
47
- }
46
+ },
47
+ },
48
48
  },
49
49
 
50
50
  session: {
@@ -56,12 +56,12 @@ const Authorization = WebexPlugin.extend({
56
56
  */
57
57
  isAuthorizing: {
58
58
  default: false,
59
- type: 'boolean'
59
+ type: 'boolean',
60
60
  },
61
61
  ready: {
62
62
  default: false,
63
- type: 'boolean'
64
- }
63
+ type: 'boolean',
64
+ },
65
65
  },
66
66
 
67
67
  namespace: 'Credentials',
@@ -90,8 +90,7 @@ const Authorization = WebexPlugin.extend({
90
90
 
91
91
  if (location.query.state) {
92
92
  location.query.state = JSON.parse(base64.decode(location.query.state));
93
- }
94
- else {
93
+ } else {
95
94
  location.query.state = {};
96
95
  }
97
96
 
@@ -99,7 +98,6 @@ const Authorization = WebexPlugin.extend({
99
98
 
100
99
  this.webex.getWindow().sessionStorage.removeItem(OAUTH2_CODE_VERIFIER);
101
100
 
102
-
103
101
  const {emailhash} = location.query.state;
104
102
 
105
103
  this._verifySecurityToken(location.query);
@@ -107,7 +105,8 @@ const Authorization = WebexPlugin.extend({
107
105
 
108
106
  // Wait until nextTick in case `credentials` hasn't initialized yet
109
107
  process.nextTick(() => {
110
- this.webex.internal.services.collectPreauthCatalog({emailhash})
108
+ this.webex.internal.services
109
+ .collectPreauthCatalog({emailhash})
111
110
  .catch(() => Promise.resolve())
112
111
  .then(() => this.requestAuthorizationCodeGrant({code, codeVerifier}))
113
112
  .then(() => {
@@ -115,7 +114,6 @@ const Authorization = WebexPlugin.extend({
115
114
  });
116
115
  });
117
116
 
118
-
119
117
  return ret;
120
118
  },
121
119
 
@@ -140,7 +138,6 @@ const Authorization = WebexPlugin.extend({
140
138
  options.code_challenge = this._generateCodeChallenge();
141
139
  options.code_challenge_method = 'S256';
142
140
 
143
-
144
141
  return this.initiateAuthorizationCodeGrant(options);
145
142
  },
146
143
 
@@ -155,7 +152,9 @@ const Authorization = WebexPlugin.extend({
155
152
  */
156
153
  initiateAuthorizationCodeGrant(options) {
157
154
  this.logger.info('authorization: initiating authorization code grant flow');
158
- this.webex.getWindow().location = this.webex.credentials.buildLoginUrl(Object.assign({response_type: 'code'}, options));
155
+ this.webex.getWindow().location = this.webex.credentials.buildLoginUrl(
156
+ Object.assign({response_type: 'code'}, options)
157
+ );
159
158
 
160
159
  return Promise.resolve();
161
160
  },
@@ -174,7 +173,6 @@ const Authorization = WebexPlugin.extend({
174
173
  }
175
174
  },
176
175
 
177
-
178
176
  @whileInFlight('isAuthorizing')
179
177
  @oneFlight
180
178
  /**
@@ -196,24 +194,25 @@ const Authorization = WebexPlugin.extend({
196
194
  grant_type: 'authorization_code',
197
195
  redirect_uri: this.config.redirect_uri,
198
196
  code: options.code,
199
- self_contained_token: true
197
+ self_contained_token: true,
200
198
  };
201
199
 
202
200
  if (options.codeVerifier) {
203
201
  form.code_verifier = options.codeVerifier;
204
202
  }
205
203
 
206
- return this.webex.request({
207
- method: 'POST',
208
- uri: this.config.tokenUrl,
209
- form,
210
- auth: {
211
- user: this.config.client_id,
212
- pass: this.config.client_secret,
213
- sendImmediately: true
214
- },
215
- shouldRefreshAccessToken: false
216
- })
204
+ return this.webex
205
+ .request({
206
+ method: 'POST',
207
+ uri: this.config.tokenUrl,
208
+ form,
209
+ auth: {
210
+ user: this.config.client_id,
211
+ pass: this.config.client_secret,
212
+ sendImmediately: true,
213
+ },
214
+ shouldRefreshAccessToken: false,
215
+ })
217
216
  .then((res) => {
218
217
  this.webex.credentials.set({supertoken: res.body});
219
218
  })
@@ -260,9 +259,10 @@ const Authorization = WebexPlugin.extend({
260
259
  Reflect.deleteProperty(location.query, 'code');
261
260
  if (isEmpty(omit(location.query.state, 'csrf_token'))) {
262
261
  Reflect.deleteProperty(location.query, 'state');
263
- }
264
- else {
265
- location.query.state = base64.encode(JSON.stringify(omit(location.query.state, 'csrf_token')));
262
+ } else {
263
+ location.query.state = base64.encode(
264
+ JSON.stringify(omit(location.query.state, 'csrf_token'))
265
+ );
266
266
  }
267
267
  location.search = querystring.stringify(location.query);
268
268
  Reflect.deleteProperty(location, 'query');
@@ -283,16 +283,13 @@ const Authorization = WebexPlugin.extend({
283
283
  // eslint-disable-next-line no-underscore-dangle
284
284
  const safeCharacterMap = base64url._safe_map;
285
285
 
286
- const codeVerifier = lodash.times(
287
- 128,
288
- () => safeCharacterMap[lodash.random(0, safeCharacterMap.length - 1)]
289
- ).join('');
286
+ const codeVerifier = lodash
287
+ .times(128, () => safeCharacterMap[lodash.random(0, safeCharacterMap.length - 1)])
288
+ .join('');
290
289
 
291
290
  const codeChallenge = CryptoJS.SHA256(codeVerifier).toString(base64url);
292
291
 
293
- this.webex.getWindow().sessionStorage.setItem(
294
- OAUTH2_CODE_VERIFIER, codeVerifier
295
- );
292
+ this.webex.getWindow().sessionStorage.setItem(OAUTH2_CODE_VERIFIER, codeVerifier);
296
293
 
297
294
  return codeChallenge;
298
295
  },
@@ -344,7 +341,7 @@ const Authorization = WebexPlugin.extend({
344
341
  if (token !== sessionToken) {
345
342
  throw new Error(`CSRF token ${token} does not match stored token ${sessionToken}`);
346
343
  }
347
- }
344
+ },
348
345
  });
349
346
 
350
347
  export default Authorization;
package/src/config.js CHANGED
@@ -15,23 +15,24 @@ export default {
15
15
 
16
16
  refreshCallback(webex, token) {
17
17
  /* eslint-disable camelcase */
18
- return webex.request({
19
- method: 'POST',
20
- uri: token.config.tokenUrl,
21
- form: {
22
- grant_type: 'refresh_token',
23
- redirect_uri: token.config.redirect_uri,
24
- refresh_token: token.refresh_token
25
- },
26
- auth: {
27
- user: token.config.client_id,
28
- pass: token.config.client_secret,
29
- sendImmediately: true
30
- },
31
- shouldRefreshAccessToken: false
32
- })
18
+ return webex
19
+ .request({
20
+ method: 'POST',
21
+ uri: token.config.tokenUrl,
22
+ form: {
23
+ grant_type: 'refresh_token',
24
+ redirect_uri: token.config.redirect_uri,
25
+ refresh_token: token.refresh_token,
26
+ },
27
+ auth: {
28
+ user: token.config.client_id,
29
+ pass: token.config.client_secret,
30
+ sendImmediately: true,
31
+ },
32
+ shouldRefreshAccessToken: false,
33
+ })
33
34
  .then((res) => res.body);
34
35
  /* eslint-enable camelcase */
35
- }
36
- }
36
+ },
37
+ },
37
38
  };
package/src/index.js CHANGED
@@ -7,14 +7,11 @@ import {registerPlugin} from '@webex/webex-core';
7
7
  import Authorization from './authorization';
8
8
  import config from './config';
9
9
 
10
- const proxies = [
11
- 'isAuthorizing',
12
- 'isAuthenticating'
13
- ];
10
+ const proxies = ['isAuthorizing', 'isAuthenticating'];
14
11
 
15
12
  registerPlugin('authorization', Authorization, {
16
13
  config,
17
- proxies
14
+ proxies,
18
15
  });
19
16
 
20
17
  export {default} from './authorization';
@@ -11,22 +11,23 @@ import WebexCore from '@webex/webex-core';
11
11
 
12
12
  import pkg from '../../../package';
13
13
 
14
- const webex = window.webex = new WebexCore({
14
+ const webex = (window.webex = new WebexCore({
15
15
  config: {
16
16
  storage: {
17
- boundedAdapter: new StorageAdapterLocalStorage('webex')
18
- }
19
- }
20
- });
17
+ boundedAdapter: new StorageAdapterLocalStorage('webex'),
18
+ },
19
+ },
20
+ }));
21
21
 
22
22
  webex.once('ready', () => {
23
23
  if (webex.canAuthorize) {
24
24
  document.getElementById('access-token').innerHTML = webex.credentials.supertoken.access_token;
25
25
  document.getElementById('refresh-token').innerHTML = webex.credentials.supertoken.refresh_token;
26
26
 
27
- webex.request({
28
- uri: 'https://locus-a.wbx2.com/locus/api/v1/ping'
29
- })
27
+ webex
28
+ .request({
29
+ uri: 'https://locus-a.wbx2.com/locus/api/v1/ping',
30
+ })
30
31
  .then(() => {
31
32
  document.getElementById('ping-complete').innerHTML = 'success';
32
33
  });
@@ -41,18 +42,17 @@ document.getElementById('initiate-authorization-code-grant').addEventListener('c
41
42
  webex.authorization.initiateLogin({
42
43
  state: {
43
44
  exchange: false,
44
- name: pkg.name
45
- }
45
+ name: pkg.name,
46
+ },
46
47
  });
47
48
  });
48
49
 
49
50
  document.getElementById('token-refresh').addEventListener('click', () => {
50
51
  document.getElementById('access-token').innerHTML = '';
51
- webex.refresh({force: true})
52
- .then(() => {
53
- document.getElementById('access-token').innerHTML = webex.credentials.supertoken.access_token;
54
- document.getElementById('refresh-token').innerHTML = webex.credentials.supertoken.refresh_token;
55
- });
52
+ webex.refresh({force: true}).then(() => {
53
+ document.getElementById('access-token').innerHTML = webex.credentials.supertoken.access_token;
54
+ document.getElementById('refresh-token').innerHTML = webex.credentials.supertoken.refresh_token;
55
+ });
56
56
  });
57
57
 
58
58
  document.getElementById('logout').addEventListener('click', () => {
@@ -1,21 +1,24 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
- <head>
4
- <title>Authorization Automation Test</title>
5
- </head>
6
- <body class="authorization-automation-test">
7
- <button title="Login with Authorization Code Grant" id="initiate-authorization-code-grant">Login with Authorization Code Grant</button>
8
- <button title="Refresh Access Token" id="token-refresh">Refresh Access Token</button>
3
+ <head>
4
+ <title>Authorization Automation Test</title>
5
+ </head>
6
+ <body class="authorization-automation-test">
7
+ <button title="Login with Authorization Code Grant" id="initiate-authorization-code-grant">
8
+ Login with Authorization Code Grant
9
+ </button>
10
+ <button title="Refresh Access Token" id="token-refresh">Refresh Access Token</button>
9
11
 
10
- <h1>Access Token</h1>
11
- <div id="access-token"></div>
12
- <h1>Refresh Token</h1>
13
- <div id="refresh-token"></div>
12
+ <h1>Access Token</h1>
13
+ <div id="access-token"></div>
14
+ <h1>Refresh Token</h1>
15
+ <div id="refresh-token"></div>
14
16
 
15
- <h1>Pinging WDM</h1>
16
- <div id="ping-complete"></div>
17
+ <h1>Pinging WDM</h1>
18
+ <div id="ping-complete"></div>
17
19
 
18
- <button title="Logout" id="logout">Logout</button>
20
+ <button title="Logout" id="logout">Logout</button>
19
21
 
20
- <script src="app.js"></script>
21
- </body>
22
+ <script src="app.js"></script>
23
+ </body>
24
+ </html>
@@ -16,9 +16,17 @@ const redirectUri = process.env.WEBEX_REDIRECT_URI || process.env.REDIRECT_URI;
16
16
  // for test users in EU (Federation) and US
17
17
  // Also try US user with Federation enabled
18
18
  const runs = [
19
- {it: 'with EU user with Federation enabled', EUUser: true, attrs: {config: {credentials: {federation: true}}}},
19
+ {
20
+ it: 'with EU user with Federation enabled',
21
+ EUUser: true,
22
+ attrs: {config: {credentials: {federation: true}}},
23
+ },
20
24
  {it: 'with US user without Federation enabled', EUUser: false, attrs: {}},
21
- {it: 'with US user with Federation enabled', EUUser: false, attrs: {config: {credentials: {federation: true}}}}
25
+ {
26
+ it: 'with US user with Federation enabled',
27
+ EUUser: false,
28
+ attrs: {config: {credentials: {federation: true}}},
29
+ },
22
30
  ];
23
31
 
24
32
  runs.forEach((run) => {
@@ -35,98 +43,108 @@ runs.forEach((run) => {
35
43
  testUserParm.config = {orgId: process.env.EU_PRIMARY_ORG_ID};
36
44
  }
37
45
 
38
- before(() => testUsers.create(testUserParm)
39
- .then((users) => {
46
+ before(() =>
47
+ testUsers.create(testUserParm).then((users) => {
40
48
  user = users[0];
41
- }));
49
+ })
50
+ );
42
51
 
43
- before(() => createBrowser(pkg)
44
- .then((b) => {
52
+ before(() =>
53
+ createBrowser(pkg).then((b) => {
45
54
  browser = b;
46
- }));
55
+ })
56
+ );
47
57
 
48
58
  after(() => browser && browser.printLogs());
49
59
 
50
- after(() => browser && browser.quit()
51
- .catch((reason) => {
52
- console.warn(reason);
53
- }));
54
-
55
- it('authorizes a user', () => browser
56
- .get(`${redirectUri}/${pkg.name}`)
57
- .waitForElementByClassName('ready')
58
- .title()
60
+ after(
61
+ () =>
62
+ browser &&
63
+ browser.quit().catch((reason) => {
64
+ console.warn(reason);
65
+ })
66
+ );
67
+
68
+ it('authorizes a user', () =>
69
+ browser
70
+ .get(`${redirectUri}/${pkg.name}`)
71
+ .waitForElementByClassName('ready')
72
+ .title()
59
73
  .should.eventually.become('Authorization Automation Test')
60
- .waitForElementByCssSelector('[title="Login with Authorization Code Grant"]')
74
+ .waitForElementByCssSelector('[title="Login with Authorization Code Grant"]')
61
75
  .click()
62
- .login(user)
63
- .waitForElementByClassName('authorization-automation-test')
64
- .waitForElementById('refresh-token')
76
+ .login(user)
77
+ .waitForElementByClassName('authorization-automation-test')
78
+ .waitForElementById('refresh-token')
65
79
  .text()
66
- .should.eventually.not.be.empty
67
- .waitForElementByCssSelector('#ping-complete:not(:empty)')
80
+ .should.eventually.not.be.empty.waitForElementByCssSelector(
81
+ '#ping-complete:not(:empty)'
82
+ )
68
83
  .text()
69
- .should.eventually.become('success'));
84
+ .should.eventually.become('success'));
70
85
 
71
- it('is still logged in after reloading the page', () => browser
72
- .waitForElementById('access-token')
73
- .text()
74
- .should.eventually.not.be.empty
75
- .get(`${redirectUri}/${pkg.name}`)
76
- .sleep(500)
77
- .waitForElementById('access-token')
86
+ it('is still logged in after reloading the page', () =>
87
+ browser
88
+ .waitForElementById('access-token')
78
89
  .text()
79
- .should.eventually.not.be.empty);
90
+ .should.eventually.not.be.empty.get(`${redirectUri}/${pkg.name}`)
91
+ .sleep(500)
92
+ .waitForElementById('access-token')
93
+ .text().should.eventually.not.be.empty);
80
94
 
81
- it('refreshes the user\'s access token', () => {
95
+ it("refreshes the user's access token", () => {
82
96
  let accessToken = '';
83
97
 
84
- return browser
85
- .waitForElementByCssSelector('#access-token:not(:empty)')
98
+ return (
99
+ browser
100
+ .waitForElementByCssSelector('#access-token:not(:empty)')
86
101
  .text()
87
- .then((text) => {
88
- accessToken = text;
89
- assert.isString(accessToken);
90
- assert.isAbove(accessToken.length, 0);
91
-
92
- return browser;
93
- })
94
- .waitForElementByCssSelector('[title="Refresh Access Token"]')
102
+ .then((text) => {
103
+ accessToken = text;
104
+ assert.isString(accessToken);
105
+ assert.isAbove(accessToken.length, 0);
106
+
107
+ return browser;
108
+ })
109
+ .waitForElementByCssSelector('[title="Refresh Access Token"]')
95
110
  .click()
96
- // Not thrilled by a sleep, but we just need to give the button click
97
- // enough time to clear the #access-token box
98
- .sleep(500)
99
- .waitForElementByCssSelector('#access-token:not(:empty)')
111
+ // Not thrilled by a sleep, but we just need to give the button click
112
+ // enough time to clear the #access-token box
113
+ .sleep(500)
114
+ .waitForElementByCssSelector('#access-token:not(:empty)')
100
115
  .text()
101
- .then((text) => {
102
- assert.isString(text);
103
- assert.isAbove(text.length, 0);
104
- assert.notEqual(text, accessToken);
105
-
106
- return browser;
107
- });
116
+ .then((text) => {
117
+ assert.isString(text);
118
+ assert.isAbove(text.length, 0);
119
+ assert.notEqual(text, accessToken);
120
+
121
+ return browser;
122
+ })
123
+ );
108
124
  });
109
125
 
110
- it('logs out a user', () => browser
111
- .title()
126
+ it('logs out a user', () =>
127
+ browser
128
+ .title()
112
129
  .should.eventually.become('Authorization Automation Test')
113
- .waitForElementByCssSelector('[title="Logout"]')
130
+ .waitForElementByCssSelector('[title="Logout"]')
114
131
  .click()
115
- // We need to revoke three tokens before the window.location assignment.
116
- // So far, I haven't found any ques to wait for, so sleep seems to be
117
- // the only option.
118
- .sleep(3000)
119
- .title()
132
+ // We need to revoke three tokens before the window.location assignment.
133
+ // So far, I haven't found any ques to wait for, so sleep seems to be
134
+ // the only option.
135
+ .sleep(3000)
136
+ .title()
120
137
  .should.eventually.become('Redirect Dispatcher')
121
- .get(`${redirectUri}/${pkg.name}`)
122
- .title()
138
+ .get(`${redirectUri}/${pkg.name}`)
139
+ .title()
123
140
  .should.eventually.become('Authorization Automation Test')
124
- .waitForElementById('access-token')
141
+ .waitForElementById('access-token')
125
142
  .text()
126
- .should.eventually.be.empty
127
- .waitForElementByCssSelector('[title="Login with Authorization Code Grant"]')
143
+ .should.eventually.be.empty.waitForElementByCssSelector(
144
+ '[title="Login with Authorization Code Grant"]'
145
+ )
128
146
  .click()
129
- .waitForElementById('IDToken1'));
147
+ .waitForElementById('IDToken1'));
130
148
  });
131
149
  });
132
150
  });
@@ -19,28 +19,28 @@ import Authorization from '@webex/plugin-authorization-browser-first-party';
19
19
  // Necessary to require lodash this way in order to stub the method
20
20
  const lodash = require('lodash');
21
21
 
22
-
23
22
  browserOnly(describe)('plugin-authorization-browser-first-party', () => {
24
23
  describe('Authorization', () => {
25
- function makeWebex(href = 'https://example.com', csrfToken = undefined, pkceVerifier = undefined, config = {}) {
24
+ function makeWebex(
25
+ href = 'https://example.com',
26
+ csrfToken = undefined,
27
+ pkceVerifier = undefined,
28
+ config = {}
29
+ ) {
26
30
  const mockWindow = {
27
31
  history: {
28
32
  replaceState(a, b, location) {
29
33
  mockWindow.location.href = location;
30
- }
34
+ },
31
35
  },
32
36
  location: {
33
- href
37
+ href,
34
38
  },
35
39
  sessionStorage: {
36
- getItem: sinon.stub()
37
- .onCall(0)
38
- .returns(pkceVerifier)
39
- .onCall(1)
40
- .returns(csrfToken),
40
+ getItem: sinon.stub().onCall(0).returns(pkceVerifier).onCall(1).returns(csrfToken),
41
41
  removeItem: sinon.spy(),
42
- setItem: sinon.spy()
43
- }
42
+ setItem: sinon.spy(),
43
+ },
44
44
  };
45
45
 
46
46
  sinon.spy(mockWindow.history, 'replaceState');
@@ -49,37 +49,52 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
49
49
  children: {
50
50
  authorization: Authorization,
51
51
  credentials: Credentials,
52
- services: Services
52
+ services: Services,
53
53
  },
54
- request: sinon.stub().returns(Promise.resolve({body: {access_token: 'AT', token_type: 'Fake', refresh_token: 'RT'}})),
55
- config: merge({
56
- credentials: {
57
- idbroker: {
58
- url: process.env.IDBROKER_BASE_URL,
59
- defaultUrl: process.env.IDBROKER_BASE_URL
60
- },
61
- identity: {
62
- url: process.env.IDENTITY_BASE_URL,
63
- defaultUrl: process.env.IDENTITY_BASE_URL
54
+ request: sinon
55
+ .stub()
56
+ .returns(
57
+ Promise.resolve({body: {access_token: 'AT', token_type: 'Fake', refresh_token: 'RT'}})
58
+ ),
59
+ config: merge(
60
+ {
61
+ credentials: {
62
+ idbroker: {
63
+ url: process.env.IDBROKER_BASE_URL,
64
+ defaultUrl: process.env.IDBROKER_BASE_URL,
65
+ },
66
+ identity: {
67
+ url: process.env.IDENTITY_BASE_URL,
68
+ defaultUrl: process.env.IDENTITY_BASE_URL,
69
+ },
70
+ activationUrl: `${
71
+ process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'
72
+ }/idb/token/v1/actions/UserActivation/invoke`,
73
+ authorizeUrl: `${
74
+ process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'
75
+ }/idb/oauth2/v1/authorize`,
76
+ setPasswordUrl: `${
77
+ process.env.IDBROKER_BASE_URL || 'https://identity.webex.com'
78
+ }/identity/scim/v1/Users`,
79
+ logoutUrl: `${
80
+ process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'
81
+ }/idb/oauth2/v1/logout`,
82
+ // eslint-disable-next-line camelcase
83
+ client_id: 'fake',
84
+ // eslint-disable-next-line camelcase
85
+ client_secret: 'fake',
86
+ // eslint-disable-next-line camelcase
87
+ redirect_uri: 'http://example.com',
88
+ // eslint-disable-next-line camelcase
89
+ scope: 'scope:one',
90
+ refreshCallback: () => Promise.resolve(),
64
91
  },
65
- activationUrl: `${process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'}/idb/token/v1/actions/UserActivation/invoke`,
66
- authorizeUrl: `${process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'}/idb/oauth2/v1/authorize`,
67
- setPasswordUrl: `${process.env.IDBROKER_BASE_URL || 'https://identity.webex.com'}/identity/scim/v1/Users`,
68
- logoutUrl: `${process.env.IDBROKER_BASE_URL || 'https://idbroker.webex.com'}/idb/oauth2/v1/logout`,
69
- // eslint-disable-next-line camelcase
70
- client_id: 'fake',
71
- // eslint-disable-next-line camelcase
72
- client_secret: 'fake',
73
- // eslint-disable-next-line camelcase
74
- redirect_uri: 'http://example.com',
75
- // eslint-disable-next-line camelcase
76
- scope: 'scope:one',
77
- refreshCallback: () => Promise.resolve()
78
- }
79
- }, config),
92
+ },
93
+ config
94
+ ),
80
95
  getWindow() {
81
96
  return mockWindow;
82
- }
97
+ },
83
98
  });
84
99
 
85
100
  return webex;
@@ -93,15 +108,14 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
93
108
  assert.isFalse(webex.authorization.ready);
94
109
  assert.isFalse(webex.credentials.canAuthorize);
95
110
 
96
- return webex.authorization.when('change:ready')
97
- .then(() => {
98
- // Webex request gets called twice:
99
- // once for the pre-auth catalog
100
- // once for auth token exchange
101
- assert.calledTwice(webex.request);
102
- assert.isTrue(webex.authorization.ready);
103
- assert.isTrue(webex.credentials.canAuthorize);
104
- });
111
+ return webex.authorization.when('change:ready').then(() => {
112
+ // Webex request gets called twice:
113
+ // once for the pre-auth catalog
114
+ // once for auth token exchange
115
+ assert.calledTwice(webex.request);
116
+ assert.isTrue(webex.authorization.ready);
117
+ assert.isTrue(webex.credentials.canAuthorize);
118
+ });
105
119
  });
106
120
 
107
121
  it('validates the csrf token', () => {
@@ -109,12 +123,20 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
109
123
 
110
124
  assert.throws(() => {
111
125
  // eslint-disable-next-line no-unused-vars
112
- const webex = makeWebex(`http://example.com/?code=5&state=${base64.encode(JSON.stringify({csrf_token: 'someothertoken'}))}`, csrfToken);
126
+ const webex = makeWebex(
127
+ `http://example.com/?code=5&state=${base64.encode(
128
+ JSON.stringify({csrf_token: 'someothertoken'})
129
+ )}`,
130
+ csrfToken
131
+ );
113
132
  }, /CSRF token someothertoken does not match stored token abcd/);
114
133
 
115
134
  assert.throws(() => {
116
135
  // eslint-disable-next-line no-unused-vars
117
- const webex = makeWebex(`http://example.com/?code=5&state=${base64.encode(JSON.stringify({}))}`, csrfToken);
136
+ const webex = makeWebex(
137
+ `http://example.com/?code=5&state=${base64.encode(JSON.stringify({}))}`,
138
+ csrfToken
139
+ );
118
140
  }, /Expected CSRF token abcd, but not found in redirect query/);
119
141
 
120
142
  assert.throws(() => {
@@ -122,33 +144,46 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
122
144
  const webex = makeWebex('http://example.com/?code=5', csrfToken);
123
145
  }, /Expected CSRF token abcd, but not found in redirect query/);
124
146
 
125
- const webex = makeWebex(`http://example.com/?code=5&state=${base64.encode(JSON.stringify({csrf_token: csrfToken}))}`, csrfToken);
147
+ const webex = makeWebex(
148
+ `http://example.com/?code=5&state=${base64.encode(
149
+ JSON.stringify({csrf_token: csrfToken})
150
+ )}`,
151
+ csrfToken
152
+ );
126
153
 
127
- return webex.authorization.when('change:ready')
128
- .then(() => {
129
- assert.isTrue(webex.credentials.canAuthorize);
130
- assert.called(webex.getWindow().sessionStorage.removeItem);
131
- });
154
+ return webex.authorization.when('change:ready').then(() => {
155
+ assert.isTrue(webex.credentials.canAuthorize);
156
+ assert.called(webex.getWindow().sessionStorage.removeItem);
157
+ });
132
158
  });
133
159
 
134
160
  it('removes the oauth parameters from the url', () => {
135
161
  const csrfToken = 'abcd';
136
162
 
137
- const webex = makeWebex(`http://example.com/?code=5&state=${base64.encode(JSON.stringify({csrf_token: csrfToken, something: true}))}`, csrfToken);
163
+ const webex = makeWebex(
164
+ `http://example.com/?code=5&state=${base64.encode(
165
+ JSON.stringify({csrf_token: csrfToken, something: true})
166
+ )}`,
167
+ csrfToken
168
+ );
138
169
 
139
- return webex.authorization.when('change:ready')
140
- .then(() => {
141
- assert.isTrue(webex.credentials.canAuthorize);
142
- assert.called(webex.getWindow().sessionStorage.removeItem);
143
- assert.called(webex.getWindow().history.replaceState);
144
- assert.equal(webex.getWindow().location.href, `http://example.com/?state=${base64.encode(JSON.stringify({something: true}))}`);
145
- });
170
+ return webex.authorization.when('change:ready').then(() => {
171
+ assert.isTrue(webex.credentials.canAuthorize);
172
+ assert.called(webex.getWindow().sessionStorage.removeItem);
173
+ assert.called(webex.getWindow().history.replaceState);
174
+ assert.equal(
175
+ webex.getWindow().location.href,
176
+ `http://example.com/?state=${base64.encode(JSON.stringify({something: true}))}`
177
+ );
178
+ });
146
179
  });
147
180
  });
148
181
  describe('when the url contains an error', () => {
149
182
  it('throws a grant error', () => {
150
183
  assert.throws(() => {
151
- makeWebex('http://127.0.0.1:8000/?error=invalid_scope&error_description=The%20requested%20scope%20is%20invalid.');
184
+ makeWebex(
185
+ 'http://127.0.0.1:8000/?error=invalid_scope&error_description=The%20requested%20scope%20is%20invalid.'
186
+ );
152
187
  }, /The requested scope is invalid./);
153
188
  });
154
189
  });
@@ -166,28 +201,14 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
166
201
  it('passes codeVerifier to requestAuthorizationCodeGrant', () => {
167
202
  const expectedVerifier = 'test verifier';
168
203
 
169
- const webex = makeWebex(
170
- 'http://example.com?code=5',
171
- undefined,
172
- expectedVerifier
173
- );
204
+ const webex = makeWebex('http://example.com?code=5', undefined, expectedVerifier);
174
205
 
175
- return webex.authorization.when('change:ready')
176
- .then(() => {
177
- assert.calledTwice(webex.request);
178
- assert.calledWith(
179
- webex.getWindow().sessionStorage.getItem,
180
- 'oauth2-code-verifier'
181
- );
182
- assert.calledWith(
183
- webex.getWindow().sessionStorage.removeItem,
184
- 'oauth2-code-verifier'
185
- );
186
- assert.equal(
187
- webex.request.getCall(1).args[0].form.code_verifier,
188
- expectedVerifier
189
- );
190
- });
206
+ return webex.authorization.when('change:ready').then(() => {
207
+ assert.calledTwice(webex.request);
208
+ assert.calledWith(webex.getWindow().sessionStorage.getItem, 'oauth2-code-verifier');
209
+ assert.calledWith(webex.getWindow().sessionStorage.removeItem, 'oauth2-code-verifier');
210
+ assert.equal(webex.request.getCall(1).args[0].form.code_verifier, expectedVerifier);
211
+ });
191
212
  });
192
213
  });
193
214
  });
@@ -196,95 +217,97 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
196
217
  it('calls #initiateAuthorizationCodeGrant()', () => {
197
218
  const webex = makeWebex(undefined, undefined, {
198
219
  credentials: {
199
- clientType: 'confidential'
200
- }
220
+ clientType: 'confidential',
221
+ },
201
222
  });
202
223
 
203
224
  sinon.spy(webex.authorization, 'initiateAuthorizationCodeGrant');
204
225
 
205
- return webex.authorization.initiateLogin()
206
- .then(() => {
207
- assert.called(webex.authorization.initiateAuthorizationCodeGrant);
208
- assert.include(webex.getWindow().location, 'response_type=code');
209
- });
226
+ return webex.authorization.initiateLogin().then(() => {
227
+ assert.called(webex.authorization.initiateAuthorizationCodeGrant);
228
+ assert.include(webex.getWindow().location, 'response_type=code');
229
+ });
210
230
  });
211
231
 
212
232
  it('adds a csrf_token to the login url and sessionStorage', () => {
213
233
  const webex = makeWebex(undefined, undefined, {
214
234
  credentials: {
215
- clientType: 'confidential'
216
- }
235
+ clientType: 'confidential',
236
+ },
217
237
  });
218
238
 
219
239
  sinon.spy(webex.authorization, 'initiateAuthorizationCodeGrant');
220
240
 
221
- return webex.authorization.initiateLogin()
222
- .then(() => {
223
- assert.called(webex.authorization.initiateAuthorizationCodeGrant);
224
- assert.include(webex.getWindow().location, 'response_type=code');
225
- const {query} = url.parse(webex.getWindow().location, true);
226
- let {state} = query;
227
-
228
- state = JSON.parse(base64.decode(state));
229
- assert.property(state, 'csrf_token');
230
- assert.isDefined(state.csrf_token);
231
- assert.match(state.csrf_token, patterns.uuid);
232
- assert.called(webex.getWindow().sessionStorage.setItem);
233
- assert.calledWith(webex.getWindow().sessionStorage.setItem, 'oauth2-csrf-token', state.csrf_token);
234
- });
241
+ return webex.authorization.initiateLogin().then(() => {
242
+ assert.called(webex.authorization.initiateAuthorizationCodeGrant);
243
+ assert.include(webex.getWindow().location, 'response_type=code');
244
+ const {query} = url.parse(webex.getWindow().location, true);
245
+ let {state} = query;
246
+
247
+ state = JSON.parse(base64.decode(state));
248
+ assert.property(state, 'csrf_token');
249
+ assert.isDefined(state.csrf_token);
250
+ assert.match(state.csrf_token, patterns.uuid);
251
+ assert.called(webex.getWindow().sessionStorage.setItem);
252
+ assert.calledWith(
253
+ webex.getWindow().sessionStorage.setItem,
254
+ 'oauth2-csrf-token',
255
+ state.csrf_token
256
+ );
257
+ });
235
258
  });
236
259
 
237
260
  it('adds a pkce code challenge', () => {
238
261
  const webex = makeWebex(undefined, undefined, {
239
262
  credentials: {
240
- clientType: 'confidential'
241
- }
263
+ clientType: 'confidential',
264
+ },
242
265
  });
243
266
 
244
267
  const expectedCodeChallenge = 'test challenge';
245
268
 
246
269
  sinon.spy(webex.authorization, 'initiateAuthorizationCodeGrant');
247
- sinon.stub(webex.authorization, '_generateCodeChallenge')
248
- .returns(expectedCodeChallenge);
249
-
250
- return webex.authorization.initiateLogin()
251
- .then(() => {
252
- assert.called(webex.authorization.initiateAuthorizationCodeGrant);
253
- const grantOptions = webex.authorization.initiateAuthorizationCodeGrant.getCall(0).args[0];
254
-
255
- assert.equal(grantOptions.code_challenge, expectedCodeChallenge);
256
- assert.equal(grantOptions.code_challenge_method, 'S256');
257
- // eslint-disable-next-line no-underscore-dangle
258
- assert.calledWith(webex.authorization._generateCodeChallenge);
259
- });
270
+ sinon.stub(webex.authorization, '_generateCodeChallenge').returns(expectedCodeChallenge);
271
+
272
+ return webex.authorization.initiateLogin().then(() => {
273
+ assert.called(webex.authorization.initiateAuthorizationCodeGrant);
274
+ const grantOptions =
275
+ webex.authorization.initiateAuthorizationCodeGrant.getCall(0).args[0];
276
+
277
+ assert.equal(grantOptions.code_challenge, expectedCodeChallenge);
278
+ assert.equal(grantOptions.code_challenge_method, 'S256');
279
+ // eslint-disable-next-line no-underscore-dangle
280
+ assert.calledWith(webex.authorization._generateCodeChallenge);
281
+ });
260
282
  });
261
283
 
262
284
  it('adds emailHash', () => {
263
285
  const webex = makeWebex(undefined, undefined, {
264
286
  credentials: {
265
- clientType: 'confidential'
266
- }
287
+ clientType: 'confidential',
288
+ },
267
289
  });
268
290
 
269
- const expectedEmailHash = '73062d872926c2a556f17b36f50e328ddf9bff9d403939bd14b6c3b7f5a33fc2';
291
+ const expectedEmailHash =
292
+ '73062d872926c2a556f17b36f50e328ddf9bff9d403939bd14b6c3b7f5a33fc2';
270
293
 
271
294
  sinon.spy(webex.authorization, 'initiateAuthorizationCodeGrant');
272
295
 
273
- return webex.authorization.initiateLogin({email: 'test@email.com'})
274
- .then(() => {
275
- assert.called(webex.authorization.initiateAuthorizationCodeGrant);
276
- const grantOptions = webex.authorization.initiateAuthorizationCodeGrant.getCall(0).args[0];
296
+ return webex.authorization.initiateLogin({email: 'test@email.com'}).then(() => {
297
+ assert.called(webex.authorization.initiateAuthorizationCodeGrant);
298
+ const grantOptions =
299
+ webex.authorization.initiateAuthorizationCodeGrant.getCall(0).args[0];
277
300
 
278
- assert.equal(grantOptions.emailHash, expectedEmailHash);
279
- assert.isUndefined(grantOptions.email);
280
- });
301
+ assert.equal(grantOptions.emailHash, expectedEmailHash);
302
+ assert.isUndefined(grantOptions.email);
303
+ });
281
304
  });
282
305
 
283
306
  it('sets #isAuthorizing', () => {
284
307
  const webex = makeWebex(undefined, undefined, {
285
308
  credentials: {
286
- clientType: 'confidential'
287
- }
309
+ clientType: 'confidential',
310
+ },
288
311
  });
289
312
 
290
313
  assert.isFalse(webex.authorization.isAuthorizing);
@@ -298,8 +321,8 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
298
321
  it('sets #isAuthenticating', () => {
299
322
  const webex = makeWebex(undefined, undefined, {
300
323
  credentials: {
301
- clientType: 'confidential'
302
- }
324
+ clientType: 'confidential',
325
+ },
303
326
  });
304
327
 
305
328
  assert.isFalse(webex.authorization.isAuthenticating);
@@ -315,17 +338,16 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
315
338
  it('redirects to the login page with response_type=code', () => {
316
339
  const webex = makeWebex(undefined, undefined, {
317
340
  credentials: {
318
- clientType: 'confidential'
319
- }
341
+ clientType: 'confidential',
342
+ },
320
343
  });
321
344
 
322
345
  sinon.spy(webex.authorization, 'initiateAuthorizationCodeGrant');
323
346
 
324
- return webex.authorization.initiateLogin()
325
- .then(() => {
326
- assert.called(webex.authorization.initiateAuthorizationCodeGrant);
327
- assert.include(webex.getWindow().location, 'response_type=code');
328
- });
347
+ return webex.authorization.initiateLogin().then(() => {
348
+ assert.called(webex.authorization.initiateAuthorizationCodeGrant);
349
+ assert.include(webex.getWindow().location, 'response_type=code');
350
+ });
329
351
  });
330
352
  });
331
353
 
@@ -342,7 +364,7 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
342
364
  const toStringStub = sinon.stub().returns(expectedCodeChallenge);
343
365
  const randomStub = sinon.stub(lodash, 'random').returns(0);
344
366
  const sha256Stub = sinon.stub(CryptoJS, 'SHA256').returns({
345
- toString: toStringStub
367
+ toString: toStringStub,
346
368
  });
347
369
 
348
370
  // eslint-disable-next-line no-underscore-dangle
@@ -365,16 +387,16 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
365
387
  it('removes the state parameter when it has no keys', () => {
366
388
  const webex = makeWebex(undefined, undefined, {
367
389
  credentials: {
368
- clientType: 'confidential'
369
- }
390
+ clientType: 'confidential',
391
+ },
370
392
  });
371
393
  const location = {
372
394
  query: {
373
395
  code: 'code',
374
396
  state: {
375
- csrf_token: 'token'
376
- }
377
- }
397
+ csrf_token: 'token',
398
+ },
399
+ },
378
400
  };
379
401
 
380
402
  sinon.spy(webex.authorization, '_cleanUrl');
@@ -386,17 +408,17 @@ browserOnly(describe)('plugin-authorization-browser-first-party', () => {
386
408
  it('keeps the parameter when it has keys', () => {
387
409
  const webex = makeWebex(undefined, undefined, {
388
410
  credentials: {
389
- clientType: 'confidential'
390
- }
411
+ clientType: 'confidential',
412
+ },
391
413
  });
392
414
  const location = {
393
415
  query: {
394
416
  code: 'code',
395
417
  state: {
396
418
  csrf_token: 'token',
397
- key: 'value'
398
- }
399
- }
419
+ key: 'value',
420
+ },
421
+ },
400
422
  };
401
423
 
402
424
  sinon.spy(webex.authorization, '_cleanUrl');