Skip to content

Commit

Permalink
feat: Integrate OneAuth library (#4677)
Browse files Browse the repository at this point in the history
* Integrated OneAuth library for Windows

* Auth updates

* chore: remove orchestrator (#4602)

* Added CSRF protection to getAccessToken route.

* Build changes to support OneAuth

* Shared types and did some renaming.

* Consolidated types

* Fixed naming issue on babel localization config

* Separated out auth service code.

* Removed TODO

* Updated shim to be at the oneauth service level

* Added interactive retry when failing to get token silently

* Added documentation for using OneAuth locally

* sign mac assets with correct entitlements

add mac signing script

add signing and verifying steps

fix path to bundles

copy provision profile into each app bundle

copy the provision profile in contents directory

sign each dylib and framework version

fix logging string

sign frameworks before bundles

* Made temporary mac auth flow dev-env-only

* Fixed bad import

* Removed manual oneauth installation steps from AUTH.md

* Linting fixes

* Setup tests

* Added more tests

* Added test for token caching.

* Added cross-platform testing for oneauth service

Co-authored-by: Andy Brown <asbrown002@gmail.com>
Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>
  • Loading branch information
3 people committed Nov 9, 2020
1 parent 7a9bfc5 commit 8881e98
Show file tree
Hide file tree
Showing 54 changed files with 2,162 additions and 450 deletions.
3 changes: 2 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@
"args": ["${workspaceRoot}/Composer/packages/electron-server"],
"env": {
"NODE_ENV": "development",
"DEBUG": "composer*"
"DEBUG": "composer*",
"COMPOSER_ENABLE_ONEAUTH": "false"
},
"outputCapture": "std"
},
Expand Down
13 changes: 13 additions & 0 deletions Composer/babel.l10n.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

module.exports = {
presets: ['@babel/react', ['@babel/typescript', { allowNamespaces: true }]],
plugins: ['@babel/plugin-proposal-class-properties'],
ignore: [
'packages/electron-server',
'packages/**/__tests__',
'packages/**/node_modules',
'packages/**/build/**/*.js',
],
};
2 changes: 1 addition & 1 deletion Composer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
"l10n:extract": "cross-env NODE_ENV=production format-message extract -g underscored_crc32 -o packages/server/src/locales/en-US.json l10ntemp/**/*.js",
"l10n:extractJson": "node scripts/l10n-extractJson.js",
"l10n:transform": "node scripts/l10n-transform.js",
"l10n:babel": "babel ./packages --extensions \".ts,.tsx,.jsx,.js\" --out-dir l10ntemp --presets=@babel/react,@babel/typescript --plugins=@babel/plugin-proposal-class-properties --ignore \"packages/electron-server\",\"packages/**/__tests__\",\"packages/**/node_modules\",\"packages/**/build/**/*.js\"",
"l10n:babel": "babel --config-file ./babel.l10n.config.js --extensions \"ts,.tsx,.jsx,.js\" --out-dir l10ntemp ./packages",
"l10n": "yarn l10n:babel && yarn l10n:extract && yarn l10n:transform packages/server/src/locales/en-US.json && yarn l10n:extractJson packages/server/schemas"
},
"husky": {
Expand Down
6 changes: 6 additions & 0 deletions Composer/packages/client/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@
</script>
<? } ?>
<% } %>
<!-- embed csrf token into window object -->
<? if (__csrf__) { ?>
<script>
window.__csrf__ = "<?= __csrf__ ?>";
</script>
<? } ?>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
Expand Down
16 changes: 6 additions & 10 deletions Composer/packages/client/src/plugins/api.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { OAuthClient, OAuthOptions } from '../utils/oauthClient';
import { AuthParameters } from '@botframework-composer/types';

import { AuthClient } from '../utils/authClient';

interface IAPI {
auth: AuthAPI;
Expand All @@ -15,8 +17,7 @@ interface PublishConfig {
}

interface AuthAPI {
login: (options: OAuthOptions) => Promise<string>; // returns an id token
getAccessToken: (options: OAuthOptions) => Promise<string>; // returns an access token
getAccessToken: (options: AuthParameters) => Promise<string>; // returns an access token
}

interface PublishAPI {
Expand All @@ -31,13 +32,8 @@ class API implements IAPI {

constructor() {
this.auth = {
login: (options: OAuthOptions) => {
const client = new OAuthClient(options);
return client.login();
},
getAccessToken: (options: OAuthOptions) => {
const client = new OAuthClient(options);
return client.getTokenSilently();
getAccessToken: (params: AuthParameters) => {
return AuthClient.getAccessToken(params);
},
};
this.publish = {
Expand Down
5 changes: 5 additions & 0 deletions Composer/packages/client/src/types/window.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,10 @@ declare global {

ExtensionClient: typeof ExtensionClient;
Fabric: typeof Fabric;

/**
* Token generated by the server, and sent with certain auth-related requests to the server to be verified and prevent CSRF attacks.
*/
__csrf__?: string;
}
}
36 changes: 36 additions & 0 deletions Composer/packages/client/src/utils/authClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import { AuthParameters } from '@botframework-composer/types';

async function getAccessToken(options: AuthParameters): Promise<string> {
try {
const { clientId = '', targetResource = '', scopes = [] } = options;
const { __csrf__ = '' } = window;

let url = '/api/auth/getAccessToken?';
const params = new URLSearchParams();
if (clientId) {
params.append('clientId', clientId);
}
if (scopes.length) {
params.append('scopes', JSON.stringify(scopes));
}
if (targetResource) {
params.append('targetResource', targetResource);
}
url += params.toString();

const result = await fetch(url, { method: 'GET', headers: { 'X-CSRF-Token': __csrf__ } });
const { accessToken = '' } = await result.json();
return accessToken;
} catch (e) {
// error handling
console.error('Did not receive an access token back from the server: ', e);
return '';
}
}

export const AuthClient = {
getAccessToken,
};
93 changes: 0 additions & 93 deletions Composer/packages/client/src/utils/oauthClient.ts

This file was deleted.

8 changes: 8 additions & 0 deletions Composer/packages/electron-server/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,12 @@ module.exports = {
rules: {
'security/detect-non-literal-fs-filename': 'off',
},
overrides: [
{
files: ['scripts/*'],
rules: {
'@typescript-eslint/no-var-requires': 'off',
},
},
],
};
1 change: 1 addition & 0 deletions Composer/packages/electron-server/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ build/
dist/
locales/en-US-pseudo.json
l10ntemp/
oneauth-temp
31 changes: 31 additions & 0 deletions Composer/packages/electron-server/AUTH.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Enabling Authentication via OneAuth

## Summary

Authentication in Composer is done using the OneAuth native node library.

This library leverages APIs within the user's OS to store and retrieve credentials in a compliant fashion, and allows Composer to get access tokens on behalf of the user once the user signs in.

We disable this authentication flow by default in the development environment. To use the flow in a dev environment, please follow the steps below to leverage the OneAuth library.

## Requirements

**NOTE:** Authentication on Linux is not (yet) supported. We plan to support this in the future.

When building Composer from source, in order to leverage the OneAuth library you will need to:

- Set the `COMPOSER_ENABLE_ONEAUTH` environment variable to `true` in whatever process you use to start the `electron-server` package
- Install the `oneauth-win64` or `oneauth-mac` NodeJS module either manually from the private registry, or by downloading it via script

## Installing the OneAuth module

Depending on your OS (Mac vs. Windows), you will need to install the `oneauth-mac` or `oneauth-win64` modules respectively.

### Using the `installOneAuth.js` script

1. Set `npm_config_registry` to `https://office.pkgs.visualstudio.com/_packaging/OneAuth/npm/registry/`
1. Set `npm_config_username` to anything other than an empty string
1. Set `npm_config__password` (note the double "_") to a base64-encoded [Personal Access Token you created in Azure DevOps](https://office.visualstudio.com/_usersSettings/tokens) for the Office org that has the Packaging (read) scope enabled
1. Run `node scripts/installOneAuth.js` from `/electron-server/`

There should now be a `/electron-server/oneauth-temp/` directory containing the contents of the OneAuth module which will be called by Composer assuming you set the `COMPOSER_ENABLE_ONEAUTH` environment variable.
Loading

0 comments on commit 8881e98

Please sign in to comment.