From 9e79e1f12b2b5df2a95a493ef3e848f493ce8b3c Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 24 Mar 2020 11:53:17 +1300 Subject: [PATCH 1/4] WIP for VSCode credential --- common/config/rush/pnpm-lock.yaml | 219 +++++++++++++++++- sdk/identity/identity/package.json | 1 + sdk/identity/identity/review/identity.api.md | 7 + .../credentials/vscodeCredential.browser.ts | 24 ++ .../src/credentials/vscodeCredential.ts | 28 +++ sdk/identity/identity/src/index.ts | 1 + 6 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 sdk/identity/identity/src/credentials/vscodeCredential.browser.ts create mode 100644 sdk/identity/identity/src/credentials/vscodeCredential.ts diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 3ef9835801a3..8b60e7a51d42 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -987,10 +987,21 @@ packages: node: '>=4' resolution: integrity: sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw== + /aproba/1.2.0: + dev: false + resolution: + integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== /archy/1.0.0: dev: false resolution: integrity: sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= + /are-we-there-yet/1.1.5: + dependencies: + delegates: 1.0.0 + readable-stream: 2.3.7 + dev: false + resolution: + integrity: sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== /arg/4.1.0: dev: false resolution: @@ -1222,6 +1233,14 @@ packages: node: '>=8' resolution: integrity: sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== + /bl/4.0.2: + dependencies: + buffer: 5.5.0 + inherits: 2.0.4 + readable-stream: 3.6.0 + dev: false + resolution: + integrity: sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ== /blob/0.0.5: dev: false resolution: @@ -1484,6 +1503,10 @@ packages: fsevents: 2.1.2 resolution: integrity: sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== + /chownr/1.1.4: + dev: false + resolution: + integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== /chrome-launcher/0.11.2: dependencies: '@types/node': 13.9.0 @@ -1558,6 +1581,12 @@ packages: node: '>= 0.12.0' resolution: integrity: sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= + /code-point-at/1.1.0: + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= /color-convert/1.9.3: dependencies: color-name: 1.1.3 @@ -1669,6 +1698,10 @@ packages: node: '>= 0.10.0' resolution: integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + /console-control-strings/1.1.0: + dev: false + resolution: + integrity: sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= /content-disposition/0.5.3: dependencies: safe-buffer: 5.1.2 @@ -1871,6 +1904,14 @@ packages: node: '>=0.10' resolution: integrity: sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= + /decompress-response/4.2.1: + dependencies: + mimic-response: 2.1.0 + dev: false + engines: + node: '>=8' + resolution: + integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== /deep-assign/3.0.0: dependencies: is-obj: 1.0.1 @@ -1941,6 +1982,10 @@ packages: node: '>=0.4.0' resolution: integrity: sha1-3zrhmayt+31ECqrgsp4icrJOxhk= + /delegates/1.0.0: + dev: false + resolution: + integrity: sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= /depd/1.1.2: dev: false engines: @@ -1951,6 +1996,13 @@ packages: dev: false resolution: integrity: sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + /detect-libc/1.0.3: + dev: false + engines: + node: '>=0.10' + hasBin: true + resolution: + integrity: sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= /di/0.0.1: dev: false resolution: @@ -2455,6 +2507,12 @@ packages: node: ^8.12.0 || >=9.7.0 resolution: integrity: sha512-r9vdGQk4bmCuK1yKQu1KTwcT2zwfWdbdaXfCtAh+5nU/4fSX+JAb7vZGvI5naJrQlvONrEB20jeruESI69530g== + /expand-template/2.0.3: + dev: false + engines: + node: '>=6' + resolution: + integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== /expand-tilde/2.0.2: dependencies: homedir-polyfill: 1.0.3 @@ -2763,6 +2821,10 @@ packages: node: '>= 0.6' resolution: integrity: sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + /fs-constants/1.0.0: + dev: false + resolution: + integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== /fs-extra/7.0.1: dependencies: graceful-fs: 4.2.3 @@ -2813,6 +2875,19 @@ packages: dev: false resolution: integrity: sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + /gauge/2.7.4: + dependencies: + aproba: 1.2.0 + console-control-strings: 1.1.0 + has-unicode: 2.0.1 + object-assign: 4.1.1 + signal-exit: 3.0.2 + string-width: 1.0.2 + strip-ansi: 3.0.1 + wide-align: 1.1.3 + dev: false + resolution: + integrity: sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= /get-caller-file/1.0.3: dev: false resolution: @@ -2878,6 +2953,10 @@ packages: dev: false resolution: integrity: sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + /github-from-package/0.0.0: + dev: false + resolution: + integrity: sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= /glob-parent/5.1.0: dependencies: is-glob: 4.0.1 @@ -3117,6 +3196,10 @@ packages: node: '>= 0.4' resolution: integrity: sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== + /has-unicode/2.0.1: + dev: false + resolution: + integrity: sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= /has/1.0.3: dependencies: function-bind: 1.1.1 @@ -3436,6 +3519,14 @@ packages: node: '>=0.10.0' resolution: integrity: sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== + /is-fullwidth-code-point/1.0.0: + dependencies: + number-is-nan: 1.0.1 + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-754xOG8DGn8NZDr4L95QxFfvAMs= /is-fullwidth-code-point/2.0.0: dev: false engines: @@ -3980,6 +4071,14 @@ packages: hasBin: true resolution: integrity: sha512-L5SIaXEYqzrh6b1wqYC42tNsFMx2PWuxky84pK9coK09MvmL7mxii3G3bZBh/0rvD27lqDd0le9jyhzvwif73A== + /keytar/5.4.0: + dependencies: + nan: 2.14.0 + prebuild-install: 5.3.3 + dev: false + requiresBuild: true + resolution: + integrity: sha512-Ta0RtUmkq7un177SPgXKQ7FGfGDV4xvsV0cGNiWVEzash5U0wyOsXpwfrK2+Oq+hHvsvsbzIZUUuJPimm3avFw== /lazy-ass/1.6.0: dev: false engines: @@ -4482,6 +4581,12 @@ packages: node: '>=6' resolution: integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + /mimic-response/2.1.0: + dev: false + engines: + node: '>=8' + resolution: + integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== /min-document/2.19.0: dependencies: dom-walk: 0.1.1 @@ -4646,6 +4751,10 @@ packages: dev: false resolution: integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + /nan/2.14.0: + dev: false + resolution: + integrity: sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== /nanoassert/1.1.0: dev: false resolution: @@ -4675,6 +4784,10 @@ packages: dev: false resolution: integrity: sha512-l3lC7v/PfOuRWQa8vV29Jo6TG10wHtnthLElFXs4Te4Aas57Fo4n1Q8LH9n+NDh9riOzTVvb2QNBhTS4JUKNjw== + /napi-build-utils/1.0.2: + dev: false + resolution: + integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== /natural-compare/1.4.0: dev: false resolution: @@ -4725,6 +4838,12 @@ packages: node: '>= 8.0' resolution: integrity: sha512-U5wPctaY4/ar2JJ5Jg4wJxlbBfayxgKbiAeGh+a1kk6Pwnc2ZEuKviLyDSG6t0uXl56q7AALIxoM6FJrBSsVXA== + /node-abi/2.15.0: + dependencies: + semver: 5.7.1 + dev: false + resolution: + integrity: sha512-FeLpTS0F39U7hHZU1srAK4Vx+5AHNVOTP+hxBNQknR/54laTHSFIJkDWDqiquY1LeLUgTfPN7sLPhMubx0PLAg== /node-abort-controller/1.0.4: dev: false resolution: @@ -4742,6 +4861,10 @@ packages: node: 4.x || >=6.0.0 resolution: integrity: sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== + /noop-logger/0.1.1: + dev: false + resolution: + integrity: sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= /nopt/3.0.6: dependencies: abbrev: 1.0.9 @@ -4805,6 +4928,21 @@ packages: node: '>=8' resolution: integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + /npmlog/4.1.2: + dependencies: + are-we-there-yet: 1.1.5 + console-control-strings: 1.1.0 + gauge: 2.7.4 + set-blocking: 2.0.0 + dev: false + resolution: + integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== + /number-is-nan/1.0.1: + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= /nyc/14.1.1: dependencies: archy: 1.0.0 @@ -5270,6 +5408,29 @@ packages: node: '>=4' resolution: integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + /prebuild-install/5.3.3: + dependencies: + detect-libc: 1.0.3 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.0 + mkdirp: 0.5.1 + napi-build-utils: 1.0.2 + node-abi: 2.15.0 + noop-logger: 0.1.1 + npmlog: 4.1.2 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 3.1.0 + tar-fs: 2.0.0 + tunnel-agent: 0.6.0 + which-pm-runs: 1.0.0 + dev: false + engines: + node: '>=6' + hasBin: true + resolution: + integrity: sha512-GV+nsUXuPW2p8Zy7SarF/2W/oiK8bFQgJcncoJ0d7kRpekEA0ftChjfEaF9/Y+QJEc/wFR7RAEa8lYByuUIe2g== /prelude-ls/1.1.2: dev: false engines: @@ -5577,6 +5738,16 @@ packages: dev: false resolution: integrity: sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== + /readable-stream/3.6.0: + dependencies: + inherits: 2.0.4 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + dev: false + engines: + node: '>= 6' + resolution: + integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== /readdirp/3.3.0: dependencies: picomatch: 2.2.1 @@ -6063,6 +6234,18 @@ packages: dev: false resolution: integrity: sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= + /simple-concat/1.0.0: + dev: false + resolution: + integrity: sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= + /simple-get/3.1.0: + dependencies: + decompress-response: 4.2.1 + once: 1.4.0 + simple-concat: 1.0.0 + dev: false + resolution: + integrity: sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA== /sinon/7.5.0: dependencies: '@sinonjs/commons': 1.7.1 @@ -6349,6 +6532,16 @@ packages: node: '>=0.10.0' resolution: integrity: sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM= + /string-width/1.0.2: + dependencies: + code-point-at: 1.1.0 + is-fullwidth-code-point: 1.0.0 + strip-ansi: 3.0.1 + dev: false + engines: + node: '>=0.10.0' + resolution: + integrity: sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= /string-width/2.1.1: dependencies: is-fullwidth-code-point: 2.0.0 @@ -6563,6 +6756,25 @@ packages: node: '>=6' resolution: integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== + /tar-fs/2.0.0: + dependencies: + chownr: 1.1.4 + mkdirp: 0.5.1 + pump: 3.0.0 + tar-stream: 2.1.2 + dev: false + resolution: + integrity: sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA== + /tar-stream/2.1.2: + dependencies: + bl: 4.0.2 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.0 + dev: false + resolution: + integrity: sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q== /terser/4.6.6: dependencies: commander: 2.20.3 @@ -7084,6 +7296,10 @@ packages: dev: false resolution: integrity: sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= + /which-pm-runs/1.0.0: + dev: false + resolution: + integrity: sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= /which/1.3.1: dependencies: isexe: 2.0.0 @@ -8221,6 +8437,7 @@ packages: karma-mocha: 1.3.0 karma-mocha-reporter: 2.2.5_karma@4.4.1 karma-remap-istanbul: 0.6.0_karma@4.4.1 + keytar: 5.4.0 mocha: 6.2.2 mocha-junit-reporter: 1.23.3_mocha@6.2.2 mocha-multi: 1.1.3_mocha@6.2.2 @@ -8241,7 +8458,7 @@ packages: dev: false name: '@rush-temp/identity' resolution: - integrity: sha512-RmqqkFY1LlYsh2Vo7LCCoV7FcY/KsNQRqRYkEHt43BP8e77UKHQZkJgRs7GJjd6E0rlIYg+JVf9P3aM9HAThTg== + integrity: sha512-bRHuYxGTDgITEV8JB+gNz3zDAD/Gx5gIQqQbKyQ2rusBLohjOgZZv+29IaNSojMKvpV8Vhj2JN39TXiaabC1uw== tarball: 'file:projects/identity.tgz' version: 0.0.0 'file:projects/keyvault-certificates.tgz': diff --git a/sdk/identity/identity/package.json b/sdk/identity/identity/package.json index c5a3d0642e8c..3dd917562a6b 100644 --- a/sdk/identity/identity/package.json +++ b/sdk/identity/identity/package.json @@ -79,6 +79,7 @@ "@opentelemetry/types": "^0.2.0", "events": "^3.0.0", "jws": "^3.2.2", + "keytar": "^5.4.0", "msal": "^1.0.2", "qs": "^6.7.0", "tslib": "^1.10.0", diff --git a/sdk/identity/identity/review/identity.api.md b/sdk/identity/identity/review/identity.api.md index d84d2d26fe50..d71ae5a5f3be 100644 --- a/sdk/identity/identity/review/identity.api.md +++ b/sdk/identity/identity/review/identity.api.md @@ -138,6 +138,13 @@ export class UsernamePasswordCredential implements TokenCredential { getToken(scopes: string | string[], options?: GetTokenOptions): Promise; } +// @public (undocumented) +export class VSCodeCredential implements TokenCredential { + constructor(options?: TokenCredentialOptions); + // (undocumented) + getToken(scopes: string | string[], options?: GetTokenOptions): Promise; +} + // (No @packageDocumentation comment for this package) diff --git a/sdk/identity/identity/src/credentials/vscodeCredential.browser.ts b/sdk/identity/identity/src/credentials/vscodeCredential.browser.ts new file mode 100644 index 000000000000..c8cc889050e8 --- /dev/null +++ b/sdk/identity/identity/src/credentials/vscodeCredential.browser.ts @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/* eslint-disable @typescript-eslint/no-unused-vars */ + +import { TokenCredential, GetTokenOptions, AccessToken } from "@azure/core-http"; +import { TokenCredentialOptions } from '../client/identityClient'; + +const BrowserNotSupportedError = new Error("VSCodeCredential is not supported in the browser."); + +export class VSCodeCredential implements TokenCredential { + constructor( + options?: TokenCredentialOptions + ) { + throw BrowserNotSupportedError; + } + + public getToken( + scopes: string | string[], + options?: GetTokenOptions + ): Promise { + throw BrowserNotSupportedError; + } +} diff --git a/sdk/identity/identity/src/credentials/vscodeCredential.ts b/sdk/identity/identity/src/credentials/vscodeCredential.ts new file mode 100644 index 000000000000..d8f278663d31 --- /dev/null +++ b/sdk/identity/identity/src/credentials/vscodeCredential.ts @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +/* eslint-disable @typescript-eslint/no-unused-vars */ + +import { TokenCredential, GetTokenOptions, AccessToken } from "@azure/core-http"; +import { TokenCredentialOptions } from '../client/identityClient'; +import * as keytar from 'keytar'; + +export class VSCodeCredential implements TokenCredential { + constructor( + options?: TokenCredentialOptions + ) { + //throw BrowserNotSupportedError; + } + + public async getToken( + scopes: string | string[], + options?: GetTokenOptions + ): Promise { + let token = await keytar.findPassword("VS Code Azure"); + if (token) { + return { token, expiresOnTimestamp: Date.now() + 2 * 60 * 1000 } + } else { + return null; + } + } +} diff --git a/sdk/identity/identity/src/index.ts b/sdk/identity/identity/src/index.ts index 35976a2e6a27..f9b80eaeeafc 100644 --- a/sdk/identity/identity/src/index.ts +++ b/sdk/identity/identity/src/index.ts @@ -24,6 +24,7 @@ export { export { DefaultAzureCredential } from "./credentials/defaultAzureCredential"; export { UsernamePasswordCredential } from "./credentials/usernamePasswordCredential"; export { AuthorizationCodeCredential } from "./credentials/authorizationCodeCredential"; +export { VSCodeCredential } from "./credentials/vscodeCredential"; export { AuthenticationError, ErrorResponse, From 0c4a0c24355de74f86b384fcbb8b39b2b6577f16 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 25 Mar 2020 12:08:22 +1300 Subject: [PATCH 2/4] Finish up VSCode credential and add to DAC. Add user-defined managed to DAC. --- sdk/identity/identity/package.json | 3 +- .../src/credentials/defaultAzureCredential.ts | 14 +++++++-- .../src/credentials/vscodeCredential.ts | 31 ++++++++++++++++--- 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/sdk/identity/identity/package.json b/sdk/identity/identity/package.json index 3dd917562a6b..6015264535d7 100644 --- a/sdk/identity/identity/package.json +++ b/sdk/identity/identity/package.json @@ -14,7 +14,8 @@ "./dist-esm/src/credentials/clientCertificateCredential.js": "./dist-esm/src/credentials/clientCertificateCredential.browser.js", "./dist-esm/src/credentials/deviceCodeCredential.js": "./dist-esm/src/credentials/deviceCodeCredential.browser.js", "./dist-esm/src/credentials/authorizationCodeCredential.js": "./dist-esm/src/credentials/authorizationCodeCredential.browser.js", - "./dist-esm/src/credentials/interactiveBrowserCredential.js": "./dist-esm/src/credentials/interactiveBrowserCredential.browser.js" + "./dist-esm/src/credentials/interactiveBrowserCredential.js": "./dist-esm/src/credentials/interactiveBrowserCredential.browser.js", + "./dist-esm/src/credentials/vscodeCredential.js": "./dist-esm/src/credentials/vscodeCredential.browser.js" }, "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", diff --git a/sdk/identity/identity/src/credentials/defaultAzureCredential.ts b/sdk/identity/identity/src/credentials/defaultAzureCredential.ts index 3db3e6a52b03..1e9842c21429 100644 --- a/sdk/identity/identity/src/credentials/defaultAzureCredential.ts +++ b/sdk/identity/identity/src/credentials/defaultAzureCredential.ts @@ -6,6 +6,7 @@ import { ChainedTokenCredential } from "./chainedTokenCredential"; import { EnvironmentCredential } from "./environmentCredential"; import { ManagedIdentityCredential } from "./managedIdentityCredential"; import { AzureCliCredential } from "./azureCliCredential"; +import { VSCodeCredential } from "./vscodeCredential"; /** * Provides a default {@link ChainedTokenCredential} configuration for @@ -25,10 +26,17 @@ export class DefaultAzureCredential extends ChainedTokenCredential { * @param options Options for configuring the client which makes the authentication request. */ constructor(tokenCredentialOptions?: TokenCredentialOptions) { + let credentials = []; + credentials.push(new EnvironmentCredential(tokenCredentialOptions)); + credentials.push(new ManagedIdentityCredential(tokenCredentialOptions)); + if (process.env.AZURE_CLIENT_ID) { + credentials.push(new ManagedIdentityCredential(process.env.AZURE_CLIENT_ID, tokenCredentialOptions)); + } + credentials.push(new AzureCliCredential()); + credentials.push(new VSCodeCredential(tokenCredentialOptions)); + super( - new EnvironmentCredential(tokenCredentialOptions), - new ManagedIdentityCredential(tokenCredentialOptions), - new AzureCliCredential() + ...credentials ); } } diff --git a/sdk/identity/identity/src/credentials/vscodeCredential.ts b/sdk/identity/identity/src/credentials/vscodeCredential.ts index d8f278663d31..51e755f946cc 100644 --- a/sdk/identity/identity/src/credentials/vscodeCredential.ts +++ b/sdk/identity/identity/src/credentials/vscodeCredential.ts @@ -4,23 +4,44 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ import { TokenCredential, GetTokenOptions, AccessToken } from "@azure/core-http"; -import { TokenCredentialOptions } from '../client/identityClient'; +import { TokenCredentialOptions, IdentityClient } from '../client/identityClient'; import * as keytar from 'keytar'; +const commonTenantId = 'common'; +const clientId = 'aebc6443-996d-45c2-90f0-388ff96faa56'; // VSC: 'aebc6443-996d-45c2-90f0-388ff96faa56' + export class VSCodeCredential implements TokenCredential { + private identityClient: IdentityClient; + constructor( options?: TokenCredentialOptions ) { - //throw BrowserNotSupportedError; + this.identityClient = new IdentityClient(); } public async getToken( scopes: string | string[], options?: GetTokenOptions ): Promise { - let token = await keytar.findPassword("VS Code Azure"); - if (token) { - return { token, expiresOnTimestamp: Date.now() + 2 * 60 * 1000 } + let scopeString = typeof scopes === "string" ? scopes : scopes.join(" "); + if (scopeString.indexOf("offline_access") < 0) { + scopeString += " offline_access"; + } + let refreshToken = await keytar.findPassword("VS Code Azure"); + if (refreshToken) { + let tokenResponse = await this.identityClient.refreshAccessToken( + commonTenantId, + clientId, + scopeString, + refreshToken, + undefined + ); + + if (tokenResponse) { + return tokenResponse.accessToken; + } else { + return null; + } } else { return null; } From 22c388c27cfec05c7471e58de8483ca2ce5ea9a5 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 26 Mar 2020 08:17:46 +1300 Subject: [PATCH 3/4] Address feedback --- .../src/credentials/vscodeCredential.ts | 24 ++++++++++++++----- sdk/identity/identity/src/index.ts | 1 - 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/sdk/identity/identity/src/credentials/vscodeCredential.ts b/sdk/identity/identity/src/credentials/vscodeCredential.ts index 51e755f946cc..da3bc947f44d 100644 --- a/sdk/identity/identity/src/credentials/vscodeCredential.ts +++ b/sdk/identity/identity/src/credentials/vscodeCredential.ts @@ -7,9 +7,15 @@ import { TokenCredential, GetTokenOptions, AccessToken } from "@azure/core-http" import { TokenCredentialOptions, IdentityClient } from '../client/identityClient'; import * as keytar from 'keytar'; -const commonTenantId = 'common'; -const clientId = 'aebc6443-996d-45c2-90f0-388ff96faa56'; // VSC: 'aebc6443-996d-45c2-90f0-388ff96faa56' +const CommonTenantId = 'common'; +const AzureAccountClientId = 'aebc6443-996d-45c2-90f0-388ff96faa56'; // VSC: 'aebc6443-996d-45c2-90f0-388ff96faa56' +const VSCodeUserName = 'VS Code Azure'; +/** + * Connect to Azure using the credential provided by the VSCode extension 'Azure Account'. + * Once the user has logged in via the extension, this credential can share the same refresh token + * that is cached by the extension. + */ export class VSCodeCredential implements TokenCredential { private identityClient: IdentityClient; @@ -26,12 +32,18 @@ export class VSCodeCredential implements TokenCredential { let scopeString = typeof scopes === "string" ? scopes : scopes.join(" "); if (scopeString.indexOf("offline_access") < 0) { scopeString += " offline_access"; - } - let refreshToken = await keytar.findPassword("VS Code Azure"); + } + + // Check to make sure the scope we get back is a valid scope + if (!scopeString.match(/^[0-9a-zA-Z-.:/]+$/)) { + throw new Error("Invalid scope was specified by the user or calling client") + } + + let refreshToken = await keytar.findPassword(VSCodeUserName); if (refreshToken) { let tokenResponse = await this.identityClient.refreshAccessToken( - commonTenantId, - clientId, + CommonTenantId, + AzureAccountClientId, scopeString, refreshToken, undefined diff --git a/sdk/identity/identity/src/index.ts b/sdk/identity/identity/src/index.ts index f9b80eaeeafc..35976a2e6a27 100644 --- a/sdk/identity/identity/src/index.ts +++ b/sdk/identity/identity/src/index.ts @@ -24,7 +24,6 @@ export { export { DefaultAzureCredential } from "./credentials/defaultAzureCredential"; export { UsernamePasswordCredential } from "./credentials/usernamePasswordCredential"; export { AuthorizationCodeCredential } from "./credentials/authorizationCodeCredential"; -export { VSCodeCredential } from "./credentials/vscodeCredential"; export { AuthenticationError, ErrorResponse, From d91ff608b52a3f3269083ee26a55b7ff1f186f9c Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 26 Mar 2020 09:34:23 +1300 Subject: [PATCH 4/4] Address feedback --- sdk/identity/identity/review/identity.api.md | 7 ------- sdk/identity/identity/src/credentials/vscodeCredential.ts | 7 ++++--- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/sdk/identity/identity/review/identity.api.md b/sdk/identity/identity/review/identity.api.md index d71ae5a5f3be..d84d2d26fe50 100644 --- a/sdk/identity/identity/review/identity.api.md +++ b/sdk/identity/identity/review/identity.api.md @@ -138,13 +138,6 @@ export class UsernamePasswordCredential implements TokenCredential { getToken(scopes: string | string[], options?: GetTokenOptions): Promise; } -// @public (undocumented) -export class VSCodeCredential implements TokenCredential { - constructor(options?: TokenCredentialOptions); - // (undocumented) - getToken(scopes: string | string[], options?: GetTokenOptions): Promise; -} - // (No @packageDocumentation comment for this package) diff --git a/sdk/identity/identity/src/credentials/vscodeCredential.ts b/sdk/identity/identity/src/credentials/vscodeCredential.ts index da3bc947f44d..48bad08070e2 100644 --- a/sdk/identity/identity/src/credentials/vscodeCredential.ts +++ b/sdk/identity/identity/src/credentials/vscodeCredential.ts @@ -30,15 +30,16 @@ export class VSCodeCredential implements TokenCredential { options?: GetTokenOptions ): Promise { let scopeString = typeof scopes === "string" ? scopes : scopes.join(" "); - if (scopeString.indexOf("offline_access") < 0) { - scopeString += " offline_access"; - } // Check to make sure the scope we get back is a valid scope if (!scopeString.match(/^[0-9a-zA-Z-.:/]+$/)) { throw new Error("Invalid scope was specified by the user or calling client") } + if (scopeString.indexOf("offline_access") < 0) { + scopeString += " offline_access"; + } + let refreshToken = await keytar.findPassword(VSCodeUserName); if (refreshToken) { let tokenResponse = await this.identityClient.refreshAccessToken(