diff --git a/packages/admin.gbapp/dialogs/AdminDialog.ts b/packages/admin.gbapp/dialogs/AdminDialog.ts index 8b1ae94e..dcd32a4d 100644 --- a/packages/admin.gbapp/dialogs/AdminDialog.ts +++ b/packages/admin.gbapp/dialogs/AdminDialog.ts @@ -145,7 +145,7 @@ export class AdminDialog extends IGBDialog { try { if (text === 'quit') { - return await step.replaceDialog('/'); + return await step.replaceDialog('/'); } else if (cmdName === 'setupSecurity') { return await step.beginDialog('/setupSecurity'); } else { @@ -221,7 +221,7 @@ export class AdminDialog extends IGBDialog { } }, async step => { - const logs = await min.core['getLatestLogs'](); + const logs = await min.core['getLatestLogs'](); await min.conversationalService.sendText(min, step, logs); return await step.replaceDialog('/ask', { isReturning: true }); } @@ -337,7 +337,7 @@ export class AdminDialog extends IGBDialog { member.name, null ); - + await GBAdminService.deployPackageCommand(min, user, cmd1, deployer); }); @@ -383,12 +383,12 @@ export class AdminDialog extends IGBDialog { min.dialogs.add( new WaterfallDialog('/setupSecurity', [ async step => { - const tokenName = step.activeDialog.state.tokenName = step.options['tokenName']; - step.activeDialog.state.clientId = min.core.getParam(min.instance, `${tokenName} Client ID`, null), - step.activeDialog.state.clientSecret = min.core.getParam(min.instance, `${tokenName} Client Secret`, null), - step.activeDialog.state.host = min.core.getParam(min.instance, `${tokenName} Host`, null), - step.activeDialog.state.tenant = min.core.getParam(min.instance, `${tokenName} Tenant`, null) - + const tokenName = step.activeDialog.state.tokenName = step.options['args']; + if (tokenName) { + step.activeDialog.state.clientId = min.core.getParam(min.instance, `${tokenName} Client ID`, null), + step.activeDialog.state.host = min.core.getParam(min.instance, `${tokenName} Host`, null), + step.activeDialog.state.tenant = min.core.getParam(min.instance, `${tokenName} Tenant`, null) + } if (step.context.activity.channelId !== 'msteams' && process.env.ENABLE_AUTH) { return await step.beginDialog('/auth'); } else { @@ -417,36 +417,36 @@ export class AdminDialog extends IGBDialog { return await min.conversationalService.prompt(min, step, prompt); }, async step => { - + step.activeDialog.state.authenticatorAuthorityHostUrl = step.result; - + const tokenName = step.activeDialog.state.tokenName; - if (tokenName){ - step.activeDialog.state.clientId - step.activeDialog.state.clientSecret - step.activeDialog.state.tenant - } - else{ + if (!tokenName) { min.instance.authenticatorAuthorityHostUrl = step.activeDialog.state.authenticatorAuthorityHostUrl; min.instance.authenticatorTenant = step.activeDialog.state.authenticatorTenant; } await min.adminService.updateSecurityInfo( min.instance.instanceId, - tokenName? step.activeDialog.state.tenant:step.activeDialog.state.authenticatorTenant, - tokenName?step.activeDialog.state.host:step.activeDialog.state.authenticatorAuthorityHostUrl + tokenName ? step.activeDialog.state.tenant : step.activeDialog.state.authenticatorTenant, + tokenName ? step.activeDialog.state.host : step.activeDialog.state.authenticatorAuthorityHostUrl ); - + const locale = step.context.activity.locale; const buf = Buffer.alloc(16); const state = `${min.instance.instanceId}${crypto.randomFillSync(buf).toString('hex')}`; min.adminService.setValue(min.instance.instanceId, `${tokenName}AntiCSRFAttackState`, state); - const redirectUri = urlJoin(process.env.BOT_URL, min.instance.botId, `/token?value=${tokenName}`); - const scope = tokenName?'': 'https://graph.microsoft.com/.default'; - const url = `https://login.microsoftonline.com/${step.activeDialog.state.authenticatorTenant}/oauth2/authorize?client_id=${min.instance.marketplaceId}&response_type=code&redirect_uri=${redirectUri}&scope=${scope}&state=${state}&response_mode=query`; + const redirectUri = urlJoin(process.env.BOT_URL, min.instance.botId, + tokenName ? `/token?value=${tokenName}` : '/token'); + const scope = tokenName ? '' : 'https://graph.microsoft.com/.default'; + const host = tokenName ? step.activeDialog.state.host : 'https://login.microsoftonline.com' + const tenant = tokenName ? step.activeDialog.state.tenant : min.instance.authenticatorTenant; + const clientId = tokenName ? step.activeDialog.state.clientId : min.instance.marketplaceId; + const oauth2 = tokenName ? 'oauth' : 'oauth2'; + const url = `${host}/${tenant}/${oauth2}/authorize?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=${scope}&state=${state}&response_mode=query`; await min.conversationalService.sendText(min, step, Messages[locale].consent(url)); diff --git a/packages/admin.gbapp/services/GBAdminService.ts b/packages/admin.gbapp/services/GBAdminService.ts index 801ffad4..8b28ede0 100644 --- a/packages/admin.gbapp/services/GBAdminService.ts +++ b/packages/admin.gbapp/services/GBAdminService.ts @@ -231,7 +231,7 @@ export class GBAdminService implements IGBAdminService { } public async acquireElevatedToken(instanceId: number, root: boolean = false, - tokenName: string = null, + tokenName: string = '', clientId: string = null, clientSecret: string = null, host: string = null, @@ -259,20 +259,21 @@ export class GBAdminService implements IGBAdminService { const accessToken = await this.getValue(instanceId, `${tokenName}accessToken`); resolve(accessToken); } else { + const oauth2 = tokenName ? 'oauth' : 'oauth2'; const authorizationUrl = urlJoin( - host ? host : instance.authenticatorAuthorityHostUrl, - tenant ? tenant : instance.authenticatorTenant, - '/oauth2/authorize' + tokenName ? host : instance.authenticatorAuthorityHostUrl, + tokenName ? tenant : instance.authenticatorTenant, + `/${oauth2}/authorize` ); const refreshToken = await this.getValue(instanceId, `${tokenName}refreshToken`); - const resource = clientId? '': 'https://graph.microsoft.com'; + const resource = tokenName ? '' : 'https://graph.microsoft.com'; const authenticationContext = new AuthenticationContext(authorizationUrl); authenticationContext.acquireTokenWithRefreshToken( refreshToken, - clientId ? clientId : instance.marketplaceId, - clientId ? clientSecret : instance.marketplacePassword, + tokenName ? clientId : instance.marketplaceId, + tokenName ? clientSecret : instance.marketplacePassword, resource, async (err, res) => { if (err !== null) { diff --git a/packages/basic.gblib/services/GBVMService.ts b/packages/basic.gblib/services/GBVMService.ts index c7348adc..3f251d3c 100644 --- a/packages/basic.gblib/services/GBVMService.ts +++ b/packages/basic.gblib/services/GBVMService.ts @@ -728,8 +728,9 @@ export class GBVMService extends GBService { await CollectionUtil.asyncForEach(tokens,async t => { const tokenName = t.replace(strFind, ''); try { - variables[t] = await (min.adminService as any)['acquireElevatedToken'] + variables[tokenName] = await (min.adminService as any)['acquireElevatedToken'] (min.instance.instanceId, false, + tokenName, min.core.getParam(min.instance, `${tokenName} Client ID`, null), min.core.getParam(min.instance, `${tokenName} Client Secret`, null), min.core.getParam(min.instance, `${tokenName} Host`, null), diff --git a/packages/core.gbapp/services/GBCoreService.ts b/packages/core.gbapp/services/GBCoreService.ts index 35e5c2a0..15f35a8d 100644 --- a/packages/core.gbapp/services/GBCoreService.ts +++ b/packages/core.gbapp/services/GBCoreService.ts @@ -695,6 +695,9 @@ ENDPOINT_UPDATE=true value = minBoot.instance[name]; } } + if (value === undefined) { + value = null; + } return value; } @@ -710,7 +713,7 @@ ENDPOINT_UPDATE=true } Object.keys(params).forEach(e => { - if (criteria.toLowerCase().indexOf(e.toLowerCase())) { + if (e.toLowerCase().indexOf(criteria.toLowerCase()) !== -1) { list.push(e); } }); diff --git a/packages/core.gbapp/services/GBMinService.ts b/packages/core.gbapp/services/GBMinService.ts index 153702b6..d6b0f98d 100644 --- a/packages/core.gbapp/services/GBMinService.ts +++ b/packages/core.gbapp/services/GBMinService.ts @@ -490,9 +490,13 @@ export class GBMinService { * on https:////token URL. */ private handleOAuthTokenRequests(server: any, min: GBMinInstance, instance: IGBInstance) { + server.get(`/${min.instance.botId}/token`, async (req, res) => { - const tokenName = req.params['value']; + let tokenName = req.query['value']; + if (!tokenName) { + tokenName = ''; + } // Checks request state by reading AntiCSRFAttackState from GB Admin infrastructure. @@ -503,49 +507,93 @@ export class GBMinService { throw new Error(msg); } - const clientId = min.core.getParam(min.instance, `${tokenName} Client ID`, null), - const clientSecret = min.core.getParam(min.instance, `${tokenName} Client Secret`, null), - const host = min.core.getParam(min.instance, `${tokenName} Host`, null), - const tenant = min.core.getParam(min.instance, `${tokenName} Tenant`, null) + const clientId = min.core.getParam(min.instance, `${tokenName} Client ID`, null); + const clientSecret = min.core.getParam(min.instance, `${tokenName} Client Secret`, null); + const host = min.core.getParam(min.instance, `${tokenName} Host`, null); + const tenant = min.core.getParam(min.instance, `${tokenName} Tenant`, null); - const authenticationContext = new AuthenticationContext.AuthenticationContext( - urlJoin( - tokenName ? host : min.instance.authenticatorAuthorityHostUrl, - tokenName ? tenant : min.instance.authenticatorTenant) - ); - const resource = tokenName ? '' : 'https://graph.microsoft.com'; + if (tokenName) { + const code = req?.query?.code; - // Calls MSFT to get token. + let url = urlJoin( + host, + tenant, 'oauth/token'); + let buff = new Buffer(`${clientId}:${clientSecret}`); + const base64 = buff.toString('base64'); - authenticationContext.acquireTokenWithAuthorizationCode( - req.query.code, - urlJoin(process.env.BOT_URL, min.instance.botId, '/token'), - resource, - tokenName ? clientId : instance.marketplaceId, - tokenName ? clientSecret : instance.marketplacePassword, - async (err, token) => { - if (err) { - - const msg = `handleOAuthTokenRequests: Error acquiring token: ${err}`; - - GBLog.error(msg); - res.send(msg); + const options = { + method: 'POST', + headers: { + Accept: '1.0', + Authorization: `Basic ${base64}`, + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: new URLSearchParams({ + 'grant_type': 'authorization_code', + 'code': code + }) + }; + const result = await fetch(url, options); - } else { - - // Saves token to the database. - - await this.adminService.setValue(instance.instanceId, `${tokenName}accessToken`, token['accessToken']); - await this.adminService.setValue(instance.instanceId, `${tokenName}refreshToken`, token['refreshToken']); - await this.adminService.setValue(instance.instanceId, `${tokenName}expiresOn`, token['expiresOn'].toString()); - await this.adminService.setValue(instance.instanceId, `${tokenName}AntiCSRFAttackState`, null); - - // Inform the home for default .gbui after finishing token retrival. - - res.redirect(process.env.BOT_URL); - } + if (result.status != 200) { + throw new Error(`handleOAuthTokenRequests error: ${result.status}: ${result.statusText}.`) } - ); + + const text = await result.text(); + const token = JSON.parse(text); + + // Saves token to the database. + + await this.adminService.setValue(instance.instanceId, + `${tokenName}accessToken`, token['accessToken']?token['accessToken']:token['access_token']); + await this.adminService.setValue(instance.instanceId, + `${tokenName}refreshToken`, token['refreshToken']?token['refreshToken']:token['refresh_token']); + await this.adminService.setValue(instance.instanceId, + `${tokenName}expiresOn`, token['expiresOn']?token['expiresOn'].toString():token['expires_in'].toString()); + await this.adminService.setValue(instance.instanceId, `${tokenName}AntiCSRFAttackState`, null); + + + } + else { + const authenticationContext = new AuthenticationContext.AuthenticationContext( + urlJoin( + tokenName ? host : min.instance.authenticatorAuthorityHostUrl, + tokenName ? tenant : min.instance.authenticatorTenant) + ); + const resource = 'https://graph.microsoft.com'; + + // Calls MSFT to get token. + + authenticationContext.acquireTokenWithAuthorizationCode( + req.query.code, + urlJoin(process.env.BOT_URL, min.instance.botId, '/token'), + resource, + tokenName ? clientId : instance.marketplaceId, + tokenName ? clientSecret : instance.marketplacePassword, + async (err, token) => { + if (err) { + + const msg = `handleOAuthTokenRequests: Error acquiring token: ${err}`; + + GBLog.error(msg); + res.send(msg); + + } else { + + // Saves token to the database. + + await this.adminService.setValue(instance.instanceId, `${tokenName}accessToken`, token['accessToken']); + await this.adminService.setValue(instance.instanceId, `${tokenName}refreshToken`, token['refreshToken']); + await this.adminService.setValue(instance.instanceId, `${tokenName}expiresOn`, token['expiresOn'].toString()); + await this.adminService.setValue(instance.instanceId, `${tokenName}AntiCSRFAttackState`, null); + + // Inform the home for default .gbui after finishing token retrival. + + res.redirect(process.env.BOT_URL); + } + } + ); + } }); }