From dde42278a70d27c02e381840cca44bc04443c7af Mon Sep 17 00:00:00 2001 From: Daniel Rodriguez Date: Thu, 18 Mar 2021 00:20:40 +0000 Subject: [PATCH 01/12] squashed the history --- common/config/rush/pnpm-lock.yaml | 114 ++++-- sdk/identity/identity/.gitignore | 1 + sdk/identity/identity/CHANGELOG.md | 14 + sdk/identity/identity/package.json | 13 +- ...ng_allows_cancelling_the_authentication.js | 5 + .../recording_authenticates.js | 111 ++++++ ...authenticates_with_sendcertificatechain.js | 111 ++++++ .../recording_supports_tracing.js | 5 + ...ng_accepts_tokencachepersistenceoptions.js | 111 ++++++ ...ates_silently_after_the_initial_request.js | 182 ++++++++++ ...ently_with_tokencachepersistenceoptions.js | 111 ++++++ ...esnt_contain_a_pemformatted_certificate.js | 5 + ...ng_allows_cancelling_the_authentication.js | 5 + .../recording_authenticates.js | 111 ++++++ .../recording_supports_tracing.js | 111 ++++++ ...ng_accepts_tokencachepersistenceoptions.js | 111 ++++++ ...ates_silently_after_the_initial_request.js | 182 ++++++++++ ...ently_with_tokencachepersistenceoptions.js | 111 ++++++ ..._setting_disableautomaticauthentication.js | 148 ++++++++ ...he_customization_of_the_prompt_callback.js | 148 ++++++++ ...rding_authenticates_with_default_values.js | 148 ++++++++ ...ding_authenticates_with_provided_values.js | 148 ++++++++ .../recording_supports_tracing.js | 148 ++++++++ ...ng_accepts_tokencachepersistenceoptions.js | 148 ++++++++ ...to_avoid_further_manual_authentications.js | 219 ++++++++++++ ...ates_silently_after_the_initial_request.js | 219 ++++++++++++ ...ently_with_tokencachepersistenceoptions.js | 219 ++++++++++++ ...ertificate_on_the_environment_variables.js | 111 ++++++ ...ent_secret_on_the_environment_variables.js | 111 ++++++ ..._usernamepassword_environment_variables.js | 111 ++++++ ...ing_with_environment_client_certificate.js | 111 ++++++ ..._tracing_with_environment_client_secret.js | 111 ++++++ ...acing_with_environment_usernamepassword.js | 111 ++++++ ...ronmentcredential_authentication_failed.js | 76 ++++ ...called_and_no_credential_was_configured.js | 5 + ...ates_silently_after_the_initial_request.js | 182 ++++++++++ ...ng_allows_cancelling_the_authentication.js | 5 + .../recording_authenticates.js | 111 ++++++ .../recording_supports_tracing.js | 111 ++++++ ...ng_accepts_tokencachepersistenceoptions.js | 111 ++++++ ...to_avoid_further_manual_authentications.js | 182 ++++++++++ ...authenticationrecord_that_is_serialized.js | 182 ++++++++++ ...ates_silently_after_the_initial_request.js | 182 ++++++++++ ...ently_with_tokencachepersistenceoptions.js | 182 ++++++++++ sdk/identity/identity/review/identity.api.md | 91 +++-- sdk/identity/identity/src/client/errors.ts | 12 +- .../identity/src/client/identityClient.ts | 101 +++++- .../identity/src/client/msalClient.ts | 215 ------------ sdk/identity/identity/src/constants.ts | 5 + .../src/credentials/chainedTokenCredential.ts | 4 +- .../clientCertificateCredential.ts | 171 ++------- .../clientCertificateCredentialOptions.ts | 6 +- .../src/credentials/clientSecretCredential.ts | 84 ++--- .../clientSecretCredentialOptions.ts | 9 + .../src/credentials/defaultAzureCredential.ts | 22 +- .../src/credentials/deviceCodeCredential.ts | 198 ++++------- .../deviceCodeCredentialOptions.ts | 49 +++ .../src/credentials/environmentCredential.ts | 79 ++--- .../interactiveBrowserCredential.browser.ts | 177 ++++------ .../interactiveBrowserCredential.ts | 267 ++++---------- .../interactiveBrowserCredentialOptions.ts | 136 +++----- .../interactiveCredentialOptions.ts | 28 ++ .../managedIdentityCredential/imdsMsi.ts | 2 +- .../managedIdentityCredential/index.ts | 2 +- .../credentials/msalBrowser/msalAuthCode.ts | 265 -------------- .../src/credentials/msalBrowser/msalCommon.ts | 45 --- .../credentials/msalBrowser/msalImplicit.ts | 232 ------------- .../persistentCredentialOptions.ts | 20 ++ .../credentials/usernamePasswordCredential.ts | 124 +++---- .../usernamePasswordCredentialOptions.ts | 9 + sdk/identity/identity/src/index.ts | 34 +- .../src/msal/browserFlows/browserCommon.ts | 156 +++++++++ .../src/msal/browserFlows/msalAuthCode.ts | 207 +++++++++++ .../src/msal/browserFlows/msalImplicit.ts | 203 +++++++++++ sdk/identity/identity/src/msal/credentials.ts | 40 +++ sdk/identity/identity/src/msal/errors.ts | 14 + sdk/identity/identity/src/msal/flows.ts | 47 +++ .../msal/nodeFlows/msalClientCertificate.ts | 119 +++++++ .../src/msal/nodeFlows/msalClientSecret.ts | 43 +++ .../src/msal/nodeFlows/msalDeviceCode.ts | 52 +++ .../src/msal/nodeFlows/msalOpenBrowser.ts | 179 ++++++++++ .../msal/nodeFlows/msalUsernamePassword.ts | 49 +++ .../identity/src/msal/nodeFlows/nodeCommon.ts | 255 ++++++++++++++ .../identity/src/msal/transformations.ts | 57 +++ sdk/identity/identity/src/msal/types.ts | 74 ++++ sdk/identity/identity/src/msal/utils.ts | 158 +++++++++ .../src/tokenCache/InMemoryPersistence.ts | 43 +++ .../TokenCachePersistence.browser.ts | 23 ++ .../src/tokenCache/TokenCachePersistence.ts | 44 +++ .../src/tokenCache/persistencePlatforms.ts | 182 ++++++++++ sdk/identity/identity/src/tokenCache/types.ts | 42 +++ .../identity/src/util/resolveTenantId.ts | 24 ++ sdk/identity/identity/src/util/tracing.ts | 45 +++ sdk/identity/identity/test/assets/cert.pem | 48 +++ .../test/internal/identityClient.spec.ts | 14 +- .../node/clientCertificateCredential.spec.ts | 145 ++++++++ .../node/clientSecretCredential.spec.ts | 112 ++++++ .../node/deviceCodeCredential.spec.ts | 165 +++++++++ .../node/environmentCredential.spec.ts | 51 +++ .../node/managedIdentityCredential.spec.ts | 21 +- .../node/usernamePasswordCredential.spec.ts | 239 +++++++++++++ sdk/identity/identity/test/manual/nodeTest.js | 43 +++ .../identity/test/manual/nodeTestSilent.js | 66 ++++ sdk/identity/identity/test/msalTestUtils.ts | 133 +++++++ .../public/clientSecretCredential.spec.ts | 23 -- .../node/clientCertificateCredential.spec.ts | 272 +++++---------- .../node/clientSecretCredential.spec.ts | 85 +++++ .../public/node/deviceCodeCredential.spec.ts | 133 +++++++ .../public/node/environmentCredential.spec.ts | 327 ++++++++++-------- .../node/usernamePasswordCredential.spec.ts | 87 +++++ .../public/usernamePasswordCredential.spec.ts | 48 --- 111 files changed, 9291 insertions(+), 2118 deletions(-) create mode 100644 sdk/identity/identity/recordings/node/clientcertificatecredential/recording_allows_cancelling_the_authentication.js create mode 100644 sdk/identity/identity/recordings/node/clientcertificatecredential/recording_authenticates.js create mode 100644 sdk/identity/identity/recordings/node/clientcertificatecredential/recording_authenticates_with_sendcertificatechain.js create mode 100644 sdk/identity/identity/recordings/node/clientcertificatecredential/recording_supports_tracing.js create mode 100644 sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_accepts_tokencachepersistenceoptions.js create mode 100644 sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_authenticates_silently_after_the_initial_request.js create mode 100644 sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js create mode 100644 sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_throws_when_given_a_file_that_doesnt_contain_a_pemformatted_certificate.js create mode 100644 sdk/identity/identity/recordings/node/clientsecretcredential/recording_allows_cancelling_the_authentication.js create mode 100644 sdk/identity/identity/recordings/node/clientsecretcredential/recording_authenticates.js create mode 100644 sdk/identity/identity/recordings/node/clientsecretcredential/recording_supports_tracing.js create mode 100644 sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_accepts_tokencachepersistenceoptions.js create mode 100644 sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_authenticates_silently_after_the_initial_request.js create mode 100644 sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js create mode 100644 sdk/identity/identity/recordings/node/devicecodecredential/recording_allows_setting_disableautomaticauthentication.js create mode 100644 sdk/identity/identity/recordings/node/devicecodecredential/recording_authenticates_and_allows_the_customization_of_the_prompt_callback.js create mode 100644 sdk/identity/identity/recordings/node/devicecodecredential/recording_authenticates_with_default_values.js create mode 100644 sdk/identity/identity/recordings/node/devicecodecredential/recording_authenticates_with_provided_values.js create mode 100644 sdk/identity/identity/recordings/node/devicecodecredential/recording_supports_tracing.js create mode 100644 sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_accepts_tokencachepersistenceoptions.js create mode 100644 sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_allows_passing_an_authenticationrecord_to_avoid_further_manual_authentications.js create mode 100644 sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_authenticates_silently_after_the_initial_request.js create mode 100644 sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js create mode 100644 sdk/identity/identity/recordings/node/environmentcredential/recording_authenticates_with_a_client_certificate_on_the_environment_variables.js create mode 100644 sdk/identity/identity/recordings/node/environmentcredential/recording_authenticates_with_a_client_secret_on_the_environment_variables.js create mode 100644 sdk/identity/identity/recordings/node/environmentcredential/recording_finds_and_uses_client_usernamepassword_environment_variables.js create mode 100644 sdk/identity/identity/recordings/node/environmentcredential/recording_supports_tracing_with_environment_client_certificate.js create mode 100644 sdk/identity/identity/recordings/node/environmentcredential/recording_supports_tracing_with_environment_client_secret.js create mode 100644 sdk/identity/identity/recordings/node/environmentcredential/recording_supports_tracing_with_environment_usernamepassword.js create mode 100644 sdk/identity/identity/recordings/node/environmentcredential/recording_throws_an_authenticationerror_when_gettoken_is_called_and_environmentcredential_authentication_failed.js create mode 100644 sdk/identity/identity/recordings/node/environmentcredential/recording_throws_an_credentialunavailable_when_gettoken_is_called_and_no_credential_was_configured.js create mode 100644 sdk/identity/identity/recordings/node/environmentcredential_internal/recording_authenticates_silently_after_the_initial_request.js create mode 100644 sdk/identity/identity/recordings/node/usernamepasswordcredential/recording_allows_cancelling_the_authentication.js create mode 100644 sdk/identity/identity/recordings/node/usernamepasswordcredential/recording_authenticates.js create mode 100644 sdk/identity/identity/recordings/node/usernamepasswordcredential/recording_supports_tracing.js create mode 100644 sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_accepts_tokencachepersistenceoptions.js create mode 100644 sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_allows_passing_an_authenticationrecord_to_avoid_further_manual_authentications.js create mode 100644 sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_allows_working_with_an_authenticationrecord_that_is_serialized.js create mode 100644 sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_authenticates_silently_after_the_initial_request.js create mode 100644 sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js delete mode 100644 sdk/identity/identity/src/client/msalClient.ts create mode 100644 sdk/identity/identity/src/credentials/clientSecretCredentialOptions.ts create mode 100644 sdk/identity/identity/src/credentials/deviceCodeCredentialOptions.ts create mode 100644 sdk/identity/identity/src/credentials/interactiveCredentialOptions.ts delete mode 100644 sdk/identity/identity/src/credentials/msalBrowser/msalAuthCode.ts delete mode 100644 sdk/identity/identity/src/credentials/msalBrowser/msalCommon.ts delete mode 100644 sdk/identity/identity/src/credentials/msalBrowser/msalImplicit.ts create mode 100644 sdk/identity/identity/src/credentials/persistentCredentialOptions.ts create mode 100644 sdk/identity/identity/src/credentials/usernamePasswordCredentialOptions.ts create mode 100644 sdk/identity/identity/src/msal/browserFlows/browserCommon.ts create mode 100644 sdk/identity/identity/src/msal/browserFlows/msalAuthCode.ts create mode 100644 sdk/identity/identity/src/msal/browserFlows/msalImplicit.ts create mode 100644 sdk/identity/identity/src/msal/credentials.ts create mode 100644 sdk/identity/identity/src/msal/errors.ts create mode 100644 sdk/identity/identity/src/msal/flows.ts create mode 100644 sdk/identity/identity/src/msal/nodeFlows/msalClientCertificate.ts create mode 100644 sdk/identity/identity/src/msal/nodeFlows/msalClientSecret.ts create mode 100644 sdk/identity/identity/src/msal/nodeFlows/msalDeviceCode.ts create mode 100644 sdk/identity/identity/src/msal/nodeFlows/msalOpenBrowser.ts create mode 100644 sdk/identity/identity/src/msal/nodeFlows/msalUsernamePassword.ts create mode 100644 sdk/identity/identity/src/msal/nodeFlows/nodeCommon.ts create mode 100644 sdk/identity/identity/src/msal/transformations.ts create mode 100644 sdk/identity/identity/src/msal/types.ts create mode 100644 sdk/identity/identity/src/msal/utils.ts create mode 100644 sdk/identity/identity/src/tokenCache/InMemoryPersistence.ts create mode 100644 sdk/identity/identity/src/tokenCache/TokenCachePersistence.browser.ts create mode 100644 sdk/identity/identity/src/tokenCache/TokenCachePersistence.ts create mode 100644 sdk/identity/identity/src/tokenCache/persistencePlatforms.ts create mode 100644 sdk/identity/identity/src/tokenCache/types.ts create mode 100644 sdk/identity/identity/src/util/resolveTenantId.ts create mode 100644 sdk/identity/identity/test/assets/cert.pem create mode 100644 sdk/identity/identity/test/internal/node/clientCertificateCredential.spec.ts create mode 100644 sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts create mode 100644 sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts create mode 100644 sdk/identity/identity/test/internal/node/environmentCredential.spec.ts create mode 100644 sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts create mode 100644 sdk/identity/identity/test/manual/nodeTest.js create mode 100644 sdk/identity/identity/test/manual/nodeTestSilent.js create mode 100644 sdk/identity/identity/test/msalTestUtils.ts delete mode 100644 sdk/identity/identity/test/public/clientSecretCredential.spec.ts create mode 100644 sdk/identity/identity/test/public/node/clientSecretCredential.spec.ts create mode 100644 sdk/identity/identity/test/public/node/deviceCodeCredential.spec.ts create mode 100644 sdk/identity/identity/test/public/node/usernamePasswordCredential.spec.ts delete mode 100644 sdk/identity/identity/test/public/usernamePasswordCredential.spec.ts diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index f6926d0ae2f1..e9a5d31dd5df 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -396,6 +396,14 @@ packages: node: '>=0.8.0' resolution: integrity: sha512-Zyus+skNaVWL5fXfSjC17c94XZ95Z3a+bZc7YKkP26KT3Dj26jbfz2oT9KJxAT4XVtH/1WPY/fPFeEFTRvytJQ== + /@azure/msal-common/1.7.2: + dependencies: + debug: 4.3.1 + dev: false + engines: + node: '>=0.8.0' + resolution: + integrity: sha512-3/voCdFKONENX+5tMrNOBSrVJb6NbE7YB8vc4FZ/4ZbjpK7GVtq9Bu1MW+HZhrmsUzSF/joHx0ZIJDYIequ/jg== /@azure/msal-common/2.1.0: dependencies: debug: 4.3.1 @@ -412,6 +420,18 @@ packages: node: '>=0.8.0' resolution: integrity: sha512-Z6FiDV+uWUZ4jcchRciKYYYKRWOc0sh/UaF5evfx7lXEp/8+KxO7cY1efgD7VOK75FkpRI5YyUzZAdX7I7sTAg== + /@azure/msal-node-extensions/1.0.0-alpha.6: + dependencies: + '@azure/msal-common': 1.7.2 + bindings: 1.5.0 + keytar: 7.0.0 + nan: 2.14.2 + dev: false + engines: + node: '>=10' + requiresBuild: true + resolution: + integrity: sha512-fVufHc02C+daYOMAHBnE998abB4qUIeJ9gmTxmSelHhGfBGvvzMbCohCu4sTlSVDKUndF3yD/Nxvw/cEtpcZKg== /@azure/msal-node/1.0.0-beta.6: dependencies: '@azure/msal-common': 4.0.2 @@ -432,6 +452,17 @@ packages: debug: '*' resolution: integrity: sha512-ZQI11Uz1j0HJohb9JZLRD8z0moVcPks1AFW4Q/Gcl67+QvH4aKEJti7fjCcipEEZYb/qzLSO8U6IZgPYytsiJQ== + /@azure/msal-node/1.0.1: + dependencies: + '@azure/msal-common': 4.0.2 + axios: 0.21.1 + jsonwebtoken: 8.5.1 + uuid: 8.3.2 + dev: false + engines: + node: 10 || 12 || 14 + resolution: + integrity: sha512-6MEI4XItcvUE9Zns1W3aXt7Bzzhhitq56uhg36KzuaSWRxW+Zuu4wx4x5iJjv4WP8DLGeWpaWoRxN3UNXyQHfQ== /@babel/code-frame/7.12.11: dependencies: '@babel/highlight': 7.13.10 @@ -1535,7 +1566,6 @@ packages: integrity: sha512-P009oYkeHyU742iSZJzZZywj4QRJdnTWffaKuJQLablCZ1uz6/cW4yaRgcDaoQ+uwOxxnt0gRUcwfsNP2ri0gw== /aproba/1.2.0: dev: false - optional: true resolution: integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== /archy/1.0.0: @@ -1547,7 +1577,6 @@ packages: delegates: 1.0.0 readable-stream: 2.3.7 dev: false - optional: true resolution: integrity: sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w== /arg/4.1.3: @@ -1775,6 +1804,12 @@ packages: node: '>=8' resolution: integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + /bindings/1.5.0: + dependencies: + file-uri-to-path: 1.0.0 + dev: false + resolution: + integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== /bl/4.1.0: dependencies: buffer: 5.7.1 @@ -2085,7 +2120,6 @@ packages: dev: false engines: node: '>=0.10.0' - optional: true resolution: integrity: sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= /color-convert/1.9.3: @@ -2169,7 +2203,6 @@ packages: integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== /console-control-strings/1.1.0: dev: false - optional: true resolution: integrity: sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4= /contains-path/0.1.0: @@ -2403,7 +2436,6 @@ packages: dev: false engines: node: '>=8' - optional: true resolution: integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== /deep-eql/3.0.1: @@ -2418,7 +2450,6 @@ packages: dev: false engines: node: '>=4.0.0' - optional: true resolution: integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== /deep-freeze/0.0.1: @@ -2473,7 +2504,6 @@ packages: integrity: sha1-3zrhmayt+31ECqrgsp4icrJOxhk= /delegates/1.0.0: dev: false - optional: true resolution: integrity: sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= /depd/1.1.2: @@ -2491,7 +2521,6 @@ packages: engines: node: '>=0.10' hasBin: true - optional: true resolution: integrity: sha1-+hN8S9aY7fVc1c0CrFWfkaTEups= /di/0.0.1: @@ -3018,7 +3047,6 @@ packages: dev: false engines: node: '>=6' - optional: true resolution: integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== /expand-tilde/2.0.2: @@ -3419,7 +3447,6 @@ packages: strip-ansi: 3.0.1 wide-align: 1.1.3 dev: false - optional: true resolution: integrity: sha1-LANAXHU4w51+s3sxcCLjJfsBi/c= /gensync/1.0.0-beta.2: @@ -3477,7 +3504,6 @@ packages: integrity: sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= /github-from-package/0.0.0: dev: false - optional: true resolution: integrity: sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= /glob-parent/5.1.2: @@ -3660,7 +3686,6 @@ packages: integrity: sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== /has-unicode/2.0.1: dev: false - optional: true resolution: integrity: sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= /has/1.0.3: @@ -3985,7 +4010,6 @@ packages: dev: false engines: node: '>=0.10.0' - optional: true resolution: integrity: sha1-754xOG8DGn8NZDr4L95QxFfvAMs= /is-fullwidth-code-point/2.0.0: @@ -4641,6 +4665,14 @@ packages: debug: '*' resolution: integrity: sha512-pCB8eNxGgdIdZeC885rbhZ/VyuOPNHUIDNL9EaaMf1NVzpvTjMO8a7zRTn51ZJhOOOxCSpalUdT1A8x76LyVqg== + /keytar/7.0.0: + dependencies: + node-addon-api: 3.1.0 + prebuild-install: 5.3.5 + dev: false + requiresBuild: true + resolution: + integrity: sha512-uvmdb5ZE2NgegcUDrmhutI9BUh+bTbt8+bwPliOMiLiWmrV76Tfg6DyI7Ud903a/4xlkJpKGnR0TyRpRyFOc3A== /keytar/7.4.0: dependencies: node-addon-api: 3.1.0 @@ -4978,7 +5010,6 @@ packages: dev: false engines: node: '>=8' - optional: true resolution: integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== /min-document/2.19.0: @@ -5101,6 +5132,10 @@ packages: node: '>=0.8.0' resolution: integrity: sha512-tPwgKoWBRf+d2YG4CgCm2C9MiRUwzdn2aOwlLtaBCj3ekM1afkWMKbAsbKuuWSdoMPhhxrvALIOV0FfX3WKJlg== + /nan/2.14.2: + dev: false + resolution: + integrity: sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== /nanoid/3.1.20: dev: false engines: @@ -5110,7 +5145,6 @@ packages: integrity: sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== /napi-build-utils/1.0.2: dev: false - optional: true resolution: integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg== /natural-compare/1.4.0: @@ -5166,7 +5200,6 @@ packages: dependencies: semver: 5.7.1 dev: false - optional: true resolution: integrity: sha512-smhrivuPqEM3H5LmnY3KU6HfYv0u4QklgAxfFyRNujKUzbUcYZ+Jc2EhukB9SRcD2VpqhxM7n/MIcp1Ua1/JMg== /node-abort-controller/1.1.0: @@ -5175,7 +5208,6 @@ packages: integrity: sha512-dEYmUqjtbivotqjraOe8UvhT/poFfog1BQRNsZm/MSEDDESk2cQ1tvD8kGyuN07TM/zoW+n42odL8zTeJupYdQ== /node-addon-api/3.1.0: dev: false - optional: true resolution: integrity: sha512-flmrDNB06LIl5lywUz7YlNGZH/5p0M7W28k8hzd9Lshtdh1wshD2Y+U4h9LD6KObOy1f+fEVdgprPrEymjM5uw== /node-environment-flags/1.0.6: @@ -5197,7 +5229,6 @@ packages: integrity: sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== /noop-logger/0.1.1: dev: false - optional: true resolution: integrity: sha1-lKKxYzxPExdVMAfYlm/Q6EG2pMI= /normalize-package-data/2.5.0: @@ -5255,14 +5286,12 @@ packages: gauge: 2.7.4 set-blocking: 2.0.0 dev: false - optional: true resolution: integrity: sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== /number-is-nan/1.0.1: dev: false engines: node: '>=0.10.0' - optional: true resolution: integrity: sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= /nyc/14.1.1: @@ -5692,6 +5721,29 @@ packages: node: '>=4' resolution: integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== + /prebuild-install/5.3.5: + dependencies: + detect-libc: 1.0.3 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.5 + mkdirp: 0.5.5 + napi-build-utils: 1.0.2 + node-abi: 2.21.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.1.1 + tunnel-agent: 0.6.0 + which-pm-runs: 1.0.0 + dev: false + engines: + node: '>=6' + hasBin: true + resolution: + integrity: sha512-YmMO7dph9CYKi5IR/BzjOJlRzpxGGVo1EsLSUZ0mt/Mq0HWZIHOKHHcHdT69yG54C9m6i45GpItwRHpk0Py7Uw== /prebuild-install/6.0.1: dependencies: detect-libc: 1.0.3 @@ -5942,7 +5994,6 @@ packages: strip-json-comments: 2.0.1 dev: false hasBin: true - optional: true resolution: integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== /read-pkg-up/2.0.0: @@ -6456,7 +6507,6 @@ packages: integrity: sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== /simple-concat/1.0.1: dev: false - optional: true resolution: integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== /simple-get/3.1.0: @@ -6465,7 +6515,6 @@ packages: once: 1.4.0 simple-concat: 1.0.1 dev: false - optional: true resolution: integrity: sha512-bCR6cP+aTdScaQCnQKbPKtJOKDp/hj9EDLJo3Nw4y1QksqaovlW/bnptB6/c1e+qmNIDHRK+oXFDdEqBT8WzUA== /sinon/9.2.4: @@ -6751,7 +6800,6 @@ packages: dev: false engines: node: '>=0.10.0' - optional: true resolution: integrity: sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= /string-width/2.1.1: @@ -7380,7 +7428,6 @@ packages: integrity: sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= /which-pm-runs/1.0.0: dev: false - optional: true resolution: integrity: sha1-Zws6+8VS4LVd9rd4DKdGFfI60cs= /which-typed-array/1.1.4: @@ -8056,7 +8103,7 @@ packages: dev: false name: '@rush-temp/communication-administration' resolution: - integrity: sha512-whG+7mmbnOf6EaKIV++fJN9KVUD3ILO3cUHOLqAQKDlFtmj69QDy6qsbHyTyIOGsKSHHB8KyPPZItGgULngPpw== + integrity: sha512-DDT0w71HvXCS7Ww3gM2HO5GXtm8CtitIpHAtGU00lKdFohfzINn7onva3zBKyvLfVMTvv+1TsRrXti5UTu3biA== tarball: file:projects/communication-administration.tgz version: 0.0.0 file:projects/communication-chat.tgz: @@ -8113,7 +8160,7 @@ packages: dev: false name: '@rush-temp/communication-chat' resolution: - integrity: sha512-GeSjm0QX1D7rgFhTlH7X8STkmD5h5fP3S6OEOyPY/pRbVHSbf10Qe1yRYrhroA1Ziyh2MCERCkG/0uuah4wg8w== + integrity: sha512-P3psB6ukRXVXT/9OHJ+e1euw1S4HOuFBbvPhqGIMlBZuiBXyEqicw6PaG4oVKgjdGJtaOTyvY3E6OXW0hm+9/A== tarball: file:projects/communication-chat.tgz version: 0.0.0 file:projects/communication-common.tgz: @@ -8223,7 +8270,7 @@ packages: dev: false name: '@rush-temp/communication-identity' resolution: - integrity: sha512-Nvy14vvoy4ekaOHptUflWXpEeGuYVNZr6NWBe2Xue1qc4gf9ovl/t41QqlJcpSvIUE8UzC3eH1arEytc8oWaMA== + integrity: sha512-y1ywIYptiMuH4NREzzPxXJQg6Gt+zSwp3WJDUGEu99tQcpyToc8oz+4l1FBgq0Dlnxzg0RINJd90sc/SUGisUw== tarball: file:projects/communication-identity.tgz version: 0.0.0 file:projects/communication-phone-numbers.tgz: @@ -8279,7 +8326,7 @@ packages: dev: false name: '@rush-temp/communication-phone-numbers' resolution: - integrity: sha512-4hBuz3yoYH2R0qzZGW+TdpIAvEkGL6pWaHdaNI3PA2ikBCKYuHsCZarHgy8Vx//tsLEGb9aJcRgeX68U98BKxQ== + integrity: sha512-KNLkg6N1OQAvBSsdOc/XTNn5DV8mz2wWvq2X28FhvYPEbIl1R2L5mw6uvYBpVzrC+/QHo5l2ET+MlEudm+c+PQ== tarball: file:projects/communication-phone-numbers.tgz version: 0.0.0 file:projects/communication-sms.tgz: @@ -8333,7 +8380,7 @@ packages: dev: false name: '@rush-temp/communication-sms' resolution: - integrity: sha512-OG8/pwUCKSVDjLzSgPNGBtZdRvdDdMiq2BJ1WSwjCZSAwkhsIAwo2vgh6ZwwAntxJ6QnCTtO6nZ4Vw1fdjDtRA== + integrity: sha512-ctnXT8NoEAG2J/IqdPc0qYMmRqtBP6Ceg0owV0b4gVT/AhWURQGDW8nPAXzmeqQWqr5QxfJt4CMnyZchw5gB2w== tarball: file:projects/communication-sms.tgz version: 0.0.0 file:projects/container-registry.tgz: @@ -9377,7 +9424,9 @@ packages: file:projects/identity.tgz: dependencies: '@azure/msal-browser': 2.9.0 - '@azure/msal-node': 1.0.0-beta.6 + '@azure/msal-common': 4.0.2 + '@azure/msal-node': 1.0.1 + '@azure/msal-node-extensions': 1.0.0-alpha.6 '@microsoft/api-extractor': 7.7.11 '@opentelemetry/api': 0.10.2 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 @@ -9395,6 +9444,7 @@ packages: assert: 1.5.0 axios: 0.21.1 cross-env: 7.0.3 + dotenv: 8.2.0 eslint: 7.21.0 events: 3.3.0 inherits: 2.0.4 @@ -9432,7 +9482,7 @@ packages: optionalDependencies: keytar: 7.4.0 resolution: - integrity: sha512-F95wTEAG36GwwaMseI9locyPZ0vngMXmrjNMo4hFv4MIyL/PLzidKuEJxdCW10hJp34x8WSBUFIsh2YOFPsI8A== + integrity: sha512-QtXUN2R3w1YEWgQLFt4o/poL1YBt0mLNerFbuQBEbwSVP6KWM1uKUelXhY6o8L0bzQln1kPVdNI8yS7cZ4FmwQ== tarball: file:projects/identity.tgz version: 0.0.0 file:projects/iot-device-update.tgz: diff --git a/sdk/identity/identity/.gitignore b/sdk/identity/identity/.gitignore index 93cf72c40335..3007b678679e 100644 --- a/sdk/identity/identity/.gitignore +++ b/sdk/identity/identity/.gitignore @@ -1 +1,2 @@ src/**/*.js +!test/assets/cert.pem diff --git a/sdk/identity/identity/CHANGELOG.md b/sdk/identity/identity/CHANGELOG.md index 4987b707e34f..1357e0e42f39 100644 --- a/sdk/identity/identity/CHANGELOG.md +++ b/sdk/identity/identity/CHANGELOG.md @@ -4,10 +4,24 @@ This release continues with the changes from `1.2.4` and `1.2.4-beta.1`. +- The `getToken` methods will now never return `null`. If a token is not available, we will throw. - `DefaultAzureCredential`'s implementation for browsers was simplified to throw a simple error instead of trying credentials that were already not supported for the browser. - Breaking Change: `InteractiveBrowserCredential` for the browser now requires the client ID to be provided. - Documentation was added to elaborate on how to configure an AAD application to support `InteractiveBrowserCredential`. - Replaced the use of the 'express' module with a Node-native http server, shrinking the resulting identity module considerably +- Updated `@azure/msal-node-extensions` to [1.0.0-alpha.6](https://www.npmjs.com/package/@azure/msal-node-extensions/v/1.0.0-alpha.6). +- Refactored our use of MSAL to better centralize the handling of inputs, outputs and errors. +- Migrated the `InteractiveBrowserCredential`, `DeviceCodeCredential`, `ClientSecretCredential`, `ClientCertificateCredential` and `UsernamePasswordCredential` to the latest MSAL. + - This update improves caching of tokens, significantly reducing the number of network requests. +- Credentials `InteractiveBrowserCredential`, `DeviceCodeCredential` and `UsernamePasswordCredential` now can: + - Receive an `authenticationRecord` from a previous authentication on their constructors, which skips the initial request altogether. + - Receive a `disableAutomaticAuthentication` setting on the constructor, which stops `getToken` from requesting the user to authenticate manually. + - An `authenticate()` method has been added besides the `getToken()` method. + - The `authenticate()` method returns an `AuthenticationRecord` which can be serialized into strings with their property `serialize()`. To later deserialize from string into an `AuthenticationRecord`, use the new function `deserializeAuthenticationRecord()`. + - If `disableAutomaticAuthentication` is set on the constructor of these credentials, developers can now control when to manually authenticate by calling to these credential's `authenticate()` method. +- `DeviceCodeCredential` now can receive its optional parameters as a single parameter object. +- `InteractiveBrowserCredential` now only has `loginStyle` and `flow` in the optional parameters when the credential is bundled for browsers. This reflects the intended behavior. +- Removed the `postLogoutRedirectUri` from the optional properties of the `InteractiveBrowserCredential`. ## 1.2.4 (2021-03-08) diff --git a/sdk/identity/identity/package.json b/sdk/identity/identity/package.json index eae1d033b50a..7398c6ced07e 100644 --- a/sdk/identity/identity/package.json +++ b/sdk/identity/identity/package.json @@ -16,7 +16,8 @@ "./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/visualStudioCodeCredential.js": "./dist-esm/src/credentials/visualStudioCodeCredential.browser.js", - "./dist-esm/src/util/authHostEnv.js": "./dist-esm/src/util/authHostEnv.browser.js" + "./dist-esm/src/util/authHostEnv.js": "./dist-esm/src/util/authHostEnv.browser.js", + "./dist-esm/src/tokenCache/TokenCachePersistence.js": "./dist-esm/src/tokenCache/TokenCachePersistence.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", @@ -43,7 +44,7 @@ "test:node": "npm run clean && npm run build:test && npm run unit-test:node && npm run integration-test:node", "test": "npm run clean && npm run build:test && npm run unit-test && npm run integration-test", "unit-test:browser": "karma start", - "unit-test:node": "mocha test-dist/**/*.js --reporter ../../../common/tools/mocha-multi-reporter.js", + "unit-test:node": "mocha --require source-map-support/register --reporter ../../../common/tools/mocha-multi-reporter.js --timeout 180000 --full-trace test-dist/**/*.js", "unit-test": "npm run unit-test:node && npm run unit-test:browser", "docs": "typedoc --excludePrivate --excludeNotExported --excludeExternals --stripInternal --mode file --out ./dist/docs ./src" }, @@ -83,7 +84,10 @@ "@azure/core-http": "^1.2.0", "@azure/core-tracing": "1.0.0-preview.10", "@azure/logger": "^1.0.0", - "@azure/msal-node": "1.0.0-beta.6", + "@azure/abort-controller": "^1.0.0", + "@azure/msal-common": "~4.0.1", + "@azure/msal-node": "^1.0.0", + "@azure/msal-node-extensions": "~1.0.0-alpha.6", "@azure/msal-browser": "2.9.0", "@opentelemetry/api": "^0.10.2", "@types/stoppable": "^1.1.0", @@ -102,8 +106,8 @@ }, "devDependencies": { "@azure/eslint-plugin-azure-sdk": "^3.0.0", - "@azure/abort-controller": "^1.0.0", "@azure/dev-tool": "^1.0.0", + "@azure/test-utils-recorder": "^1.0.0", "@microsoft/api-extractor": "7.7.11", "@rollup/plugin-commonjs": "11.0.2", "@rollup/plugin-json": "^4.0.0", @@ -117,6 +121,7 @@ "@types/uuid": "^8.0.0", "assert": "^1.4.1", "cross-env": "^7.0.2", + "dotenv": "^8.2.0", "eslint": "^7.15.0", "inherits": "^2.0.3", "karma": "^6.2.0", diff --git a/sdk/identity/identity/recordings/node/clientcertificatecredential/recording_allows_cancelling_the_authentication.js b/sdk/identity/identity/recordings/node/clientcertificatecredential/recording_allows_cancelling_the_authentication.js new file mode 100644 index 000000000000..e871fa437626 --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientcertificatecredential/recording_allows_cancelling_the_authentication.js @@ -0,0 +1,5 @@ +let nock = require('nock'); + +module.exports.hash = "f3bac15d185f87a2651bd519dec603ee"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} diff --git a/sdk/identity/identity/recordings/node/clientcertificatecredential/recording_authenticates.js b/sdk/identity/identity/recordings/node/clientcertificatecredential/recording_authenticates.js new file mode 100644 index 000000000000..dc34362868d5 --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientcertificatecredential/recording_authenticates.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "38da4cb7a62135aa74a632f2c43db5f5"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '2f3aa91d-85fa-46e5-9332-53f7527d5c00', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:25 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:25 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'ed427c4c-2412-4779-b953-022030bb9f00', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:25 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:25 GMT', + 'Content-Length', + '1651' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=client_credentials&client-request-id=client-request-id&client_assertion=client_assertion&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer") + .reply(200, {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '1313', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '765f6b5f-61c2-4629-b6b7-6f0949a92e01', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:25 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:25 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/clientcertificatecredential/recording_authenticates_with_sendcertificatechain.js b/sdk/identity/identity/recordings/node/clientcertificatecredential/recording_authenticates_with_sendcertificatechain.js new file mode 100644 index 000000000000..d53d00af4a1b --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientcertificatecredential/recording_authenticates_with_sendcertificatechain.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "23ee9e07c6e44e077c762577ff59938f"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '08cf43c1-6add-4ae2-b576-bc03560d0a01', + 'x-ms-ests-server', + '2.1.11530.17 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:25 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:25 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'cf8d2a57-e8e2-4030-afdf-9aa68bb8cb00', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:25 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:25 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=client_credentials&client-request-id=client-request-id&client_assertion=client_assertion&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer") + .reply(200, {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '1313', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '150c7035-7fe9-4e90-a0c9-00b7ffa02901', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:25 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:25 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/clientcertificatecredential/recording_supports_tracing.js b/sdk/identity/identity/recordings/node/clientcertificatecredential/recording_supports_tracing.js new file mode 100644 index 000000000000..58489bfe3a21 --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientcertificatecredential/recording_supports_tracing.js @@ -0,0 +1,5 @@ +let nock = require('nock'); + +module.exports.hash = "418f1bb706566e72722e45cb64f3b306"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} diff --git a/sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_accepts_tokencachepersistenceoptions.js b/sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_accepts_tokencachepersistenceoptions.js new file mode 100644 index 000000000000..03be6c573e3b --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_accepts_tokencachepersistenceoptions.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "c4c4d653b316a4584965ac2cc04ee112"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '83fa69fb-a5df-4f72-a626-c44659a01100', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:27 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:26 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '2e208320-1bea-4184-bab2-76590bd56901', + 'x-ms-ests-server', + '2.1.11530.17 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:27 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:26 GMT', + 'Content-Length', + '1651' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=client_credentials&client-request-id=client-request-id&client_assertion=client_assertion&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer") + .reply(200, {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '1313', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '89281210-eaec-4b3c-926b-160637c92f01', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:27 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:26 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_authenticates_silently_after_the_initial_request.js b/sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_authenticates_silently_after_the_initial_request.js new file mode 100644 index 000000000000..4b527fb69182 --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_authenticates_silently_after_the_initial_request.js @@ -0,0 +1,182 @@ +let nock = require('nock'); + +module.exports.hash = "c264be8e2eaa25489727b89fb91521be"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'adc05f6b-e520-4578-9d86-9db35cd3e700', + 'x-ms-ests-server', + '2.1.11530.17 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:26 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:26 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '705eb9e8-034c-4f57-94a5-c09b375ab000', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:26 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:26 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=client_credentials&client-request-id=client-request-id&client_assertion=client_assertion&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer") + .reply(200, {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '309a90cc-7717-49e8-8f86-70cc1e35f100', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:26 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:26 GMT', + 'Content-Length', + '1313' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '0d3fb9b4-5445-48c0-8325-118fd5d90800', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:26 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:26 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '22494221-4f30-4cbd-8906-fb6a65eae200', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:27 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:26 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js b/sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js new file mode 100644 index 000000000000..b7ba30a6ac03 --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "fb2298b013667ff1dcce629e74663574"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '94f7535d-b57b-4cce-be6f-768beed9ae00', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:27 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:26 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '4bf38e9a-f2e6-4aff-a109-7c4641052601', + 'x-ms-ests-server', + '2.1.11530.17 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:27 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:26 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=client_credentials&client-request-id=client-request-id&client_assertion=client_assertion&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer") + .reply(200, {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '1313', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '0782b585-9142-4935-a901-5e688b0a1f01', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:27 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:26 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_throws_when_given_a_file_that_doesnt_contain_a_pemformatted_certificate.js b/sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_throws_when_given_a_file_that_doesnt_contain_a_pemformatted_certificate.js new file mode 100644 index 000000000000..0784b40c686c --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientcertificatecredential_internal/recording_throws_when_given_a_file_that_doesnt_contain_a_pemformatted_certificate.js @@ -0,0 +1,5 @@ +let nock = require('nock'); + +module.exports.hash = "c100a894aa2deeedb7bb51aef7bef444"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} diff --git a/sdk/identity/identity/recordings/node/clientsecretcredential/recording_allows_cancelling_the_authentication.js b/sdk/identity/identity/recordings/node/clientsecretcredential/recording_allows_cancelling_the_authentication.js new file mode 100644 index 000000000000..5b5bc5059953 --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientsecretcredential/recording_allows_cancelling_the_authentication.js @@ -0,0 +1,5 @@ +let nock = require('nock'); + +module.exports.hash = "96ceae62628a226bf5cddbe7b1bf1c97"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} diff --git a/sdk/identity/identity/recordings/node/clientsecretcredential/recording_authenticates.js b/sdk/identity/identity/recordings/node/clientsecretcredential/recording_authenticates.js new file mode 100644 index 000000000000..ff9e76d945c1 --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientsecretcredential/recording_authenticates.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "da77f9d13a19f372275bf2eeb1d1edcf"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '9909a8f1-bf0d-4fa6-906f-2425bf526a00', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 00:47:43 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 00:47:42 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '6bc70287-bdb2-4c49-a1e3-30634a85a700', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 00:47:43 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 00:47:42 GMT', + 'Content-Length', + '1651' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=client_credentials&client-request-id=client-request-id&client_secret=azure_client_secret") + .reply(200, {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '1313', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '150c7035-7fe9-4e90-a0c9-00b75973e000', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 00:47:43 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 00:47:42 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/clientsecretcredential/recording_supports_tracing.js b/sdk/identity/identity/recordings/node/clientsecretcredential/recording_supports_tracing.js new file mode 100644 index 000000000000..369b1dd95149 --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientsecretcredential/recording_supports_tracing.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "cae582bcef5a80c24789c22380046ad4"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'ca0bfed3-00c0-4366-bcc3-0109f7958b00', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 00:47:44 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 00:47:43 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'd1e7c158-3351-4a5d-8f41-09e4fb72b300', + 'x-ms-ests-server', + '2.1.11530.17 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 00:47:44 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 00:47:43 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=client_credentials&client-request-id=client-request-id&client_secret=azure_client_secret") + .reply(200, {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '1313', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '97c8dfdf-7d98-4116-aebc-f7ae8b579d00', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 00:47:44 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 00:47:43 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_accepts_tokencachepersistenceoptions.js b/sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_accepts_tokencachepersistenceoptions.js new file mode 100644 index 000000000000..af6c10a02673 --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_accepts_tokencachepersistenceoptions.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "712634510da2503b543e341f6e1c289e"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'f343fe87-1627-4cc4-8f7e-1a59df100800', + 'x-ms-ests-server', + '2.1.11530.17 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 01:45:44 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 01:45:44 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'd2c8cc64-8f34-42f0-86d0-f52f9ddae700', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 01:45:44 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 01:45:44 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=client_credentials&client-request-id=client-request-id&client_secret=azure_client_secret") + .reply(200, {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '1313', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '6d0fee6b-d7be-4c99-9fff-063267f68700', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 01:45:44 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 01:45:44 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_authenticates_silently_after_the_initial_request.js b/sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_authenticates_silently_after_the_initial_request.js new file mode 100644 index 000000000000..16de60847873 --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_authenticates_silently_after_the_initial_request.js @@ -0,0 +1,182 @@ +let nock = require('nock'); + +module.exports.hash = "e754db3290cbb7814cf5b2d3f408e7b6"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '34bd2110-1987-4705-8045-ade33e6fa000', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 01:45:43 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 01:45:43 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'd2c8cc64-8f34-42f0-86d0-f52f81dae700', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 01:45:43 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 01:45:43 GMT', + 'Content-Length', + '1651' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=client_credentials&client-request-id=client-request-id&client_secret=azure_client_secret") + .reply(200, {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '6d0fee6b-d7be-4c99-9fff-06324ef68700', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 01:45:43 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 01:45:43 GMT', + 'Content-Length', + '1313' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'd07630a5-5cb7-41b1-9967-d30e73c7ad00', + 'x-ms-ests-server', + '2.1.11530.17 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 01:45:43 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 01:45:43 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '5a6f75cd-92fa-447d-963d-da83b3522300', + 'x-ms-ests-server', + '2.1.11530.17 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 01:45:44 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 01:45:43 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js b/sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js new file mode 100644 index 000000000000..f0ddc98dfb0c --- /dev/null +++ b/sdk/identity/identity/recordings/node/clientsecretcredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "d878bd369f214c9ae5403891cc22df25"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '7ead46ef-a6cb-4304-a16e-594b66820300', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 01:45:44 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 01:45:44 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'cfe3b4b2-08e3-484a-9a2d-b069c9a01300', + 'x-ms-ests-server', + '2.1.11530.17 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 01:45:44 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 01:45:44 GMT', + 'Content-Length', + '1651' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=client_credentials&client-request-id=client-request-id&client_secret=azure_client_secret") + .reply(200, {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '6d0fee6b-d7be-4c99-9fff-063276f68700', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 01:45:44 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 01:45:44 GMT', + 'Content-Length', + '1313' +]); diff --git a/sdk/identity/identity/recordings/node/devicecodecredential/recording_allows_setting_disableautomaticauthentication.js b/sdk/identity/identity/recordings/node/devicecodecredential/recording_allows_setting_disableautomaticauthentication.js new file mode 100644 index 000000000000..6c472653c918 --- /dev/null +++ b/sdk/identity/identity/recordings/node/devicecodecredential/recording_allows_setting_disableautomaticauthentication.js @@ -0,0 +1,148 @@ +let nock = require('nock'); + +module.exports.hash = "8324c86b6cb5bdbbf710bfe253b2b7d3"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'ad9380db-706b-4d9e-b074-e454eb4a3501', + 'x-ms-ests-server', + '2.1.11530.17 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:14 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:14 GMT', + 'Content-Length', + '957' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/organizations/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/organizations/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/organizations/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/{tenantid}/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/organizations/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":null,"cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1510', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '624096e1-7fa1-43d8-9c44-532f61125d00', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:14 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:14 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/organizations/oauth2/v2.0/devicecode', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46") + .reply(200, {"user_code":"USER_CODE","device_code":"DEVICE_CODE","verification_uri":"https://microsoft.com/devicelogin","expires_in":900,"interval":5,"message":"To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code USER_CODE to authenticate."}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '597145f5-9afb-4c4f-a190-035af7c5d900', + 'x-ms-ests-server', + '2.1.11530.17 - SCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:14 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:14 GMT', + 'Content-Length', + '473' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/organizations/oauth2/v2.0/token', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46&grant_type=device_code&device_code=DEVICE_CODE&client-request-id=client-request-id&client_info=1") + .reply(200, {"token_type":"Bearer","scope":"https://vault.azure.net/user_impersonation https://vault.azure.net/.default","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token","refresh_token":"refresh_token","foci":"1","id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz","client_info":"eyJ1aWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtY2Q5Mi04YTMwZTc2MmE4MmEiLCJ1dGlkIjoiOTE4ODA0MGQtNmM2Ny00YzViLWIxMTItMzZhMzA0YjY2ZGFkIn0"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '4752', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '446e9805-7631-4b59-b1cf-82ae70b45100', + 'x-ms-ests-server', + '2.1.11530.17 - SCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:29 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:29 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/devicecodecredential/recording_authenticates_and_allows_the_customization_of_the_prompt_callback.js b/sdk/identity/identity/recordings/node/devicecodecredential/recording_authenticates_and_allows_the_customization_of_the_prompt_callback.js new file mode 100644 index 000000000000..152aa4841236 --- /dev/null +++ b/sdk/identity/identity/recordings/node/devicecodecredential/recording_authenticates_and_allows_the_customization_of_the_prompt_callback.js @@ -0,0 +1,148 @@ +let nock = require('nock'); + +module.exports.hash = "c2b17c07e80b188ed51b01dd652fa37f"; + +module.exports.testInfo = { "uniqueName": {}, "newDate": {} } + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/common/discovery/instance') + .query(true) + .reply(200, { "tenant_discovery_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration", "api-version": "1.1", "metadata": [{ "preferred_network": "login.microsoftonline.com", "preferred_cache": "login.windows.net", "aliases": ["login.microsoftonline.com", "login.windows.net", "login.microsoft.com", "sts.windows.net"] }, { "preferred_network": "login.partner.microsoftonline.cn", "preferred_cache": "login.partner.microsoftonline.cn", "aliases": ["login.partner.microsoftonline.cn", "login.chinacloudapi.cn"] }, { "preferred_network": "login.microsoftonline.de", "preferred_cache": "login.microsoftonline.de", "aliases": ["login.microsoftonline.de"] }, { "preferred_network": "login.microsoftonline.us", "preferred_cache": "login.microsoftonline.us", "aliases": ["login.microsoftonline.us", "login.usgovcloudapi.net"] }, { "preferred_network": "login-us.microsoftonline.com", "preferred_cache": "login-us.microsoftonline.com", "aliases": ["login-us.microsoftonline.com"] }] }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '8c6d7cfa-a266-46c4-9957-733c2df36000', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:14:58 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:14:57 GMT' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, { "token_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token", "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys", "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0", "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", "authorization_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize", "device_authorization_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode", "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout", "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", "email"], "tenant_region_scope": "NA", "cloud_instance_name": "microsoftonline.com", "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", "rbac_url": "https://pas.windows.net" }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '89281210-eaec-4b3c-926b-160666f8e800', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:14:58 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:14:57 GMT', + 'Content-Length', + '1651' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=azure_client_id") + .reply(200, { "user_code": "USER_CODE", "device_code": "DEVICE_CODE", "verification_uri": "https://microsoft.com/devicelogin", "expires_in": 900, "interval": 5, "message": "To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code USER_CODE to authenticate." }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'ab208d82-3482-422d-9690-522a5dd0a500', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:14:58 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:14:57 GMT', + 'Content-Length', + '473' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=azure_client_id&grant_type=device_code&device_code=DEVICE_CODE&client-request-id=client-request-id&client_info=1") + .reply(200, { "token_type": "Bearer", "scope": "https://vault.azure.net/user_impersonation https://vault.azure.net/.default", "expires_in": 3599, "ext_expires_in": 3599, "access_token": "access_token", "refresh_token": "refresh_token", "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz", "client_info": "eyJ1aWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtY2Q5Mi04YTMwZTc2MmE4MmEiLCJ1dGlkIjoiOTE4ODA0MGQtNmM2Ny00YzViLWIxMTItMzZhMzA0YjY2ZGFkIn0" }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '4243cb64-c6a0-49f5-abc9-9d68eef9ac00', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:14 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:14 GMT', + 'Content-Length', + '5163' + ]); diff --git a/sdk/identity/identity/recordings/node/devicecodecredential/recording_authenticates_with_default_values.js b/sdk/identity/identity/recordings/node/devicecodecredential/recording_authenticates_with_default_values.js new file mode 100644 index 000000000000..80a72e99fbeb --- /dev/null +++ b/sdk/identity/identity/recordings/node/devicecodecredential/recording_authenticates_with_default_values.js @@ -0,0 +1,148 @@ +let nock = require('nock'); + +module.exports.hash = "5f88b7b7aa25747e72a082b2eedd8670"; + +module.exports.testInfo = { "uniqueName": {}, "newDate": {} } + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/common/discovery/instance') + .query(true) + .reply(200, { "tenant_discovery_endpoint": "https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration", "api-version": "1.1", "metadata": [{ "preferred_network": "login.microsoftonline.com", "preferred_cache": "login.windows.net", "aliases": ["login.microsoftonline.com", "login.windows.net", "login.microsoft.com", "sts.windows.net"] }, { "preferred_network": "login.partner.microsoftonline.cn", "preferred_cache": "login.partner.microsoftonline.cn", "aliases": ["login.partner.microsoftonline.cn", "login.chinacloudapi.cn"] }, { "preferred_network": "login.microsoftonline.de", "preferred_cache": "login.microsoftonline.de", "aliases": ["login.microsoftonline.de"] }, { "preferred_network": "login.microsoftonline.us", "preferred_cache": "login.microsoftonline.us", "aliases": ["login.microsoftonline.us", "login.usgovcloudapi.net"] }, { "preferred_network": "login-us.microsoftonline.com", "preferred_cache": "login-us.microsoftonline.com", "aliases": ["login-us.microsoftonline.com"] }] }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'abcf271f-fd95-4121-9bb3-56a844faaf00', + 'x-ms-ests-server', + '2.1.11530.17 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:14:32 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:14:31 GMT', + 'Content-Length', + '957' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/organizations/v2.0/.well-known/openid-configuration') + .reply(200, { "token_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/token", "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/organizations/discovery/v2.0/keys", "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0", "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", "authorization_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize", "device_authorization_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode", "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/logout", "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", "email"], "tenant_region_scope": null, "cloud_instance_name": "microsoftonline.com", "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", "rbac_url": "https://pas.windows.net" }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1510', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '2ece5d78-b8a8-4640-b9dc-2658cf670300', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:14:32 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:14:31 GMT' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/organizations/oauth2/v2.0/devicecode', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46") + .reply(200, { "user_code": "USER_CODE", "device_code": "DEVICE_CODE", "verification_uri": "https://microsoft.com/devicelogin", "expires_in": 900, "interval": 5, "message": "To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code USER_CODE to authenticate." }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '6b9040bc-1944-4f8c-9886-8faf11799600', + 'x-ms-ests-server', + '2.1.11530.17 - EUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:14:32 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:14:31 GMT', + 'Content-Length', + '473' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/organizations/oauth2/v2.0/token', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46&grant_type=device_code&device_code=DEVICE_CODE&client-request-id=client-request-id&client_info=1") + .reply(200, { "token_type": "Bearer", "scope": "https://vault.azure.net/user_impersonation https://vault.azure.net/.default", "expires_in": 3599, "ext_expires_in": 3599, "access_token": "access_token", "refresh_token": "refresh_token", "foci": "1", "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz", "client_info": "eyJ1aWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtY2Q5Mi04YTMwZTc2MmE4MmEiLCJ1dGlkIjoiOTE4ODA0MGQtNmM2Ny00YzViLWIxMTItMzZhMzA0YjY2ZGFkIn0" }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '00062513-788c-45d7-b87b-294bae79b100', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:14:42 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:14:41 GMT', + 'Content-Length', + '4752' + ]); diff --git a/sdk/identity/identity/recordings/node/devicecodecredential/recording_authenticates_with_provided_values.js b/sdk/identity/identity/recordings/node/devicecodecredential/recording_authenticates_with_provided_values.js new file mode 100644 index 000000000000..adc05d73b931 --- /dev/null +++ b/sdk/identity/identity/recordings/node/devicecodecredential/recording_authenticates_with_provided_values.js @@ -0,0 +1,148 @@ +let nock = require('nock'); + +module.exports.hash = "a8c3a7443fc1b8404ae1c204643029c4"; + +module.exports.testInfo = { "uniqueName": {}, "newDate": {} } + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/common/discovery/instance') + .query(true) + .reply(200, { "tenant_discovery_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration", "api-version": "1.1", "metadata": [{ "preferred_network": "login.microsoftonline.com", "preferred_cache": "login.windows.net", "aliases": ["login.microsoftonline.com", "login.windows.net", "login.microsoft.com", "sts.windows.net"] }, { "preferred_network": "login.partner.microsoftonline.cn", "preferred_cache": "login.partner.microsoftonline.cn", "aliases": ["login.partner.microsoftonline.cn", "login.chinacloudapi.cn"] }, { "preferred_network": "login.microsoftonline.de", "preferred_cache": "login.microsoftonline.de", "aliases": ["login.microsoftonline.de"] }, { "preferred_network": "login.microsoftonline.us", "preferred_cache": "login.microsoftonline.us", "aliases": ["login.microsoftonline.us", "login.usgovcloudapi.net"] }, { "preferred_network": "login-us.microsoftonline.com", "preferred_cache": "login-us.microsoftonline.com", "aliases": ["login-us.microsoftonline.com"] }] }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '298aa27f-5f3d-4250-a767-aacbc74cdf00', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:14:42 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:14:42 GMT', + 'Content-Length', + '980' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, { "token_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token", "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys", "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0", "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", "authorization_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize", "device_authorization_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode", "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout", "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", "email"], "tenant_region_scope": "NA", "cloud_instance_name": "microsoftonline.com", "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", "rbac_url": "https://pas.windows.net" }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '439c8564-0337-4b49-b7f8-4c9c63f4ad00', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:14:42 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:14:42 GMT' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=azure_client_id") + .reply(200, { "user_code": "USER_CODE", "device_code": "DEVICE_CODE", "verification_uri": "https://microsoft.com/devicelogin", "expires_in": 900, "interval": 5, "message": "To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code USER_CODE to authenticate." }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '0782b585-9142-4935-a901-5e684da4dd00', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:14:43 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:14:42 GMT', + 'Content-Length', + '473' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=azure_client_id&grant_type=device_code&device_code=DEVICE_CODE&client-request-id=client-request-id&client_info=1") + .reply(200, { "token_type": "Bearer", "scope": "https://vault.azure.net/user_impersonation https://vault.azure.net/.default", "expires_in": 3599, "ext_expires_in": 3599, "access_token": "access_token", "refresh_token": "refresh_token", "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz", "client_info": "eyJ1aWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtY2Q5Mi04YTMwZTc2MmE4MmEiLCJ1dGlkIjoiOTE4ODA0MGQtNmM2Ny00YzViLWIxMTItMzZhMzA0YjY2ZGFkIn0" }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '6542baa1-c678-4c37-a972-673cf275f200', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:14:58 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:14:57 GMT', + 'Content-Length', + '5163' + ]); diff --git a/sdk/identity/identity/recordings/node/devicecodecredential/recording_supports_tracing.js b/sdk/identity/identity/recordings/node/devicecodecredential/recording_supports_tracing.js new file mode 100644 index 000000000000..4f66cd3ade85 --- /dev/null +++ b/sdk/identity/identity/recordings/node/devicecodecredential/recording_supports_tracing.js @@ -0,0 +1,148 @@ +let nock = require('nock'); + +module.exports.hash = "0cc0e69d5d19c4a93a654ccd0aa4824d"; + +module.exports.testInfo = { "uniqueName": {}, "newDate": {} } + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/common/discovery/instance') + .query(true) + .reply(200, { "tenant_discovery_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration", "api-version": "1.1", "metadata": [{ "preferred_network": "login.microsoftonline.com", "preferred_cache": "login.windows.net", "aliases": ["login.microsoftonline.com", "login.windows.net", "login.microsoft.com", "sts.windows.net"] }, { "preferred_network": "login.partner.microsoftonline.cn", "preferred_cache": "login.partner.microsoftonline.cn", "aliases": ["login.partner.microsoftonline.cn", "login.chinacloudapi.cn"] }, { "preferred_network": "login.microsoftonline.de", "preferred_cache": "login.microsoftonline.de", "aliases": ["login.microsoftonline.de"] }, { "preferred_network": "login.microsoftonline.us", "preferred_cache": "login.microsoftonline.us", "aliases": ["login.microsoftonline.us", "login.usgovcloudapi.net"] }, { "preferred_network": "login-us.microsoftonline.com", "preferred_cache": "login-us.microsoftonline.com", "aliases": ["login-us.microsoftonline.com"] }] }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'cb631946-3944-4558-9d4a-e84919f44100', + 'x-ms-ests-server', + '2.1.11530.17 - SCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:29 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:29 GMT', + 'Content-Length', + '980' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, { "token_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token", "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys", "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0", "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", "authorization_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize", "device_authorization_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode", "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout", "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", "email"], "tenant_region_scope": "NA", "cloud_instance_name": "microsoftonline.com", "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", "rbac_url": "https://pas.windows.net" }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '4243cb64-c6a0-49f5-abc9-9d68aafcac00', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:29 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:29 GMT' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=azure_client_id") + .reply(200, { "user_code": "USER_CODE", "device_code": "DEVICE_CODE", "verification_uri": "https://microsoft.com/devicelogin", "expires_in": 900, "interval": 5, "message": "To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code USER_CODE to authenticate." }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '473', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '54546ab9-9349-4c3d-a0b0-b77180359900', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:29 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:29 GMT' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=azure_client_id&grant_type=device_code&device_code=DEVICE_CODE&client-request-id=client-request-id&client_info=1") + .reply(200, { "token_type": "Bearer", "scope": "https://vault.azure.net/user_impersonation https://vault.azure.net/.default", "expires_in": 3599, "ext_expires_in": 3599, "access_token": "access_token", "refresh_token": "refresh_token", "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz", "client_info": "eyJ1aWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtY2Q5Mi04YTMwZTc2MmE4MmEiLCJ1dGlkIjoiOTE4ODA0MGQtNmM2Ny00YzViLWIxMTItMzZhMzA0YjY2ZGFkIn0" }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '439c8564-0337-4b49-b7f8-4c9cd9fdad00', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:45 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:45 GMT', + 'Content-Length', + '5163' + ]); diff --git a/sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_accepts_tokencachepersistenceoptions.js b/sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_accepts_tokencachepersistenceoptions.js new file mode 100644 index 000000000000..91118b039a35 --- /dev/null +++ b/sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_accepts_tokencachepersistenceoptions.js @@ -0,0 +1,148 @@ +let nock = require('nock'); + +module.exports.hash = "3769aa39f848da5e2833119c576ae8d2"; + +module.exports.testInfo = { "uniqueName": {}, "newDate": {} } + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/common/discovery/instance') + .query(true) + .reply(200, { "tenant_discovery_endpoint": "https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration", "api-version": "1.1", "metadata": [{ "preferred_network": "login.microsoftonline.com", "preferred_cache": "login.windows.net", "aliases": ["login.microsoftonline.com", "login.windows.net", "login.microsoft.com", "sts.windows.net"] }, { "preferred_network": "login.partner.microsoftonline.cn", "preferred_cache": "login.partner.microsoftonline.cn", "aliases": ["login.partner.microsoftonline.cn", "login.chinacloudapi.cn"] }, { "preferred_network": "login.microsoftonline.de", "preferred_cache": "login.microsoftonline.de", "aliases": ["login.microsoftonline.de"] }, { "preferred_network": "login.microsoftonline.us", "preferred_cache": "login.microsoftonline.us", "aliases": ["login.microsoftonline.us", "login.usgovcloudapi.net"] }, { "preferred_network": "login-us.microsoftonline.com", "preferred_cache": "login-us.microsoftonline.com", "aliases": ["login-us.microsoftonline.com"] }] }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '53a34641-bc7e-4560-8d9d-67f030dd9700', + 'x-ms-ests-server', + '2.1.11530.17 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:56 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:56 GMT', + 'Content-Length', + '957' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/organizations/v2.0/.well-known/openid-configuration') + .reply(200, { "token_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/token", "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/organizations/discovery/v2.0/keys", "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0", "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", "authorization_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize", "device_authorization_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode", "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/logout", "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", "email"], "tenant_region_scope": null, "cloud_instance_name": "microsoftonline.com", "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", "rbac_url": "https://pas.windows.net" }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1510', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'c2830140-50d0-427e-8187-6e3c32173b00', + 'x-ms-ests-server', + '2.1.11530.17 - SCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:56 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:56 GMT' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/organizations/oauth2/v2.0/devicecode', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46") + .reply(200, { "user_code": "USER_CODE", "device_code": "DEVICE_CODE", "verification_uri": "https://microsoft.com/devicelogin", "expires_in": 900, "interval": 5, "message": "To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code USER_CODE to authenticate." }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '473', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '1513a02c-4561-43f9-943b-9de9390bb500', + 'x-ms-ests-server', + '2.1.11530.17 - EUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:56 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:56 GMT' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/organizations/oauth2/v2.0/token', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46&grant_type=device_code&device_code=DEVICE_CODE&client-request-id=client-request-id&client_info=1") + .reply(200, { "token_type": "Bearer", "scope": "https://vault.azure.net/user_impersonation https://vault.azure.net/.default", "expires_in": 3599, "ext_expires_in": 3599, "access_token": "access_token", "refresh_token": "refresh_token", "foci": "1", "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz", "client_info": "eyJ1aWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtY2Q5Mi04YTMwZTc2MmE4MmEiLCJ1dGlkIjoiOTE4ODA0MGQtNmM2Ny00YzViLWIxMTItMzZhMzA0YjY2ZGFkIn0" }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '4752', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '092ff551-e0ae-49bc-bc2a-184d74ffb500', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:16:12 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:16:11 GMT' + ]); diff --git a/sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_allows_passing_an_authenticationrecord_to_avoid_further_manual_authentications.js b/sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_allows_passing_an_authenticationrecord_to_avoid_further_manual_authentications.js new file mode 100644 index 000000000000..fe46ec72ecdb --- /dev/null +++ b/sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_allows_passing_an_authenticationrecord_to_avoid_further_manual_authentications.js @@ -0,0 +1,219 @@ +let nock = require('nock'); + +module.exports.hash = "de418f7b804ff45bed1ab489fa0fee94"; + +module.exports.testInfo = { "uniqueName": {}, "newDate": {} } + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/common/discovery/instance') + .query(true) + .reply(200, { "tenant_discovery_endpoint": "https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration", "api-version": "1.1", "metadata": [{ "preferred_network": "login.microsoftonline.com", "preferred_cache": "login.windows.net", "aliases": ["login.microsoftonline.com", "login.windows.net", "login.microsoft.com", "sts.windows.net"] }, { "preferred_network": "login.partner.microsoftonline.cn", "preferred_cache": "login.partner.microsoftonline.cn", "aliases": ["login.partner.microsoftonline.cn", "login.chinacloudapi.cn"] }, { "preferred_network": "login.microsoftonline.de", "preferred_cache": "login.microsoftonline.de", "aliases": ["login.microsoftonline.de"] }, { "preferred_network": "login.microsoftonline.us", "preferred_cache": "login.microsoftonline.us", "aliases": ["login.microsoftonline.us", "login.usgovcloudapi.net"] }, { "preferred_network": "login-us.microsoftonline.com", "preferred_cache": "login-us.microsoftonline.com", "aliases": ["login-us.microsoftonline.com"] }] }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '957', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '96d36824-dbbd-4a55-ab81-228c0a972200', + 'x-ms-ests-server', + '2.1.11530.17 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:19 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:18 GMT' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/organizations/v2.0/.well-known/openid-configuration') + .reply(200, { "token_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/token", "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/organizations/discovery/v2.0/keys", "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0", "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", "authorization_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize", "device_authorization_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode", "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/logout", "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", "email"], "tenant_region_scope": null, "cloud_instance_name": "microsoftonline.com", "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", "rbac_url": "https://pas.windows.net" }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '94f92e2c-2897-4369-b267-e8240758d100', + 'x-ms-ests-server', + '2.1.11530.17 - SCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:19 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:18 GMT', + 'Content-Length', + '1510' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/organizations/oauth2/v2.0/devicecode', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46") + .reply(200, { "user_code": "USER_CODE", "device_code": "DEVICE_CODE", "verification_uri": "https://microsoft.com/devicelogin", "expires_in": 900, "interval": 5, "message": "To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code USER_CODE to authenticate." }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '473', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '94128d6e-141c-4e8f-a979-4a9969bb0300', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:19 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:18 GMT' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/organizations/oauth2/v2.0/token', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46&grant_type=device_code&device_code=DEVICE_CODE&client-request-id=client-request-id&client_info=1") + .reply(200, { "token_type": "Bearer", "scope": "https://vault.azure.net/user_impersonation https://vault.azure.net/.default", "expires_in": 3599, "ext_expires_in": 3599, "access_token": "access_token", "refresh_token": "refresh_token", "foci": "1", "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz", "client_info": "eyJ1aWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtY2Q5Mi04YTMwZTc2MmE4MmEiLCJ1dGlkIjoiOTE4ODA0MGQtNmM2Ny00YzViLWIxMTItMzZhMzA0YjY2ZGFkIn0" }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'fb79f069-30de-4935-868f-69a092bf2c00', + 'x-ms-ests-server', + '2.1.11530.17 - SCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:45 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:44 GMT', + 'Content-Length', + '4752' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/common/discovery/instance') + .query(true) + .reply(200, { "tenant_discovery_endpoint": "https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration", "api-version": "1.1", "metadata": [{ "preferred_network": "login.microsoftonline.com", "preferred_cache": "login.windows.net", "aliases": ["login.microsoftonline.com", "login.windows.net", "login.microsoft.com", "sts.windows.net"] }, { "preferred_network": "login.partner.microsoftonline.cn", "preferred_cache": "login.partner.microsoftonline.cn", "aliases": ["login.partner.microsoftonline.cn", "login.chinacloudapi.cn"] }, { "preferred_network": "login.microsoftonline.de", "preferred_cache": "login.microsoftonline.de", "aliases": ["login.microsoftonline.de"] }, { "preferred_network": "login.microsoftonline.us", "preferred_cache": "login.microsoftonline.us", "aliases": ["login.microsoftonline.us", "login.usgovcloudapi.net"] }, { "preferred_network": "login-us.microsoftonline.com", "preferred_cache": "login-us.microsoftonline.com", "aliases": ["login-us.microsoftonline.com"] }] }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '957', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '031d801b-6643-422b-93f8-30f14b160500', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:45 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:44 GMT' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/organizations/v2.0/.well-known/openid-configuration') + .reply(200, { "token_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/token", "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/organizations/discovery/v2.0/keys", "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0", "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", "authorization_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize", "device_authorization_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode", "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/logout", "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", "email"], "tenant_region_scope": null, "cloud_instance_name": "microsoftonline.com", "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", "rbac_url": "https://pas.windows.net" }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1510', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '33f6aeca-7f4d-4995-8b30-0ca4e0592e00', + 'x-ms-ests-server', + '2.1.11530.17 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:45 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:44 GMT' + ]); diff --git a/sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_authenticates_silently_after_the_initial_request.js b/sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_authenticates_silently_after_the_initial_request.js new file mode 100644 index 000000000000..ad4f4a530aba --- /dev/null +++ b/sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_authenticates_silently_after_the_initial_request.js @@ -0,0 +1,219 @@ +let nock = require('nock'); + +module.exports.hash = "a0c55aa7bea51893f598b002f6a0774d"; + +module.exports.testInfo = { "uniqueName": {}, "newDate": {} } + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/common/discovery/instance') + .query(true) + .reply(200, { "tenant_discovery_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration", "api-version": "1.1", "metadata": [{ "preferred_network": "login.microsoftonline.com", "preferred_cache": "login.windows.net", "aliases": ["login.microsoftonline.com", "login.windows.net", "login.microsoft.com", "sts.windows.net"] }, { "preferred_network": "login.partner.microsoftonline.cn", "preferred_cache": "login.partner.microsoftonline.cn", "aliases": ["login.partner.microsoftonline.cn", "login.chinacloudapi.cn"] }, { "preferred_network": "login.microsoftonline.de", "preferred_cache": "login.microsoftonline.de", "aliases": ["login.microsoftonline.de"] }, { "preferred_network": "login.microsoftonline.us", "preferred_cache": "login.microsoftonline.us", "aliases": ["login.microsoftonline.us", "login.usgovcloudapi.net"] }, { "preferred_network": "login-us.microsoftonline.com", "preferred_cache": "login-us.microsoftonline.com", "aliases": ["login-us.microsoftonline.com"] }] }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '19081f99-37bc-4198-9878-7c7bda904001', + 'x-ms-ests-server', + '2.1.11530.17 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:45 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:45 GMT' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, { "token_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token", "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys", "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0", "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", "authorization_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize", "device_authorization_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode", "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout", "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", "email"], "tenant_region_scope": "NA", "cloud_instance_name": "microsoftonline.com", "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", "rbac_url": "https://pas.windows.net" }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '0782b585-9142-4935-a901-5e6806aedd00', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:45 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:45 GMT', + 'Content-Length', + '1651' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=azure_client_id") + .reply(200, { "user_code": "USER_CODE", "device_code": "DEVICE_CODE", "verification_uri": "https://microsoft.com/devicelogin", "expires_in": 900, "interval": 5, "message": "To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code USER_CODE to authenticate." }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '473', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '4243cb64-c6a0-49f5-abc9-9d6802ffac00', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:45 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:45 GMT' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=azure_client_id&grant_type=device_code&device_code=DEVICE_CODE&client-request-id=client-request-id&client_info=1") + .reply(200, { "token_type": "Bearer", "scope": "https://vault.azure.net/user_impersonation https://vault.azure.net/.default", "expires_in": 3599, "ext_expires_in": 3599, "access_token": "access_token", "refresh_token": "refresh_token", "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz", "client_info": "eyJ1aWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtY2Q5Mi04YTMwZTc2MmE4MmEiLCJ1dGlkIjoiOTE4ODA0MGQtNmM2Ny00YzViLWIxMTItMzZhMzA0YjY2ZGFkIn0" }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '4243cb64-c6a0-49f5-abc9-9d686800ad00', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:56 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:55 GMT', + 'Content-Length', + '5163' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/common/discovery/instance') + .query(true) + .reply(200, { "tenant_discovery_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration", "api-version": "1.1", "metadata": [{ "preferred_network": "login.microsoftonline.com", "preferred_cache": "login.windows.net", "aliases": ["login.microsoftonline.com", "login.windows.net", "login.microsoft.com", "sts.windows.net"] }, { "preferred_network": "login.partner.microsoftonline.cn", "preferred_cache": "login.partner.microsoftonline.cn", "aliases": ["login.partner.microsoftonline.cn", "login.chinacloudapi.cn"] }, { "preferred_network": "login.microsoftonline.de", "preferred_cache": "login.microsoftonline.de", "aliases": ["login.microsoftonline.de"] }, { "preferred_network": "login.microsoftonline.us", "preferred_cache": "login.microsoftonline.us", "aliases": ["login.microsoftonline.us", "login.usgovcloudapi.net"] }, { "preferred_network": "login-us.microsoftonline.com", "preferred_cache": "login-us.microsoftonline.com", "aliases": ["login-us.microsoftonline.com"] }] }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '2e90a74c-1a95-495d-841d-497896c63d00', + 'x-ms-ests-server', + '2.1.11530.17 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:56 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:55 GMT', + 'Content-Length', + '980' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, { "token_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token", "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys", "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0", "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", "authorization_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize", "device_authorization_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode", "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": "https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout", "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", "email"], "tenant_region_scope": "NA", "cloud_instance_name": "microsoftonline.com", "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", "rbac_url": "https://pas.windows.net" }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '4d8dedab-701c-4dc9-a27c-26a8532aac00', + 'x-ms-ests-server', + '2.1.11530.17 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:15:56 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:15:56 GMT', + 'Content-Length', + '1651' + ]); diff --git a/sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js b/sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js new file mode 100644 index 000000000000..7a6d2c13fef3 --- /dev/null +++ b/sdk/identity/identity/recordings/node/devicecodecredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js @@ -0,0 +1,219 @@ +let nock = require('nock'); + +module.exports.hash = "d1e4dc22fa8231456f63436e179a0cdd"; + +module.exports.testInfo = { "uniqueName": {}, "newDate": {} } + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/common/discovery/instance') + .query(true) + .reply(200, { "tenant_discovery_endpoint": "https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration", "api-version": "1.1", "metadata": [{ "preferred_network": "login.microsoftonline.com", "preferred_cache": "login.windows.net", "aliases": ["login.microsoftonline.com", "login.windows.net", "login.microsoft.com", "sts.windows.net"] }, { "preferred_network": "login.partner.microsoftonline.cn", "preferred_cache": "login.partner.microsoftonline.cn", "aliases": ["login.partner.microsoftonline.cn", "login.chinacloudapi.cn"] }, { "preferred_network": "login.microsoftonline.de", "preferred_cache": "login.microsoftonline.de", "aliases": ["login.microsoftonline.de"] }, { "preferred_network": "login.microsoftonline.us", "preferred_cache": "login.microsoftonline.us", "aliases": ["login.microsoftonline.us", "login.usgovcloudapi.net"] }, { "preferred_network": "login-us.microsoftonline.com", "preferred_cache": "login-us.microsoftonline.com", "aliases": ["login-us.microsoftonline.com"] }] }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '280a7dad-2b79-49af-903f-3cc26d529d00', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:16:12 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:16:11 GMT', + 'Content-Length', + '957' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/organizations/v2.0/.well-known/openid-configuration') + .reply(200, { "token_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/token", "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/organizations/discovery/v2.0/keys", "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0", "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", "authorization_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize", "device_authorization_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode", "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/logout", "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", "email"], "tenant_region_scope": null, "cloud_instance_name": "microsoftonline.com", "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", "rbac_url": "https://pas.windows.net" }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '6b599e1f-290e-4767-8229-138dec900400', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:16:12 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:16:11 GMT', + 'Content-Length', + '1510' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/organizations/oauth2/v2.0/devicecode', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46") + .reply(200, { "user_code": "USER_CODE", "device_code": "DEVICE_CODE", "verification_uri": "https://microsoft.com/devicelogin", "expires_in": 900, "interval": 5, "message": "To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code USER_CODE to authenticate." }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '08e5c00a-09f4-4dff-9a0f-686fa58f8300', + 'x-ms-ests-server', + '2.1.11530.17 - NCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:16:12 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:16:12 GMT', + 'Content-Length', + '473' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .post('/organizations/oauth2/v2.0/token', "scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46&grant_type=device_code&device_code=DEVICE_CODE&client-request-id=client-request-id&client_info=1") + .reply(200, { "token_type": "Bearer", "scope": "https://vault.azure.net/user_impersonation https://vault.azure.net/.default", "expires_in": 3599, "ext_expires_in": 3599, "access_token": "access_token", "refresh_token": "refresh_token", "foci": "1", "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz", "client_info": "eyJ1aWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtY2Q5Mi04YTMwZTc2MmE4MmEiLCJ1dGlkIjoiOTE4ODA0MGQtNmM2Ny00YzViLWIxMTItMzZhMzA0YjY2ZGFkIn0" }, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '19f7d337-aa89-436d-98e4-3d8d4164b700', + 'x-ms-ests-server', + '2.1.11530.17 - SCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:16:22 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:16:22 GMT', + 'Content-Length', + '4752' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/common/discovery/instance') + .query(true) + .reply(200, { "tenant_discovery_endpoint": "https://login.microsoftonline.com/organizations/v2.0/.well-known/openid-configuration", "api-version": "1.1", "metadata": [{ "preferred_network": "login.microsoftonline.com", "preferred_cache": "login.windows.net", "aliases": ["login.microsoftonline.com", "login.windows.net", "login.microsoft.com", "sts.windows.net"] }, { "preferred_network": "login.partner.microsoftonline.cn", "preferred_cache": "login.partner.microsoftonline.cn", "aliases": ["login.partner.microsoftonline.cn", "login.chinacloudapi.cn"] }, { "preferred_network": "login.microsoftonline.de", "preferred_cache": "login.microsoftonline.de", "aliases": ["login.microsoftonline.de"] }, { "preferred_network": "login.microsoftonline.us", "preferred_cache": "login.microsoftonline.us", "aliases": ["login.microsoftonline.us", "login.usgovcloudapi.net"] }, { "preferred_network": "login-us.microsoftonline.com", "preferred_cache": "login-us.microsoftonline.com", "aliases": ["login-us.microsoftonline.com"] }] }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '957', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '076c78e8-9078-4ad2-b193-04fd1c28b900', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:16:22 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:16:22 GMT' + ]); + +nock('https://login.microsoftonline.com:443', { "encodedQueryParams": true }) + .get('/organizations/v2.0/.well-known/openid-configuration') + .reply(200, { "token_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/token", "token_endpoint_auth_methods_supported": ["client_secret_post", "private_key_jwt", "client_secret_basic"], "jwks_uri": "https://login.microsoftonline.com/organizations/discovery/v2.0/keys", "response_modes_supported": ["query", "fragment", "form_post"], "subject_types_supported": ["pairwise"], "id_token_signing_alg_values_supported": ["RS256"], "response_types_supported": ["code", "id_token", "code id_token", "id_token token"], "scopes_supported": ["openid", "profile", "email", "offline_access"], "issuer": "https://login.microsoftonline.com/{tenantid}/v2.0", "request_uri_parameter_supported": false, "userinfo_endpoint": "https://graph.microsoft.com/oidc/userinfo", "authorization_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize", "device_authorization_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode", "http_logout_supported": true, "frontchannel_logout_supported": true, "end_session_endpoint": "https://login.microsoftonline.com/organizations/oauth2/v2.0/logout", "claims_supported": ["sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "nonce", "preferred_username", "name", "tid", "ver", "at_hash", "c_hash", "email"], "tenant_region_scope": null, "cloud_instance_name": "microsoftonline.com", "cloud_graph_host_name": "graph.windows.net", "msgraph_host": "graph.microsoft.com", "rbac_url": "https://pas.windows.net" }, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1510', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'b49f588b-8b5a-4a28-bbfe-bc5dc047d900', + 'x-ms-ests-server', + '2.1.11530.17 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:16:22 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:16:22 GMT' + ]); diff --git a/sdk/identity/identity/recordings/node/environmentcredential/recording_authenticates_with_a_client_certificate_on_the_environment_variables.js b/sdk/identity/identity/recordings/node/environmentcredential/recording_authenticates_with_a_client_certificate_on_the_environment_variables.js new file mode 100644 index 000000000000..26f5b9227f9c --- /dev/null +++ b/sdk/identity/identity/recordings/node/environmentcredential/recording_authenticates_with_a_client_certificate_on_the_environment_variables.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "b8cb0633657ec7a7a5fa2566befb1cba"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'd9128cbb-0540-4c1b-913a-5d3ca92de600', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:26 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:25 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'ab208d82-3482-422d-9690-522a8c93ea00', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:26 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:25 GMT', + 'Content-Length', + '1651' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=client_credentials&client-request-id=client-request-id&client_assertion=client_assertion&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer") + .reply(200, {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '1313', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '1c3dd338-e8fb-4fe9-8bb3-8e425a26be00', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:26 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:25 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/environmentcredential/recording_authenticates_with_a_client_secret_on_the_environment_variables.js b/sdk/identity/identity/recordings/node/environmentcredential/recording_authenticates_with_a_client_secret_on_the_environment_variables.js new file mode 100644 index 000000000000..41e007946729 --- /dev/null +++ b/sdk/identity/identity/recordings/node/environmentcredential/recording_authenticates_with_a_client_secret_on_the_environment_variables.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "c94f43ec81c80cd097bdb6d9fc607540"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'cd9b2fd8-f816-4c08-b17f-c4ed9a707201', + 'x-ms-ests-server', + '2.1.11530.17 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:17 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:17 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '0e44795d-e53b-46d9-ab44-c651af81ab00', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:18 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:17 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=client_credentials&client-request-id=client-request-id&client_secret=azure_client_secret") + .reply(200, {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '1313', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '89281210-eaec-4b3c-926b-1606bf65e900', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:18 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:17 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/environmentcredential/recording_finds_and_uses_client_usernamepassword_environment_variables.js b/sdk/identity/identity/recordings/node/environmentcredential/recording_finds_and_uses_client_usernamepassword_environment_variables.js new file mode 100644 index 000000000000..047bc9dada07 --- /dev/null +++ b/sdk/identity/identity/recordings/node/environmentcredential/recording_finds_and_uses_client_usernamepassword_environment_variables.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "629b990a4ac41f153e885e02584ef481"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '596f678c-7619-4024-9eec-ba6426ede000', + 'x-ms-ests-server', + '2.1.11530.17 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:18 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:17 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"azure_azure_usernamenameinfo_endpoint":"https://graph.microsoft.com/oidc/azure_azure_usernamenameinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_azure_azure_usernamenamename","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '23a89080-be41-4ba1-b8ea-0c1febc3a500', + 'x-ms-ests-server', + '2.1.11530.17 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:18 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:17 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&azure_azure_usernamenamename=azure_azure_usernamename&azure_azure_password=azure_azure_password&scope=scope%20openid%20profile%20offline_access&grant_type=azure_azure_password&client_info=1&client-request-id=client-request-id") + .reply(400, {"error":"invalid_grant","error_description":"AADSTS50034: The azure_azure_usernamename account azure_azure_usernamename does not exist in the 12345678-1234-1234-1234-123456789012 directory. To sign into this application, the account must be added to the directory.\r\nTrace ID: 1e7d3a0b-7b5e-4329-b4b2-5e98b2148200\r\nCorrelation ID: 18918110-8a3c-4494-b2f6-88f34ce99dde\r\nTimestamp: 2021-03-17 03:26:18Z","error_codes":[50034],"timestamp":"2021-03-17 03:26:18Z","trace_id":"1e7d3a0b-7b5e-4329-b4b2-5e98b2148200","correlation_id":"18918110-8a3c-4494-b2f6-88f34ce99dde","error_uri":"https://login.microsoftonline.com/error?code=50034","suberror":"bad_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '619', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '1e7d3a0b-7b5e-4329-b4b2-5e98b2148200', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'x-ms-clitelem', + '1,50034,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:18 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:17 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/environmentcredential/recording_supports_tracing_with_environment_client_certificate.js b/sdk/identity/identity/recordings/node/environmentcredential/recording_supports_tracing_with_environment_client_certificate.js new file mode 100644 index 000000000000..4de389be1f2b --- /dev/null +++ b/sdk/identity/identity/recordings/node/environmentcredential/recording_supports_tracing_with_environment_client_certificate.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "378580f1d4f4599a3937456ff75ff565"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'fa20ecd8-cc9e-4384-9e97-02f52232b400', + 'x-ms-ests-server', + '2.1.11530.17 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:26 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:25 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '89281210-eaec-4b3c-926b-160616c92f01', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:26 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:25 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=client_credentials&client-request-id=client-request-id&client_assertion=client_assertion&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer") + .reply(200, {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '1313', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '4243cb64-c6a0-49f5-abc9-9d68ad28f400', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Sat, 17-Apr-2021 00:06:26 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Thu, 18 Mar 2021 00:06:25 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/environmentcredential/recording_supports_tracing_with_environment_client_secret.js b/sdk/identity/identity/recordings/node/environmentcredential/recording_supports_tracing_with_environment_client_secret.js new file mode 100644 index 000000000000..45cebe9295c2 --- /dev/null +++ b/sdk/identity/identity/recordings/node/environmentcredential/recording_supports_tracing_with_environment_client_secret.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "cae582bcef5a80c24789c22380046ad4"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'eda5563f-6130-4168-9da7-bce45445da00', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:18 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:17 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '78b82ad4-b47d-4b0c-902c-1801d872b200', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:18 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:17 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=client_credentials&client-request-id=client-request-id&client_secret=azure_client_secret") + .reply(200, {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '1313', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '4243cb64-c6a0-49f5-abc9-9d68e465ad00', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:18 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:17 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/environmentcredential/recording_supports_tracing_with_environment_usernamepassword.js b/sdk/identity/identity/recordings/node/environmentcredential/recording_supports_tracing_with_environment_usernamepassword.js new file mode 100644 index 000000000000..beff75a92fcb --- /dev/null +++ b/sdk/identity/identity/recordings/node/environmentcredential/recording_supports_tracing_with_environment_usernamepassword.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "cae582bcef5a80c24789c22380046ad4"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '7d22bdc7-5c7a-4112-a1b0-6da855ec4200', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:19 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:18 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"azure_azure_usernamenameinfo_endpoint":"https://graph.microsoft.com/oidc/azure_azure_usernamenameinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_azure_azure_usernamenamename","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '705eb9e8-034c-4f57-94a5-c09b7b147f00', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:19 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:18 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&azure_azure_usernamenamename=azure_azure_usernamename&azure_azure_password=azure_azure_password&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=azure_azure_password&client_info=1&client-request-id=client-request-id") + .reply(400, {"error":"invalid_grant","error_description":"AADSTS50034: The azure_azure_usernamename account azure_azure_usernamename does not exist in the 12345678-1234-1234-1234-123456789012 directory. To sign into this application, the account must be added to the directory.\r\nTrace ID: 89281210-eaec-4b3c-926b-1606ea65e900\r\nCorrelation ID: f4363b01-ae32-4f95-b6bb-cfa29339c34e\r\nTimestamp: 2021-03-17 03:26:19Z","error_codes":[50034],"timestamp":"2021-03-17 03:26:19Z","trace_id":"89281210-eaec-4b3c-926b-1606ea65e900","correlation_id":"f4363b01-ae32-4f95-b6bb-cfa29339c34e","error_uri":"https://login.microsoftonline.com/error?code=50034","suberror":"bad_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '619', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '89281210-eaec-4b3c-926b-1606ea65e900', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'x-ms-clitelem', + '1,50034,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:19 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:18 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/environmentcredential/recording_throws_an_authenticationerror_when_gettoken_is_called_and_environmentcredential_authentication_failed.js b/sdk/identity/identity/recordings/node/environmentcredential/recording_throws_an_authenticationerror_when_gettoken_is_called_and_environmentcredential_authentication_failed.js new file mode 100644 index 000000000000..f101e6e11f3c --- /dev/null +++ b/sdk/identity/identity/recordings/node/environmentcredential/recording_throws_an_authenticationerror_when_gettoken_is_called_and_environmentcredential_authentication_failed.js @@ -0,0 +1,76 @@ +let nock = require('nock'); + +module.exports.hash = "2e787f040b2b3d4e9b8018136fdfaf32"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"12345678-1234-1234-1234-123456789012_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '950', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'fe836dd9-bca5-4328-b92d-f817010e3401', + 'x-ms-ests-server', + '2.1.11530.17 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:19 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:18 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(400, {"error":"invalid_12345678-1234-1234-1234-123456789012","error_description":"AADSTS90002: Tenant '12345678-1234-1234-1234-123456789012' not found. This may happen if there are no active subscriptions for the 12345678-1234-1234-1234-123456789012. Check to make sure you have the correct 12345678-1234-1234-1234-123456789012 ID. Check with your subscription administrator.\r\nTrace ID: 52dd626e-7771-44eb-8c84-09b7e4cd1200\r\nCorrelation ID: 80f90d68-d687-4276-8c4d-e764609d8163\r\nTimestamp: 2021-03-17 03:26:19Z","error_codes":[90002],"timestamp":"2021-03-17 03:26:19Z","trace_id":"52dd626e-7771-44eb-8c84-09b7e4cd1200","correlation_id":"80f90d68-d687-4276-8c4d-e764609d8163","error_uri":"https://login.microsoftonline.com/error?code=90002"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '621', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '52dd626e-7771-44eb-8c84-09b7e4cd1200', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:19 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:18 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/environmentcredential/recording_throws_an_credentialunavailable_when_gettoken_is_called_and_no_credential_was_configured.js b/sdk/identity/identity/recordings/node/environmentcredential/recording_throws_an_credentialunavailable_when_gettoken_is_called_and_no_credential_was_configured.js new file mode 100644 index 000000000000..9bb4a3ab0c0b --- /dev/null +++ b/sdk/identity/identity/recordings/node/environmentcredential/recording_throws_an_credentialunavailable_when_gettoken_is_called_and_no_credential_was_configured.js @@ -0,0 +1,5 @@ +let nock = require('nock'); + +module.exports.hash = "a18d847315ce22e8cbd1f3b255d60e6b"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} diff --git a/sdk/identity/identity/recordings/node/environmentcredential_internal/recording_authenticates_silently_after_the_initial_request.js b/sdk/identity/identity/recordings/node/environmentcredential_internal/recording_authenticates_silently_after_the_initial_request.js new file mode 100644 index 000000000000..eeb078a0d6b9 --- /dev/null +++ b/sdk/identity/identity/recordings/node/environmentcredential_internal/recording_authenticates_silently_after_the_initial_request.js @@ -0,0 +1,182 @@ +let nock = require('nock'); + +module.exports.hash = "d666385de7a7cb0b5052a55c4023c285"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'f8310480-be11-4134-b301-c45d9ed7b500', + 'x-ms-ests-server', + '2.1.11530.17 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:45 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:44 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '705eb9e8-034c-4f57-94a5-c09b36177f00', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:45 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:44 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=client_credentials&client-request-id=client-request-id&client_secret=azure_client_secret") + .reply(200, {"token_type":"Bearer","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '1313', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '643cc099-1ad1-4d07-9431-2518c92db300', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:45 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:44 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'c5b5464b-48dd-4c88-a688-2ed677982c00', + 'x-ms-ests-server', + '2.1.11530.17 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:45 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:44 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'a25d81ee-2107-4a09-8e39-6ffd5eeaaf00', + 'x-ms-ests-server', + '2.1.11530.17 - SCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:26:45 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:26:44 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/usernamepasswordcredential/recording_allows_cancelling_the_authentication.js b/sdk/identity/identity/recordings/node/usernamepasswordcredential/recording_allows_cancelling_the_authentication.js new file mode 100644 index 000000000000..0bcef078f6e8 --- /dev/null +++ b/sdk/identity/identity/recordings/node/usernamepasswordcredential/recording_allows_cancelling_the_authentication.js @@ -0,0 +1,5 @@ +let nock = require('nock'); + +module.exports.hash = "2445bb167b170f68ed1212ab29d45325"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} diff --git a/sdk/identity/identity/recordings/node/usernamepasswordcredential/recording_authenticates.js b/sdk/identity/identity/recordings/node/usernamepasswordcredential/recording_authenticates.js new file mode 100644 index 000000000000..2033554b5ad1 --- /dev/null +++ b/sdk/identity/identity/recordings/node/usernamepasswordcredential/recording_authenticates.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "c40ad8871e4e8a7a64354121789cace2"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '8a9f1255-760c-42fe-8b40-a35d0de8e000', + 'x-ms-ests-server', + '2.1.11530.17 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:12 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:12 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '75f07ac9-cac6-40d3-abb8-1a7df5c0ae00', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:12 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:12 GMT', + 'Content-Length', + '1651' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&username=azure_username&password=azure_password&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=password&client_info=1&client-request-id=client-request-id") + .reply(200, {"token_type":"Bearer","scope":"https://vault.azure.net/user_impersonation https://vault.azure.net/.default","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token","refresh_token":"refresh_token","id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz","client_info":"eyJ1aWQiOiI4NTJiZWZkNS0wMGIzLTQyM2EtODg3MS02Zjc5ODQ0ODJjMzQiLCJ1dGlkIjoiZmY0OGJjM2UtMzdhMS00MzNlLWE3OTgtYjU0NDBjOTU0MzdiIn0"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '4186', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'f8f42750-2609-4cb7-ad93-618d5abcac00', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:13 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:12 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/usernamepasswordcredential/recording_supports_tracing.js b/sdk/identity/identity/recordings/node/usernamepasswordcredential/recording_supports_tracing.js new file mode 100644 index 000000000000..0de6d1869087 --- /dev/null +++ b/sdk/identity/identity/recordings/node/usernamepasswordcredential/recording_supports_tracing.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "cae582bcef5a80c24789c22380046ad4"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '43ea8611-b3c4-4aad-9b0b-ab644350b400', + 'x-ms-ests-server', + '2.1.11530.17 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:13 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:12 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '1b1bc27e-2ea6-43ee-a531-26062f24e800', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:13 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:12 GMT', + 'Content-Length', + '1651' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&username=azure_username&password=azure_password&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=password&client_info=1&client-request-id=client-request-id") + .reply(200, {"token_type":"Bearer","scope":"https://vault.azure.net/user_impersonation https://vault.azure.net/.default","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token","refresh_token":"refresh_token","id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz","client_info":"eyJ1aWQiOiI4NTJiZWZkNS0wMGIzLTQyM2EtODg3MS02Zjc5ODQ0ODJjMzQiLCJ1dGlkIjoiZmY0OGJjM2UtMzdhMS00MzNlLWE3OTgtYjU0NDBjOTU0MzdiIn0"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '4186', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'a484aa5b-fbe1-4ac5-a11a-9b120944ab00', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:13 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:13 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_accepts_tokencachepersistenceoptions.js b/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_accepts_tokencachepersistenceoptions.js new file mode 100644 index 000000000000..e7f97d175564 --- /dev/null +++ b/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_accepts_tokencachepersistenceoptions.js @@ -0,0 +1,111 @@ +let nock = require('nock'); + +module.exports.hash = "c27b3e1694b775f2b6e89cf82baa35bc"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '4227c0b4-e383-4727-aa74-dffd7d489400', + 'x-ms-ests-server', + '2.1.11530.17 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:13 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:13 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'c291668c-7464-45bb-b60a-14d2b6d8ab00', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:14 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:13 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&username=azure_username&password=azure_password&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=password&client_info=1&client-request-id=client-request-id") + .reply(200, {"token_type":"Bearer","scope":"https://vault.azure.net/user_impersonation https://vault.azure.net/.default","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token","refresh_token":"refresh_token","id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz","client_info":"eyJ1aWQiOiI4NTJiZWZkNS0wMGIzLTQyM2EtODg3MS02Zjc5ODQ0ODJjMzQiLCJ1dGlkIjoiZmY0OGJjM2UtMzdhMS00MzNlLWE3OTgtYjU0NDBjOTU0MzdiIn0"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '4186', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '1ca062b2-3aad-45f4-ad46-bb5eb2afae00', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:14 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:14 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_allows_passing_an_authenticationrecord_to_avoid_further_manual_authentications.js b/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_allows_passing_an_authenticationrecord_to_avoid_further_manual_authentications.js new file mode 100644 index 000000000000..3c4526d0635c --- /dev/null +++ b/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_allows_passing_an_authenticationrecord_to_avoid_further_manual_authentications.js @@ -0,0 +1,182 @@ +let nock = require('nock'); + +module.exports.hash = "78f5ba2b942fe05ae65a69dd3d012eec"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '189b0ce6-fca3-4b16-b655-3e7d9e394700', + 'x-ms-ests-server', + '2.1.11530.17 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:15 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:14 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '0794382a-d26d-4d29-8f73-61da0067b100', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:15 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:15 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&username=azure_username&password=azure_password&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=password&client_info=1&client-request-id=client-request-id") + .reply(200, {"token_type":"Bearer","scope":"https://vault.azure.net/user_impersonation https://vault.azure.net/.default","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token","refresh_token":"refresh_token","id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz","client_info":"eyJ1aWQiOiI4NTJiZWZkNS0wMGIzLTQyM2EtODg3MS02Zjc5ODQ0ODJjMzQiLCJ1dGlkIjoiZmY0OGJjM2UtMzdhMS00MzNlLWE3OTgtYjU0NDBjOTU0MzdiIn0"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '4186', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '1ca062b2-3aad-45f4-ad46-bb5ee8afae00', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:15 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:15 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'eec79095-503f-4fe7-9894-7a0778c23100', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:15 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:15 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '2592b062-f209-4e76-ad39-39776cb98b00', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:15 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:15 GMT', + 'Content-Length', + '1651' +]); diff --git a/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_allows_working_with_an_authenticationrecord_that_is_serialized.js b/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_allows_working_with_an_authenticationrecord_that_is_serialized.js new file mode 100644 index 000000000000..d958fc8ef97c --- /dev/null +++ b/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_allows_working_with_an_authenticationrecord_that_is_serialized.js @@ -0,0 +1,182 @@ +let nock = require('nock'); + +module.exports.hash = "f5bf2adfd34dad7c3005f27aaa3d30d0"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '51947296-dc48-4a99-82f8-c57fa921bf00', + 'x-ms-ests-server', + '2.1.11530.17 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 11:42:09 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 11:42:09 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'b1b3a1d8-9269-48a4-b9a8-fc2f92c9a900', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 11:42:10 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 11:42:09 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&username=azure_username&password=azure_password&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=password&client_info=1&client-request-id=client-request-id") + .reply(200, {"token_type":"Bearer","scope":"https://vault.azure.net/user_impersonation https://vault.azure.net/.default","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token","refresh_token":"refresh_token","id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz","client_info":"eyJ1aWQiOiI4NTJiZWZkNS0wMGIzLTQyM2EtODg3MS02Zjc5ODQ0ODJjMzQiLCJ1dGlkIjoiZmY0OGJjM2UtMzdhMS00MzNlLWE3OTgtYjU0NDBjOTU0MzdiIn0"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '4178', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '787fb876-adb8-4779-87ef-d2545d4ebf00', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 11:42:10 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 11:42:09 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '10c11d93-756f-4db4-b32d-37b9d8cf3400', + 'x-ms-ests-server', + '2.1.11530.17 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 11:42:10 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 11:42:09 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'b1b3a1d8-9269-48a4-b9a8-fc2fabc9a900', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 11:42:10 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 11:42:09 GMT', + 'Content-Length', + '1651' +]); diff --git a/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_authenticates_silently_after_the_initial_request.js b/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_authenticates_silently_after_the_initial_request.js new file mode 100644 index 000000000000..b106966c86ef --- /dev/null +++ b/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_authenticates_silently_after_the_initial_request.js @@ -0,0 +1,182 @@ +let nock = require('nock'); + +module.exports.hash = "3eff3e3d758f18b70903a4dfadbdae5a"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'f0210c0a-0816-42f6-b01a-b0d9be810401', + 'x-ms-ests-server', + '2.1.11530.17 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:13 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:13 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '70a97e1c-423a-4cff-baed-a9c5a6bfa700', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:13 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:13 GMT', + 'Content-Length', + '1651' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&username=azure_username&password=azure_password&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=password&client_info=1&client-request-id=client-request-id") + .reply(200, {"token_type":"Bearer","scope":"https://vault.azure.net/user_impersonation https://vault.azure.net/.default","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token","refresh_token":"refresh_token","id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz","client_info":"eyJ1aWQiOiI4NTJiZWZkNS0wMGIzLTQyM2EtODg3MS02Zjc5ODQ0ODJjMzQiLCJ1dGlkIjoiZmY0OGJjM2UtMzdhMS00MzNlLWE3OTgtYjU0NDBjOTU0MzdiIn0"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '4186', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '816bc474-783d-480e-98d2-43ca5817a800', + 'x-ms-ests-server', + '2.1.11562.8 - SCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:13 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:13 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '00778f13-a2f5-4d48-b984-a66094cc3600', + 'x-ms-ests-server', + '2.1.11530.17 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:13 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:13 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '1651', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '3cee4ec5-3913-4213-84d6-4f553744f100', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:13 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:13 GMT' +]); diff --git a/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js b/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js new file mode 100644 index 000000000000..c4d51705da9c --- /dev/null +++ b/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_authenticates_silently_with_tokencachepersistenceoptions.js @@ -0,0 +1,182 @@ +let nock = require('nock'); + +module.exports.hash = "ef0285e7747d926394e06b6585a84a80"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '2e336e9a-243e-402b-9da4-988a36729a00', + 'x-ms-ests-server', + '2.1.11530.17 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:14 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:14 GMT', + 'Content-Length', + '980' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'dc392ca4-1471-4064-a13f-fa839247e100', + 'x-ms-ests-server', + '2.1.11562.8 - WUS2 ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:14 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:14 GMT', + 'Content-Length', + '1651' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .post('/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token', "client_id=azure_client_id&username=azure_username&password=azure_password&scope=https%3A%2F%2Fvault.azure.net%2F.default%20openid%20profile%20offline_access&grant_type=password&client_info=1&client-request-id=client-request-id") + .reply(200, {"token_type":"Bearer","scope":"https://vault.azure.net/user_impersonation https://vault.azure.net/.default","expires_in":3599,"ext_expires_in":3599,"access_token":"access_token","refresh_token":"refresh_token","id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz","client_info":"eyJ1aWQiOiI4NTJiZWZkNS0wMGIzLTQyM2EtODg3MS02Zjc5ODQ0ODJjMzQiLCJ1dGlkIjoiZmY0OGJjM2UtMzdhMS00MzNlLWE3OTgtYjU0NDBjOTU0MzdiIn0"}, [ + 'Cache-Control', + 'no-store, no-cache', + 'Pragma', + 'no-cache', + 'Content-Length', + '4186', + 'Content-Type', + 'application/json; charset=utf-8', + 'Expires', + '-1', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + '2592b062-f209-4e76-ad39-39773db98b00', + 'x-ms-ests-server', + '2.1.11562.8 - NCUS ProdSlices', + 'x-ms-clitelem', + '1,0,0,,', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:14 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:14 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/common/discovery/instance') + .query(true) + .reply(200, {"tenant_discovery_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration","api-version":"1.1","metadata":[{"preferred_network":"login.microsoftonline.com","preferred_cache":"login.windows.net","aliases":["login.microsoftonline.com","login.windows.net","login.microsoft.com","sts.windows.net"]},{"preferred_network":"login.partner.microsoftonline.cn","preferred_cache":"login.partner.microsoftonline.cn","aliases":["login.partner.microsoftonline.cn","login.chinacloudapi.cn"]},{"preferred_network":"login.microsoftonline.de","preferred_cache":"login.microsoftonline.de","aliases":["login.microsoftonline.de"]},{"preferred_network":"login.microsoftonline.us","preferred_cache":"login.microsoftonline.us","aliases":["login.microsoftonline.us","login.usgovcloudapi.net"]},{"preferred_network":"login-us.microsoftonline.com","preferred_cache":"login-us.microsoftonline.com","aliases":["login-us.microsoftonline.com"]}]}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Length', + '980', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'ebc7b203-e186-4b0d-8976-ffafbecd1800', + 'x-ms-ests-server', + '2.1.11530.17 - NCUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:14 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:14 GMT' +]); + +nock('https://login.microsoftonline.com:443', {"encodedQueryParams":true}) + .get('/12345678-1234-1234-1234-123456789012/v2.0/.well-known/openid-configuration') + .reply(200, {"token_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/token","token_endpoint_auth_methods_supported":["client_secret_post","private_key_jwt","client_secret_basic"],"jwks_uri":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/discovery/v2.0/keys","response_modes_supported":["query","fragment","form_post"],"subject_types_supported":["pairwise"],"id_token_signing_alg_values_supported":["RS256"],"response_types_supported":["code","id_token","code id_token","id_token token"],"scopes_supported":["openid","profile","email","offline_access"],"issuer":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","request_uri_parameter_supported":false,"userinfo_endpoint":"https://graph.microsoft.com/oidc/userinfo","authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/authorize","device_authorization_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/devicecode","http_logout_supported":true,"frontchannel_logout_supported":true,"end_session_endpoint":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/oauth2/v2.0/logout","claims_supported":["sub","iss","cloud_instance_name","cloud_instance_host_name","cloud_graph_host_name","msgraph_host","aud","exp","iat","auth_time","acr","nonce","preferred_username","name","tid","ver","at_hash","c_hash","email"],"tenant_region_scope":"NA","cloud_instance_name":"microsoftonline.com","cloud_graph_host_name":"graph.windows.net","msgraph_host":"graph.microsoft.com","rbac_url":"https://pas.windows.net"}, [ + 'Cache-Control', + 'max-age=86400, private', + 'Content-Type', + 'application/json; charset=utf-8', + 'Strict-Transport-Security', + 'max-age=31536000; includeSubDomains', + 'X-Content-Type-Options', + 'nosniff', + 'Access-Control-Allow-Origin', + '*', + 'Access-Control-Allow-Methods', + 'GET, OPTIONS', + 'P3P', + 'CP="DSP CUR OTPi IND OTRi ONL FIN"', + 'x-ms-request-id', + 'a484aa5b-fbe1-4ac5-a11a-9b124b44ab00', + 'x-ms-ests-server', + '2.1.11562.8 - EUS ProdSlices', + 'Set-Cookie', + 'fpc=fpc;; expires=Fri, 16-Apr-2021 03:41:14 GMT; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'esctx=esctx; domain=.login.microsoftonline.com; path=/; secure; HttpOnly; SameSite=None', + 'Set-Cookie', + 'x-ms-gateway-slice=estsfd; path=/; secure; samesite=none; httponly', + 'Set-Cookie', + 'stsservicecookie=estsfd; path=/; secure; samesite=none; httponly', + 'Date', + 'Wed, 17 Mar 2021 03:41:14 GMT', + 'Content-Length', + '1651' +]); diff --git a/sdk/identity/identity/review/identity.api.md b/sdk/identity/identity/review/identity.api.md index 83908f09ff9c..d5c169b81461 100644 --- a/sdk/identity/identity/review/identity.api.md +++ b/sdk/identity/identity/review/identity.api.md @@ -33,14 +33,18 @@ export const AuthenticationErrorName = "AuthenticationError"; // @public export interface AuthenticationRecord { - authority?: string; - environment: string; + authority: string; homeAccountId: string; - localAccountId: string; + serialize: () => string; tenantId: string; username: string; } +// @public +export class AuthenticationRequired extends CredentialUnavailable { + constructor(message?: string); +} + // @public export class AuthorizationCodeCredential implements TokenCredential { constructor(tenantId: string | "common", clientId: string, clientSecret: string, authorizationCode: string, redirectUri: string, options?: TokenCredentialOptions); @@ -79,24 +83,32 @@ export class ChainedTokenCredential implements TokenCredential { // @public export class ClientCertificateCredential implements TokenCredential { constructor(tenantId: string, clientId: string, certificatePath: string, options?: ClientCertificateCredentialOptions); - getToken(scopes: string | string[], options?: GetTokenOptions): Promise; + getToken(scopes: string | string[], options?: GetTokenOptions): Promise; } // @public -export interface ClientCertificateCredentialOptions extends TokenCredentialOptions { +export interface ClientCertificateCredentialOptions extends PersistentCredentialOptions { sendCertificateChain?: boolean; } // @public export class ClientSecretCredential implements TokenCredential { - constructor(tenantId: string, clientId: string, clientSecret: string, options?: TokenCredentialOptions); - getToken(scopes: string | string[], options?: GetTokenOptions): Promise; + constructor(tenantId: string, clientId: string, clientSecret: string, options?: ClientSecretCredentialOptions); + getToken(scopes: string | string[], options?: GetTokenOptions): Promise; } +// @public +export interface ClientSecretCredentialOptions extends PersistentCredentialOptions { +} + // @public export class CredentialUnavailable extends Error { + constructor(message?: string); } +// @public +export const CredentialUnavailableName = "CredentialUnavailable"; + // @public export class DefaultAzureCredential extends ChainedTokenCredential { constructor(tokenCredentialOptions?: DefaultAzureCredentialOptions); @@ -108,12 +120,23 @@ export interface DefaultAzureCredentialOptions extends TokenCredentialOptions { tenantId?: string; } +// @public +export function deserializeAuthenticationRecord(serializedRecord: string): AuthenticationRecord; + // @public export class DeviceCodeCredential implements TokenCredential { - constructor(tenantId?: string, clientId?: string, userPromptCallback?: DeviceCodePromptCallback, options?: TokenCredentialOptions); - getToken(scopes: string | string[], options?: GetTokenOptions): Promise; + constructor(options?: DeviceCodeCredentialOptions); + constructor(tenantId?: string, clientId?: string, userPromptCallback?: DeviceCodePromptCallback, options?: DeviceCodeCredentialOptions); + authenticate(scopes: string | string[], options?: GetTokenOptions): Promise; + getToken(scopes: string | string[], options?: GetTokenOptions): Promise; } +// @public +export interface DeviceCodeCredentialOptions extends InteractiveCredentialOptions { + clientId?: string; + tenantId?: string; +} + // @public export interface DeviceCodeInfo { message: string; @@ -150,29 +173,33 @@ export type InteractiveBrowserAuthenticationFlow = "implicit-grant" | "auth-code // @public export class InteractiveBrowserCredential implements TokenCredential { - constructor(options?: InteractiveBrowserCredentialOptions); - getToken(scopes: string | string[], _options?: GetTokenOptions): Promise; + constructor(options?: InteractiveBrowserCredentialOptions | InteractiveBrowserCredentialBrowserOptions); + authenticate(scopes: string | string[], options?: GetTokenOptions): Promise; + getToken(scopes: string | string[], options?: GetTokenOptions): Promise; } // @public -export interface InteractiveBrowserCredentialBrowserOptions extends InteractiveBrowserCredentialCommonOptions { +export type InteractiveBrowserCredentialBrowserOptions = TokenCredentialOptions & InteractiveCredentialOptions & { + redirectUri?: string | (() => string); + postLogoutRedirectUri?: string | (() => string); + tenantId?: string; clientId: string; -} + loginStyle?: BrowserLoginStyle; + flow?: InteractiveBrowserAuthenticationFlow; +}; // @public -export interface InteractiveBrowserCredentialCommonOptions extends TokenCredentialOptions { - authenticationRecord?: AuthenticationRecord; - correlationId?: string; - flow?: InteractiveBrowserAuthenticationFlow; - loginStyle?: BrowserLoginStyle; - postLogoutRedirectUri?: string | (() => string); +export type InteractiveBrowserCredentialOptions = TokenCredentialOptions & InteractiveCredentialOptions & { redirectUri?: string | (() => string); + postLogoutRedirectUri?: string | (() => string); tenantId?: string; -} + clientId?: string; +}; // @public -export interface InteractiveBrowserCredentialOptions extends InteractiveBrowserCredentialCommonOptions { - clientId?: string; +export interface InteractiveCredentialOptions extends PersistentCredentialOptions { + authenticationRecord?: AuthenticationRecord; + disableAutomaticAuthentication?: boolean; } // @public @@ -185,6 +212,17 @@ export class ManagedIdentityCredential implements TokenCredential { getToken(scopes: string | string[], options?: GetTokenOptions): Promise; } +// @public +export interface PersistentCredentialOptions extends TokenCredentialOptions { + tokenCachePersistenceOptions?: TokenCachePersistenceOptions; +} + +// @public +export interface TokenCachePersistenceOptions { + allowUnencryptedStorage?: boolean; + name?: string; +} + export { TokenCredential } // @public @@ -194,10 +232,15 @@ export interface TokenCredentialOptions extends PipelineOptions { // @public export class UsernamePasswordCredential implements TokenCredential { - constructor(tenantIdOrName: string, clientId: string, username: string, password: string, options?: TokenCredentialOptions); - getToken(scopes: string | string[], options?: GetTokenOptions): Promise; + constructor(tenantId: string, clientId: string, username: string, password: string, options?: UsernamePasswordCredentialOptions); + authenticate(scopes: string | string[], options?: GetTokenOptions): Promise; + getToken(scopes: string | string[], options?: GetTokenOptions): Promise; } +// @public +export interface UsernamePasswordCredentialOptions extends InteractiveCredentialOptions { +} + // @public export class VisualStudioCodeCredential implements TokenCredential { constructor(options?: VisualStudioCodeCredentialOptions); diff --git a/sdk/identity/identity/src/client/errors.ts b/sdk/identity/identity/src/client/errors.ts index 2905c1f8f1d5..a4cdaee5bbf7 100644 --- a/sdk/identity/identity/src/client/errors.ts +++ b/sdk/identity/identity/src/client/errors.ts @@ -62,12 +62,22 @@ function isErrorResponse(errorResponse: any): errorResponse is OAuthErrorRespons ); } +/** + * The Error.name value of an CredentialUnavailable + */ +export const CredentialUnavailableName = "CredentialUnavailable"; + /** * This signifies that the credential that was tried in a chained credential * was not available to be used as the credential. Rather than treating this as * an error that should halt the chain, it's caught and the chain continues */ -export class CredentialUnavailable extends Error {} +export class CredentialUnavailable extends Error { + constructor(message?: string) { + super(message); + this.name = CredentialUnavailableName; + } +} /** * The Error.name value of an AuthenticationError diff --git a/sdk/identity/identity/src/client/identityClient.ts b/sdk/identity/identity/src/client/identityClient.ts index 8e7075359224..a937f0283567 100644 --- a/sdk/identity/identity/src/client/identityClient.ts +++ b/sdk/identity/identity/src/client/identityClient.ts @@ -13,19 +13,19 @@ import { isNode } from "@azure/core-http"; import { INetworkModule, NetworkRequestOptions, NetworkResponse } from "@azure/msal-node"; - import { CanonicalCode } from "@opentelemetry/api"; +import { AbortController, AbortSignalLike } from "@azure/abort-controller"; import { AuthenticationError, AuthenticationErrorName } from "./errors"; -import { createSpan } from "../util/tracing"; -import { logger } from "../util/logging"; import { getAuthorityHostEnvironment } from "../util/authHostEnv"; import { getIdentityTokenEndpointSuffix } from "../util/identityTokenEndpoint"; - -const DefaultAuthorityHost = "https://login.microsoftonline.com"; +import { DefaultAuthorityHost } from "../constants"; +import { createSpan } from "../util/tracing"; +import { logger } from "../util/logging"; /** * An internal type used to communicate details of a token request's * response that should not be sent back as part of the access token. + * @internal */ export interface TokenResponse { /** @@ -39,14 +39,27 @@ export interface TokenResponse { refreshToken?: string; } +/** + * The network module used by the Identity credentials. + * + * It allows for credentials to abort any pending request independently of the MSAL flow, + * by calling to the `abortRequests()` method. + * + * @internal + */ export class IdentityClient extends ServiceClient implements INetworkModule { public authorityHost: string; + private abortControllers: Map; constructor(options?: TokenCredentialOptions) { if (isNode) { options = options || getAuthorityHostEnvironment(); } - options = options || IdentityClient.getDefaultOptions(); + // Only if the authorityHost is not provided, we use the default one. + options = { + authorityHost: DefaultAuthorityHost, + ...options + }; super( undefined, createPipelineFromOptions({ @@ -64,6 +77,8 @@ export class IdentityClient extends ServiceClient implements INetworkModule { if (!this.baseUri.startsWith("https:")) { throw new Error("The authorityHost address must use the 'https' protocol."); } + + this.abortControllers = new Map(); } createWebResource(requestOptions: RequestPrepareOptions): WebResource { @@ -188,11 +203,61 @@ export class IdentityClient extends ServiceClient implements INetworkModule { } } + // Here is a custom layer that allows us to abort requests that go through MSAL, + // since MSAL doesn't allow us to pass options all the way through. + + generateAbortSignal(correlationId?: string): AbortSignalLike { + const controller = new AbortController(); + const key = correlationId || "noCorrelationId"; + + const controllers = this.abortControllers.get(key) || []; + controllers.push(controller); + this.abortControllers.set(key, controllers); + + return controller.signal; + } + + abortRequests(correlationId?: string): void { + const key = correlationId || "noCorrelationId"; + const controllers = [ + ...(this.abortControllers.get(key) || []), + // MSAL passes no correlation ID to the get requests... + ...(this.abortControllers.get("noCorrelationId") || []) + ]; + if (!controllers.length) { + return; + } + for (const controller of controllers) { + controller.abort(); + } + this.abortControllers.set(key, undefined); + } + + getCorrelationId(options?: NetworkRequestOptions): string | undefined { + const parameter = options?.body + ?.split("&") + .map((part) => part.split("=")) + .find(([key]) => key === "client-request-id"); + return parameter && parameter.length ? parameter[1] : undefined; + } + + // The MSAL network module methods follow + sendGetRequestAsync( url: string, options?: NetworkRequestOptions ): Promise> { - const webResource = new WebResource(url, "GET", options?.body, {}, options?.headers); + const webResource = new WebResource( + url, + "GET", + options?.body, + {}, + options?.headers, + false, + false, + // MSAL doesn't send the correlation ID on the get requests. + this.generateAbortSignal() + ); return this.sendRequest(webResource).then((response) => { return { @@ -207,7 +272,17 @@ export class IdentityClient extends ServiceClient implements INetworkModule { url: string, options?: NetworkRequestOptions ): Promise> { - const webResource = new WebResource(url, "POST", options?.body, {}, options?.headers); + const webResource = new WebResource( + url, + "POST", + options?.body, + {}, + options?.headers, + false, + false, + // MSAL doesn't send the correlation ID on the get requests. + this.generateAbortSignal(this.getCorrelationId(options)) + ); return this.sendRequest(webResource).then((response) => { return { @@ -217,12 +292,6 @@ export class IdentityClient extends ServiceClient implements INetworkModule { }; }); } - - static getDefaultOptions(): TokenCredentialOptions { - return { - authorityHost: DefaultAuthorityHost - }; - } } /** @@ -231,8 +300,8 @@ export class IdentityClient extends ServiceClient implements INetworkModule { */ export interface TokenCredentialOptions extends PipelineOptions { /** - * The authority host to use for authentication requests. The default is - * "https://login.microsoftonline.com". + * The authority host to use for authentication requests. + * The default is "https://login.microsoftonline.com". */ authorityHost?: string; } diff --git a/sdk/identity/identity/src/client/msalClient.ts b/sdk/identity/identity/src/client/msalClient.ts deleted file mode 100644 index 7a8f3767bf6d..000000000000 --- a/sdk/identity/identity/src/client/msalClient.ts +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import { CredentialUnavailable } from "./errors"; -import { - PublicClientApplication, - Configuration, - AuthorizationCodeRequest, - AuthenticationResult, - DeviceCodeRequest, - ConfidentialClientApplication, - ClientCredentialRequest, - NetworkRequestOptions, - NetworkResponse, - INetworkModule -} from "@azure/msal-node"; -import axios, { AxiosRequestConfig } from "axios"; - -import { IdentityClient, TokenCredentialOptions } from "./identityClient"; -import { AccessToken } from "@azure/core-http"; -import { credentialLogger } from "../util/logging"; -import { NodeAuthOptions } from "@azure/msal-node/dist/config/Configuration"; - -const logger = credentialLogger("InteractiveBrowserCredential"); - -/** - * The record to use to find the cached tokens in the cache - */ -export interface AuthenticationRecord { - /** - * The associated authority, if used - */ - authority?: string; - - /** - * The home account Id - */ - homeAccountId: string; - - /** - * The login environment, eg "login.windows.net" - */ - environment: string; - - /** - * The associated tenant ID - */ - tenantId: string; - - /** - * Local, tenant-specific account identifer for this account object, usually used in legacy cases - */ - localAccountId: string; - - /** - * The username of the logged in account - */ - username: string; -} - -export class AuthenticationRequired extends CredentialUnavailable {} - -export class MsalClient { - private persistenceEnabled: boolean; - private authenticationRecord: AuthenticationRecord | undefined; - private identityClient: IdentityClient; - private pca: PublicClientApplication | undefined; - private cca: ConfidentialClientApplication | undefined; - private msalConfig: NodeAuthOptions; - - constructor( - msalConfig: NodeAuthOptions, - persistenceEnabled: boolean, - authenticationRecord?: AuthenticationRecord, - options?: TokenCredentialOptions - ) { - this.identityClient = new IdentityClient(options); - this.msalConfig = msalConfig; - this.persistenceEnabled = persistenceEnabled; - this.authenticationRecord = authenticationRecord; - } - - async prepareClientApplications(): Promise { - // If we've already initialized the public client application, return - if (this.pca) { - return; - } - - // Construct the public client application, since it hasn't been initialized, yet - const clientConfig: Configuration = { - auth: this.msalConfig, - cache: undefined, - system: { networkClient: this.identityClient } - }; - - this.pca = new PublicClientApplication(clientConfig); - } - - async acquireTokenFromCache(scopes: string[]): Promise { - await this.prepareClientApplications(); - - if (!this.persistenceEnabled || !this.authenticationRecord) { - throw new AuthenticationRequired(); - } - - const silentRequest = { - account: this.authenticationRecord!, - scopes - }; - - try { - const response = await this.pca!.acquireTokenSilent(silentRequest); - logger.info("Successful silent token acquisition"); - if (response && response.expiresOn) { - return { - expiresOnTimestamp: response.expiresOn.getTime(), - token: response.accessToken - }; - } else { - throw new AuthenticationRequired("Could not authenticate silently using the cache"); - } - } catch (e) { - throw new AuthenticationRequired("Could not authenticate silently using the cache"); - } - } - - async getAuthCodeUrl(request: { scopes: string[]; redirectUri: string }): Promise { - await this.prepareClientApplications(); - - return this.pca!.getAuthCodeUrl(request); - } - - async acquireTokenByCode( - request: AuthorizationCodeRequest - ): Promise { - await this.prepareClientApplications(); - - return this.pca!.acquireTokenByCode(request); - } - - async acquireTokenByDeviceCode(request: DeviceCodeRequest): Promise { - await this.prepareClientApplications(); - - return this.pca!.acquireTokenByDeviceCode(request); - } - - async acquireTokenByClientCredential( - request: ClientCredentialRequest - ): Promise { - await this.prepareClientApplications(); - - return this.cca!.acquireTokenByClientCredential(request); - } -} - -export enum HttpMethod { - GET = "get", - POST = "post" -} -/** - * This class implements the API for network requests. - */ -export class HttpClient implements INetworkModule { - /** - * Http Get request - * @param url - - * @param options - - */ - async sendGetRequestAsync( - url: string, - options?: NetworkRequestOptions - ): Promise> { - const request: AxiosRequestConfig = { - method: HttpMethod.GET, - url: url, - headers: options && options.headers, - validateStatus: () => true - }; - - const response = await axios(request); - const out = { - headers: response.headers, - body: response.data as T, - status: response.status - }; - return out; - } - - /** - * Http Post request - * @param url - - * @param options - - */ - async sendPostRequestAsync( - url: string, - options?: NetworkRequestOptions - ): Promise> { - const request: AxiosRequestConfig = { - method: HttpMethod.POST, - url: url, - data: (options && options.body) || "", - headers: options && options.headers, - validateStatus: () => true - }; - - const response = await axios(request); - const out = { - headers: response.headers, - body: response.data as T, - status: response.status - }; - - return out; - } -} diff --git a/sdk/identity/identity/src/constants.ts b/sdk/identity/identity/src/constants.ts index 3ba1d3f3ce21..799bd55b1e3b 100644 --- a/sdk/identity/identity/src/constants.ts +++ b/sdk/identity/identity/src/constants.ts @@ -37,3 +37,8 @@ export enum AzureAuthorityHosts { */ AzurePublicCloud = "https://login.microsoftonline.com" } + +/** + * The default authority host. + */ +export const DefaultAuthorityHost = AzureAuthorityHosts.AzurePublicCloud; diff --git a/sdk/identity/identity/src/credentials/chainedTokenCredential.ts b/sdk/identity/identity/src/credentials/chainedTokenCredential.ts index 1f7330248678..9d846ab12f3b 100644 --- a/sdk/identity/identity/src/credentials/chainedTokenCredential.ts +++ b/sdk/identity/identity/src/credentials/chainedTokenCredential.ts @@ -2,7 +2,7 @@ // Licensed under the MIT license. import { AccessToken, TokenCredential, GetTokenOptions } from "@azure/core-http"; -import { AggregateAuthenticationError, CredentialUnavailable } from "../client/errors"; +import { AggregateAuthenticationError } from "../client/errors"; import { createSpan } from "../util/tracing"; import { CanonicalCode } from "@opentelemetry/api"; import { credentialLogger, formatSuccess, formatError } from "../util/logging"; @@ -64,7 +64,7 @@ export class ChainedTokenCredential implements TokenCredential { try { token = await this._sources[i].getToken(scopes, updatedOptions); } catch (err) { - if (err instanceof CredentialUnavailable) { + if (err.name === "CredentialUnavailable") { errors.push(err); } else { logger.getToken.info(formatError(scopes, err)); diff --git a/sdk/identity/identity/src/credentials/clientCertificateCredential.ts b/sdk/identity/identity/src/credentials/clientCertificateCredential.ts index 03efcfe5e5de..0b8e975704b9 100644 --- a/sdk/identity/identity/src/credentials/clientCertificateCredential.ts +++ b/sdk/identity/identity/src/credentials/clientCertificateCredential.ts @@ -1,50 +1,25 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import qs from "qs"; -import jws from "jws"; -import { v4 as uuidV4 } from "uuid"; -import { readFileSync } from "fs"; -import { createHash } from "crypto"; -import { TokenCredential, GetTokenOptions, AccessToken } from "@azure/core-http"; -import { IdentityClient } from "../client/identityClient"; +import { AccessToken, GetTokenOptions, TokenCredential } from "@azure/core-http"; +import { MsalClientCertificate } from "../msal/nodeFlows/msalClientCertificate"; +import { credentialLogger } from "../util/logging"; +import { trace } from "../util/tracing"; +import { MsalFlow } from "../msal/flows"; import { ClientCertificateCredentialOptions } from "./clientCertificateCredentialOptions"; -import { createSpan } from "../util/tracing"; -import { AuthenticationErrorName } from "../client/errors"; -import { CanonicalCode } from "@opentelemetry/api"; -import { credentialLogger, formatSuccess, formatError } from "../util/logging"; -import { getIdentityTokenEndpointSuffix } from "../util/identityTokenEndpoint"; -import { checkTenantId } from "../util/checkTenantId"; - -const SelfSignedJwtLifetimeMins = 10; - -function timestampInSeconds(date: Date): number { - return Math.floor(date.getTime() / 1000); -} - -function addMinutes(date: Date, minutes: number): Date { - date.setMinutes(date.getMinutes() + minutes); - return date; -} const logger = credentialLogger("ClientCertificateCredential"); /** * Enables authentication to Azure Active Directory using a PEM-encoded - * certificate that is assigned to an App Registration. More information + * certificate that is assigned to an App Registration. More information * on how to configure certificate authentication can be found here: * * https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials#register-your-certificate-with-azure-ad * */ export class ClientCertificateCredential implements TokenCredential { - private identityClient: IdentityClient; - private tenantId: string; - private clientId: string; - private certificateString: string; - private certificateThumbprint: string; - private certificateX5t: string; - private certificateX5c?: Array; + private msalFlow: MsalFlow; /** * Creates an instance of the ClientCertificateCredential with the details @@ -59,45 +34,17 @@ export class ClientCertificateCredential implements TokenCredential { tenantId: string, clientId: string, certificatePath: string, - options?: ClientCertificateCredentialOptions + options: ClientCertificateCredentialOptions = {} ) { - checkTenantId(logger, tenantId); - - this.identityClient = new IdentityClient(options); - this.tenantId = tenantId; - this.clientId = clientId; - this.certificateString = readFileSync(certificatePath, "utf8"); - - const certificatePattern = /(-+BEGIN CERTIFICATE-+)(\n\r?|\r\n?)([A-Za-z0-9+/\n\r]+=*)(\n\r?|\r\n?)(-+END CERTIFICATE-+)/g; - - const publicKeys: string[] = []; - - // Match all possible certificates, in the order they are in the file. These will form the chain that is used for x5c - let match; - do { - match = certificatePattern.exec(this.certificateString); - if (match) { - publicKeys.push(match[3]); - } - } while (match); - - if (publicKeys.length === 0) { - const error = new Error( - "The file at the specified path does not contain a PEM-encoded certificate." - ); - logger.info(formatError("", error)); - throw error; - } - - this.certificateThumbprint = createHash("sha1") - .update(Buffer.from(publicKeys[0], "base64")) - .digest("hex") - .toUpperCase(); - - this.certificateX5t = Buffer.from(this.certificateThumbprint, "hex").toString("base64"); - if (options && options.sendCertificateChain) { - this.certificateX5c = publicKeys; - } + this.msalFlow = new MsalClientCertificate({ + ...options, + certificatePath, + logger, + clientId, + tenantId, + sendCertificateChain: options.sendCertificateChain, + tokenCredentialOptions: options + }); } /** @@ -110,84 +57,10 @@ export class ClientCertificateCredential implements TokenCredential { * @param options - The options used to configure any requests this * TokenCredential implementation might make. */ - public async getToken( - scopes: string | string[], - options?: GetTokenOptions - ): Promise { - const { span, updatedOptions } = createSpan("ClientCertificateCredential-getToken", options); - try { - const tokenId = uuidV4(); - const urlSuffix = getIdentityTokenEndpointSuffix(this.tenantId); - const audienceUrl = `${this.identityClient.authorityHost}/${this.tenantId}/${urlSuffix}`; - let header: jws.Header; - - if (this.certificateX5c) { - header = { - typ: "JWT", - alg: "RS256", - x5t: this.certificateX5t, - x5c: this.certificateX5c - }; - } else { - header = { - typ: "JWT", - alg: "RS256", - x5t: this.certificateX5t - }; - } - - const payload = { - iss: this.clientId, - sub: this.clientId, - aud: audienceUrl, - jti: tokenId, - nbf: timestampInSeconds(new Date()), - exp: timestampInSeconds(addMinutes(new Date(), SelfSignedJwtLifetimeMins)) - }; - - const clientAssertion = jws.sign({ - header, - payload, - secret: this.certificateString - }); - - const webResource = this.identityClient.createWebResource({ - url: audienceUrl, - method: "POST", - disableJsonStringifyOnBody: true, - deserializationMapper: undefined, - body: qs.stringify({ - response_type: "token", - grant_type: "client_credentials", - client_id: this.clientId, - client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", - client_assertion: clientAssertion, - scope: typeof scopes === "string" ? scopes : scopes.join(" ") - }), - headers: { - Accept: "application/json", - "Content-Type": "application/x-www-form-urlencoded" - }, - abortSignal: options && options.abortSignal, - spanOptions: updatedOptions?.tracingOptions?.spanOptions - }); - - const tokenResponse = await this.identityClient.sendTokenRequest(webResource); - logger.getToken.info(formatSuccess(scopes)); - return (tokenResponse && tokenResponse.accessToken) || null; - } catch (err) { - const code = - err.name === AuthenticationErrorName - ? CanonicalCode.UNAUTHENTICATED - : CanonicalCode.UNKNOWN; - span.setStatus({ - code, - message: err.message - }); - logger.getToken.info(formatError("", err)); - throw err; - } finally { - span.end(); - } + async getToken(scopes: string | string[], options: GetTokenOptions = {}): Promise { + return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => { + const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + return this.msalFlow.getToken(arrayScopes, newOptions); + }); } } diff --git a/sdk/identity/identity/src/credentials/clientCertificateCredentialOptions.ts b/sdk/identity/identity/src/credentials/clientCertificateCredentialOptions.ts index 511f608f4533..ccee9c5e5da1 100644 --- a/sdk/identity/identity/src/credentials/clientCertificateCredentialOptions.ts +++ b/sdk/identity/identity/src/credentials/clientCertificateCredentialOptions.ts @@ -1,12 +1,12 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { TokenCredentialOptions } from "../client/identityClient"; +import { PersistentCredentialOptions } from "./persistentCredentialOptions"; /** - * Defines options for the SubjectNameAndIssuerCredential class. + * Optional parameters for the {@link ClientCertificateCredential} class. */ -export interface ClientCertificateCredentialOptions extends TokenCredentialOptions { +export interface ClientCertificateCredentialOptions extends PersistentCredentialOptions { /** * Option to include x5c header for SubjectName and Issuer name authorization. * Set this option to send base64 encoded public certificate in the client assertion header as an x5c claim diff --git a/sdk/identity/identity/src/credentials/clientSecretCredential.ts b/sdk/identity/identity/src/credentials/clientSecretCredential.ts index 4024515025e8..e19a949a7346 100644 --- a/sdk/identity/identity/src/credentials/clientSecretCredential.ts +++ b/sdk/identity/identity/src/credentials/clientSecretCredential.ts @@ -1,30 +1,25 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import qs from "qs"; -import { TokenCredential, GetTokenOptions, AccessToken } from "@azure/core-http"; -import { TokenCredentialOptions, IdentityClient } from "../client/identityClient"; -import { createSpan } from "../util/tracing"; -import { AuthenticationErrorName } from "../client/errors"; -import { CanonicalCode } from "@opentelemetry/api"; -import { credentialLogger, formatError, formatSuccess } from "../util/logging"; -import { getIdentityTokenEndpointSuffix } from "../util/identityTokenEndpoint"; +import { AccessToken, GetTokenOptions, TokenCredential } from "@azure/core-http"; +import { MsalClientSecret } from "../msal/nodeFlows/msalClientSecret"; +import { credentialLogger } from "../util/logging"; +import { trace } from "../util/tracing"; +import { MsalFlow } from "../msal/flows"; +import { ClientSecretCredentialOptions } from "./clientSecretCredentialOptions"; const logger = credentialLogger("ClientSecretCredential"); /** * Enables authentication to Azure Active Directory using a client secret - * that was generated for an App Registration. More information on how + * that was generated for an App Registration. More information on how * to configure a client secret can be found here: * * https://docs.microsoft.com/en-us/azure/active-directory/develop/quickstart-configure-app-access-web-apis#add-credentials-to-your-web-application * */ export class ClientSecretCredential implements TokenCredential { - private identityClient: IdentityClient; - private tenantId: string; - private clientId: string; - private clientSecret: string; + private msalFlow: MsalFlow; /** * Creates an instance of the ClientSecretCredential with the details @@ -40,12 +35,16 @@ export class ClientSecretCredential implements TokenCredential { tenantId: string, clientId: string, clientSecret: string, - options?: TokenCredentialOptions + options: ClientSecretCredentialOptions = {} ) { - this.identityClient = new IdentityClient(options); - this.tenantId = tenantId; - this.clientId = clientId; - this.clientSecret = clientSecret; + this.msalFlow = new MsalClientSecret({ + ...options, + logger, + clientId, + tenantId, + clientSecret, + tokenCredentialOptions: options + }); } /** @@ -58,49 +57,10 @@ export class ClientSecretCredential implements TokenCredential { * @param options - The options used to configure any requests this * TokenCredential implementation might make. */ - public async getToken( - scopes: string | string[], - options?: GetTokenOptions - ): Promise { - const { span, updatedOptions } = createSpan("ClientSecretCredential-getToken", options); - try { - const urlSuffix = getIdentityTokenEndpointSuffix(this.tenantId); - const webResource = this.identityClient.createWebResource({ - url: `${this.identityClient.authorityHost}/${this.tenantId}/${urlSuffix}`, - method: "POST", - disableJsonStringifyOnBody: true, - deserializationMapper: undefined, - body: qs.stringify({ - response_type: "token", - grant_type: "client_credentials", - client_id: this.clientId, - client_secret: this.clientSecret, - scope: typeof scopes === "string" ? scopes : scopes.join(" ") - }), - headers: { - Accept: "application/json", - "Content-Type": "application/x-www-form-urlencoded" - }, - abortSignal: options && options.abortSignal, - spanOptions: updatedOptions?.tracingOptions?.spanOptions - }); - - const tokenResponse = await this.identityClient.sendTokenRequest(webResource); - logger.getToken.info(formatSuccess(scopes)); - return (tokenResponse && tokenResponse.accessToken) || null; - } catch (err) { - const code = - err.name === AuthenticationErrorName - ? CanonicalCode.UNAUTHENTICATED - : CanonicalCode.UNKNOWN; - span.setStatus({ - code, - message: err.message - }); - logger.getToken.info(formatError(scopes, err)); - throw err; - } finally { - span.end(); - } + async getToken(scopes: string | string[], options: GetTokenOptions = {}): Promise { + return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => { + const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + return this.msalFlow.getToken(arrayScopes, newOptions); + }); } } diff --git a/sdk/identity/identity/src/credentials/clientSecretCredentialOptions.ts b/sdk/identity/identity/src/credentials/clientSecretCredentialOptions.ts new file mode 100644 index 000000000000..dfc5fa566740 --- /dev/null +++ b/sdk/identity/identity/src/credentials/clientSecretCredentialOptions.ts @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { PersistentCredentialOptions } from "./persistentCredentialOptions"; + +/** + * Optional parameters for the {@link ClientSecretCredential} class. + */ +export interface ClientSecretCredentialOptions extends PersistentCredentialOptions {} diff --git a/sdk/identity/identity/src/credentials/defaultAzureCredential.ts b/sdk/identity/identity/src/credentials/defaultAzureCredential.ts index f5688bd9c6c6..21caf5e02b84 100644 --- a/sdk/identity/identity/src/credentials/defaultAzureCredential.ts +++ b/sdk/identity/identity/src/credentials/defaultAzureCredential.ts @@ -9,26 +9,29 @@ import { AzureCliCredential } from "./azureCliCredential"; import { VisualStudioCodeCredential } from "./visualStudioCodeCredential"; /** - * Provides options to configure the default Azure credentials. + * Provides options to configure the {@link DefaultAzureCredential} class. */ export interface DefaultAzureCredentialOptions extends TokenCredentialOptions { /** - * Optionally pass in a Tenant ID to be used as part of the credential + * Optionally pass in a Tenant ID to be used as part of the credential. + * By default it may use a generic tenant ID depending on the underlying credential. */ tenantId?: string; /** - * Optionally pass in a user assigned client ID for the ManagedIdentityCredential + * Optionally pass in a user assigned client ID to be used by the {@link ManagedIdentityCredential}. + * This client ID can also be passed through to the {@link ManagedIdentityCredential} through the environment variable: AZURE_CLIENT_ID. */ managedIdentityClientId?: string; } /** - * Provides a default {@link ChainedTokenCredential} configuration for - * applications that will be deployed to Azure. The following credential - * types will be tried, in order: + * Provides a default {@link ChainedTokenCredential} configuration that should work for most applications that use the Azure SDK. + * The following credential types will be tried, in order: * * - {@link EnvironmentCredential} * - {@link ManagedIdentityCredential} + * - {@link AzureCliCredential} + * - {@link VisualStudioCodeCredential} * * Consult the documentation of these credential types for more information * on how they attempt authentication. @@ -37,22 +40,23 @@ export class DefaultAzureCredential extends ChainedTokenCredential { /** * Creates an instance of the DefaultAzureCredential class. * - * @param options - Options for configuring the client which makes the authentication request. + * @param options - Optional parameters. See {@link DefaultAzureCredentialOptions}. */ constructor(tokenCredentialOptions?: DefaultAzureCredentialOptions) { const credentials = []; credentials.push(new EnvironmentCredential(tokenCredentialOptions)); - // In case a user assigned ID has been provided. + // A client ID for the ManagedIdentityCredential + // can be provided either through the optional parameters or through the environment variables. const managedIdentityClientId = tokenCredentialOptions?.managedIdentityClientId || process.env.AZURE_CLIENT_ID; + // If a client ID is not provided, we will try with the system assigned ID. if (managedIdentityClientId) { credentials.push( new ManagedIdentityCredential(managedIdentityClientId, tokenCredentialOptions) ); } else { - // If the user didn't provide an ID, we'll try with a system assigned ID. credentials.push(new ManagedIdentityCredential(tokenCredentialOptions)); } diff --git a/sdk/identity/identity/src/credentials/deviceCodeCredential.ts b/sdk/identity/identity/src/credentials/deviceCodeCredential.ts index aebf6f5e21a8..e7e3cc678e83 100644 --- a/sdk/identity/identity/src/credentials/deviceCodeCredential.ts +++ b/sdk/identity/identity/src/credentials/deviceCodeCredential.ts @@ -1,47 +1,17 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { AccessToken, TokenCredential, GetTokenOptions } from "@azure/core-http"; -import { AuthenticationRequired, MsalClient } from "../client/msalClient"; -import { createSpan } from "../util/tracing"; -import { credentialLogger, formatError, formatSuccess } from "../util/logging"; -import { AuthenticationErrorName } from "../client/errors"; -import { CanonicalCode } from "@opentelemetry/api"; -import { TokenCredentialOptions } from "../client/identityClient"; -import { DeviceCodeRequest } from "@azure/msal-node"; -import { checkTenantId } from "../util/checkTenantId"; -import { DeveloperSignOnClientId } from "../constants"; - -/** - * Provides the user code and verification URI where the code must be - * entered. Also provides a message to display to the user which - * contains an instruction with these details. - */ -export interface DeviceCodeInfo { - /** - * The device code that the user must enter into the verification page. - */ - userCode: string; - - /** - * The verification URI to which the user must navigate to enter the device - * code. - */ - verificationUri: string; - - /** - * A message that may be shown to the user to instruct them on how to enter - * the device code in the page specified by the verification URI. - */ - message: string; -} - -/** - * Defines the signature of a callback which will be passed to - * DeviceCodeCredential for the purpose of displaying authentication - * details to the user. - */ -export type DeviceCodePromptCallback = (deviceCodeInfo: DeviceCodeInfo) => void; +import { AccessToken, GetTokenOptions, TokenCredential } from "@azure/core-http"; +import { credentialLogger } from "../util/logging"; +import { MsalDeviceCode } from "../msal/nodeFlows/msalDeviceCode"; +import { MsalFlow } from "../msal/flows"; +import { AuthenticationRecord } from "../msal/types"; +import { trace } from "../util/tracing"; +import { + DeviceCodeCredentialOptions, + DeviceCodeInfo, + DeviceCodePromptCallback +} from "./deviceCodeCredentialOptions"; const logger = credentialLogger("DeviceCodeCredential"); @@ -58,8 +28,8 @@ export function defaultDeviceCodePromptCallback(deviceCodeInfo: DeviceCodeInfo): * that the user can enter into https://microsoft.com/devicelogin. */ export class DeviceCodeCredential implements TokenCredential { - private userPromptCallback: DeviceCodePromptCallback; - private msalClient: MsalClient; + private msalFlow: MsalFlow; + private disableAutomaticAuthentication?: boolean; /** * Creates an instance of DeviceCodeCredential with the details needed @@ -68,39 +38,42 @@ export class DeviceCodeCredential implements TokenCredential { * @param tenantId - The Azure Active Directory tenant (directory) ID or name. * The default value is 'organizations'. * 'organizations' may be used when dealing with multi-tenant scenarios. + * Users can also pass the options as the first parameter, and skip the other parammeters entirely. * @param clientId - The client (application) ID of an App Registration in the tenant. * By default we will try to use the Azure CLI's client ID to authenticate. * @param userPromptCallback - A callback function that will be invoked to show - {@link DeviceCodeInfo} to the user. If left unassigned, we will automatically log the device code information and the authentication instructions in the console. - * @param options - Options for configuring the client which makes the authentication request. + * {@link DeviceCodeInfo} to the user. If left unassigned, we will automatically log the device code information and the authentication instructions in the console. + * Users can also pass the options as the third parameter, and skip the prompt callback entirely. + * @param options - Options for configuring the client which makes the authentication requests. */ + constructor(options?: DeviceCodeCredentialOptions); constructor( - tenantId: string = "organizations", - clientId: string = DeveloperSignOnClientId, - userPromptCallback: DeviceCodePromptCallback = defaultDeviceCodePromptCallback, - options?: TokenCredentialOptions + tenantId?: string, + clientId?: string, + userPromptCallback?: DeviceCodePromptCallback, + options?: DeviceCodeCredentialOptions + ); + constructor( + tenantIdOrOptions?: string | DeviceCodeCredentialOptions, + clientId?: string, + userPromptCallback?: DeviceCodePromptCallback, + options?: DeviceCodeCredentialOptions ) { - checkTenantId(logger, tenantId); - - this.userPromptCallback = userPromptCallback; - - let authorityHost; - if (options && options.authorityHost) { - if (options.authorityHost.endsWith("/")) { - authorityHost = options.authorityHost + tenantId; - } else { - authorityHost = options.authorityHost + "/" + tenantId; - } + let tenantId: string | undefined; + if (typeof tenantIdOrOptions === "string") { + tenantId = tenantIdOrOptions; } else { - authorityHost = "https://login.microsoftonline.com/" + tenantId; + options = tenantIdOrOptions; } - - this.msalClient = new MsalClient( - { clientId: clientId, authority: authorityHost }, - false, - undefined, - options - ); + this.msalFlow = new MsalDeviceCode({ + ...options, + logger, + clientId, + tenantId, + userPromptCallback: userPromptCallback || defaultDeviceCodePromptCallback, + tokenCredentialOptions: options || {} + }); + this.disableAutomaticAuthentication = options?.disableAutomaticAuthentication; } /** @@ -109,69 +82,44 @@ export class DeviceCodeCredential implements TokenCredential { * return null. If an error occurs during authentication, an {@link AuthenticationError} * containing failure details will be thrown. * + * If the user provided the option `disableAutomaticAuthentication`, + * once the token can't be retrieved silently, + * this method won't attempt to request user interaction to retrieve the token. + * * @param scopes - The list of scopes for which the token will have access. * @param options - The options used to configure any requests this * TokenCredential implementation might make. */ - async getToken( - scopes: string | string[], - options?: GetTokenOptions - ): Promise { - const { span } = createSpan("DeviceCodeCredential-getToken", options); - - const scopeArray = typeof scopes === "object" ? scopes : [scopes]; - - const deviceCodeRequest = { - deviceCodeCallback: this.userPromptCallback, - scopes: scopeArray - }; - - logger.info(`DeviceCodeCredential invoked. Scopes: ${scopeArray.join(", ")}`); - - return this.msalClient.acquireTokenFromCache(scopeArray).catch(async (e) => { - if (e instanceof AuthenticationRequired) { - try { - const token = await this.acquireTokenByDeviceCode(deviceCodeRequest, scopeArray); - logger.getToken.info(formatSuccess(scopeArray)); - return token; - } catch (err) { - const code = - err.name === AuthenticationErrorName - ? CanonicalCode.UNAUTHENTICATED - : CanonicalCode.UNKNOWN; - span.setStatus({ - code, - message: err.message - }); - logger.getToken.info(formatError(scopeArray, err)); - throw err; - } finally { - span.end(); - } - } else { - throw e; - } + async getToken(scopes: string | string[], options: GetTokenOptions = {}): Promise { + return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => { + const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + return this.msalFlow.getToken(arrayScopes, { + ...newOptions, + disableAutomaticAuthentication: this.disableAutomaticAuthentication + }); }); } - private async acquireTokenByDeviceCode( - deviceCodeRequest: DeviceCodeRequest, - scopes: string[] - ): Promise { - try { - const deviceResponse = await this.msalClient.acquireTokenByDeviceCode(deviceCodeRequest); - if (deviceResponse && deviceResponse.expiresOn) { - const expiresOnTimestamp = deviceResponse.expiresOn.getTime(); - logger.getToken.info(formatSuccess(scopes)); - return { - expiresOnTimestamp, - token: deviceResponse.accessToken - }; - } else { - throw new Error("Did not receive token with a valid expiration"); - } - } catch (error) { - throw new Error(`Device Authentication Error "${JSON.stringify(error)}"`); - } + /** + * Authenticates with Azure Active Directory and returns an access token if + * successful. If authentication cannot be performed at this time, this method may + * return null. If an error occurs during authentication, an {@link AuthenticationError} + * containing failure details will be thrown. + * + * If the token can't be retrieved silently, this method will require user interaction to retrieve the token. + * + * @param scopes - The list of scopes for which the token will have access. + * @param options - The options used to configure any requests this + * TokenCredential implementation might make. + */ + async authenticate( + scopes: string | string[], + options: GetTokenOptions = {} + ): Promise { + return trace(`${this.constructor.name}.authenticate`, options, async (newOptions) => { + const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + await this.msalFlow.getToken(arrayScopes, newOptions); + return this.msalFlow.getActiveAccount(); + }); } } diff --git a/sdk/identity/identity/src/credentials/deviceCodeCredentialOptions.ts b/sdk/identity/identity/src/credentials/deviceCodeCredentialOptions.ts new file mode 100644 index 000000000000..d311271ba4dc --- /dev/null +++ b/sdk/identity/identity/src/credentials/deviceCodeCredentialOptions.ts @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { InteractiveCredentialOptions } from "./interactiveCredentialOptions"; + +/** + * Provides the user code and verification URI where the code must be + * entered. Also provides a message to display to the user which + * contains an instruction with these details. + */ +export interface DeviceCodeInfo { + /** + * The device code that the user must enter into the verification page. + */ + userCode: string; + + /** + * The verification URI to which the user must navigate to enter the device + * code. + */ + verificationUri: string; + + /** + * A message that may be shown to the user to instruct them on how to enter + * the device code in the page specified by the verification URI. + */ + message: string; +} + +/** + * Defines the signature of a callback which will be passed to + * DeviceCodeCredential for the purpose of displaying authentication + * details to the user. + */ +export type DeviceCodePromptCallback = (deviceCodeInfo: DeviceCodeInfo) => void; + +/** + * Defines options for the InteractiveBrowserCredential class for NodeJS. + */ +export interface DeviceCodeCredentialOptions extends InteractiveCredentialOptions { + /** + * The Azure Active Directory tenant (directory) ID. + */ + tenantId?: string; + /** + * The client (application) ID of an App Registration in the tenant. + */ + clientId?: string; +} diff --git a/sdk/identity/identity/src/credentials/environmentCredential.ts b/sdk/identity/identity/src/credentials/environmentCredential.ts index 537e852b1214..bcc5dba88835 100644 --- a/sdk/identity/identity/src/credentials/environmentCredential.ts +++ b/sdk/identity/identity/src/credentials/environmentCredential.ts @@ -2,19 +2,14 @@ // Licensed under the MIT license. import { AccessToken, TokenCredential, GetTokenOptions } from "@azure/core-http"; +import { credentialLogger, processEnvVars, formatSuccess, formatError } from "../util/logging"; import { TokenCredentialOptions } from "../client/identityClient"; import { ClientSecretCredential } from "./clientSecretCredential"; -import { createSpan } from "../util/tracing"; -import { - AuthenticationError, - AuthenticationErrorName, - CredentialUnavailable -} from "../client/errors"; -import { CanonicalCode } from "@opentelemetry/api"; +import { AuthenticationError } from "../client/errors"; +import { checkTenantId } from "../util/checkTenantId"; +import { trace } from "../util/tracing"; import { ClientCertificateCredential } from "./clientCertificateCredential"; import { UsernamePasswordCredential } from "./usernamePasswordCredential"; -import { credentialLogger, processEnvVars, formatSuccess, formatError } from "../util/logging"; -import { checkTenantId } from "../util/checkTenantId"; /** * Contains the list of all supported environment variable names so that an @@ -109,56 +104,34 @@ export class EnvironmentCredential implements TokenCredential { } /** - * Authenticates with Azure Active Directory and returns an access token if - * successful. If authentication cannot be performed at this time, this method may - * return null. If an error occurs during authentication, an {@link AuthenticationError} - * containing failure details will be thrown. + * Authenticates with Azure Active Directory and returns an access token if successful. * * @param scopes - The list of scopes for which the token will have access. - * @param options - The options used to configure any requests this - * TokenCredential implementation might make. + * @param options - Optional parameters. See {@link GetTokenOptions}. */ async getToken( scopes: string | string[], - options?: GetTokenOptions + options: GetTokenOptions = {} ): Promise { - const { span, updatedOptions } = createSpan("EnvironmentCredential-getToken", options); - if (this._credential) { - try { - const result = await this._credential.getToken(scopes, updatedOptions); - logger.getToken.info(formatSuccess(scopes)); - return result; - } catch (err) { - const code = - err.name === AuthenticationErrorName - ? CanonicalCode.UNAUTHENTICATED - : CanonicalCode.UNKNOWN; - span.setStatus({ - code, - message: err.message - }); - const authenticationError = new AuthenticationError(400, { - error: "EnvironmentCredential authentication failed.", - error_description: err.message - .toString() - .split("More details:") - .join("") - }); - logger.getToken.info(formatError(scopes, authenticationError)); - throw authenticationError; - } finally { - span.end(); + return trace("EnvironmentCredential.getToken", options, async (newOptions) => { + if (this._credential) { + try { + const result = await this._credential.getToken(scopes, newOptions); + logger.getToken.info(formatSuccess(scopes)); + return result; + } catch (err) { + const authenticationError = new AuthenticationError(400, { + error: "EnvironmentCredential authentication failed.", + error_description: err.message + .toString() + .split("More details:") + .join("") + }); + logger.getToken.info(formatError(scopes, authenticationError)); + throw authenticationError; + } } - } - - // If by this point we don't have a credential, throw an exception so that - // the user knows the credential was not configured appropriately - span.setStatus({ code: CanonicalCode.UNAUTHENTICATED }); - span.end(); - const error = new CredentialUnavailable( - "EnvironmentCredential is unavailable. Environment variables are not fully configured." - ); - logger.getToken.info(formatError(scopes, error)); - throw error; + return null; + }); } } diff --git a/sdk/identity/identity/src/credentials/interactiveBrowserCredential.browser.ts b/sdk/identity/identity/src/credentials/interactiveBrowserCredential.browser.ts index 99a075173fa1..9e2c3b8fd6d3 100644 --- a/sdk/identity/identity/src/credentials/interactiveBrowserCredential.browser.ts +++ b/sdk/identity/identity/src/credentials/interactiveBrowserCredential.browser.ts @@ -1,32 +1,37 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { AccessToken, TokenCredential, GetTokenOptions } from "@azure/core-http"; -import { IdentityClient } from "../client/identityClient"; +import { AccessToken, GetTokenOptions, TokenCredential } from "@azure/core-http"; +import { credentialLogger, formatError } from "../util/logging"; +import { trace } from "../util/tracing"; +import { MsalFlow } from "../msal/flows"; +import { AuthenticationRecord } from "../msal/types"; +import { MSALAuthCode } from "../msal/browserFlows/msalAuthCode"; +import { MSALImplicit } from "../msal/browserFlows/msalImplicit"; +import { MsalBrowserFlowOptions } from "../msal/browserFlows/browserCommon"; import { - BrowserLoginStyle, - InteractiveBrowserCredentialBrowserOptions + InteractiveBrowserCredentialBrowserOptions, + InteractiveBrowserCredentialOptions } from "./interactiveBrowserCredentialOptions"; -import { createSpan } from "../util/tracing"; -import { CanonicalCode } from "@opentelemetry/api"; -import { DefaultTenantId } from "../constants"; -import { credentialLogger, formatSuccess, formatError } from "../util/logging"; -import { MSALAuthCode } from "./msalBrowser/msalAuthCode"; -import { MSALImplicit } from "./msalBrowser/msalImplicit"; -import { IMSALBrowserFlow, MSALOptions } from "./msalBrowser/msalCommon"; const logger = credentialLogger("InteractiveBrowserCredential"); /** * Enables authentication to Azure Active Directory inside of the web browser - * using the interactive login flow, either via browser redirects or a popup - * window. + * using the interactive login flow. + * + * On NodeJS, it will open a browser window while it listens for a redirect response from the authentication service. + * + * On browsers, by default it uses the [Authorization Code Flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow) + * and authenticates via popups. The `loginStyle` can be configured to use `redirect` instead, and the authentication flow can be configured to use the [Implicit Grant Flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-implicit-grant-flow) + * by specifying the option `flow` with the value `implicit-grant`. + * + * It's recommended that the AAD Applications used are configured to authenticate using Single Page Applications. + * More information here: [link](https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-spa-app-registration#redirect-uri-msaljs-20-with-auth-code-flow). */ export class InteractiveBrowserCredential implements TokenCredential { - private tenantId: string; - private clientId: string; - private loginStyle: BrowserLoginStyle; - private msal: IMSALBrowserFlow; + private msalFlow: MsalFlow; + private disableAutomaticAuthentication?: boolean; /** * Creates an instance of the InteractiveBrowserCredential with the @@ -35,9 +40,9 @@ export class InteractiveBrowserCredential implements TokenCredential { * * @param options - Options for configuring the client which makes the authentication request. */ - constructor(options: InteractiveBrowserCredentialBrowserOptions) { - this.tenantId = options.tenantId || DefaultTenantId; - + constructor( + options: InteractiveBrowserCredentialBrowserOptions | InteractiveBrowserCredentialOptions + ) { if (!options?.clientId) { const error = new Error( "The parameter `clientId` cannot be left undefined for the `InteractiveBrowserCredential`" @@ -45,57 +50,35 @@ export class InteractiveBrowserCredential implements TokenCredential { logger.info(formatError("", error)); throw error; } - this.clientId = options.clientId; - - options = { - ...IdentityClient.getDefaultOptions(), - ...options, - tenantId: this.tenantId, - clientId: this.clientId - }; - this.loginStyle = options.loginStyle || "popup"; + const browserOptions = options as InteractiveBrowserCredentialBrowserOptions; + const loginStyle = browserOptions.loginStyle || "popup"; const loginStyles = ["redirect", "popup"]; - if (loginStyles.indexOf(this.loginStyle) === -1) { + + if (loginStyles.indexOf(loginStyle) === -1) { const error = new Error( `Invalid loginStyle: ${ - options.loginStyle + browserOptions.loginStyle }. Should be any of the following: ${loginStyles.join(", ")}.` ); logger.info(formatError("", error)); throw error; } - const { - clientId, - tenantId, - authorityHost, - correlationId, - redirectUri, - postLogoutRedirectUri, - authenticationRecord - } = options; - - const msalOptions: MSALOptions = { - clientId, - tenantId, - authorityHost, - correlationId, - authenticationRecord, - loginStyle: this.loginStyle, - knownAuthorities: tenantId === "adfs" ? (authorityHost ? [authorityHost] : []) : [], - redirectUri: typeof redirectUri === "function" ? redirectUri() : redirectUri, - postLogoutRedirectUri: - typeof postLogoutRedirectUri === "function" - ? postLogoutRedirectUri() - : postLogoutRedirectUri + const msalOptions: MsalBrowserFlowOptions = { + ...options, + logger, + loginStyle: loginStyle, + redirectUri: + typeof options.redirectUri === "function" ? options.redirectUri() : options.redirectUri }; - if (options.flow === "implicit-grant") { - this.msal = new MSALImplicit(msalOptions); + if (browserOptions.flow === "implicit-grant") { + this.msalFlow = new MSALImplicit(msalOptions); } else { - this.msal = new MSALAuthCode(msalOptions); + this.msalFlow = new MSALAuthCode(msalOptions); } + this.disableAutomaticAuthentication = options?.disableAutomaticAuthentication; } /** @@ -104,56 +87,44 @@ export class InteractiveBrowserCredential implements TokenCredential { * return null. If an error occurs during authentication, an {@link AuthenticationError} * containing failure details will be thrown. * + * If the user provided the option `disableAutomaticAuthentication`, + * once the token can't be retrieved silently, + * this method won't attempt to request user interaction to retrieve the token. + * * @param scopes - The list of scopes for which the token will have access. * @param options - The options used to configure any requests this - * TokenCredential implementation might make. + * TokenCredential implementation might make. */ - async getToken( - scopes: string | string[], - options?: GetTokenOptions - ): Promise { - const { span } = createSpan("InteractiveBrowserCredential-getToken", options); - try { - const authResponse = await this.msal.acquireToken({ - scopes, - ...options + async getToken(scopes: string | string[], options: GetTokenOptions = {}): Promise { + return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => { + const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + return this.msalFlow.getToken(arrayScopes, { + ...newOptions, + disableAutomaticAuthentication: this.disableAutomaticAuthentication }); + }); + } - if (!authResponse) { - logger.getToken.info("No response"); - return null; - } - - if (!authResponse.expiresOn) { - logger.getToken.info(`Response had no "expiresOn" property.`); - return null; - } - - if (!authResponse.accessToken) { - logger.getToken.info(`Response had no "accessToken" property.`); - return null; - } - - if (authResponse) { - const expiresOnTimestamp = authResponse.expiresOn.getTime(); - logger.getToken.info(formatSuccess(scopes)); - return { - token: authResponse.accessToken, - expiresOnTimestamp - }; - } else { - logger.getToken.info("No response"); - return null; - } - } catch (err) { - span.setStatus({ - code: CanonicalCode.UNKNOWN, - message: err.message - }); - logger.getToken.info(formatError(scopes, err)); - throw err; - } finally { - span.end(); - } + /** + * Authenticates with Azure Active Directory and returns an access token if + * successful. If authentication cannot be performed at this time, this method may + * return null. If an error occurs during authentication, an {@link AuthenticationError} + * containing failure details will be thrown. + * + * If the token can't be retrieved silently, this method will require user interaction to retrieve the token. + * + * @param scopes - The list of scopes for which the token will have access. + * @param options - The options used to configure any requests this + * TokenCredential implementation might make. + */ + async authenticate( + scopes: string | string[], + options: GetTokenOptions = {} + ): Promise { + return trace(`${this.constructor.name}.authenticate`, options, async (newOptions) => { + const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + await this.msalFlow.getToken(arrayScopes, newOptions); + return this.msalFlow.getActiveAccount(); + }); } } diff --git a/sdk/identity/identity/src/credentials/interactiveBrowserCredential.ts b/sdk/identity/identity/src/credentials/interactiveBrowserCredential.ts index adf5443c99b8..7acab7eb0c72 100644 --- a/sdk/identity/identity/src/credentials/interactiveBrowserCredential.ts +++ b/sdk/identity/identity/src/credentials/interactiveBrowserCredential.ts @@ -3,81 +3,52 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { TokenCredential, GetTokenOptions, AccessToken } from "@azure/core-http"; -import { InteractiveBrowserCredentialOptions } from "./interactiveBrowserCredentialOptions"; -import { credentialLogger, formatError, formatSuccess } from "../util/logging"; -import { DefaultTenantId, DeveloperSignOnClientId } from "../constants"; -import { Socket } from "net"; -import { AuthenticationRequired, MsalClient } from "../client/msalClient"; -import { AuthorizationCodeRequest } from "@azure/msal-node"; - -import open from "open"; -import http from "http"; -import stoppable from "stoppable"; - -import { checkTenantId } from "../util/checkTenantId"; +import { AccessToken, GetTokenOptions, TokenCredential } from "@azure/core-http"; +import { credentialLogger } from "../util/logging"; +import { trace } from "../util/tracing"; +import { AuthenticationRecord } from "../msal/types"; +import { MsalOpenBrowser } from "../msal/nodeFlows/msalOpenBrowser"; +import { MsalFlow } from "../msal/flows"; +import { + InteractiveBrowserCredentialBrowserOptions, + InteractiveBrowserCredentialOptions +} from "./interactiveBrowserCredentialOptions"; const logger = credentialLogger("InteractiveBrowserCredential"); /** * Enables authentication to Azure Active Directory inside of the web browser - * using the interactive login flow, either via browser redirects or a popup - * window. + * using the interactive login flow. + * + * On NodeJS, it will open a browser window while it listens for a redirect response from the authentication service. + * + * It's recommended that the AAD Applications used are configured to authenticate using Single Page Applications. + * More information here: [link](https://docs.microsoft.com/en-us/azure/active-directory/develop/scenario-spa-app-registration#redirect-uri-msaljs-20-with-auth-code-flow). */ export class InteractiveBrowserCredential implements TokenCredential { - private redirectUri: string; - private port: number; - private hostname: string; - private msalClient: MsalClient; - - constructor(options: InteractiveBrowserCredentialOptions = {}) { - const tenantId = options.tenantId || DefaultTenantId; - const clientId = options.clientId || DeveloperSignOnClientId; - - checkTenantId(logger, tenantId); - - // const persistenceEnabled = options?.persistenceEnabled ? options?.persistenceEnabled : false; - // const authenticationRecord = options?.authenticationRecord; - - if (options.redirectUri) { - if (typeof options.redirectUri === "string") { - this.redirectUri = options.redirectUri; - } else { - this.redirectUri = options.redirectUri(); - } - } else { - this.redirectUri = "http://localhost"; - } + private msalFlow: MsalFlow; + private disableAutomaticAuthentication?: boolean; - const url = new URL(this.redirectUri); - this.port = parseInt(url.port); - if (isNaN(this.port)) { - this.port = 80; - } - - this.hostname = url.hostname; - - let authorityHost; - if (options.authorityHost) { - if (options.authorityHost.endsWith("/")) { - authorityHost = options.authorityHost + tenantId; - } else { - authorityHost = options.authorityHost + "/" + tenantId; - } - } else { - authorityHost = "https://login.microsoftonline.com/" + tenantId; - } - - this.msalClient = new MsalClient( - { - clientId, - authority: authorityHost, - knownAuthorities: tenantId === "adfs" ? (authorityHost ? [authorityHost] : []) : [] - }, - false, - undefined, - options - ); + /** + * Creates an instance of InteractiveBrowserCredential with the details needed. + * + * @param options - Options for configuring the client which makes the authentication requests. + */ + constructor( + options: InteractiveBrowserCredentialOptions | InteractiveBrowserCredentialBrowserOptions = {} + ) { + const redirectUri = + typeof options.redirectUri === "function" + ? options.redirectUri() + : options.redirectUri || "http://localhost"; + + this.msalFlow = new MsalOpenBrowser({ + ...options, + tokenCredentialOptions: options, + logger, + redirectUri + }); + this.disableAutomaticAuthentication = options?.disableAutomaticAuthentication; } /** @@ -86,142 +57,44 @@ export class InteractiveBrowserCredential implements TokenCredential { * return null. If an error occurs during authentication, an {@link AuthenticationError} * containing failure details will be thrown. * + * If the user provided the option `disableAutomaticAuthentication`, + * once the token can't be retrieved silently, + * this method won't attempt to request user interaction to retrieve the token. + * * @param scopes - The list of scopes for which the token will have access. * @param options - The options used to configure any requests this - * TokenCredential implementation might make. + * TokenCredential implementation might make. */ - public getToken( - scopes: string | string[], - _options?: GetTokenOptions - ): Promise { - const scopeArray = typeof scopes === "object" ? scopes : [scopes]; - - return this.msalClient.acquireTokenFromCache(scopeArray).catch((e) => { - if (e instanceof AuthenticationRequired) { - return this.acquireTokenFromBrowser(scopeArray); - } else { - logger.getToken.info(formatError(scopes, e)); - throw e; - } + async getToken(scopes: string | string[], options: GetTokenOptions = {}): Promise { + return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => { + const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + return this.msalFlow.getToken(arrayScopes, { + ...newOptions, + disableAutomaticAuthentication: this.disableAutomaticAuthentication + }); }); } - private async openAuthCodeUrl(scopeArray: string[]): Promise { - const authCodeUrlParameters = { - scopes: scopeArray, - redirectUri: this.redirectUri - }; - - const response = await this.msalClient.getAuthCodeUrl(authCodeUrlParameters); - await open(response); - } - - private acquireTokenFromBrowser(scopeArray: string[]): Promise { - return new Promise((resolve, reject) => { - const socketToDestroy: Socket[] = []; - - const requestListener = (req: http.IncomingMessage, res: http.ServerResponse) => { - if (!req.url) { - reject( - new Error( - `Interactive Browser Authentication Error "Did not receive token with a valid expiration"` - ) - ); - return; - } - let url: URL; - try { - url = new URL(req.url, this.redirectUri); - } catch (e) { - reject( - new Error( - `Interactive Browser Authentication Error "Did not receive token with a valid expiration"` - ) - ); - return; - } - const tokenRequest: AuthorizationCodeRequest = { - code: url.searchParams.get("code")!, - redirectUri: this.redirectUri, - scopes: scopeArray - }; - - this.msalClient - .acquireTokenByCode(tokenRequest) - .then((authResponse) => { - const successMessage = `Authentication Complete. You can close the browser and return to the application.`; - if (authResponse && authResponse.expiresOn) { - const expiresOnTimestamp = authResponse?.expiresOn.valueOf(); - res.writeHead(200); - res.end(successMessage); - logger.getToken.info(formatSuccess(scopeArray)); - - resolve({ - expiresOnTimestamp, - token: authResponse.accessToken - }); - } else { - const errorMessage = formatError( - scopeArray, - `${url.searchParams.get("error")}. ${url.searchParams.get("error_description")}` - ); - res.writeHead(500); - res.end(errorMessage); - logger.getToken.info(errorMessage); - - reject( - new Error( - `Interactive Browser Authentication Error "Did not receive token with a valid expiration"` - ) - ); - } - cleanup(); - return; - }) - .catch(() => { - const errorMessage = formatError( - scopeArray, - `${url.searchParams.get("error")}. ${url.searchParams.get("error_description")}` - ); - res.writeHead(500); - res.end(errorMessage); - logger.getToken.info(errorMessage); - - reject( - new Error( - `Interactive Browser Authentication Error "Did not receive token with a valid expiration"` - ) - ); - cleanup(); - }); - }; - const app = http.createServer(requestListener); - - const listen = app.listen(this.port, this.hostname, () => - logger.info(`InteractiveBrowerCredential listening on port ${this.port}!`) - ); - app.on("connection", (socket) => socketToDestroy.push(socket)); - const server = stoppable(app); - - this.openAuthCodeUrl(scopeArray).catch((e) => { - cleanup(); - reject(e); - }); - - function cleanup(): void { - if (listen) { - listen.close(); - } - - for (const socket of socketToDestroy) { - socket.destroy(); - } - - if (server) { - server.close(); - server.stop(); - } - } + /** + * Authenticates with Azure Active Directory and returns an access token if + * successful. If authentication cannot be performed at this time, this method may + * return null. If an error occurs during authentication, an {@link AuthenticationError} + * containing failure details will be thrown. + * + * If the token can't be retrieved silently, this method will require user interaction to retrieve the token. + * + * @param scopes - The list of scopes for which the token will have access. + * @param options - The options used to configure any requests this + * TokenCredential implementation might make. + */ + async authenticate( + scopes: string | string[], + options: GetTokenOptions = {} + ): Promise { + return trace(`${this.constructor.name}.authenticate`, options, async (newOptions) => { + const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + await this.msalFlow.getToken(arrayScopes, newOptions); + return this.msalFlow.getActiveAccount(); }); } } diff --git a/sdk/identity/identity/src/credentials/interactiveBrowserCredentialOptions.ts b/sdk/identity/identity/src/credentials/interactiveBrowserCredentialOptions.ts index 7c9fc014f727..f46bd83c660e 100644 --- a/sdk/identity/identity/src/credentials/interactiveBrowserCredentialOptions.ts +++ b/sdk/identity/identity/src/credentials/interactiveBrowserCredentialOptions.ts @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { AbortSignalLike } from "@azure/core-http"; import { TokenCredentialOptions } from "../client/identityClient"; -import { AuthenticationRecord } from "../client/msalClient"; +import { InteractiveCredentialOptions } from "./interactiveCredentialOptions"; /** + * (Browser-only feature) * The "login style" to use in the authentication flow: * - "redirect" redirects the user to the authentication page and then * redirects them back to the page once authentication is completed. @@ -25,93 +25,59 @@ export type InteractiveBrowserAuthenticationFlow = "implicit-grant" | "auth-code /** * Defines the common options for the InteractiveBrowserCredential class. */ -export interface InteractiveBrowserCredentialCommonOptions extends TokenCredentialOptions { - /** - * (Only available if used from a browser) - * Specifies whether a redirect or a popup window should be used to - * initiate the user authentication flow. Possible values are "redirect" - * or "popup" (default) for browser and "popup" (default) for node. - * - */ - loginStyle?: BrowserLoginStyle; +export type InteractiveBrowserCredentialOptions = TokenCredentialOptions & + InteractiveCredentialOptions & { + /** + * Gets the redirect URI of the application. This should be same as the value + * in the application registration portal. Defaults to `window.location.href`. + */ + redirectUri?: string | (() => string); - /** - * Gets the redirect URI of the application. This should be same as the value - * in the application registration portal. Defaults to `window.location.href`. - */ - redirectUri?: string | (() => string); + /** + * The Azure Active Directory tenant (directory) ID. + */ + tenantId?: string; - /** - * Gets the URI to which the user will be redirected when logging out. - * Defaults to `window.location.href`. - */ - postLogoutRedirectUri?: string | (() => string); - - /** - * The Azure Active Directory tenant (directory) ID. - */ - tenantId?: string; - - /** - * Correlation ID that can be customized to keep track of the browser authentication requests. - */ - correlationId?: string; - - /** - * (Only available if used from a browser) - * Result of a previous authentication that can be used to retrieve the cached credentials of each individual account. - * This is necessary to provide in case the application wants to work with more than one account per - * Client ID and Tenant ID pair. - * - * This record can be retrieved by calling to the InteractiveBrowserCredential's `authenticate()` method, as follows: - * - * const authenticationRecord = await credential.authenticate(); - * - */ - authenticationRecord?: AuthenticationRecord; - - /** - * (Only available if used from a browser) - * Authentication flow to use. - * If the user specifies the implicit-grant flow, we will use MSAL 1. - * Otherwise, auth-code will be assumed, which uses PKCE and MSAL 2. - */ - flow?: InteractiveBrowserAuthenticationFlow; -} + /** + * The client (application) ID of an App Registration in the tenant. + */ + clientId?: string; + }; /** - * Defines options for the InteractiveBrowserCredential class for NodeJS. + * Defines the common options for the InteractiveBrowserCredential class. */ -export interface InteractiveBrowserCredentialOptions - extends InteractiveBrowserCredentialCommonOptions { - /** - * The client (application) ID of an App Registration in the tenant. - */ - clientId?: string; -} +export type InteractiveBrowserCredentialBrowserOptions = TokenCredentialOptions & + InteractiveCredentialOptions & { + /** + * Gets the redirect URI of the application. This should be same as the value + * in the application registration portal. Defaults to `window.location.href`. + */ + redirectUri?: string | (() => string); -/** - * Defines options for the InteractiveBrowserCredential class for the browser. - */ -export interface InteractiveBrowserCredentialBrowserOptions - extends InteractiveBrowserCredentialCommonOptions { - /** - * The client (application) ID of an App Registration in the tenant. - * This parameter is required on the browser. - */ - clientId: string; -} + /** + * The Azure Active Directory tenant (directory) ID. + */ + tenantId?: string; -/** - * Optional parameters to the InteractiveBrowserCredential authenticate() method. - */ -export interface InteractiveBrowserAuthenticateOptions { - /** - * The signal which can be used to abort requests. - */ - abortSignal?: AbortSignalLike; - /** - * Scopes to authenticate with. - */ - scopes?: string | string[]; -} + /** + * The client (application) ID of an App Registration in the tenant. + * This parameter is required on the browser. + */ + clientId: string; + + /** + * Specifies whether a redirect or a popup window should be used to + * initiate the user authentication flow. Possible values are "redirect" + * or "popup" (default) for browser and "popup" (default) for node. + * + */ + loginStyle?: BrowserLoginStyle; + + /** + * Authentication flow to use. + * If the user specifies the implicit-grant flow, we will use MSAL 1. + * Otherwise, auth-code will be assumed, which uses PKCE and MSAL 2. + */ + flow?: InteractiveBrowserAuthenticationFlow; + }; diff --git a/sdk/identity/identity/src/credentials/interactiveCredentialOptions.ts b/sdk/identity/identity/src/credentials/interactiveCredentialOptions.ts new file mode 100644 index 000000000000..715d48cf47af --- /dev/null +++ b/sdk/identity/identity/src/credentials/interactiveCredentialOptions.ts @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { AuthenticationRecord } from "../msal/types"; +import { PersistentCredentialOptions } from "./persistentCredentialOptions"; + +/** + * Common constructor options for the Identity credentials that requires user interaction. + */ +export interface InteractiveCredentialOptions extends PersistentCredentialOptions { + /** + * Result of a previous authentication that can be used to retrieve the cached credentials of each individual account. + * This is necessary to provide in case the application wants to work with more than one account per + * Client ID and Tenant ID pair. + * + * This record can be retrieved by calling to the credential's `authenticate()` method, as follows: + * + * const authenticationRecord = await credential.authenticate(); + * + */ + authenticationRecord?: AuthenticationRecord; + + /** + * Makes getToken throw if a manual authentication is necessary. + * Developers will need to call to `authenticate()` to control when to manually authenticate. + */ + disableAutomaticAuthentication?: boolean; +} diff --git a/sdk/identity/identity/src/credentials/managedIdentityCredential/imdsMsi.ts b/sdk/identity/identity/src/credentials/managedIdentityCredential/imdsMsi.ts index ac781276960c..2f846770af75 100644 --- a/sdk/identity/identity/src/credentials/managedIdentityCredential/imdsMsi.ts +++ b/sdk/identity/identity/src/credentials/managedIdentityCredential/imdsMsi.ts @@ -82,7 +82,7 @@ export const imdsMsi: MSI = { await identityClient.sendRequest(webResource); } catch (err) { if ( - (err instanceof RestError && err.code === RestError.REQUEST_SEND_ERROR) || + (err.name === "RestError" && err.code === RestError.REQUEST_SEND_ERROR) || err.name === "AbortError" || err.code === "ECONNREFUSED" || // connection refused err.code === "EHOSTDOWN" // host is down diff --git a/sdk/identity/identity/src/credentials/managedIdentityCredential/index.ts b/sdk/identity/identity/src/credentials/managedIdentityCredential/index.ts index 5744ba992c7d..d1aa3934b8e3 100644 --- a/sdk/identity/identity/src/credentials/managedIdentityCredential/index.ts +++ b/sdk/identity/identity/src/credentials/managedIdentityCredential/index.ts @@ -181,7 +181,7 @@ export class ManagedIdentityCredential implements TokenCredential { } catch (err) { // CredentialUnavailable errors are expected to reach here. // We intend them to bubble up, so that DefaultAzureCredential can catch them. - if (err instanceof CredentialUnavailable) { + if (err.name === "AuthenticationRequired") { throw err; } diff --git a/sdk/identity/identity/src/credentials/msalBrowser/msalAuthCode.ts b/sdk/identity/identity/src/credentials/msalBrowser/msalAuthCode.ts deleted file mode 100644 index ea55299796c8..000000000000 --- a/sdk/identity/identity/src/credentials/msalBrowser/msalAuthCode.ts +++ /dev/null @@ -1,265 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import * as msalBrowser from "@azure/msal-browser"; -import { AuthenticationRecord } from "../../client/msalClient"; -import { credentialLogger } from "../../util/logging"; -import { - BrowserLoginStyle, - InteractiveBrowserAuthenticateOptions -} from "../interactiveBrowserCredentialOptions"; -import { IMSALBrowserFlow, IMSALToken, MSALOptions } from "./msalCommon"; - -const logger = credentialLogger("MSAL Browser v2 - Auth Code Flow"); - -// We keep a copy of the redirect hash. -const redirectHash = self.location.hash; - -/** - * Uses MSAL Browser 2.X for browser authentication, - * which uses the [Auth Code Flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow). - */ -export class MSALAuthCode implements IMSALBrowserFlow { - private config: msalBrowser.Configuration; - private app: msalBrowser.PublicClientApplication; - private loginStyle: BrowserLoginStyle; - private correlationId?: string; - - /** - * Sets up an MSAL object based on the given parameters. - * MSAL with Auth Code allows sending a previously obtained `authenticationRecord` through the optional parameters, - * which is set to be the active account. - * @param options - Parameters necessary and otherwise used to create the MSAL object. - */ - constructor(options: MSALOptions) { - this.loginStyle = options.loginStyle; - this.correlationId = options.correlationId; - this.config = { - auth: { - clientId: options.clientId!, // we just initialized it above - authority: `${options.authorityHost}/${options.tenantId}`, - knownAuthorities: options.knownAuthorities, - redirectUri: options.redirectUri, - postLogoutRedirectUri: options.postLogoutRedirectUri - }, - cache: { - cacheLocation: "sessionStorage", - storeAuthStateInCookie: true // Set to true to improve the experience on IE11 and Edge. - }, - system: { - loggerOptions: { - loggerCallback: (level, message, containsPii) => { - if (containsPii) { - return; - } - switch (level) { - case msalBrowser.LogLevel.Error: - logger.info(`MSAL Browser V2 error: ${message}`); - return; - case msalBrowser.LogLevel.Info: - logger.info(`MSAL Browser V2 info message: ${message}`); - return; - case msalBrowser.LogLevel.Verbose: - logger.info(`MSAL Browser V2 verbose message: ${message}`); - return; - case msalBrowser.LogLevel.Warning: - logger.info(`MSAL Browser V2 warning: ${message}`); - return; - } - } - } - } - }; - - this.app = new msalBrowser.PublicClientApplication(this.config); - - if (options.authenticationRecord) { - this.app.setActiveAccount(options.authenticationRecord); - } - } - - /** - * Loads the account based on the result of the authentication. - * If no result was received, tries to load the account from the cache. - * @param result - Result object received from MSAL. - */ - private async handleResult( - result?: msalBrowser.AuthenticationResult - ): Promise { - try { - if (result && result.account) { - logger.info(`MSAL Browser V2 authentication successful.`); - this.app.setActiveAccount(result.account); - return result.account; - } - - // If by this point we happen to have an active account, we should stop trying to parse this. - const activeAccount = this.app.getActiveAccount(); - if (activeAccount) { - return activeAccount; - } - - // If we don't have an active account, we try to activate it from all the already loaded accounts. - const accounts = this.app.getAllAccounts(); - if (accounts.length > 1) { - // If there's more than one account in memory, we force the user to authenticate again. - // At this point we can't identify which account should this credential work with, - // since at this point the user won't have provided enough information. - // We log a message in case that helps. - logger.info( - [ - "More than one account was found authenticated for this Client ID and Tenant ID.", - "However, no `authenticationRecord` has been provided for this credential,", - "therefore we're unable to pick between these accounts.", - "A new login attempt will be requested, to ensure the correct account is picked.", - "To work with multiple accounts for the same Client ID and Tenant ID, please provide an `authenticationRecord` when initializing `InteractiveBrowserCredential`." - ].join("\n") - ); - // To safely trigger a new login, we're also ensuring the local cache is cleared up for this MSAL object. - // However, we want to avoid kicking the user out of their authentication on the Azure side. - // We do this by calling to logout while specifying a `onRedirectNavigate` that returns false. - await this.app.logout({ - onRedirectNavigate: () => false - }); - return; - } - - // If there's only one account for this MSAL object, we can safely activate it. - if (accounts.length === 1) { - this.app.setActiveAccount(accounts[0]); - return accounts[0]; - } - - logger.info(`No accounts were found through MSAL.`); - } catch (e) { - logger.info(`Failed to acquire token through MSAL. ${e.message}`); - } - return; - } - - /** - * Uses MSAL to handle the redirect. - */ - public async handleRedirect(): Promise { - return this.handleResult((await this.app.handleRedirectPromise(redirectHash)) || undefined); - } - - /** - * Uses MSAL to trigger a redirect or a popup login. - */ - public async login(scopes: string | string[] = []): Promise { - const arrayScopes = Array.isArray(scopes) ? scopes : [scopes]; - const loginRequest = { - scopes: arrayScopes - }; - switch (this.loginStyle) { - case "redirect": { - await this.app.loginRedirect(loginRequest); - return; - } - case "popup": - return this.handleResult(await this.app.loginPopup(loginRequest)); - } - } - - /** - * Uses MSAL to retrieve the active account. - */ - public getActiveAccount(): AuthenticationRecord | undefined { - return this.app.getActiveAccount() || undefined; - } - - /** - * Allows users to manually authenticate and retrieve the AuthenticationRecord. - * @param options - Optional parameters to authenticate with, like the scope. - */ - public async authenticate( - options: InteractiveBrowserAuthenticateOptions - ): Promise { - // We ensure that redirection is handled at this point. - await this.handleRedirect(); - - // If we have an active account, we return that. - const account = this.getActiveAccount(); - if (account) { - return account; - } - - const scopes = options.scopes; - if (!scopes) { - throw new Error( - `Invalid scopes in the authenticate function of the MSAL Auth Code flow. Received: ${scopes}` - ); - } - - // Otherwise we try to login. - return this.login(scopes); - } - - /** - * Attempts to retrieve an authenticated token from MSAL. - * @param options - Properties useful to retrieve the token, like the scopes and the abortSignal. - */ - public async acquireToken( - options: InteractiveBrowserAuthenticateOptions - ): Promise { - const account = await this.authenticate(options); - - const scopes = options.scopes; - if (!scopes) { - throw new Error( - `Invalid scopes in the acquireToken function of the MSAL Auth Code flow. Received: ${scopes}` - ); - } - - const silentRequest: msalBrowser.SilentRequest = { - authority: this.config.auth.authority!, - correlationId: this.correlationId, // If undefined, MSAL will automatically generate one. - account, - forceRefresh: false, - scopes: Array.isArray(scopes) ? scopes : scopes.split(",") - }; - - let authResponse: msalBrowser.AuthenticationResult | undefined; - - try { - logger.info("Attempting to acquire token silently"); - authResponse = await this.app.acquireTokenSilent(silentRequest); - } catch (err) { - if (err instanceof msalBrowser.AuthError) { - switch (err.errorCode) { - case "consent_required": - case "interaction_required": - case "login_required": - logger.info(`Authentication returned errorCode ${err.errorCode}`); - break; - default: - logger.info(`Failed to acquire token: ${err.message}`); - throw err; - } - } - } - - if (authResponse === undefined) { - logger.info( - `Silent authentication failed, falling back to interactive method ${this.loginStyle}` - ); - switch (this.loginStyle) { - case "redirect": - // This will go out of the page. - // Once the InteractiveBrowserCredential is initialized again, - // we'll load the MSAL account in the constructor. - await this.app.acquireTokenRedirect(silentRequest); - return undefined; - case "popup": - authResponse = await this.app.acquireTokenPopup(silentRequest); - break; - } - } - - return { - accessToken: authResponse.accessToken, - expiresOn: authResponse.expiresOn - }; - } -} diff --git a/sdk/identity/identity/src/credentials/msalBrowser/msalCommon.ts b/sdk/identity/identity/src/credentials/msalBrowser/msalCommon.ts deleted file mode 100644 index 1791a1623d30..000000000000 --- a/sdk/identity/identity/src/credentials/msalBrowser/msalCommon.ts +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import { AuthenticationRecord } from "../../client/msalClient"; -import { - BrowserLoginStyle, - InteractiveBrowserAuthenticateOptions -} from "../interactiveBrowserCredentialOptions"; - -/** - * Union of the constructor parameters that all MSAL flow types take. - * Some properties might not be used by some flow types. - */ -export interface MSALOptions { - clientId?: string; - tenantId?: string; - authorityHost?: string; - knownAuthorities?: string[]; - redirectUri?: string; - correlationId?: string; - postLogoutRedirectUri?: string; - authenticationRecord?: AuthenticationRecord; - loginStyle: BrowserLoginStyle; -} - -/** - * The shape we use return the token (and the expiration date). - */ -export interface IMSALToken { - accessToken?: string; - expiresOn: Date | null; -} - -/** - * The common methods we use to work with the MSAL flows. - */ -export interface IMSALBrowserFlow { - handleRedirect(): Promise; - login(scopes: string | string[]): Promise; - getActiveAccount(): AuthenticationRecord | undefined; - acquireToken(options: InteractiveBrowserAuthenticateOptions): Promise; - authenticate( - options: InteractiveBrowserAuthenticateOptions - ): Promise; -} diff --git a/sdk/identity/identity/src/credentials/msalBrowser/msalImplicit.ts b/sdk/identity/identity/src/credentials/msalBrowser/msalImplicit.ts deleted file mode 100644 index d9651de5d102..000000000000 --- a/sdk/identity/identity/src/credentials/msalBrowser/msalImplicit.ts +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import * as msal from "msal"; -import { AuthenticationRecord } from "../../client/msalClient"; -import { credentialLogger } from "../../util/logging"; -import { - BrowserLoginStyle, - InteractiveBrowserAuthenticateOptions -} from "../interactiveBrowserCredentialOptions"; -import { IMSALBrowserFlow, IMSALToken, MSALOptions } from "./msalCommon"; - -const logger = credentialLogger("MSAL Browser v1 - Implicit Grant Flow"); - -/** - * Uses MSAL directly for browser authentication, - * which uses the [Implicit Grant Flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-implicit-grant-flow) - */ -export class MSALImplicit implements IMSALBrowserFlow { - private config: msal.Configuration; - private app: msal.UserAgentApplication; - private loginStyle: BrowserLoginStyle; - private correlationId?: string; - private tenantId: string; - private account: AuthenticationRecord | undefined; - - /** - * Sets up an MSAL object based on the given parameters. - * MSAL with Implicit Grant is not compatible with the account workflow we use from the MSAL Auth Code flow. - * In this case, any `authenticationRecord` received will be ignored. - * @param options - Parameters necessary and otherwise used to create the MSAL object. - */ - constructor(options: MSALOptions) { - this.loginStyle = options.loginStyle; - this.correlationId = options.correlationId; - this.tenantId = options.tenantId!; - this.config = { - auth: { - clientId: options.clientId!, // we just initialized it above - authority: `${options.authorityHost}/${options.tenantId}`, - knownAuthorities: options.knownAuthorities, - // If the users picked redirect as their login style, - // but they didn't provide a redirectUri, - // we can try to use the current page we're in as a default value. - redirectUri: options.redirectUri || self.location.origin, - postLogoutRedirectUri: options.postLogoutRedirectUri - }, - cache: { - cacheLocation: "localStorage", - storeAuthStateInCookie: true // Set to true to improve the experience on IE11 and Edge. - } - }; - - this.app = new msal.UserAgentApplication(this.config); - } - - /** - * Formats an MSAL 1 account into an `AuthenticationRecord`. - * @param account - The account in the shape defined by MSAL. - */ - private handleAccount(account: msal.Account): AuthenticationRecord { - return { - homeAccountId: account.homeAccountIdentifier, - environment: account.environment, - tenantId: this.tenantId, - localAccountId: account.accountIdentifier, - username: account.environment - }; - } - - /** - * Loads the account based on the result of the authentication. - * If no result was received, tries to load the account from the cache. - * @param result - Result object received from MSAL. - */ - private handleResult(result?: msal.AuthResponse): AuthenticationRecord | undefined { - if (result?.account) { - logger.info(`Authentication successful.`); - this.account = this.handleAccount(result?.account); - return this.account; - } - this.getActiveAccount(); - if (this.account) { - return this.account; - } - return; - } - - private redirectPromise: Promise | undefined; - - /** - * Attempts to handle a redirection request the least amount of times possible. - */ - public async handleRedirect(): Promise { - if (this.account) { - return this.account; - } - if (this.redirectPromise) { - return this.redirectPromise; - } - if (!self.location.hash) { - return; - } - this.redirectPromise = new Promise((resolve, reject) => { - this.app.handleRedirectCallback((result) => { - if (!result?.account) { - const errorMessage = `Authentication failed. No redirect result.`; - logger.info(errorMessage); - reject(new Error(errorMessage)); - } - this.handleResult(result!); - resolve(this.account); - }, reject); - }); - return this.redirectPromise; - } - - /** - * Uses MSAL to trigger a redirect or a popup login. - */ - public async login(): Promise { - if (this.getActiveAccount()) { - return this.account; - } - switch (this.loginStyle) { - case "redirect": { - this.handleRedirect(); - this.app.loginRedirect(); - return this.redirectPromise; - } - case "popup": - return this.handleResult(await this.app.loginPopup()); - } - } - - /** - * Returns the existing account, attempts to load the account from MSAL. - */ - public getActiveAccount(): AuthenticationRecord | undefined { - if (this.account) { - return this.account; - } - const account = this.app.getAccount(); - if (!account) { - return; - } - this.account = this.handleAccount(account); - return this.account; - } - - /** - * Allows users to manually authenticate and retrieve the AuthenticationRecord. - * @param options - Optional parameters to authenticate with, like the scope. - */ - public async authenticate(): Promise { - // We ensure that redirection is handled at this point. - await this.handleRedirect(); - - // If we've been able to retrieve the account, we return it. - if (this.account) { - return this.account; - } - - // Otherwise we try to login. - return this.login(); - } - - /** - * Attempts to retrieve an authenticated token from MSAL. - * @param options - Properties useful to retrieve the token, like the scopes and the abortSignal. - */ - public async acquireToken( - options: InteractiveBrowserAuthenticateOptions - ): Promise { - await this.authenticate(); - - const scopes = options.scopes; - if (!scopes) { - throw new Error( - `Invalid scopes in the acquireToken function of the MSAL Auth Code flow. Received: ${scopes}` - ); - } - - const silentRequest: msal.AuthenticationParameters = { - authority: this.config.auth.authority!, - correlationId: this.correlationId, // If undefined, MSAL will automatically generate one. - scopes: Array.isArray(scopes) ? scopes : scopes.split(",") - }; - - let authResponse: msal.AuthResponse | undefined; - - try { - logger.info("Attempting to acquire token silently"); - authResponse = await this.app.acquireTokenSilent(silentRequest); - } catch (err) { - if (err instanceof msal.AuthError) { - switch (err.errorCode) { - case "consent_required": - case "interaction_required": - case "login_required": - logger.info(`Authentication returned errorCode ${err.errorCode}`); - break; - default: - logger.info(`Failed to acquire token: ${err.message}`); - throw err; - } - } - } - - if (authResponse === undefined) { - logger.info( - `Silent authentication failed, falling back to interactive method ${this.loginStyle}` - ); - switch (this.loginStyle) { - case "redirect": - // This will go out of the page. - // Once the InteractiveBrowserCredential is initialized again, - // we'll load the MSAL account in the constructor. - this.app.acquireTokenRedirect(silentRequest); - return undefined; - case "popup": - authResponse = await this.app.acquireTokenPopup(silentRequest); - break; - } - } - - return { - accessToken: authResponse.accessToken, - expiresOn: authResponse.expiresOn - }; - } -} diff --git a/sdk/identity/identity/src/credentials/persistentCredentialOptions.ts b/sdk/identity/identity/src/credentials/persistentCredentialOptions.ts new file mode 100644 index 000000000000..d843be9ad642 --- /dev/null +++ b/sdk/identity/identity/src/credentials/persistentCredentialOptions.ts @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { TokenCachePersistenceOptions } from "../tokenCache/persistencePlatforms"; +import { TokenCredentialOptions } from "../client/identityClient"; + +/** + * Common constructor options for the credentials that use persistence. + */ +export interface PersistentCredentialOptions extends TokenCredentialOptions { + /** + * To provide a persistence layer to store the credentials, + * we allow users to optionally specify {@link TokenCachePersistenceOptions} for their credential. + * + * This persistence layer uses DPAPI on Windows. + * On OSX (Darwin) it tries to use the system's Keychain, otherwise if the property `allowUnencryptedStorage` is set to true, it uses an unencrypted file. + * On Linux it tries to use the system's Keyring, otherwise if the property `allowUnencryptedStorage` is set to true, it uses an unencrypted file. + */ + tokenCachePersistenceOptions?: TokenCachePersistenceOptions; +} diff --git a/sdk/identity/identity/src/credentials/usernamePasswordCredential.ts b/sdk/identity/identity/src/credentials/usernamePasswordCredential.ts index 3040b3493683..5dcfd69e360e 100644 --- a/sdk/identity/identity/src/credentials/usernamePasswordCredential.ts +++ b/sdk/identity/identity/src/credentials/usernamePasswordCredential.ts @@ -1,15 +1,13 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import qs from "qs"; -import { TokenCredential, GetTokenOptions, AccessToken } from "@azure/core-http"; -import { TokenCredentialOptions, IdentityClient } from "../client/identityClient"; -import { createSpan } from "../util/tracing"; -import { AuthenticationErrorName } from "../client/errors"; -import { CanonicalCode } from "@opentelemetry/api"; -import { credentialLogger, formatSuccess, formatError } from "../util/logging"; -import { getIdentityTokenEndpointSuffix } from "../util/identityTokenEndpoint"; -import { checkTenantId } from "../util/checkTenantId"; +import { AccessToken, GetTokenOptions, TokenCredential } from "@azure/core-http"; +import { credentialLogger } from "../util/logging"; +import { MsalUsernamePassword } from "../msal/nodeFlows/msalUsernamePassword"; +import { MsalFlow } from "../msal/flows"; +import { trace } from "../util/tracing"; +import { AuthenticationRecord } from "../msal/types"; +import { UsernamePasswordCredentialOptions } from "./usernamePasswordCredentialOptions"; const logger = credentialLogger("UsernamePasswordCredential"); @@ -19,38 +17,40 @@ const logger = credentialLogger("UsernamePasswordCredential"); * trust so you should only use it when other, more secure credential * types can't be used. */ +// We'll be using InteractiveCredential as the base of this class, which requires us to support authenticate(), +// to reduce the number of times we send the password over the network. export class UsernamePasswordCredential implements TokenCredential { - private identityClient: IdentityClient; - private tenantId: string; - private clientId: string; - private username: string; - private password: string; + private msalFlow: MsalFlow; + private disableAutomaticAuthentication?: boolean; /** * Creates an instance of the UsernamePasswordCredential with the details * needed to authenticate against Azure Active Directory with a username * and password. * - * @param tenantIdOrName - The Azure Active Directory tenant (directory) ID or name. + * @param tenantId - The Azure Active Directory tenant (directory). * @param clientId - The client (application) ID of an App Registration in the tenant. * @param username - The user account's e-mail address (user name). * @param password - The user account's account password * @param options - Options for configuring the client which makes the authentication request. */ constructor( - tenantIdOrName: string, + tenantId: string, clientId: string, username: string, password: string, - options?: TokenCredentialOptions + options: UsernamePasswordCredentialOptions = {} ) { - checkTenantId(logger, tenantIdOrName); - - this.identityClient = new IdentityClient(options); - this.tenantId = tenantIdOrName; - this.clientId = clientId; - this.username = username; - this.password = password; + this.msalFlow = new MsalUsernamePassword({ + ...options, + logger, + clientId, + tenantId, + username, + password, + tokenCredentialOptions: options || {} + }); + this.disableAutomaticAuthentication = options?.disableAutomaticAuthentication; } /** @@ -59,54 +59,44 @@ export class UsernamePasswordCredential implements TokenCredential { * return null. If an error occurs during authentication, an {@link AuthenticationError} * containing failure details will be thrown. * + * If the user provided the option `disableAutomaticAuthentication`, + * once the token can't be retrieved silently, + * this method won't attempt to request user interaction to retrieve the token. + * * @param scopes - The list of scopes for which the token will have access. * @param options - The options used to configure any requests this * TokenCredential implementation might make. */ - public async getToken( - scopes: string | string[], - options?: GetTokenOptions - ): Promise { - const { span, updatedOptions } = createSpan("UsernamePasswordCredential-getToken", options); - try { - const urlSuffix = getIdentityTokenEndpointSuffix(this.tenantId); - const webResource = this.identityClient.createWebResource({ - url: `${this.identityClient.authorityHost}/${this.tenantId}/${urlSuffix}`, - method: "POST", - disableJsonStringifyOnBody: true, - deserializationMapper: undefined, - body: qs.stringify({ - response_type: "token", - grant_type: "password", - client_id: this.clientId, - username: this.username, - password: this.password, - scope: typeof scopes === "string" ? scopes : scopes.join(" ") - }), - headers: { - Accept: "application/json", - "Content-Type": "application/x-www-form-urlencoded" - }, - abortSignal: options && options.abortSignal, - spanOptions: updatedOptions?.tracingOptions?.spanOptions + async getToken(scopes: string | string[], options: GetTokenOptions = {}): Promise { + return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => { + const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + return this.msalFlow.getToken(arrayScopes, { + ...newOptions, + disableAutomaticAuthentication: this.disableAutomaticAuthentication }); + }); + } - const tokenResponse = await this.identityClient.sendTokenRequest(webResource); - logger.getToken.info(formatSuccess(scopes)); - return (tokenResponse && tokenResponse.accessToken) || null; - } catch (err) { - const code = - err.name === AuthenticationErrorName - ? CanonicalCode.UNAUTHENTICATED - : CanonicalCode.UNKNOWN; - span.setStatus({ - code, - message: err.message - }); - logger.getToken.info(formatError(scopes, err)); - throw err; - } finally { - span.end(); - } + /** + * Authenticates with Azure Active Directory and returns an access token if + * successful. If authentication cannot be performed at this time, this method may + * return null. If an error occurs during authentication, an {@link AuthenticationError} + * containing failure details will be thrown. + * + * If the token can't be retrieved silently, this method will require user interaction to retrieve the token. + * + * @param scopes - The list of scopes for which the token will have access. + * @param options - The options used to configure any requests this + * TokenCredential implementation might make. + */ + async authenticate( + scopes: string | string[], + options: GetTokenOptions = {} + ): Promise { + return trace(`${this.constructor.name}.authenticate`, options, async (newOptions) => { + const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + await this.msalFlow.getToken(arrayScopes, newOptions); + return this.msalFlow.getActiveAccount(); + }); } } diff --git a/sdk/identity/identity/src/credentials/usernamePasswordCredentialOptions.ts b/sdk/identity/identity/src/credentials/usernamePasswordCredentialOptions.ts new file mode 100644 index 000000000000..430e8e96adfd --- /dev/null +++ b/sdk/identity/identity/src/credentials/usernamePasswordCredentialOptions.ts @@ -0,0 +1,9 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { InteractiveCredentialOptions } from "./interactiveCredentialOptions"; + +/** + * Defines options for the {@link UsernamePasswordCredential} class. + */ +export interface UsernamePasswordCredentialOptions extends InteractiveCredentialOptions {} diff --git a/sdk/identity/identity/src/index.ts b/sdk/identity/identity/src/index.ts index cfd9c508e228..5b0f9ce9cf2e 100644 --- a/sdk/identity/identity/src/index.ts +++ b/sdk/identity/identity/src/index.ts @@ -4,47 +4,55 @@ import { TokenCredential } from "@azure/core-http"; import { DefaultAzureCredential } from "./credentials/defaultAzureCredential"; -export { ChainedTokenCredential } from "./credentials/chainedTokenCredential"; +export { AuthenticationRecord } from "./msal/types"; +export { AuthenticationRequired } from "./msal/errors"; +export { deserializeAuthenticationRecord } from "./msal/transformations"; export { TokenCredentialOptions } from "./client/identityClient"; +export { PersistentCredentialOptions } from "./credentials/persistentCredentialOptions"; +export { InteractiveCredentialOptions } from "./credentials/interactiveCredentialOptions"; +export { TokenCachePersistenceOptions } from "./tokenCache/persistencePlatforms"; + +export { ChainedTokenCredential } from "./credentials/chainedTokenCredential"; +export { + DefaultAzureCredential, + DefaultAzureCredentialOptions +} from "./credentials/defaultAzureCredential"; export { EnvironmentCredential } from "./credentials/environmentCredential"; export { ClientSecretCredential } from "./credentials/clientSecretCredential"; +export { ClientSecretCredentialOptions } from "./credentials/clientSecretCredentialOptions"; export { ClientCertificateCredential } from "./credentials/clientCertificateCredential"; export { ClientCertificateCredentialOptions } from "./credentials/clientCertificateCredentialOptions"; -export { InteractiveBrowserCredential } from "./credentials/interactiveBrowserCredential"; export { VisualStudioCodeCredential, VisualStudioCodeCredentialOptions } from "./credentials/visualStudioCodeCredential"; export { AzureCliCredential } from "./credentials/azureCliCredential"; - -export { AuthenticationRecord } from "./client/msalClient"; +export { InteractiveBrowserCredential } from "./credentials/interactiveBrowserCredential"; export { InteractiveBrowserCredentialOptions, InteractiveBrowserCredentialBrowserOptions, - InteractiveBrowserCredentialCommonOptions, BrowserLoginStyle, InteractiveBrowserAuthenticationFlow } from "./credentials/interactiveBrowserCredentialOptions"; export { ManagedIdentityCredential } from "./credentials/managedIdentityCredential"; +export { DeviceCodeCredential } from "./credentials/deviceCodeCredential"; export { - DeviceCodeCredential, DeviceCodePromptCallback, DeviceCodeInfo -} from "./credentials/deviceCodeCredential"; - -export { - DefaultAzureCredential, - DefaultAzureCredentialOptions -} from "./credentials/defaultAzureCredential"; +} from "./credentials/deviceCodeCredentialOptions"; +export { DeviceCodeCredentialOptions } from "./credentials/deviceCodeCredentialOptions"; export { UsernamePasswordCredential } from "./credentials/usernamePasswordCredential"; +export { UsernamePasswordCredentialOptions } from "./credentials/usernamePasswordCredentialOptions"; export { AuthorizationCodeCredential } from "./credentials/authorizationCodeCredential"; + export { AuthenticationError, ErrorResponse, AggregateAuthenticationError, AuthenticationErrorName, AggregateAuthenticationErrorName, - CredentialUnavailable + CredentialUnavailable, + CredentialUnavailableName } from "./client/errors"; export { TokenCredential, GetTokenOptions, AccessToken } from "@azure/core-http"; diff --git a/sdk/identity/identity/src/msal/browserFlows/browserCommon.ts b/sdk/identity/identity/src/msal/browserFlows/browserCommon.ts new file mode 100644 index 000000000000..b9582d79aaf5 --- /dev/null +++ b/sdk/identity/identity/src/msal/browserFlows/browserCommon.ts @@ -0,0 +1,156 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as msal from "msal"; +import * as msalBrowser from "@azure/msal-browser"; +import { AccessToken } from "@azure/core-http"; +import { DefaultTenantId } from "../../constants"; +import { resolveTenantId } from "../../util/resolveTenantId"; +import { BrowserLoginStyle } from "../../credentials/interactiveBrowserCredentialOptions"; +import { getAuthorityHost, getKnownAuthorities, MsalBaseUtilities } from "../utils"; +import { MsalFlow, MsalFlowOptions } from "../flows"; +import { AuthenticationRecord } from "../types"; +import { CredentialFlowGetTokenOptions } from "../credentials"; +import { AuthenticationRequired } from "../errors"; + +/** + * Union of the constructor parameters that all MSAL flow types take. + * Some properties might not be used by some flow types. + */ +export interface MsalBrowserFlowOptions extends MsalFlowOptions { + redirectUri?: string; + loginStyle: BrowserLoginStyle; +} + +/** + * The common methods we use to work with the MSAL browser flows. + * @internal + */ +export interface MsalBrowserFlow extends MsalFlow { + login(scopes?: string[]): Promise; + handleRedirect(): Promise; +} + +/** + * Generates a MSAL configuration that generally works for browsers + * @internal + */ +export function defaultBrowserMsalConfig( + options: MsalBrowserFlowOptions +): msalBrowser.Configuration { + const tenantId = options.tenantId || DefaultTenantId; + const authorityHost = getAuthorityHost(tenantId, options.authorityHost); + return { + auth: { + clientId: options.clientId!, // we just initialized it above + authority: authorityHost, + knownAuthorities: getKnownAuthorities(tenantId, authorityHost), + // If the users picked redirect as their login style, + // but they didn't provide a redirectUri, + // we can try to use the current page we're in as a default value. + redirectUri: options.redirectUri || self.location.origin + } + }; +} + +/** + * MSAL partial base client for the browsers. + * + * It completes the input configuration with some default values. + * It also provides with utility protected methods that can be used from any of the clients, + * which includes handlers for successful responses and errors. + * + * @internal + */ +export abstract class MsalBrowser extends MsalBaseUtilities implements MsalBrowserFlow { + protected loginStyle: BrowserLoginStyle; + protected tenantId: string; + protected account: AuthenticationRecord | undefined; + protected msalConfig: msal.Configuration | msalBrowser.Configuration; + protected disableAutomaticAuthentication?: boolean; + protected app?: msal.UserAgentApplication | msalBrowser.PublicClientApplication; + + constructor(options: MsalBrowserFlowOptions) { + super(options); + this.logger = options.logger; + this.loginStyle = options.loginStyle; + this.tenantId = resolveTenantId(this.logger, options.tenantId, options.clientId); + this.msalConfig = defaultBrowserMsalConfig(options); + this.disableAutomaticAuthentication = options.disableAutomaticAuthentication; + + if (options.authenticationRecord) { + this.account = { + ...options.authenticationRecord, + tenantId: this.tenantId + }; + } + } + + /** + * In the browsers we don't need to init() + */ + async init(): Promise { + // Nothing to do here. + } + + /** + * Attempts to handle a redirection request the least amount of times possible. + */ + public abstract handleRedirect(): Promise; + + /** + * Clears MSAL's cache. + */ + async logout(): Promise { + this.app?.logout(); + } + + /** + * Uses MSAL to retrieve the active account. + */ + public abstract getActiveAccount(): Promise; + + /** + * Uses MSAL to trigger a redirect or a popup login. + */ + public abstract login(scopes?: string | string[]): Promise; + + /** + * Attempts to retrieve a token from cache. + */ + public abstract getTokenSilent(scopes: string[]): Promise; + + /** + * Attempts to retrieve the token in the browser. + */ + protected abstract doGetToken(scopes: string[]): Promise; + + /** + * Attempts to retrieve an authenticated token from MSAL. + */ + public async getToken( + scopes: string[], + options?: CredentialFlowGetTokenOptions + ): Promise { + // We ensure that redirection is handled at this point. + await this.handleRedirect(); + + if (!(await this.getActiveAccount()) && !this.disableAutomaticAuthentication) { + await this.login(scopes); + } + return this.getTokenSilent(scopes).catch((err) => { + if (err.name !== "AuthenticationRequired") { + throw err; + } + if (options?.disableAutomaticAuthentication) { + throw new AuthenticationRequired( + "Automatic authentication has been disabled. You may call the authentication() method." + ); + } + this.logger.info( + `Silent authentication failed, falling back to interactive method ${this.loginStyle}` + ); + return this.doGetToken(scopes); + }); + } +} diff --git a/sdk/identity/identity/src/msal/browserFlows/msalAuthCode.ts b/sdk/identity/identity/src/msal/browserFlows/msalAuthCode.ts new file mode 100644 index 000000000000..dbd0fb74df2e --- /dev/null +++ b/sdk/identity/identity/src/msal/browserFlows/msalAuthCode.ts @@ -0,0 +1,207 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as msalBrowser from "@azure/msal-browser"; +import { MsalBrowserFlowOptions, MsalBrowser } from "./browserCommon"; +import { AccessToken } from "@azure/core-http"; +import { defaultLoggerCallback } from "../utils"; +import { AuthenticationRecord } from "../types"; +import { AuthenticationRequired } from "../errors"; +import { msalToPublic, publicToMsal } from "../transformations"; +import { CredentialFlowGetTokenOptions } from "../credentials"; + +// We keep a copy of the redirect hash. +const redirectHash = self.location.hash; + +/** + * Uses MSAL Browser 2.X for browser authentication, + * which uses the [Auth Code Flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow). + * @internal + */ +export class MSALAuthCode extends MsalBrowser { + protected app: msalBrowser.PublicClientApplication; + + /** + * Sets up an MSAL object based on the given parameters. + * MSAL with Auth Code allows sending a previously obtained `authenticationRecord` through the optional parameters, + * which is set to be the active account. + * @param options - Parameters necessary and otherwise used to create the MSAL object. + */ + constructor(options: MsalBrowserFlowOptions) { + super(options); + + this.msalConfig.cache = { + cacheLocation: "sessionStorage", + storeAuthStateInCookie: true // Set to true to improve the experience on IE11 and Edge. + }; + this.msalConfig.system = { + loggerOptions: { + loggerCallback: defaultLoggerCallback(this.logger) + } + }; + + // Preparing the MSAL application. + this.app = new msalBrowser.PublicClientApplication( + this.msalConfig as msalBrowser.Configuration + ); + if (this.account) { + this.app.setActiveAccount(publicToMsal(this.account)); + } + } + + /** + * Loads the account based on the result of the authentication. + * If no result was received, tries to load the account from the cache. + * @param result - Result object received from MSAL. + */ + private async handleBrowserResult( + result?: msalBrowser.AuthenticationResult + ): Promise { + try { + if (result && result.account) { + this.logger.info(`MSAL Browser V2 authentication successful.`); + this.app.setActiveAccount(result.account); + return msalToPublic(result.account); + } + + // If by this point we happen to have an active account, we should stop trying to parse this. + const activeAccount = await this.app!.getActiveAccount(); + if (activeAccount) { + return msalToPublic(activeAccount); + } + + // If we don't have an active account, we try to activate it from all the already loaded accounts. + const accounts = this.app.getAllAccounts(); + if (accounts.length > 1) { + // If there's more than one account in memory, we force the user to authenticate again. + // At this point we can't identify which account should this credential work with, + // since at this point the user won't have provided enough information. + // We log a message in case that helps. + this.logger.info( + `More than one account was found authenticated for this Client ID and Tenant ID. +However, no "authenticationRecord" has been provided for this credential, +therefore we're unable to pick between these accounts. +A new login attempt will be requested, to ensure the correct account is picked. +To work with multiple accounts for the same Client ID and Tenant ID, please provide an "authenticationRecord" when initializing "InteractiveBrowserCredential".` + ); + // To safely trigger a new login, we're also ensuring the local cache is cleared up for this MSAL object. + // However, we want to avoid kicking the user out of their authentication on the Azure side. + // We do this by calling to logout while specifying a `onRedirectNavigate` that returns false. + await this.app.logout({ + onRedirectNavigate: () => false + }); + return; + } + + // If there's only one account for this MSAL object, we can safely activate it. + if (accounts.length === 1) { + const account = accounts[0]; + this.app.setActiveAccount(account); + return msalToPublic(account); + } + + this.logger.info(`No accounts were found through MSAL.`); + } catch (e) { + this.logger.info(`Failed to acquire token through MSAL. ${e.message}`); + } + return; + } + + /** + * Uses MSAL to handle the redirect. + */ + public async handleRedirect(): Promise { + return this.handleBrowserResult( + (await this.app.handleRedirectPromise(redirectHash)) || undefined + ); + } + + /** + * Uses MSAL to trigger a redirect or a popup login. + */ + public async login(scopes: string | string[] = []): Promise { + const arrayScopes = Array.isArray(scopes) ? scopes : [scopes]; + const loginRequest = { + scopes: arrayScopes + }; + switch (this.loginStyle) { + case "redirect": { + await this.app.loginRedirect(loginRequest); + return; + } + case "popup": + return this.handleBrowserResult(await this.app.loginPopup(loginRequest)); + } + } + + /** + * Uses MSAL to retrieve the active account. + */ + public async getActiveAccount(): Promise { + const account = this.app.getActiveAccount(); + if (!account) { + return; + } + return msalToPublic(account); + } + + /** + * Attempts to retrieve a token from cache. + */ + public async getTokenSilent( + scopes: string[], + options?: CredentialFlowGetTokenOptions + ): Promise { + const account = await this.getActiveAccount(); + if (!account) { + throw new AuthenticationRequired(); + } + + const parameters: msalBrowser.SilentRequest = { + authority: this.msalConfig.auth.authority!, + correlationId: options?.correlationId, + account: publicToMsal(account), + forceRefresh: false, + scopes + }; + + try { + this.logger.info("Attempting to acquire token silently"); + const response = await this.app.acquireTokenSilent(parameters); + return this.handleResult(scopes, response); + } catch (err) { + throw this.handleError(scopes, err); + } + } + + /** + * Attempts to retrieve the token in the browser. + */ + protected async doGetToken( + scopes: string[], + options?: CredentialFlowGetTokenOptions + ): Promise { + const account = await this.getActiveAccount(); + if (!account) { + throw new AuthenticationRequired(); + } + + const parameters: msalBrowser.RedirectRequest = { + authority: this.msalConfig.auth.authority!, + correlationId: options?.correlationId, + account: publicToMsal(account), + scopes + }; + + switch (this.loginStyle) { + case "redirect": + // This will go out of the page. + // Once the InteractiveBrowserCredential is initialized again, + // we'll load the MSAL account in the constructor. + await this.app.acquireTokenRedirect(parameters); + return { token: "", expiresOnTimestamp: 0 }; + case "popup": + return this.handleResult(scopes, await this.app.acquireTokenPopup(parameters)); + } + } +} diff --git a/sdk/identity/identity/src/msal/browserFlows/msalImplicit.ts b/sdk/identity/identity/src/msal/browserFlows/msalImplicit.ts new file mode 100644 index 000000000000..1ba1d2fd861b --- /dev/null +++ b/sdk/identity/identity/src/msal/browserFlows/msalImplicit.ts @@ -0,0 +1,203 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as msal from "msal"; +import { MsalBrowserFlowOptions, MsalBrowser } from "./browserCommon"; +import { AccessToken } from "@azure/core-http"; +import { AuthenticationRecord } from "../types"; +import { AuthenticationRequired } from "../errors"; +import { CredentialFlowGetTokenOptions } from "../credentials"; +import { publicToMsal, serializeAuthenticationRecord } from "../transformations"; +import { getAuthorityHost } from "../utils"; + +/** + * Uses MSAL directly for browser authentication, + * which uses the [Implicit Grant Flow](https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-implicit-grant-flow) + * @internal + */ +export class MSALImplicit extends MsalBrowser { + protected app: msal.UserAgentApplication; + + /** + * Sets up an MSAL object based on the given parameters. + * MSAL with Implicit Grant is not compatible with the account workflow we use from the MSAL Auth Code flow. + * In this case, any `authenticationRecord` received will be ignored. + * @param options - Parameters necessary and otherwise used to create the MSAL object. + */ + constructor(options: MsalBrowserFlowOptions) { + super(options); + + this.msalConfig.cache = { + cacheLocation: "localStorage", + storeAuthStateInCookie: true // Set to true to improve the experience on IE11 and Edge. + }; + + // Preparing the MSAL application. + this.app = new msal.UserAgentApplication(this.msalConfig as msal.Configuration); + } + + /** + * Formats an MSAL 1 account into an `AuthenticationRecord`. + * @param account - The account in the shape defined by MSAL. + */ + private msalBrowserToPublicAccount(account: msal.Account): AuthenticationRecord { + const record = { + homeAccountId: account.homeAccountIdentifier, + authority: getAuthorityHost(this.tenantId, account.environment), + tenantId: this.tenantId, + username: account.environment, + serialize: () => serializeAuthenticationRecord(record) + }; + return record; + } + + /** + * Loads the account based on the result of the authentication. + * If no result was received, tries to load the account from the cache. + * @param result - Result object received from MSAL. + */ + private async handleBrowserResult( + result?: msal.AuthResponse + ): Promise { + if (result?.account) { + this.logger.info(`Authentication successful.`); + this.account = this.msalBrowserToPublicAccount(result?.account); + return this.account; + } + await this.getActiveAccount(); + if (this.account) { + return this.account; + } + return; + } + + private redirectPromise: Promise | undefined; + + /** + * Attempts to handle a redirection request the least amount of times possible. + */ + public async handleRedirect(): Promise { + if (this.account) { + return this.account; + } + if (this.redirectPromise) { + return this.redirectPromise; + } + if (!self.location.hash) { + return; + } + this.redirectPromise = new Promise((resolve, reject) => { + this.app.handleRedirectCallback(async (result) => { + if (!result?.account) { + const errorMessage = `Authentication failed. No redirect result.`; + this.logger.info(errorMessage); + reject(new Error(errorMessage)); + } + await this.handleBrowserResult(result!); + resolve(this.account); + }, reject); + }); + return this.redirectPromise; + } + + /** + * Uses MSAL to trigger a redirect or a popup login. + */ + public async login(): Promise { + switch (this.loginStyle) { + case "redirect": { + this.handleRedirect(); + this.app.loginRedirect(); + return this.redirectPromise; + } + case "popup": + return this.handleBrowserResult(await this.app.loginPopup()); + } + } + + /** + * Returns the existing account, attempts to load the account from MSAL. + */ + public async getActiveAccount(): Promise { + if (this.account) { + return this.account; + } + const account = this.app.getAccount(); + if (!account) { + return; + } + this.account = this.msalBrowserToPublicAccount(account); + return this.account; + } + + /** + * Attempts to retrieve a token from cache. + */ + public async getTokenSilent( + scopes: string[], + options?: CredentialFlowGetTokenOptions + ): Promise { + const account = await this.getActiveAccount(); + if (!account) { + throw new AuthenticationRequired(); + } + + const parameters: msal.AuthenticationParameters = { + authority: this.msalConfig.auth.authority!, + correlationId: options?.correlationId, + scopes + }; + + try { + this.logger.info("Attempting to acquire token silently"); + const response = await this.app.acquireTokenSilent(parameters); + return this.handleResult(scopes, { + account: { + ...this.msalBrowserToPublicAccount(response.account), + localAccountId: response.account.accountIdentifier + }, + accessToken: response.accessToken, + expiresOn: response.expiresOn + }); + } catch (err) { + throw this.handleError(scopes, err); + } + } + + /** + * Attempts to retrieve the token in the browser. + */ + protected async doGetToken( + scopes: string[], + options?: CredentialFlowGetTokenOptions + ): Promise { + const account = await this.getActiveAccount(); + if (!account) { + throw new AuthenticationRequired(); + } + + const parameters: msal.AuthenticationParameters = { + authority: this.msalConfig.auth.authority!, + correlationId: options?.correlationId, + scopes + }; + + let response: msal.AuthResponse | undefined; + + switch (this.loginStyle) { + case "redirect": + // This will go out of the page. + // Once the InteractiveBrowserCredential is initialized again, + // we'll load the MSAL account in the constructor. + this.app.acquireTokenRedirect(parameters); + throw new Error("Redirecting..."); + case "popup": + response = await this.app.acquireTokenPopup(parameters); + return this.handleResult(scopes, { + account: publicToMsal(this.msalBrowserToPublicAccount(response.account)), + accessToken: response.accessToken, + expiresOn: response.expiresOn + }); + } + } +} diff --git a/sdk/identity/identity/src/msal/credentials.ts b/sdk/identity/identity/src/msal/credentials.ts new file mode 100644 index 000000000000..4d0238c83338 --- /dev/null +++ b/sdk/identity/identity/src/msal/credentials.ts @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { AccessToken, GetTokenOptions } from "@azure/core-http"; +import { AuthenticationRecord } from "./types"; + +/** + * The MSAL clients `getToken` requests can receive a `correlationId` and `disableAutomaticAuthentication`. + * (which is used to prevent `getToken` from triggering the manual authentication if `getTokenSilent` fails). + * @internal + */ +export interface CredentialFlowGetTokenOptions extends GetTokenOptions { + /** + * Unique identifier useful to track outgoing requests. + */ + correlationId?: string; + /** + * Makes getToken throw if a manual authentication is necessary. + */ + disableAutomaticAuthentication?: boolean; +} + +/** + * Simplified representation of the internal authentication flow. + * @internal + */ +export interface CredentialFlow { + /** + * Clears the MSAL cache. + */ + logout(): Promise; + /** + * Tries to load the active account, either from memory or from MSAL. + */ + getActiveAccount(): Promise; + /** + * Calls to the implementation's doGetToken method. + */ + getToken(scopes?: string[], options?: CredentialFlowGetTokenOptions): Promise; +} diff --git a/sdk/identity/identity/src/msal/errors.ts b/sdk/identity/identity/src/msal/errors.ts new file mode 100644 index 000000000000..74311fb241e3 --- /dev/null +++ b/sdk/identity/identity/src/msal/errors.ts @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { CredentialUnavailable } from "../client/errors"; + +/** + * Error used to enforce authentication after trying to retrieve a token silently. + */ +export class AuthenticationRequired extends CredentialUnavailable { + constructor(message?: string) { + super(message); + this.name = "AuthenticationRequired"; + } +} diff --git a/sdk/identity/identity/src/msal/flows.ts b/sdk/identity/identity/src/msal/flows.ts new file mode 100644 index 000000000000..80d9a69dd38e --- /dev/null +++ b/sdk/identity/identity/src/msal/flows.ts @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { AccessToken } from "@azure/core-http"; +import { CredentialLogger } from "../util/logging"; +import { AuthenticationRecord } from "./types"; +import { CredentialFlowGetTokenOptions } from "./credentials"; + +/** + * Union of the constructor parameters that all MSAL flow types take. + * @internal + */ +export interface MsalFlowOptions { + logger: CredentialLogger; + clientId?: string; + tenantId?: string; + authorityHost?: string; + authenticationRecord?: AuthenticationRecord; + disableAutomaticAuthentication?: boolean; +} + +/** + * The common methods we use to work with the MSAL flows. + * @internal + */ +export interface MsalFlow { + /** + * Allows for any setup before any request is processed. + */ + init(options?: CredentialFlowGetTokenOptions): Promise; + /** + * Clears the MSAL cache. + */ + logout(): Promise; + /** + * Tries to load the active account, either from memory or from MSAL. + */ + getActiveAccount(): Promise; + /** + * Tries to retrieve the token silently using MSAL. + */ + getTokenSilent(scopes?: string[], options?: CredentialFlowGetTokenOptions): Promise; + /** + * Calls to the implementation's doGetToken method. + */ + getToken(scopes?: string[], options?: CredentialFlowGetTokenOptions): Promise; +} diff --git a/sdk/identity/identity/src/msal/nodeFlows/msalClientCertificate.ts b/sdk/identity/identity/src/msal/nodeFlows/msalClientCertificate.ts new file mode 100644 index 000000000000..6bea2c72706b --- /dev/null +++ b/sdk/identity/identity/src/msal/nodeFlows/msalClientCertificate.ts @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { readFileSync } from "fs"; +import { createHash } from "crypto"; +import { AccessToken } from "@azure/core-http"; +import { MsalNodeOptions, MsalNode } from "./nodeCommon"; +import { formatError } from "../../util/logging"; +import { CredentialFlowGetTokenOptions } from "../credentials"; + +/** + * Options that can be passed to configure MSAL to handle client certificates. + * @internal + */ +export interface MSALClientCertificateOptions extends MsalNodeOptions { + /** + * Location of the certificate. + */ + certificatePath: string; + /** + * Option to include x5c header for SubjectName and Issuer name authorization. + * Set this option to send base64 encoded public certificate in the client assertion header as an x5c claim + */ + sendCertificateChain?: boolean; +} + +/** + * Parts of a certificate, as understood by MSAL. + * @internal + */ +interface CertificateParts { + /** + * Hex encoded X.509 SHA-1 thumbprint of the certificate + */ + thumbprint: string; + /** + * The PEM encoded private key (string should contain -----BEGIN PRIVATE KEY----- ... -----END PRIVATE KEY----- + */ + certificateContents: string; + /** + * x5c header. + */ + x5c: string; +} + +/** + * MSAL client certificate client. Calls to MSAL's confidential application's `acquireTokenByClientCredential` during `doGetToken`. + * @internal + */ +export class MsalClientCertificate extends MsalNode { + private sendCertificateChain?: boolean; + + constructor(options: MSALClientCertificateOptions) { + super(options); + this.requiresConfidential = true; + this.sendCertificateChain = options.sendCertificateChain; + + const parts = this.parseCertificate(options.certificatePath); + this.msalConfig.auth.clientCertificate = { + thumbprint: parts.thumbprint, + privateKey: parts.certificateContents, + x5c: parts.x5c + }; + } + + private parseCertificate(certificatePath: string): CertificateParts { + const certificateParts: Partial = {}; + + certificateParts.certificateContents = readFileSync(certificatePath, "utf8"); + if (this.sendCertificateChain) { + certificateParts.x5c = certificateParts.certificateContents; + } + + const certificatePattern = /(-+BEGIN CERTIFICATE-+)(\n\r?|\r\n?)([A-Za-z0-9+/\n\r]+=*)(\n\r?|\r\n?)(-+END CERTIFICATE-+)/g; + const publicKeys: string[] = []; + + // Match all possible certificates, in the order they are in the file. These will form the chain that is used for x5c + let match; + do { + match = certificatePattern.exec(certificateParts.certificateContents); + if (match) { + publicKeys.push(match[3]); + } + } while (match); + + if (publicKeys.length === 0) { + const error = new Error( + "The file at the specified path does not contain a PEM-encoded certificate." + ); + this.logger.info(formatError("", error)); + throw error; + } + + certificateParts.thumbprint = createHash("sha1") + .update(Buffer.from(publicKeys[0], "base64")) + .digest("hex") + .toUpperCase(); + + return certificateParts as CertificateParts; + } + + protected async doGetToken( + scopes: string[], + options: CredentialFlowGetTokenOptions = {} + ): Promise { + try { + const result = await this.confidentialApp!.acquireTokenByClientCredential({ + scopes, + correlationId: options.correlationId + }); + // Even though we're providing the same default in memory persistence cache that we use for DeviceCodeCredential, + // The Client Credential flow does not return the account information from the authentication service, + // so each time getToken gets called, we will have to acquire a new token through the service. + return this.handleResult(scopes, result || undefined); + } catch (err) { + throw this.handleError(scopes, err); + } + } +} diff --git a/sdk/identity/identity/src/msal/nodeFlows/msalClientSecret.ts b/sdk/identity/identity/src/msal/nodeFlows/msalClientSecret.ts new file mode 100644 index 000000000000..651cc2de5691 --- /dev/null +++ b/sdk/identity/identity/src/msal/nodeFlows/msalClientSecret.ts @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { AccessToken } from "@azure/core-http"; +import { CredentialFlowGetTokenOptions } from "../credentials"; +import { MsalNodeOptions, MsalNode } from "./nodeCommon"; + +/** + * Options that can be passed to configure MSAL to handle client secrets. + * @internal + */ +export interface MSALClientSecretOptions extends MsalNodeOptions { + clientSecret: string; +} + +/** + * MSAL client secret client. Calls to MSAL's confidential application's `acquireTokenByClientCredential` during `doGetToken`. + * @internal + */ +export class MsalClientSecret extends MsalNode { + constructor(options: MSALClientSecretOptions) { + super(options); + this.requiresConfidential = true; + this.msalConfig.auth.clientSecret = options.clientSecret; + } + + protected async doGetToken( + scopes: string[], + options: CredentialFlowGetTokenOptions = {} + ): Promise { + try { + const result = await this.confidentialApp!.acquireTokenByClientCredential({ + scopes, + correlationId: options.correlationId + }); + // The Client Credential flow does not return an account, + // so each time getToken gets called, we will have to acquire a new token through the service. + return this.handleResult(scopes, result || undefined); + } catch (err) { + throw this.handleError(scopes, err); + } + } +} diff --git a/sdk/identity/identity/src/msal/nodeFlows/msalDeviceCode.ts b/sdk/identity/identity/src/msal/nodeFlows/msalDeviceCode.ts new file mode 100644 index 000000000000..b7bc62d27ada --- /dev/null +++ b/sdk/identity/identity/src/msal/nodeFlows/msalDeviceCode.ts @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as msalNode from "@azure/msal-node"; +import { AccessToken } from "@azure/core-http"; +import { DeviceCodePromptCallback } from "../../credentials/deviceCodeCredentialOptions"; +import { CredentialFlowGetTokenOptions } from "../credentials"; +import { MsalNodeOptions, MsalNode } from "./nodeCommon"; + +/** + * Options that can be passed to configure MSAL to handle authentication through device codes. + * @internal + */ +export interface MSALDeviceCodeOptions extends MsalNodeOptions { + userPromptCallback: DeviceCodePromptCallback; +} + +/** + * MSAL device code client. Calls to the MSAL's public application's `acquireTokenByDeviceCode` during `doGetToken`. + * @internal + */ +export class MsalDeviceCode extends MsalNode { + private userPromptCallback: DeviceCodePromptCallback; + + constructor(options: MSALDeviceCodeOptions) { + super(options); + this.userPromptCallback = options.userPromptCallback; + } + + protected async doGetToken( + scopes: string[], + options?: CredentialFlowGetTokenOptions + ): Promise { + try { + const requestOptions: msalNode.DeviceCodeRequest = { + deviceCodeCallback: this.userPromptCallback, + scopes, + cancel: false, + correlationId: options?.correlationId + }; + const promise = this.publicApp!.acquireTokenByDeviceCode(requestOptions); + // TODO: + // This should work, but it currently doesn't. I'm waiting for an answer from the MSAL team. + const deviceResponse = await this.withCancellation(promise, options?.abortSignal, () => { + requestOptions.cancel = true; + }); + return this.handleResult(scopes, deviceResponse || undefined); + } catch (error) { + throw this.handleError(scopes, error); + } + } +} diff --git a/sdk/identity/identity/src/msal/nodeFlows/msalOpenBrowser.ts b/sdk/identity/identity/src/msal/nodeFlows/msalOpenBrowser.ts new file mode 100644 index 000000000000..b4f465108eba --- /dev/null +++ b/sdk/identity/identity/src/msal/nodeFlows/msalOpenBrowser.ts @@ -0,0 +1,179 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as msalNode from "@azure/msal-node"; +import { Socket } from "net"; +import http from "http"; +import open from "open"; +import stoppable from "stoppable"; +import { AccessToken, GetTokenOptions } from "@azure/core-http"; +import { credentialLogger, formatError, formatSuccess } from "../../util/logging"; +import { MsalNodeOptions, MsalNode } from "./nodeCommon"; +import { msalToPublic } from "../transformations"; + +/** + * Options that can be passed to configure MSAL to handle authentication through opening a browser window. + * @internal + */ +export interface MSALOpenBrowserOptions extends MsalNodeOptions { + redirectUri: string; +} + +/** + * This MSAL client sets up a web server to listen for redirect callbacks, then calls to the MSAL's public application's `acquireTokenByDeviceCode` during `doGetToken` + * to trigger the authentication flow, and then respond based on the values obtained from the redirect callback + * @internal + */ +export class MsalOpenBrowser extends MsalNode { + private redirectUri: string; + private port: number; + private hostname: string; + + constructor(options: MSALOpenBrowserOptions) { + super(options); + this.logger = credentialLogger("NodeJS MSAL Open Browser"); + this.redirectUri = options.redirectUri; + + const url = new URL(this.redirectUri); + this.port = parseInt(url.port); + if (isNaN(this.port)) { + this.port = 80; + } + this.hostname = url.hostname; + } + + private async acquireTokenByCode( + request: msalNode.AuthorizationCodeRequest + ): Promise { + return this.publicApp!.acquireTokenByCode(request); + } + + protected doGetToken(scopes: string[], options?: GetTokenOptions): Promise { + return new Promise((resolve, reject) => { + const socketToDestroy: Socket[] = []; + + const requestListener = (req: http.IncomingMessage, res: http.ServerResponse): void => { + if (!req.url) { + reject( + new Error( + `Interactive Browser Authentication Error "Did not receive token with a valid expiration"` + ) + ); + return; + } + let url: URL; + try { + url = new URL(req.url, this.redirectUri); + } catch (e) { + reject( + new Error( + `Interactive Browser Authentication Error "Did not receive token with a valid expiration"` + ) + ); + return; + } + const tokenRequest: msalNode.AuthorizationCodeRequest = { + code: url.searchParams.get("code")!, + redirectUri: this.redirectUri, + scopes: scopes + }; + + this.acquireTokenByCode(tokenRequest) + .then((authResponse) => { + if (authResponse?.account) { + this.account = msalToPublic(authResponse.account); + } + const successMessage = `Authentication Complete. You can close the browser and return to the application.`; + if (authResponse && authResponse.expiresOn) { + const expiresOnTimestamp = authResponse?.expiresOn.valueOf(); + res.writeHead(200); + res.end(successMessage); + this.logger.getToken.info(formatSuccess(scopes)); + + resolve({ + expiresOnTimestamp, + token: authResponse.accessToken + }); + } else { + const errorMessage = formatError( + scopes, + `${url.searchParams.get("error")}. ${url.searchParams.get("error_description")}` + ); + res.writeHead(500); + res.end(errorMessage); + this.logger.getToken.info(errorMessage); + + reject( + new Error( + `Interactive Browser Authentication Error "Did not receive token with a valid expiration"` + ) + ); + } + cleanup(); + return; + }) + .catch(() => { + const errorMessage = formatError( + scopes, + `${url.searchParams.get("error")}. ${url.searchParams.get("error_description")}` + ); + res.writeHead(500); + res.end(errorMessage); + this.logger.getToken.info(errorMessage); + + reject( + new Error( + `Interactive Browser Authentication Error "Did not receive token with a valid expiration"` + ) + ); + cleanup(); + }); + }; + const app = http.createServer(requestListener); + + const listen = app.listen(this.port, this.hostname, () => + this.logger.info(`InteractiveBrowserCredential listening on port ${this.port}!`) + ); + app.on("connection", (socket) => socketToDestroy.push(socket)); + const server = stoppable(app); + + this.openAuthCodeUrl(scopes).catch((e) => { + cleanup(); + reject(e); + }); + + function cleanup(): void { + if (listen) { + listen.close(); + } + + for (const socket of socketToDestroy) { + socket.destroy(); + } + + if (server) { + server.close(); + server.stop(); + } + } + + const abortSignal = options?.abortSignal; + if (abortSignal) { + abortSignal.addEventListener("abort", () => { + cleanup(); + reject(new Error("Aborted")); + }); + } + }); + } + + private async openAuthCodeUrl(scopeArray: string[]): Promise { + const authCodeUrlParameters: msalNode.AuthorizationUrlRequest = { + scopes: scopeArray, + redirectUri: this.redirectUri + }; + + const response = await this.publicApp!.getAuthCodeUrl(authCodeUrlParameters); + await open(response); + } +} diff --git a/sdk/identity/identity/src/msal/nodeFlows/msalUsernamePassword.ts b/sdk/identity/identity/src/msal/nodeFlows/msalUsernamePassword.ts new file mode 100644 index 000000000000..00552b0e22ea --- /dev/null +++ b/sdk/identity/identity/src/msal/nodeFlows/msalUsernamePassword.ts @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as msalNode from "@azure/msal-node"; +import { AccessToken } from "@azure/core-http"; +import { MsalNodeOptions, MsalNode } from "./nodeCommon"; +import { CredentialFlowGetTokenOptions } from "../credentials"; + +/** + * Options that can be passed to configure MSAL to handle authentication through username and password. + * @internal + */ +export interface MSALUsernamePasswordOptions extends MsalNodeOptions { + username: string; + password: string; +} + +/** + * MSAL username and password client. Calls to the MSAL's public application's `acquireTokenByUsernamePassword` during `doGetToken`. + * @internal + */ +export class MsalUsernamePassword extends MsalNode { + private username: string; + private password: string; + + constructor(options: MSALUsernamePasswordOptions) { + super(options); + this.username = options.username; + this.password = options.password; + } + + protected async doGetToken( + scopes: string[], + options?: CredentialFlowGetTokenOptions + ): Promise { + try { + const requestOptions: msalNode.UsernamePasswordRequest = { + scopes, + username: this.username, + password: this.password, + correlationId: options?.correlationId + }; + const result = await this.publicApp!.acquireTokenByUsernamePassword(requestOptions); + return this.handleResult(scopes, result || undefined); + } catch (error) { + throw this.handleError(scopes, error); + } + } +} diff --git a/sdk/identity/identity/src/msal/nodeFlows/nodeCommon.ts b/sdk/identity/identity/src/msal/nodeFlows/nodeCommon.ts new file mode 100644 index 000000000000..80afc23456f3 --- /dev/null +++ b/sdk/identity/identity/src/msal/nodeFlows/nodeCommon.ts @@ -0,0 +1,255 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as msalNode from "@azure/msal-node"; +import * as msalCommon from "@azure/msal-common"; +import { AccessToken, GetTokenOptions } from "@azure/core-http"; +import { AbortSignalLike } from "@azure/abort-controller"; +import { DeveloperSignOnClientId } from "../../constants"; +import { IdentityClient, TokenCredentialOptions } from "../../client/identityClient"; +import { TokenCachePersistenceOptions } from "../../tokenCache/persistencePlatforms"; +import { TokenCachePersistence } from "../../tokenCache/TokenCachePersistence"; +import { inMemoryPersistence } from "../../tokenCache/InMemoryPersistence"; +import { resolveTenantId } from "../../util/resolveTenantId"; +import { TokenCache } from "../../tokenCache/types"; +import { CredentialFlowGetTokenOptions } from "../credentials"; +import { MsalFlow, MsalFlowOptions } from "../flows"; +import { AuthenticationRequired } from "../errors"; +import { AuthenticationRecord } from "../types"; +import { + defaultLoggerCallback, + getAuthorityHost, + getKnownAuthorities, + MsalBaseUtilities +} from "../utils"; +import { msalToPublic, publicToMsal } from "../transformations"; + +/** + * Union of the constructor parameters that all MSAL flow types for Node. + * @internal + */ +export interface MsalNodeOptions extends MsalFlowOptions { + tokenCachePersistenceOptions?: TokenCachePersistenceOptions; + tokenCredentialOptions: TokenCredentialOptions; +} + +/** + * MSAL partial base client for NodeJS. + * + * It completes the input configuration with some default values. + * It also provides with utility protected methods that can be used from any of the clients, + * which includes handlers for successful responses and errors. + * + * @internal + */ +export abstract class MsalNode extends MsalBaseUtilities implements MsalFlow { + protected publicApp: msalNode.PublicClientApplication | undefined; + protected confidentialApp: msalNode.ConfidentialClientApplication | undefined; + protected msalConfig: msalNode.Configuration; + protected tokenCache: TokenCache | undefined; + protected identityClient?: IdentityClient; + protected requiresConfidential: boolean = false; + + constructor(options: MsalNodeOptions) { + super(options); + this.msalConfig = this.defaultNodeMsalConfig(options); + + if (options.tokenCachePersistenceOptions) { + this.tokenCache = new TokenCachePersistence(options.tokenCachePersistenceOptions); + } else { + // To allow silent authentications on the same credential, we provide a very simple in memory token cache. + // It can't be used to re-use the account information returned from the authenticate() method. + this.tokenCache = inMemoryPersistence(); + } + } + + /** + * Generates a MSAL configuration that generally works for NodeJS + */ + protected defaultNodeMsalConfig(options: MsalNodeOptions): msalNode.Configuration { + const clientId = options.clientId || DeveloperSignOnClientId; + const tenantId = resolveTenantId(options.logger, options.tenantId, options.clientId); + const authorityHost = getAuthorityHost(tenantId, options.authorityHost); + this.identityClient = new IdentityClient({ + ...options.tokenCredentialOptions, + authorityHost + }); + return { + auth: { + clientId, + authority: authorityHost, + knownAuthorities: getKnownAuthorities(tenantId, authorityHost) + }, + // Cache is defined in this.prepare(); + system: { + networkClient: this.identityClient, + loggerOptions: { + loggerCallback: defaultLoggerCallback(options.logger) + } + } + }; + } + + /** + * Prepares the MSAL applications. + */ + async init(options?: CredentialFlowGetTokenOptions): Promise { + if (options?.abortSignal) { + options.abortSignal.addEventListener("abort", () => { + // This will abort any pending request in the IdentityClient, + // based on the received or generated correlationId + this.identityClient!.abortRequests(options.correlationId); + }); + } + + if (this.publicApp && this.confidentialApp) { + return; + } + + if (this.tokenCache) { + this.msalConfig.cache = { + cachePlugin: await this.tokenCache.register() + }; + } + + this.publicApp = new msalNode.PublicClientApplication(this.msalConfig); + // The confidential client requires either a secret, assertion or certificate. + if ( + this.msalConfig.auth.clientSecret || + this.msalConfig.auth.clientAssertion || + this.msalConfig.auth.clientCertificate + ) { + this.confidentialApp = new msalNode.ConfidentialClientApplication(this.msalConfig); + } else { + if (this.requiresConfidential) { + throw new Error( + "Unable to generate the MSAL confidential client. Missing either the client's secret, certificate or assertion." + ); + } + } + } + + /** + * Allows the cancellation of a MSAL request. + */ + protected withCancellation( + promise: Promise, + abortSignal?: AbortSignalLike, + onCancel?: () => void + ): Promise { + return new Promise((resolve, reject) => { + promise + .then((msalToken) => { + return resolve(msalToken!); + }) + .catch(reject); + if (abortSignal) { + abortSignal.addEventListener("abort", () => { + onCancel?.(); + }); + } + }); + } + + /** + * Returns the existing account, attempts to load the account from MSAL. + */ + async getActiveAccount(): Promise { + if (this.account) { + return this.account; + } + const cache = this.publicApp?.getTokenCache(); + const accountsByTenant = await cache?.getAllAccounts(); + + if (!accountsByTenant) { + return; + } + + if (accountsByTenant.length === 1) { + this.account = msalToPublic(accountsByTenant[0]); + } else { + this.logger + .info(`More than one account was found authenticated for this Client ID and Tenant ID.", +However, no "authenticationRecord" has been provided for this credential,", +therefore we're unable to pick between these accounts.", +A new login attempt will be requested, to ensure the correct account is picked.", +To work with multiple accounts for the same Client ID and Tenant ID, please provide an "authenticationRecord" when initializing "InteractiveBrowserCredential".`); + return; + } + + return this.account; + } + + /** + * Clears MSAL's cache. + */ + async logout(): Promise { + const cache = await this.publicApp?.getTokenCache(); + if (!this.account || !cache) { + return; + } + cache.removeAccount(publicToMsal(this.account)); + } + + /** + * Attempts to retrieve a token from cache. + */ + async getTokenSilent( + scopes: string[], + options?: CredentialFlowGetTokenOptions + ): Promise { + await this.getActiveAccount(); + if (!this.account) { + throw new AuthenticationRequired(); + } + + const silentRequest: msalNode.SilentFlowRequest = { + // To be able to re-use the account, the Token Cache must also have been provided. + account: publicToMsal(this.account), + correlationId: options?.correlationId, + scopes + }; + + // Currently we need to call getAllAccounts before the silent request is attempted. + // The MSAL team is actively investigating why this is necessary. + // TODO: Remove this once this is no longer necessary. + await this.publicApp?.getTokenCache().getAllAccounts(); + + try { + this.logger.info("Attempting to acquire token silently"); + const response = await this.publicApp!.acquireTokenSilent(silentRequest); + return this.handleResult(scopes, response || undefined); + } catch (err) { + throw this.handleError(scopes, err); + } + } + + /** + * Attempts to retrieve an authenticated token from MSAL. + */ + protected abstract doGetToken(scopes: string[], options?: GetTokenOptions): Promise; + + /** + * Wrapper around each MSAL flow get token operation: doGetToken. + * If disableAutomaticAuthentication is sent through the constructor, it will prevent MSAL from requesting the user input. + */ + public async getToken( + scopes: string[], + options: CredentialFlowGetTokenOptions = {} + ): Promise { + options.correlationId = options?.correlationId || this.generateUuid(); + await this.init(options); + return this.getTokenSilent(scopes, options).catch((err) => { + if (err.name !== "AuthenticationRequired") { + throw err; + } + if (options?.disableAutomaticAuthentication) { + throw new AuthenticationRequired( + "Automatic authentication has been disabled. You may call the authentication() method." + ); + } + this.logger.info(`Silent authentication failed, falling back to interactive method.`); + return this.doGetToken(scopes, options); + }); + } +} diff --git a/sdk/identity/identity/src/msal/transformations.ts b/sdk/identity/identity/src/msal/transformations.ts new file mode 100644 index 000000000000..53dbfa9848ef --- /dev/null +++ b/sdk/identity/identity/src/msal/transformations.ts @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as msalCommon from "@azure/msal-common"; +import { DefaultTenantId } from "../constants"; +import { MsalAccountInfo, AuthenticationRecord } from "./types"; +import { getAuthorityHost } from "./utils"; + +export function publicToMsal(account: AuthenticationRecord): msalCommon.AccountInfo { + const [environment] = account.authority.match(/([a-z]*\.[a-z]*\.[a-z]*)/) || []; + return { + ...account, + localAccountId: account.homeAccountId, + environment + }; +} + +export function msalToPublic(account: MsalAccountInfo): AuthenticationRecord { + const record = { + authority: getAuthorityHost(account.tenantId, account.environment), + homeAccountId: account.homeAccountId, + tenantId: account.tenantId || DefaultTenantId, + username: account.username, + serialize: () => serializeAuthenticationRecord(record) + }; + return record; +} + +/** + * Serializes a given authentication record to string. + * @param record - Authentication Record + * @internal + */ +export function serializeAuthenticationRecord(record: AuthenticationRecord): string { + return JSON.stringify({ + authority: record.authority, + home_account_id: record.homeAccountId, + tenant_id: record.tenantId, + username: record.username + }); +} + +/** + * Deserializes a previously serialzied authentication record from a string into an object. + * @param serializedRecord - Authentication record previously serialized into string. + * @returns AuthenticationRecord. + */ +export function deserializeAuthenticationRecord(serializedRecord: string): AuthenticationRecord { + const parsed = JSON.parse(serializedRecord); + return { + authority: parsed.authority, + homeAccountId: parsed.home_account_id, + tenantId: parsed.tenant_id, + username: parsed.username, + serialize: () => serializedRecord + }; +} diff --git a/sdk/identity/identity/src/msal/types.ts b/sdk/identity/identity/src/msal/types.ts new file mode 100644 index 000000000000..3a738d2c0fc0 --- /dev/null +++ b/sdk/identity/identity/src/msal/types.ts @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * The shape we use return the token (and the expiration date). + * @internal + */ +export interface MsalToken { + accessToken?: string; + expiresOn: Date | null; +} + +/** + * Internal representation of MSAL's Account information. + * Helps us to disambiguate the MSAL classes accross environments. + * @internal + */ +export interface MsalAccountInfo { + homeAccountId: string; + environment?: string; + tenantId: string; + username: string; + localAccountId: string; + name?: string; + // Leaving idTokenClaims as object since that's how MSAL has this assigned. + /* eslint-disable-next-line @typescript-eslint/ban-types */ + idTokenClaims?: object; +} + +/** + * Represents the common properties of any of the MSAL responses. + * @internal + */ +export interface MsalResult { + authority?: string; + account: MsalAccountInfo | null; + accessToken: string; + expiresOn: Date | null; +} + +/** + * The record to use to find the cached tokens in the cache. + */ +export interface AuthenticationRecord { + /** + * The associated authority, if used. + */ + authority: string; + /** + * The home account Id. + */ + homeAccountId: string; + /** + * The associated tenant ID. + */ + tenantId: string; + /** + * The username of the logged in account. + */ + username: string; + /** + * Function that returns a serialized version of the `AuthenticationRecord`. + * + * The output of a serialized authentication record will contain the following properties: + * + * "authority", + * "home_account_id", + * "tenant_id", + * "username" + * + * To later use a serialized `AuthenticationRecord`, please use the exported function `deserializeAuthenticationRecord()`. + */ + serialize: () => string; +} diff --git a/sdk/identity/identity/src/msal/utils.ts b/sdk/identity/identity/src/msal/utils.ts new file mode 100644 index 000000000000..6abf3c47af1f --- /dev/null +++ b/sdk/identity/identity/src/msal/utils.ts @@ -0,0 +1,158 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as msalNode from "@azure/msal-node"; +import * as msalCommon from "@azure/msal-common"; +import { AccessToken } from "@azure/core-http"; +import { v4 as uuidv4 } from "uuid"; +import { CredentialLogger, formatError, formatSuccess } from "../util/logging"; +import { CredentialUnavailable } from "../client/errors"; +import { DefaultAuthorityHost } from "../constants"; +import { AuthenticationRecord, MsalResult, MsalToken } from "./types"; +import { AuthenticationRequired } from "./errors"; +import { MsalFlowOptions } from "./flows"; +import { msalToPublic } from "./transformations"; + +/** + * Ensures the validity of the MSAL token + * @internal + */ +export function ensureValidMsalToken(logger: CredentialLogger, msalToken?: MsalToken): void { + const error = (message: string): Error => { + logger.getToken.info(message); + return new AuthenticationRequired(message); + }; + if (!msalToken) { + throw error("No response"); + } + if (!msalToken.expiresOn) { + throw error(`Response had no "expiresOn" property.`); + } + if (!msalToken.accessToken) { + throw error(`Response had no "accessToken" property.`); + } +} + +/** + * Generates a valid authorityHost by combining a host with a tenantId. + * @internal + */ +export function getAuthorityHost(tenantId: string, host: string = DefaultAuthorityHost): string { + if (host.endsWith("/")) { + return host + tenantId; + } else { + return `${host}/${tenantId}`; + } +} + +/** + * Generates the known authorities. + * If the tenantId is "adfs", we will return an array with the authorityHost as the only known authority. + * Otherwise, it is safe to return an empty array. + * @internal + */ +export function getKnownAuthorities(tenantId: string, authorityHost: string): string[] { + if (tenantId === "adfs" && authorityHost) { + return [authorityHost]; + } + return []; +} + +/** + * Generates a logger that can be passed to the MSAL clients. + * @param logger - The logger of the credential. + * @internal + */ +export const defaultLoggerCallback: (logger: CredentialLogger) => msalCommon.ILoggerCallback = ( + logger: CredentialLogger +) => (level, message, containsPii): void => { + if (containsPii) { + return; + } + switch (level) { + case msalNode.LogLevel.Error: + logger.info(`MSAL Browser V2 error: ${message}`); + return; + case msalNode.LogLevel.Info: + logger.info(`MSAL Browser V2 info message: ${message}`); + return; + case msalNode.LogLevel.Verbose: + logger.info(`MSAL Browser V2 verbose message: ${message}`); + return; + case msalNode.LogLevel.Warning: + logger.info(`MSAL Browser V2 warning: ${message}`); + return; + } +}; + +/** + * The common utility functions for the MSAL clients. + * Defined as a class so that the classes extending this one can have access to its methods and protected properties. + * + * It keeps track of a logger and an in-memory copy of the AuthenticationRecord. + * + * @internal + */ +export class MsalBaseUtilities { + protected logger: CredentialLogger; + protected account: AuthenticationRecord | undefined; + + constructor(options: MsalFlowOptions) { + this.logger = options.logger; + this.account = options.authenticationRecord; + } + + /** + * Generates a UUID + */ + generateUuid(): string { + return uuidv4(); + } + + /** + * Handles the MSAL authentication result. + * If the result has an account, we update the local account reference. + * If the token received is invalid, an error will be thrown depending on what's missing. + */ + protected handleResult(scopes: string | string[], result?: MsalResult): AccessToken { + if (result?.account) { + this.account = msalToPublic(result.account); + } + ensureValidMsalToken(this.logger, result); + this.logger.getToken.info(formatSuccess(scopes)); + return { + token: result!.accessToken!, + expiresOnTimestamp: result!.expiresOn!.getTime() + }; + } + + /** + * Handles MSAL errors. + */ + protected handleError(scopes: string[], error: Error): Error { + if (error instanceof msalCommon.AuthError) { + switch (error.errorCode) { + case "endpoints_resolution_error": + this.logger.info(formatError(scopes, error.message)); + return new CredentialUnavailable(error.message); + case "consent_required": + case "interaction_required": + case "login_required": + this.logger.info( + formatError(scopes, `Authentication returned errorCode ${error.errorCode}`) + ); + break; + default: + this.logger.info(formatError(scopes, `Failed to acquire token: ${error.message}`)); + break; + } + } + if (error instanceof msalCommon.ClientConfigurationError) { + return error; + } + if (error.name === "AbortError") { + return error; + } + return new AuthenticationRequired(error.message); + } +} diff --git a/sdk/identity/identity/src/tokenCache/InMemoryPersistence.ts b/sdk/identity/identity/src/tokenCache/InMemoryPersistence.ts new file mode 100644 index 000000000000..fff05cfaa7b7 --- /dev/null +++ b/sdk/identity/identity/src/tokenCache/InMemoryPersistence.ts @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import * as msalCommon from "@azure/msal-common"; +import { CachePlugin } from "./types"; + +/** + * IMPORTANT: + * Do not use this outside the Identity source. + * Do not instantiate this more than once per credential instance. + */ + +/** + * To allow silent authentications on the same credential, we provide a very simple in memory token cache. + * It can't be used to re-use the account information returned from the authenticate() method. + * @internal + */ +export function inMemoryPersistence(): { register(): Promise } { + let inMemoryCache: string = ""; + + return { + async register(): Promise { + return { + beforeCacheAccess: async ( + tokenCacheContext: msalCommon.TokenCacheContext + ): Promise => { + if (inMemoryCache) { + tokenCacheContext.tokenCache.deserialize(inMemoryCache); + } else { + inMemoryCache = tokenCacheContext.tokenCache.serialize(); + } + }, + afterCacheAccess: async ( + tokenCacheContext: msalCommon.TokenCacheContext + ): Promise => { + if (tokenCacheContext.cacheHasChanged) { + inMemoryCache = tokenCacheContext.tokenCache.serialize(); + } + } + }; + } + }; +} diff --git a/sdk/identity/identity/src/tokenCache/TokenCachePersistence.browser.ts b/sdk/identity/identity/src/tokenCache/TokenCachePersistence.browser.ts new file mode 100644 index 000000000000..81152a567cd6 --- /dev/null +++ b/sdk/identity/identity/src/tokenCache/TokenCachePersistence.browser.ts @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { TokenCachePersistenceOptions } from "./persistencePlatforms"; +import { MsalPersistence, CachePlugin, TokenCacheRegisterOptions } from "./types"; + +/** + * TokenCachePersistence is not available for the browsers. + * @internal + */ +export class TokenCachePersistence { + constructor(_options: TokenCachePersistenceOptions = {}) { + throw new Error("TokenCachePersistence is not available in the browser"); + } + + async getPersistence(): Promise { + throw new Error("TokenCachePersistence is not available in the browser"); + } + + async register(_options?: TokenCacheRegisterOptions): Promise { + throw new Error("TokenCachePersistence is not available in the browser"); + } +} diff --git a/sdk/identity/identity/src/tokenCache/TokenCachePersistence.ts b/sdk/identity/identity/src/tokenCache/TokenCachePersistence.ts new file mode 100644 index 000000000000..b5091dc8ccb3 --- /dev/null +++ b/sdk/identity/identity/src/tokenCache/TokenCachePersistence.ts @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { + PersistenceCachePlugin, + IPersistence as MsalIPersistence +} from "@azure/msal-node-extensions"; +import { msalPersistencePlatforms, TokenCachePersistenceOptions } from "./persistencePlatforms"; +import { MsalPersistence, CachePlugin, TokenCacheRegisterOptions } from "./types"; + +/** + * Determines which platform we can trust to deliver a persistence layer for MSAL, + * and also provides a way to extract a CachePlugin that can be sent to MSAL through the MSAL applications' configurations. + * @internal + */ +export class TokenCachePersistence { + private options: TokenCachePersistenceOptions; + + constructor(options: TokenCachePersistenceOptions = {}) { + this.options = options; + } + + async getPersistence(): Promise { + const { win32, darwin, linux } = msalPersistencePlatforms; + + const availablePlatform = [win32, darwin, linux].find((platform) => platform.isAvailable()); + + return availablePlatform?.persistence(this.options); + } + + async register(_options?: TokenCacheRegisterOptions): Promise { + const lockOptions = { + retryNumber: 100, + retryDelay: 50 + }; + + const persistence = await this.getPersistence(); + if (!persistence) { + throw new Error("No persistence implementations available."); + } + + return new PersistenceCachePlugin(persistence as MsalIPersistence, lockOptions); + } +} diff --git a/sdk/identity/identity/src/tokenCache/persistencePlatforms.ts b/sdk/identity/identity/src/tokenCache/persistencePlatforms.ts new file mode 100644 index 000000000000..0a998c648f12 --- /dev/null +++ b/sdk/identity/identity/src/tokenCache/persistencePlatforms.ts @@ -0,0 +1,182 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/* eslint-disable tsdoc/syntax */ + +import { + DataProtectionScope, + FilePersistence, + FilePersistenceWithDataProtection, + KeychainPersistence, + LibSecretPersistence +} from "@azure/msal-node-extensions"; +import { MsalPersistence } from "./types"; +import * as path from "path"; + +/** + * Local application data folder + * Expected values: + * - Darwin: '/Users/user/' + * - Windows 8: 'C:\Users\user\AppData\Local' + * - Windows XP: 'C:\Documents and Settings\user\Application Data\Local' + * - Linux: '/home/user/.local/share' + * @internal + */ +const localApplicationDataFolder = process.env.APPDATA + ? process.env.APPDATA.replace(/(.Roaming)*$/, "\\Local") + : (process.platform === "darwin" ? process.env.HOME : process.env.HOME)!; + +/** + * Dictionary of values that we use as default as we discover, pick and enable the persistence layer. + * @internal + */ +export const defaultMsalValues = { + tokenCache: { + name: "msal.cache", + // Expected values: + // - Darwin: '/Users/user/.IdentityService' + // - Windows 8: 'C:\Users\user\AppData\Local\.IdentityService' + // - Windows XP: 'C:\Documents and Settings\user\Application Data\Local\.IdentityService' + // - Linux: '/home/user/.IdentityService' + directory: path.join(localApplicationDataFolder, ".IdentityService") + }, + keyRing: { + label: "MSALCache", + schema: "msal.cache", + collection: "default", + attributes: { + MsalClientID: "Microsoft.Developer.IdentityService", + "Microsoft.Developer.IdentityService": "1.0.0.0" + }, + service: "Microsoft.Developer.IdentityService", + account: "MSALCache" + }, + keyChain: { + service: "Microsoft.Developer.IdentityService", + account: "MSALCache" + } +}; + +/** + * Parameters that enable token cache persistence in the Identity credentials. + */ +export interface TokenCachePersistenceOptions { + /** + * Unique identifier for the persistent token cache. + * + * Based on this identifier, the persistence file will be located in any of the following places: + * - Darwin: '/Users/user/.IdentityService/' + * - Windows 8: 'C:\Users\user\AppData\Local\.IdentityService\' + * - Windows XP: 'C:\Documents and Settings\user\Application Data\Local\.IdentityService\' + * - Linux: '/home/user/.IdentityService/' + */ + name?: string; + /** + * If set to true, the cache will be stored without encryption if no OS level user encryption is available. + * When set to false, the PersistentTokenCache will throw an error if no OS level user encryption is available. + */ + allowUnencryptedStorage?: boolean; +} + +/** + * The platforms we currently support. + * @internal + */ +export type MsalPersistencePlatforms = "win32" | "darwin" | "linux"; + +/** + * The common set of properties of all the persistence implementations we have. + * @internal + */ +export interface MsalPersistenceImplementation { + name: string; + isAvailable(): boolean; + /** + * Returns the available persistance. + * @param options Input options provided by the end user. + */ + persistence(options?: TokenCachePersistenceOptions): Promise; +} + +/** + * Expected responses: + * - Darwin: '/Users/user/.IdentityService/' + * - Windows 8: 'C:\Users\user\AppData\Local\.IdentityService\' + * - Windows XP: 'C:\Documents and Settings\user\Application Data\Local\.IdentityService\' + * - Linux: '/home/user/.IdentityService/' + * @internal + */ +function getPersistencePath(name: string): string { + return path.join(defaultMsalValues.tokenCache.directory, name); +} + +/** + * Set of the platforms we attempt to deliver persistence on. + * + * - On Windows we use DPAPI. + * - On OSX (Darwin), we try to use the system's Keychain, otherwise if the property `allowUnencryptedStorage` is set to true, we use an unencrypted file. + * - On Linux, we try to use the system's Keyring, otherwise if the property `allowUnencryptedStorage` is set to true, we use an unencrypted file. + * + * @internal + */ +export const msalPersistencePlatforms: Record< + MsalPersistencePlatforms, + MsalPersistenceImplementation +> = { + win32: { + name: "win32", + isAvailable: () => process.platform === "win32", + persistence: ({ name = defaultMsalValues.tokenCache.name } = {}) => + FilePersistenceWithDataProtection.create( + getPersistencePath(name), + DataProtectionScope.CurrentUser + ) + }, + + darwin: { + name: "darwin", + isAvailable: () => process.platform === "darwin", + async persistence(options: TokenCachePersistenceOptions = {}): Promise { + const { name, allowUnencryptedStorage } = options; + const { service, account } = defaultMsalValues.keyChain; + const persistencePath = getPersistencePath(name || defaultMsalValues.tokenCache.name); + + try { + const persistence = await KeychainPersistence.create(persistencePath, service, account); + // If we don't encounter an error when trying to read from the keychain, then we should be good to go. + await persistence.load(); + return persistence; + } catch (e) { + // If we got an error while trying to read from the keyring, + // we will proceed only if the user has specified that unencrypted storage is allowed. + if (!allowUnencryptedStorage) { + throw new Error("MSAL was unable to read from the system's keyring."); + } + return FilePersistence.create(persistencePath); + } + } + }, + + linux: { + name: "linux", + isAvailable: () => process.platform === "linux", + async persistence(options: TokenCachePersistenceOptions = {}): Promise { + const { name, allowUnencryptedStorage } = options; + const { service, account } = defaultMsalValues.keyRing; + const persistencePath = getPersistencePath(name || defaultMsalValues.tokenCache.name); + + try { + const persistence = await LibSecretPersistence.create(persistencePath, service, account); + // If we don't encounter an error when trying to read from the keyring, then we should be good to go. + await persistence.load(); + return persistence; + } catch (e) { + // If we got an error while trying to read from the keyring, + // we will proceed only if the user has specified that unencrypted storage is allowed. + if (!allowUnencryptedStorage) { + throw new Error("MSAL was unable to read from the system's keyring."); + } + return FilePersistence.create(persistencePath); + } + } + } +}; diff --git a/sdk/identity/identity/src/tokenCache/types.ts b/sdk/identity/identity/src/tokenCache/types.ts new file mode 100644 index 000000000000..31be755e8385 --- /dev/null +++ b/sdk/identity/identity/src/tokenCache/types.ts @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +export interface TokenCacheRegisterOptions {} + +/** + * Custom definition of MSAL's MsalPersistence interface. + * @internal + */ +export interface MsalPersistence { + save(contents: string): Promise; + + // Load returns Promise on MsalPersistence, + // but it returns Promise on FilePersistenceWithDataProtection. + load(): Promise; + + delete(): Promise; + reloadNecessary(lastSync: number): Promise; + getFilePath(): string; + + // // Incompatible between MsalPersistence and FilePersistenceWithDataProtection + // // Error says: + // // Type 'Logger' is missing the following properties from type 'Logger': packageName, packageVersion, clone + // getLogger(): Logger; +} + +/** + * Custom definition of MSAL's MsalCachePlugin interface. + * @internal + */ +export interface CachePlugin { + beforeCacheAccess: (tokenCacheContext: any) => Promise; + afterCacheAccess: (tokenCacheContext: any) => Promise; +} + +/** + * A simple representation of the TokenCache. + * @internal + */ +export interface TokenCache { + register(_options?: TokenCacheRegisterOptions): Promise; +} diff --git a/sdk/identity/identity/src/util/resolveTenantId.ts b/sdk/identity/identity/src/util/resolveTenantId.ts new file mode 100644 index 000000000000..1031d45f1d24 --- /dev/null +++ b/sdk/identity/identity/src/util/resolveTenantId.ts @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { DeveloperSignOnClientId } from "../constants"; +import { checkTenantId } from "./checkTenantId"; +import { CredentialLogger } from "./logging"; + +export function resolveTenantId( + logger: CredentialLogger, + tenantId?: string, + clientId?: string +): string { + if (tenantId) { + checkTenantId(logger, tenantId); + return tenantId; + } + if (!clientId) { + clientId = DeveloperSignOnClientId; + } + if (clientId !== DeveloperSignOnClientId) { + return "common"; + } + return "organizations"; +} diff --git a/sdk/identity/identity/src/util/tracing.ts b/sdk/identity/identity/src/util/tracing.ts index 2e2f0a0ce4c4..e0c40d8cf318 100644 --- a/sdk/identity/identity/src/util/tracing.ts +++ b/sdk/identity/identity/src/util/tracing.ts @@ -1,7 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +import { OperationOptions } from "@azure/core-http"; import { createSpanFunction } from "@azure/core-tracing"; +import { CanonicalCode, Span } from "@opentelemetry/api"; +import { AuthenticationErrorName } from "../client/errors"; /** * Creates a span using the global tracer. @@ -11,3 +14,45 @@ export const createSpan = createSpanFunction({ packagePrefix: "Azure.Identity", namespace: "Microsoft.AAD" }); + +/** + * From: https://github.com/Azure/azure-sdk-for-js/blob/46139daa3317a0d12e8b55b02b9d9cdf1b2e762a/sdk/appconfiguration/app-configuration/src/internal/tracingHelpers.ts + * Traces an operation and properly handles reporting start, end and errors for a given span + * + * @param operationName - Name of a method in the TClient type + * @param options - An options class, typically derived from \@azure/core-http/RequestOptionsBase + * @param fn - The function to call with an options class that properly propagates the span context + * + * @internal + */ +export async function trace( + operationName: string, + options: OperationOptions, + fn: (options: OperationOptions, span: Span) => Promise, + createSpanFn = createSpan +): Promise { + const { updatedOptions, span } = createSpanFn(operationName, options); + + try { + // NOTE: we really do need to await on this function here so we can handle any exceptions thrown and properly + // close the span. + const result = await fn(updatedOptions, span); + + // otel 0.16+ needs this or else the code ends up being set as UNSET + span.setStatus({ + code: CanonicalCode.OK + }); + return result; + } catch (err) { + const code = + err.name === AuthenticationErrorName ? CanonicalCode.UNAUTHENTICATED : CanonicalCode.UNKNOWN; + + span.setStatus({ + code, + message: err.message + }); + throw err; + } finally { + span.end(); + } +} diff --git a/sdk/identity/identity/test/assets/cert.pem b/sdk/identity/identity/test/assets/cert.pem new file mode 100644 index 000000000000..e8eed9c3ad69 --- /dev/null +++ b/sdk/identity/identity/test/assets/cert.pem @@ -0,0 +1,48 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC5CSXUbIXh6UuU +NksDELjHXBxldJq4+2wELAK9LtGjAX/VTrKj5j1bSGL4cK21wOHRhYVwHDNozlox +uhq8j+pbpPplqv/dBdo2V/AcScgjHiNpjyjxeJ7L2raqqM7tBnWk1Kn64MrpFBSS +EhZPOPv4YYlqVSVhZx5SAnbeCLtsveR1IHZmGj5Oa0tZkNo80Zg0hnx3ZWe0tg2j +3XiWHrqb7kWxtNS4QKDrKPp1Pb9QlFedOwho6MWQgktZ5LJ3VCqozD56bhKZR3cz +cGlz7l3xOzKlD0eEq1aoTj+I6AM5sdENAbo33jostSjtF1NB+DfoiSdacTZMvOH4 +p27gspblAgMBAAECggEAHXKlQJ9c101Hp/jUC1LX9XJlWogqL5Jj6G4QqYcP+f89 +FveFWqxDy4lDpwJXwvtFdRDo4aS7Ucy1cgCKTMQiqh8hcpi6S7Sou8lW/0mGvoAH +Zr9P5s4ph4vxUku3UuWaR3dI7hJkeJ+nfm++eTE6eJcdRXzzjALbQnX98Ow21+FD +dLmwTzM20aTh8wROH+AVB3+v1b+iJlz43Szgg34i0F+eS1vAjUOnRrNhxFcOyEOq +opB2qzjG65lA6sDA+zmbCaPadsfuAPpwMzZXo82u3/cwYB+8ifjnrH2ElmpBo/2h +2r2qDTpOL7P1lH+nNywqS0EznkXpC6anxqo5eEteqQKBgQDfABx+E0NK4XQzN05Y +88wZnafzUcvFM3daCc5Sr3aFobk1HI0nL/coNZV2Wb/MzBABmQ2uE10gqkINfN8k +fte4nsm07Dm2XrQcvWCg5YZ+CZpsBmogFBBXiuRjh0EwEWG+uzGPw7oQSXNYIF+d +Pa0o65VpeLqmhYxut6ZR0S+OcwKBgQDUatNxzCaSsuyyhY+YmSFBuqNAm2DKwhBt +8M5bpQJHLUxqMgr2gMV1HfkFXbFqEP/F7xdIFKnICbz27eqcc9UxNfjQq4zZSSTs +Q7tg08kN9YTJYrRa/ZpqiW302OMOTckhFhVcsvd1MLdaSYVm0NBZRjSdvb4KN5xc +vRoMKUNXRwKBgAaZ5j4FK/THf70UYglYDi6j8BjPzwxjXaEYsTbZYtTJ2MWttq/2 +R4a1L1t1hKv3TnH9qd4BTLxuzc3AaIqYBqK8cJQeegbf/szq/jVFFhodBqkz92hD +r8gnoGMh2dgma5JN0EXFMXjR88wkCTCKZtnTP3UD0eKkCWgtn4rEenfpAoGACdZE +ooF8y9Bja8UJqFx3EM8u3kAT1G/2SNEdDVtv5pHvdv+ISHAgNNNFg/0ZyTquTaFL +57elQTFKQfk0ozguCFBijG9VX36mqhZc6BgGuJHFK3pZtdkGvKZOpEcjBLePd+vI +43kaQqAV7aV3+xHyhB/fMermkRyQLi9HtIZ4quMCgYEAinV4zdB7DiTVv1H5cVfx +EbwvKIABmjusiKHQOoZgk+anvzf5D9qJbws1I46CnwVrkMyHWshDC0iMSO0Md9ZM +4rp0UIiLhj2F+eWKi6I6NJeW5zcZypdS9hFNwzb1bsSAwOFuFwbNfDmCGsepZgsv +c9CAuS643vXTLX81ko/jTJs= +-----END PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIIDLjCCAhagAwIBAgIQc7oih3bBS7Gk7POB2yxh+jANBgkqhkiG9w0BAQsFADAU +MRIwEAYDVQQDEwlhenVyZS5jb20wHhcNMjEwMzA2MTg0MTU5WhcNMjMwMzA2MTg1 +MTU5WjAUMRIwEAYDVQQDEwlhenVyZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQC5CSXUbIXh6UuUNksDELjHXBxldJq4+2wELAK9LtGjAX/VTrKj +5j1bSGL4cK21wOHRhYVwHDNozloxuhq8j+pbpPplqv/dBdo2V/AcScgjHiNpjyjx +eJ7L2raqqM7tBnWk1Kn64MrpFBSSEhZPOPv4YYlqVSVhZx5SAnbeCLtsveR1IHZm +Gj5Oa0tZkNo80Zg0hnx3ZWe0tg2j3XiWHrqb7kWxtNS4QKDrKPp1Pb9QlFedOwho +6MWQgktZ5LJ3VCqozD56bhKZR3czcGlz7l3xOzKlD0eEq1aoTj+I6AM5sdENAbo3 +3jostSjtF1NB+DfoiSdacTZMvOH4p27gspblAgMBAAGjfDB6MA4GA1UdDwEB/wQE +AwIFoDAJBgNVHRMEAjAAMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAf +BgNVHSMEGDAWgBTA57oC1nQYK/mKk9HVg2/7rPSUYDAdBgNVHQ4EFgQUwOe6AtZ0 +GCv5ipPR1YNv+6z0lGAwDQYJKoZIhvcNAQELBQADggEBAKM8dfw+iMsdSsEYjNdr +XaKyi7ZVgrEakh3RHI1CkNItG0bhgG+3Aq3q0GchkR4Dz2m02Lhi2moDLuOKUyZK ++hWRfS5Jay0Vf2PTldXVRyqYqV8DmGI6gNWWlWsg9M4Nyjd6LR0zlv9IhwCSE3ek +R9hij2PwyKWSmpIIbN1fwgj5FsZ05RCk4wOCDWwUEYTqwLW8na0hEThNrVlcOmXu +ChGzMtI5UsPmph9y0hw4Y4k7A8G1zBWNK4dNrEMoYcSBFQjImHELGpRv1RDAJ1RP +YUcKBkl8A3oM/6Z57Zl1gL49vAnDji5GcdDwMZIGJmeE9dS/57p/8Vhs8QLazcvi +o7g= +-----END CERTIFICATE----- diff --git a/sdk/identity/identity/test/internal/identityClient.spec.ts b/sdk/identity/identity/test/internal/identityClient.spec.ts index f3b2188a8e05..be51be197996 100644 --- a/sdk/identity/identity/test/internal/identityClient.spec.ts +++ b/sdk/identity/identity/test/internal/identityClient.spec.ts @@ -4,13 +4,13 @@ import assert from "assert"; import { assertRejects, MockAuthHttpClient } from "../authTestUtils"; import { IdentityClient } from "../../src/client/identityClient"; -import { ClientSecretCredential, AuthenticationError } from "../../src"; +import { ClientSecretCredential } from "../../src"; import { setLogLevel, AzureLogger, getLogLevel, AzureLogLevel } from "@azure/logger"; import { isNode } from "@azure/core-http"; function isExpectedError(expectedErrorName: string): (error: any) => boolean { return (error: any) => { - if (!(error instanceof AuthenticationError)) { + if (error?.name !== "AuthenticationError") { assert.ifError(error); } return error.errorResponse.error === expectedErrorName; @@ -55,7 +55,7 @@ describe("IdentityClient", function() { mockHttp.tokenCredentialOptions ); await assertRejects(credential.getToken("https://test/.default"), (error) => { - assert.strictEqual(error.name, "AuthenticationError"); + assert.equal(error.name, "CredentialUnavailable"); return true; }); }); @@ -122,10 +122,10 @@ describe("IdentityClient", function() { mockHttp.tokenCredentialOptions ); - await assertRejects( - credential.getToken("https://test/.default"), - isExpectedError("unknown_error") - ); + await assertRejects(credential.getToken("https://test/.default"), (error) => { + assert.equal(error.name, "CredentialUnavailable"); + return true; + }); }); it("returns null when the token refresh request returns an 'interaction_required' error", async () => { diff --git a/sdk/identity/identity/test/internal/node/clientCertificateCredential.spec.ts b/sdk/identity/identity/test/internal/node/clientCertificateCredential.spec.ts new file mode 100644 index 000000000000..996096b8a19a --- /dev/null +++ b/sdk/identity/identity/test/internal/node/clientCertificateCredential.spec.ts @@ -0,0 +1,145 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/* eslint-disable no-invalid-this */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ + +import Sinon from "sinon"; +import assert from "assert"; +import * as path from "path"; +import { env, isPlaybackMode } from "@azure/test-utils-recorder"; +import { ConfidentialClientApplication } from "@azure/msal-node"; +import { ClientCertificateCredential, TokenCachePersistenceOptions } from "../../../src"; +import { MsalTestCleanup, msalNodeTestSetup } from "../../msalTestUtils"; +import { TokenCachePersistence } from "../../../src/tokenCache/TokenCachePersistence"; +import { MsalNode } from "../../../src/msal/nodeFlows/nodeCommon"; + +describe("ClientCertificateCredential (internal)", function() { + let cleanup: MsalTestCleanup; + let getTokenSilentSpy: Sinon.SinonSpy; + let doGetTokenSpy: Sinon.SinonSpy; + + beforeEach(function() { + const setup = msalNodeTestSetup(this); + cleanup = setup.cleanup; + + getTokenSilentSpy = setup.sandbox.spy(MsalNode.prototype, "getTokenSilent"); + + // MsalClientSecret calls to this method underneath. + doGetTokenSpy = setup.sandbox.spy( + ConfidentialClientApplication.prototype, + "acquireTokenByClientCredential" + ); + }); + afterEach(async function() { + await cleanup(); + }); + + const certificatePath = path.resolve(__dirname, "../test/assets/cert.pem"); + const scope = "https://vault.azure.net/.default"; + + it("throws when given a file that doesn't contain a PEM-formatted certificate", () => { + assert.throws(() => { + new ClientCertificateCredential( + "tenant", + "client", + path.resolve(__dirname, "../src/index.ts") + ); + }); + }); + + it("Authenticates silently after the initial request", async function() { + if (isPlaybackMode()) { + // MSAL creates a client assertion based on the certificate that I haven't been able to mock. + // This assertion could be provided as parameters, but we don't have that in the public API yet, + // and I'm trying to avoid having to generate one ourselves. + this.skip(); + } + + const credential = new ClientCertificateCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + certificatePath + ); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 1); + assert.equal(doGetTokenSpy.callCount, 1); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 2); + + // Even though we're providing the same default in memory persistence cache that we use for DeviceCodeCredential, + // The Client Credential flow does not return the account information from the authentication service, + // so each time getToken gets called, we will have to acquire a new token through the service. + assert.equal(doGetTokenSpy.callCount, 2); + }); + + it("Accepts tokenCachePersistenceOptions", async function() { + if (isPlaybackMode()) { + // MSAL creates a client assertion based on the certificate that I haven't been able to mock. + // This assertion could be provided as parameters, but we don't have that in the public API yet, + // and I'm trying to avoid having to generate one ourselves. + this.skip(); + } + + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { + name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), + allowUnencryptedStorage: true + }; + + // Emptying the token cache before we start. + const tokenCache = new TokenCachePersistence(tokenCachePersistenceOptions); + const persistence = await tokenCache.getPersistence(); + persistence?.save(""); + + const credential = new ClientCertificateCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + certificatePath, + { tokenCachePersistenceOptions } + ); + + await credential.getToken(scope); + const result = await persistence?.load(); + const parsedResult = JSON.parse(result!); + assert.ok(parsedResult.AccessToken); + }); + + it("Authenticates silently with tokenCachePersistenceOptions", async function() { + if (isPlaybackMode()) { + // MSAL creates a client assertion based on the certificate that I haven't been able to mock. + // This assertion could be provided as parameters, but we don't have that in the public API yet, + // and I'm trying to avoid having to generate one ourselves. + this.skip(); + } + + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { + name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), + allowUnencryptedStorage: true + }; + + // Emptying the token cache before we start. + const tokenCache = new TokenCachePersistence(tokenCachePersistenceOptions); + const persistence = await tokenCache.getPersistence(); + persistence?.save(""); + + const credential = new ClientCertificateCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + certificatePath, + { tokenCachePersistenceOptions } + ); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 1); + assert.equal(doGetTokenSpy.callCount, 1); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 2); + // Even though we're providing a file persistence cache, + // The Client Credential flow does not return the account information from the authentication service, + // so each time getToken gets called, we will have to acquire a new token through the service. + // MSAL also doesn't store the account in the cache (getAllAccounts returns an empty array). + assert.equal(doGetTokenSpy.callCount, 2); + }); +}); diff --git a/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts b/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts new file mode 100644 index 000000000000..5b4860dd839e --- /dev/null +++ b/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/* eslint-disable no-invalid-this */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ + +import Sinon from "sinon"; +import assert from "assert"; +import { env } from "@azure/test-utils-recorder"; +import { ConfidentialClientApplication } from "@azure/msal-node"; +import { ClientSecretCredential, TokenCachePersistenceOptions } from "../../../src"; +import { MsalTestCleanup, msalNodeTestSetup } from "../../msalTestUtils"; +import { TokenCachePersistence } from "../../../src/tokenCache/TokenCachePersistence"; +import { MsalNode } from "../../../src/msal/nodeFlows/nodeCommon"; + +describe("ClientSecretCredential (internal)", function() { + let cleanup: MsalTestCleanup; + let getTokenSilentSpy: Sinon.SinonSpy; + let doGetTokenSpy: Sinon.SinonSpy; + + beforeEach(function() { + const setup = msalNodeTestSetup(this); + cleanup = setup.cleanup; + + getTokenSilentSpy = setup.sandbox.spy(MsalNode.prototype, "getTokenSilent"); + + // MsalClientSecret calls to this method underneath. + doGetTokenSpy = setup.sandbox.spy( + ConfidentialClientApplication.prototype, + "acquireTokenByClientCredential" + ); + }); + afterEach(async function() { + await cleanup(); + }); + + const scope = "https://vault.azure.net/.default"; + + it("Authenticates silently after the initial request", async function() { + const credential = new ClientSecretCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_CLIENT_SECRET + ); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 1); + assert.equal(doGetTokenSpy.callCount, 1); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 2); + + // Even though we're providing the same default in memory persistence cache that we use for DeviceCodeCredential, + // The Client Secret flow does not return the account information from the authentication service, + // so each time getToken gets called, we will have to acquire a new token through the service. + assert.equal(doGetTokenSpy.callCount, 2); + }); + + it("Accepts tokenCachePersistenceOptions", async function() { + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { + name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), + allowUnencryptedStorage: true + }; + + // Emptying the token cache before we start. + const tokenCache = new TokenCachePersistence(tokenCachePersistenceOptions); + const persistence = await tokenCache.getPersistence(); + persistence?.save(""); + + const credential = new ClientSecretCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_CLIENT_SECRET, + { tokenCachePersistenceOptions } + ); + + await credential.getToken(scope); + const result = await persistence?.load(); + const parsedResult = JSON.parse(result!); + assert.ok(parsedResult.AccessToken); + }); + + it("Authenticates silently with tokenCachePersistenceOptions", async function() { + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { + name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), + allowUnencryptedStorage: true + }; + + // Emptying the token cache before we start. + const tokenCache = new TokenCachePersistence(tokenCachePersistenceOptions); + const persistence = await tokenCache.getPersistence(); + persistence?.save(""); + + const credential = new ClientSecretCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_CLIENT_SECRET, + { tokenCachePersistenceOptions } + ); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 1); + assert.equal(doGetTokenSpy.callCount, 1); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 2); + + // Even though we're providing the same default in memory persistence cache that we use for DeviceCodeCredential, + // The Client Secret flow does not return the account information from the authentication service, + // so each time getToken gets called, we will have to acquire a new token through the service. + assert.equal(doGetTokenSpy.callCount, 2); + }); +}); diff --git a/sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts b/sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts new file mode 100644 index 000000000000..564b73ebdeb9 --- /dev/null +++ b/sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts @@ -0,0 +1,165 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/* eslint-disable no-invalid-this */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ + +import Sinon from "sinon"; +import assert from "assert"; +import { env, isLiveMode } from "@azure/test-utils-recorder"; +import { DeviceCodeCredential, TokenCachePersistenceOptions } from "../../../src"; +import { MsalTestCleanup, msalNodeTestSetup } from "../../msalTestUtils"; +import { TokenCachePersistence } from "../../../src/tokenCache/TokenCachePersistence"; +import { MsalNode } from "../../../src/msal/nodeFlows/nodeCommon"; +import { PublicClientApplication } from "@azure/msal-node"; + +describe("DeviceCodeCredential (internal)", function() { + let cleanup: MsalTestCleanup; + let getTokenSilentSpy: Sinon.SinonSpy; + let doGetTokenSpy: Sinon.SinonSpy; + + beforeEach(function() { + const setup = msalNodeTestSetup(this); + cleanup = setup.cleanup; + + getTokenSilentSpy = setup.sandbox.spy(MsalNode.prototype, "getTokenSilent"); + + // MsalClientSecret calls to this method underneath. + doGetTokenSpy = setup.sandbox.spy( + PublicClientApplication.prototype, + "acquireTokenByDeviceCode" + ); + }); + afterEach(async function() { + await cleanup(); + }); + + const scope = "https://vault.azure.net/.default"; + + it("Authenticates silently after the initial request", async function() { + // These tests should not run live because this credential requires user interaction. + if (isLiveMode()) { + this.skip(); + } + const credential = new DeviceCodeCredential(env.AZURE_TENANT_ID, env.AZURE_CLIENT_ID); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 1); + assert.equal(doGetTokenSpy.callCount, 1); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 2); + assert.equal(doGetTokenSpy.callCount, 1); + }); + + it("Accepts tokenCachePersistenceOptions", async function() { + // These tests should not run live because this credential requires user interaction. + if (isLiveMode()) { + this.skip(); + } + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { + name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), + allowUnencryptedStorage: true + }; + + // Emptying the token cache before we start. + const tokenCache = new TokenCachePersistence(tokenCachePersistenceOptions); + const persistence = await tokenCache.getPersistence(); + persistence?.save(""); + + const credential = new DeviceCodeCredential({ + tenantId: env.AZURE_TENANT_ID, + clientId: env.AZURE_CLIENT_ID, + tokenCachePersistenceOptions + }); + + await credential.getToken(scope); + const result = await persistence?.load(); + const parsedResult = JSON.parse(result!); + assert.ok(parsedResult.AccessToken); + }); + + it("Authenticates silently with tokenCachePersistenceOptions", async function() { + // These tests should not run live because this credential requires user interaction. + if (isLiveMode()) { + this.skip(); + } + + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { + name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), + allowUnencryptedStorage: true + }; + + // Emptying the token cache before we start. + const tokenCache = new TokenCachePersistence(tokenCachePersistenceOptions); + const persistence = await tokenCache.getPersistence(); + persistence?.save(""); + + const credential = new DeviceCodeCredential({ + tenantId: env.AZURE_TENANT_ID, + clientId: env.AZURE_CLIENT_ID, + tokenCachePersistenceOptions + }); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 1); + assert.equal(doGetTokenSpy.callCount, 1); + + // The cache should have a token a this point + const result = await persistence?.load(); + const parsedResult = JSON.parse(result!); + assert.ok(parsedResult.AccessToken); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 2); + assert.equal(doGetTokenSpy.callCount, 1); + }); + + it("allows passing an authenticationRecord to avoid further manual authentications", async function() { + // These tests should not run live because this credential requires user interaction. + if (isLiveMode()) { + this.skip(); + } + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { + name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), + allowUnencryptedStorage: true + }; + + // Emptying the token cache before we start. + const tokenCache = new TokenCachePersistence(tokenCachePersistenceOptions); + const persistence = await tokenCache.getPersistence(); + persistence?.save(""); + + const credential = new DeviceCodeCredential({ + tenantId: env.AZURE_TENANT_ID, + clientId: env.AZURE_CLIENT_ID, + // To be able to re-use the account, the Token Cache must also have been provided. + // TODO: Perhaps make the account parameter part of the tokenCachePersistenceOptions? + tokenCachePersistenceOptions + }); + + const account = await credential.authenticate(scope); + assert.ok(account); + assert.equal(getTokenSilentSpy.callCount, 1); + assert.equal(doGetTokenSpy.callCount, 1); + + const credential2 = new DeviceCodeCredential({ + tenantId: env.AZURE_TENANT_ID, + clientId: env.AZURE_CLIENT_ID, + authenticationRecord: account, + // To be able to re-use the account, the Token Cache must also have been provided. + // TODO: Perhaps make the account parameter part of the tokenCachePersistenceOptions? + tokenCachePersistenceOptions + }); + + // The cache should have a token a this point + const result = await persistence?.load(); + const parsedResult = JSON.parse(result!); + assert.ok(parsedResult.AccessToken); + + const token = await credential2.getToken(scope); + assert.ok(token?.token); + assert.ok(token?.expiresOnTimestamp! > Date.now()); + assert.equal(getTokenSilentSpy.callCount, 2); + assert.equal(doGetTokenSpy.callCount, 1); + }); +}); diff --git a/sdk/identity/identity/test/internal/node/environmentCredential.spec.ts b/sdk/identity/identity/test/internal/node/environmentCredential.spec.ts new file mode 100644 index 000000000000..03301dfd026d --- /dev/null +++ b/sdk/identity/identity/test/internal/node/environmentCredential.spec.ts @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/* eslint-disable no-invalid-this */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ + +import Sinon from "sinon"; +import assert from "assert"; +import { ConfidentialClientApplication } from "@azure/msal-node"; +import { EnvironmentCredential } from "../../../src"; +import { MsalTestCleanup, msalNodeTestSetup } from "../../msalTestUtils"; +import { MsalNode } from "../../../src/msal/nodeFlows/nodeCommon"; + +describe("EnvironmentCredential (internal)", function() { + let cleanup: MsalTestCleanup; + let getTokenSilentSpy: Sinon.SinonSpy; + let doGetTokenSpy: Sinon.SinonSpy; + + beforeEach(function() { + const setup = msalNodeTestSetup(this); + cleanup = setup.cleanup; + + getTokenSilentSpy = setup.sandbox.spy(MsalNode.prototype, "getTokenSilent"); + + // MsalClientSecret calls to this method underneath. + doGetTokenSpy = setup.sandbox.spy( + ConfidentialClientApplication.prototype, + "acquireTokenByClientCredential" + ); + }); + afterEach(async function() { + await cleanup(); + }); + + const scope = "https://vault.azure.net/.default"; + + it("Authenticates silently after the initial request", async function() { + const credential = new EnvironmentCredential(); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 1); + assert.equal(doGetTokenSpy.callCount, 1); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 2); + + // Even though we're providing the same default in memory persistence cache that we use for DeviceCodeCredential, + // The Client Credential and Client Secret flows do not return the account information from the authentication service, + // so each time getToken gets called, we will have to acquire a new token through the service. + assert.equal(doGetTokenSpy.callCount, 2); + }); +}); diff --git a/sdk/identity/identity/test/internal/node/managedIdentityCredential.spec.ts b/sdk/identity/identity/test/internal/node/managedIdentityCredential.spec.ts index 0a1819183bfd..35daa8711759 100644 --- a/sdk/identity/identity/test/internal/node/managedIdentityCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/managedIdentityCredential.spec.ts @@ -3,13 +3,13 @@ import qs from "qs"; import assert from "assert"; +import { WebResource, AccessToken, HttpHeaders, RestError } from "@azure/core-http"; import { ManagedIdentityCredential, AuthenticationError } from "../../../src"; import { imdsEndpoint, imdsApiVersion } from "../../../src/credentials/managedIdentityCredential/constants"; import { MockAuthHttpClient, MockAuthHttpClientOptions, assertRejects } from "../../authTestUtils"; -import { WebResource, AccessToken, HttpHeaders, RestError } from "@azure/core-http"; import { OAuthErrorResponse } from "../../../src/client/errors"; interface AuthRequestDetails { @@ -18,13 +18,28 @@ interface AuthRequestDetails { } describe("ManagedIdentityCredential", function() { - afterEach(() => { + // There are no types available for this dependency, at least at the time this test file was written. + // eslint-disable-next-line @typescript-eslint/no-require-imports + const mockFs = require("mock-fs"); + + let envCopy: string = ""; + beforeEach(() => { + envCopy = JSON.stringify(process.env); delete process.env.IDENTITY_ENDPOINT; delete process.env.IDENTITY_HEADER; delete process.env.MSI_ENDPOINT; delete process.env.MSI_SECRET; delete process.env.IDENTITY_SERVER_THUMBPRINT; }); + afterEach(() => { + mockFs.restore(); + const env = JSON.parse(envCopy); + process.env.IDENTITY_ENDPOINT = env.IDENTITY_ENDPOINT; + process.env.IDENTITY_HEADER = env.IDENTITY_HEADER; + process.env.MSI_ENDPOINT = env.MSI_ENDPOINT; + process.env.MSI_SECRET = env.MSI_SECRET; + process.env.IDENTITY_SERVER_THUMBPRINT = env.IDENTITY_SERVER_THUMBPRINT; + }); it("sends an authorization request with a modified resource name", async function() { const authDetails = await getMsiTokenAuthRequest(["https://service/.default"], "client", { @@ -254,8 +269,6 @@ describe("ManagedIdentityCredential", function() { process.env.IMDS_ENDPOINT = "https://endpoint"; process.env.IDENTITY_ENDPOINT = "https://endpoint"; - // eslint-disable-next-line @typescript-eslint/no-require-imports - const mockFs = require("mock-fs"); const filePath = "path/to/file"; const key = "challenge key"; diff --git a/sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts b/sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts new file mode 100644 index 000000000000..8a37540bc8a1 --- /dev/null +++ b/sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts @@ -0,0 +1,239 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/* eslint-disable no-invalid-this */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ + +import Sinon from "sinon"; +import assert from "assert"; +import { env, isLiveMode } from "@azure/test-utils-recorder"; +import { + UsernamePasswordCredential, + TokenCachePersistenceOptions, + deserializeAuthenticationRecord +} from "../../../src"; +import { MsalTestCleanup, msalNodeTestSetup } from "../../msalTestUtils"; +import { TokenCachePersistence } from "../../../src/tokenCache/TokenCachePersistence"; +import { MsalNode } from "../../../src/msal/nodeFlows/nodeCommon"; +import { PublicClientApplication } from "@azure/msal-node"; + +describe("UsernamePasswordCredential (internal)", function() { + let cleanup: MsalTestCleanup; + let getTokenSilentSpy: Sinon.SinonSpy; + let doGetTokenSpy: Sinon.SinonSpy; + + beforeEach(function() { + const setup = msalNodeTestSetup(this); + cleanup = setup.cleanup; + + getTokenSilentSpy = setup.sandbox.spy(MsalNode.prototype, "getTokenSilent"); + + // MsalClientSecret calls to this method underneath. + doGetTokenSpy = setup.sandbox.spy( + PublicClientApplication.prototype, + "acquireTokenByUsernamePassword" + ); + }); + + afterEach(async function() { + await cleanup(); + }); + + const scope = "https://vault.azure.net/.default"; + + it("Authenticates silently after the initial request", async function() { + // These tests should not run live because this credential requires user interaction. + if (isLiveMode()) { + this.skip(); + } + const credential = new UsernamePasswordCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_USERNAME, + env.AZURE_PASSWORD + ); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 1); + assert.equal(doGetTokenSpy.callCount, 1); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 2); + assert.equal(doGetTokenSpy.callCount, 1); + }); + + it("Accepts tokenCachePersistenceOptions", async function() { + // These tests should not run live because this credential requires user interaction. + if (isLiveMode()) { + this.skip(); + } + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { + name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), + allowUnencryptedStorage: true + }; + + // Emptying the token cache before we start. + const tokenCache = new TokenCachePersistence(tokenCachePersistenceOptions); + const persistence = await tokenCache.getPersistence(); + persistence?.save(""); + + const credential = new UsernamePasswordCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_USERNAME, + env.AZURE_PASSWORD, + { tokenCachePersistenceOptions } + ); + + await credential.getToken(scope); + const result = await persistence?.load(); + const parsedResult = JSON.parse(result!); + assert.ok(parsedResult.AccessToken); + }); + + it("Authenticates silently with tokenCachePersistenceOptions", async function() { + // These tests should not run live because this credential requires user interaction. + if (isLiveMode()) { + this.skip(); + } + + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { + name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), + allowUnencryptedStorage: true + }; + + // Emptying the token cache before we start. + const tokenCache = new TokenCachePersistence(tokenCachePersistenceOptions); + const persistence = await tokenCache.getPersistence(); + persistence?.save(""); + + const credential = new UsernamePasswordCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_USERNAME, + env.AZURE_PASSWORD, + { tokenCachePersistenceOptions } + ); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 1); + assert.equal(doGetTokenSpy.callCount, 1); + + // The cache should have a token a this point + const result = await persistence?.load(); + const parsedResult = JSON.parse(result!); + assert.ok(parsedResult.AccessToken); + + await credential.getToken(scope); + assert.equal(getTokenSilentSpy.callCount, 2); + assert.equal(doGetTokenSpy.callCount, 1); + }); + + it("allows passing an authenticationRecord to avoid further manual authentications", async function() { + // These tests should not run live because this credential requires user interaction. + if (isLiveMode()) { + this.skip(); + } + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { + name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), + allowUnencryptedStorage: true + }; + + // Emptying the token cache before we start. + const tokenCache = new TokenCachePersistence(tokenCachePersistenceOptions); + const persistence = await tokenCache.getPersistence(); + persistence?.save(""); + + const credential = new UsernamePasswordCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_USERNAME, + env.AZURE_PASSWORD, + // To be able to re-use the account, the Token Cache must also have been provided. + // TODO: Perhaps make the account parameter part of the tokenCachePersistenceOptions? + { tokenCachePersistenceOptions } + ); + + const account = await credential.authenticate(scope); + assert.equal(getTokenSilentSpy.callCount, 1); + assert.equal(doGetTokenSpy.callCount, 1); + assert.equal(account?.tenantId, env.AZURE_TENANT_ID); + + const credential2 = new UsernamePasswordCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_USERNAME, + env.AZURE_PASSWORD, + // To be able to re-use the account, the Token Cache must also have been provided. + // TODO: Perhaps make the account parameter part of the tokenCachePersistenceOptions? + { authenticationRecord: account, tokenCachePersistenceOptions } + ); + + // The cache should have a token a this point + const result = await persistence?.load(); + const parsedResult = JSON.parse(result!); + assert.ok(parsedResult.AccessToken); + + const token = await credential2.getToken(scope); + assert.ok(token?.token); + assert.ok(token?.expiresOnTimestamp! > Date.now()); + assert.equal(getTokenSilentSpy.callCount, 2); + assert.equal(doGetTokenSpy.callCount, 1); + }); + + it("allows working with an authenticationRecord that is serialized", async function() { + // These tests should not run live because this credential requires user interaction. + if (isLiveMode()) { + this.skip(); + } + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { + name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), + allowUnencryptedStorage: true + }; + + // Emptying the token cache before we start. + const tokenCache = new TokenCachePersistence(tokenCachePersistenceOptions); + const persistence = await tokenCache.getPersistence(); + persistence?.save(""); + + const credential = new UsernamePasswordCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_USERNAME, + env.AZURE_PASSWORD, + // To be able to re-use the account, the Token Cache must also have been provided. + // TODO: Perhaps make the account parameter part of the tokenCachePersistenceOptions? + { tokenCachePersistenceOptions } + ); + + const account = await credential.authenticate(scope); + assert.equal(getTokenSilentSpy.callCount, 1); + assert.equal(doGetTokenSpy.callCount, 1); + assert.equal(account?.tenantId, env.AZURE_TENANT_ID); + + const serializedAccount = account?.serialize()!; + + const credential2 = new UsernamePasswordCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_USERNAME, + env.AZURE_PASSWORD, + // To be able to re-use the account, the Token Cache must also have been provided. + // TODO: Perhaps make the account parameter part of the tokenCachePersistenceOptions? + { + authenticationRecord: deserializeAuthenticationRecord(serializedAccount), + tokenCachePersistenceOptions + } + ); + + // The cache should have a token a this point + const result = await persistence?.load(); + const parsedResult = JSON.parse(result!); + assert.ok(parsedResult.AccessToken); + + const token = await credential2.getToken(scope); + assert.ok(token?.token); + assert.ok(token?.expiresOnTimestamp! > Date.now()); + assert.equal(getTokenSilentSpy.callCount, 2); + assert.equal(doGetTokenSpy.callCount, 1); + }); +}); diff --git a/sdk/identity/identity/test/manual/nodeTest.js b/sdk/identity/identity/test/manual/nodeTest.js new file mode 100644 index 000000000000..1ca648a1f80f --- /dev/null +++ b/sdk/identity/identity/test/manual/nodeTest.js @@ -0,0 +1,43 @@ +const identity = require("@azure/identity"); +const serviceBus = require("@azure/service-bus"); +const dotenv = require("dotenv"); + +// Ensure you have a .env file in the same directory with something similar to: +// +// AZURE_CLIENT_ID="value" +// AZURE_CLIENT_SECRET="value" +// SERVICE_BUS_ENDPOINT="value" +// QUEUE_NAME="value" +// +dotenv.config(); + +const credential = new identity.InteractiveBrowserCredential({ + redirectUri: "http://localhost:8081", + tenantId: process.env.AZURE_TENANT_ID, + clientId: process.env.AZURE_CLIENT_ID, +}); + +const client = new serviceBus.ServiceBusClient(process.env.SERVICE_BUS_ENDPOINT, credential); + +(async () => { + const sender = client.createSender(process.env.QUEUE_NAME); + const body = [ + "--- Message ---", + `Sent at: ${new Date()}`, + "Body:", + "Hello.", + "--- Message End ---", + ].join("\n"); + console.log("Attempting to send a message..."); + await sender.sendMessages({ body }); + console.log("Message sent successfully!"); + await sender.close(); + const receiver = client.createReceiver(process.env.QUEUE_NAME); + const messages = await receiver.receiveMessages(10); + for (let message of messages) { + await receiver.completeMessage(message); + } + await receiver.close(); + const output = `Received messages:\n${messages.map((m) => m.body.toString()).join("\n")}`; + console.log(output); +})().then(console.log).catch(console.error); diff --git a/sdk/identity/identity/test/manual/nodeTestSilent.js b/sdk/identity/identity/test/manual/nodeTestSilent.js new file mode 100644 index 000000000000..f366dfd00c25 --- /dev/null +++ b/sdk/identity/identity/test/manual/nodeTestSilent.js @@ -0,0 +1,66 @@ +const identity = require("@azure/identity"); +const serviceBus = require("@azure/service-bus"); +const dotenv = require("dotenv"); + +// Ensure you have a .env file in the same directory with something similar to: +// +// AZURE_CLIENT_ID="value" +// AZURE_CLIENT_SECRET="value" +// SERVICE_BUS_ENDPOINT="value" +// QUEUE_NAME="value" +// +dotenv.config(); + +const credential1 = new identity.InteractiveBrowserCredential({ + redirectUri: "http://localhost:8081", + tenantId: process.env.AZURE_TENANT_ID, + clientId: process.env.AZURE_CLIENT_ID, + tokenCachePersistenceOptions: { + name: "nodeTestSilent", + allowUnencryptedStorage: true + } +}); + +(async () => { + console.log("Manually authenticating..."); + + const account = await credential1.authenticate(`https://servicebus.azure.net/.default`); + + const credential2 = new identity.InteractiveBrowserCredential({ + redirectUri: "http://localhost:8081", + tenantId: process.env.AZURE_TENANT_ID, + clientId: process.env.AZURE_CLIENT_ID, + authenticationRecord: account, + // To be able to re-use the account, the Token Cache must also have been provided. + // TODO: Perhaps make the account parameter part of the tokenCachePersistenceOptions? + tokenCachePersistenceOptions: { + name: "nodeTestSilent", + allowUnencryptedStorage: true + } + }); + + const client = new serviceBus.ServiceBusClient(process.env.SERVICE_BUS_ENDPOINT, credential2); + + console.log("Silently authenticating..."); + + const sender = client.createSender(process.env.QUEUE_NAME); + const body = [ + "--- Message ---", + `Sent at: ${new Date()}`, + "Body:", + "Hello.", + "--- Message End ---", + ].join("\n"); + console.log("Attempting to send a message..."); + await sender.sendMessages({ body }); + console.log("Message sent successfully!"); + await sender.close(); + const receiver = client.createReceiver(process.env.QUEUE_NAME); + const messages = await receiver.receiveMessages(10); + for (let message of messages) { + await receiver.completeMessage(message); + } + await receiver.close(); + const output = `Received messages:\n${messages.map((m) => m.body.toString()).join("\n")}`; + console.log(output); +})().then(console.log).catch(console.error); diff --git a/sdk/identity/identity/test/msalTestUtils.ts b/sdk/identity/identity/test/msalTestUtils.ts new file mode 100644 index 000000000000..8760caf1f757 --- /dev/null +++ b/sdk/identity/identity/test/msalTestUtils.ts @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { + record, + Recorder, + RecorderEnvironmentSetup, + TestContextInterface +} from "@azure/test-utils-recorder"; +import Sinon, { createSandbox } from "sinon"; +import assert from "assert"; +import { setTracer, SpanGraph, SpanOptions, TestTracer } from "@azure/core-tracing"; +import { MsalBaseUtilities } from "../src/msal/utils"; +import * as dotenv from "dotenv"; +dotenv.config(); + +export type MsalTestCleanup = () => Promise; + +export interface MsalTestSetupResponse { + cleanup: MsalTestCleanup; + recorder: Recorder; + sandbox: Sinon.SinonSandbox; +} + +export const PlaybackTenantId = "12345678-1234-1234-1234-123456789012"; + +export function msalNodeTestSetup( + testContext: TestContextInterface | Mocha.Context +): MsalTestSetupResponse { + const playbackValues = { + correlationId: "client-request-id" + }; + const recorderEnvSetup: RecorderEnvironmentSetup = { + replaceableVariables: { + AZURE_TENANT_ID: PlaybackTenantId, + AZURE_CLIENT_ID: "azure_client_id", + AZURE_CLIENT_SECRET: "azure_client_secret", + AZURE_USERNAME: "azure_username", + AZURE_PASSWORD: "azure_password" + }, + customizationsOnRecordings: [ + (recording: string): string => + recording.replace(/"access_token":"[^"]*"/g, `"access_token":"access_token"`), + (recording: string): string => + recording.replace(/"refresh_token":"[^"]*"/g, `"refresh_token":"refresh_token"`), + (recording: string): string => + recording.replace( + /client-request-id=[a-z0-9-]*/g, + `client-request-id=${playbackValues.correlationId}` + ), + (recording: string): string => + recording.replace( + /client_assertion=[a-zA-Z0-9-\._]*/g, + `client_assertion=client_assertion` + ), + (recording: string): string => recording.replace(/esctx=[a-zA-Z0-9-_]*/g, `esctx=esctx`), + (recording: string): string => recording.replace(/'fpc=[^;]*/g, `'fpc=fpc;`), + // Device code specific + (recording: string): string => + recording.replace(/user_code":"[^"]*/g, `user_code":"USER_CODE`), + (recording: string): string => + recording.replace( + /enter the code [A-Z0-9]* to authenticate/g, + `enter the code USER_CODE to authenticate` + ), + (recording: string): string => + recording.replace(/device_code":"[^"]*/g, `device_code":"DEVICE_CODE`), + (recording: string): string => + recording.replace(/device_code=[^&]*/g, `device_code=DEVICE_CODE`), + // This last part is a JWT token that comes from the service, that has three parts joined by a dot. + // Our fake id_token has the following parts encoded in base64 and joined by a dot: + // - {"typ":"JWT","alg":"RS256","kid":"kid"} + // - {"aud":"aud","iss":"https://login.microsoftonline.com/12345678-1234-1234-1234-123456789012/v2.0","iat":1615337163,"nbf":1615337163,"exp":1615341063,"aio":"aio","idp":"https://sts.windows.net/idp/","name":"Daniel Rodríguez","oid":"oid","preferred_username":"danrodri@microsoft.com","rh":"rh.","sub":"sub","tid":"12345678-1234-1234-1234-123456789012","uti":"uti","ver":"2.0"} + // - no_idea_whats_this + (recording: string): string => + recording.replace( + /id_token":"[^"]*/g, + `id_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6ImtpZCJ9.eyJhdWQiOiJhdWQiLCJpc3MiOiJodHRwczovL2xvZ2luLm1pY3Jvc29mdG9ubGluZS5jb20vMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyL3YyLjAiLCJpYXQiOjE2MTUzMzcxNjMsIm5iZiI6MTYxNTMzNzE2MywiZXhwIjoxNjE1MzQxMDYzLCJhaW8iOiJhaW8iLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9pZHAvIiwibmFtZSI6IkRhbmllbCBSb2Ryw61ndWV6Iiwib2lkIjoib2lkIiwicHJlZmVycmVkX3VzZXJuYW1lIjoiZGFucm9kcmlAbWljcm9zb2Z0LmNvbSIsInJoIjoicmguIiwic3ViIjoic3ViIiwidGlkIjoiMTIzNDU2NzgtMTIzNC0xMjM0LTEyMzQtMTIzNDU2Nzg5MDEyIiwidXRpIjoidXRpIiwidmVyIjoiMi4wIn0=.bm9faWRlYV93aGF0c190aGlz` + ) + ], + queryParametersToSkip: [] + }; + const recorder = record(testContext, recorderEnvSetup); + const sandbox = createSandbox(); + + const stub = sandbox.stub(MsalBaseUtilities.prototype, "generateUuid"); + stub.returns(playbackValues.correlationId); + + return { + sandbox, + recorder, + async cleanup() { + await recorder.stop(); + sandbox.restore(); + } + }; +} + +export interface TestTracingOptions { + test(spanOptions: SpanOptions): Promise; + children: any[]; +} + +export function testTracing(options: TestTracingOptions): () => Promise { + return async function() { + const { test, children } = options; + const tracer = new TestTracer(); + setTracer(tracer); + const rootSpan = tracer.startSpan("root"); + + await test({ + parent: rootSpan.context() + }); + + rootSpan.end(); + + const rootSpans = tracer.getRootSpans(); + assert.strictEqual(rootSpans.length, 1, "Should only have one root span."); + assert.strictEqual(rootSpan, rootSpans[0], "The root span should match what was passed in."); + + const expectedGraph: SpanGraph = { + roots: [ + { + name: rootSpan.name, + children + } + ] + }; + + assert.deepStrictEqual(tracer.getSpanGraph(rootSpan.context().traceId), expectedGraph); + assert.strictEqual(tracer.getActiveSpans().length, 0, "All spans should have had end called"); + }; +} diff --git a/sdk/identity/identity/test/public/clientSecretCredential.spec.ts b/sdk/identity/identity/test/public/clientSecretCredential.spec.ts deleted file mode 100644 index 2ccbe14fa1b7..000000000000 --- a/sdk/identity/identity/test/public/clientSecretCredential.spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import { ClientSecretCredential } from "../../src"; -import { MockAuthHttpClient, assertClientCredentials } from "../authTestUtils"; - -describe("ClientSecretCredential", function() { - it("sends an authorization request with the given credentials", async () => { - const mockHttpClient = new MockAuthHttpClient(); - - const credential = new ClientSecretCredential( - "tenant", - "client", - "secret", - mockHttpClient.tokenCredentialOptions - ); - - await credential.getToken("scope"); - - const authRequest = mockHttpClient.requests[0]; - assertClientCredentials(authRequest, "tenant", "client", "secret"); - }); -}); diff --git a/sdk/identity/identity/test/public/node/clientCertificateCredential.spec.ts b/sdk/identity/identity/test/public/node/clientCertificateCredential.spec.ts index 0febae7df3b4..82ad4337616a 100644 --- a/sdk/identity/identity/test/public/node/clientCertificateCredential.spec.ts +++ b/sdk/identity/identity/test/public/node/clientCertificateCredential.spec.ts @@ -1,226 +1,118 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +/* eslint-disable no-invalid-this */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ -import qs from "qs"; -import jws from "jws"; -import path from "path"; import assert from "assert"; +import * as path from "path"; +import { AbortController } from "@azure/abort-controller"; +import { env, isPlaybackMode, delay } from "@azure/test-utils-recorder"; +import { MsalTestCleanup, msalNodeTestSetup, testTracing } from "../../msalTestUtils"; import { ClientCertificateCredential } from "../../../src"; -import { MockAuthHttpClient } from "../../authTestUtils"; -import { setTracer, TestTracer, SpanGraph } from "@azure/core-tracing"; describe("ClientCertificateCredential", function() { - it("loads a PEM-formatted certificate from a file", () => { - const credential = new ClientCertificateCredential( - "tenant", - "client", - path.resolve(__dirname, "../test/azure-identity-test.crt") - ); - - assert.strictEqual( - (credential as any).certificateThumbprint, - "47080F3BAA6BF8DF068531106FBCF2DC6E5F6919" - ); - - assert.strictEqual((credential as any).certificateX5t, "RwgPO6pr+N8GhTEQb7zy3G5faRk="); + let cleanup: MsalTestCleanup; + beforeEach(function() { + cleanup = msalNodeTestSetup(this).cleanup; }); - - it("throws when given a file that doesn't contain a PEM-formatted certificate", () => { - assert.throws(() => { - new ClientCertificateCredential( - "tenant", - "client", - path.resolve(__dirname, "../src/index.ts") - ); - }); + afterEach(async function() { + await cleanup(); }); - it("sends a correctly formatted token request", async () => { - const tenantId = "tenantId"; - const clientId = "clientId"; - const mockHttpClient = new MockAuthHttpClient(); - - const credential = new ClientCertificateCredential( - tenantId, - clientId, - path.resolve(__dirname, "../test/azure-identity-test.crt"), - mockHttpClient.tokenCredentialOptions - ); - - await credential.getToken("scope"); - - const authRequest = mockHttpClient.requests[0]; - if (!authRequest) { - assert.fail("No authentication request was intercepted"); - } else { - assert.strictEqual( - authRequest.url.startsWith(`https://authority/${tenantId}`), - true, - "Request URL doesn't contain expected tenantId" - ); - assert.strictEqual( - authRequest.body.indexOf(`client_id=${clientId}`) > -1, - true, - "Request URL doesn't contain expected clientId" - ); + const certificatePath = path.resolve(__dirname, "../test/assets/cert.pem"); + const scope = "https://vault.azure.net/.default"; - const queryParams = qs.parse(authRequest.body); - const jwtToken = jws.decode(queryParams.client_assertion as string); - - assert.strictEqual(jwtToken.header.x5t, (credential as any).certificateX5t); - assert.strictEqual(jwtToken.payload.iss, clientId); - assert.strictEqual(jwtToken.payload.sub, clientId); - assert.strictEqual( - jwtToken.payload.aud.startsWith(`https://authority/${tenantId}`), - true, - "Audience does not have the correct authority or tenantId" - ); + it("authenticates", async function() { + if (isPlaybackMode()) { + // MSAL creates a client assertion based on the certificate that I haven't been able to mock. + // This assertion could be provided as parameters, but we don't have that in the public API yet, + // and I'm trying to avoid having to generate one ourselves. + this.skip(); } - }); - - it("sends a correctly formatted token request with x5c enabled", async () => { - const tenantId = "tenantId"; - const clientId = "clientId"; - const mockHttpClient = new MockAuthHttpClient(); - // Enable X5c flag - mockHttpClient.tokenCredentialOptions.sendCertificateChain = true; const credential = new ClientCertificateCredential( - tenantId, - clientId, - path.resolve(__dirname, "../test/azure-identity-test.crt"), - mockHttpClient.tokenCredentialOptions + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + certificatePath ); - await credential.getToken("scope"); - - const authRequest = mockHttpClient.requests[0]; - if (!authRequest) { - assert.fail("No authentication request was intercepted"); - } else { - assert.strictEqual( - authRequest.url.startsWith(`https://authority/${tenantId}`), - true, - "Request URL doesn't contain expected tenantId" - ); - assert.strictEqual( - authRequest.body.indexOf(`client_id=${clientId}`) > -1, - true, - "Request URL doesn't contain expected clientId" - ); - - const queryParams = qs.parse(authRequest.body); - const jwtToken = jws.decode(queryParams.client_assertion as string); - - assert.strictEqual(jwtToken.header.x5t, (credential as any).certificateX5t); - assert.deepStrictEqual(jwtToken.header.x5c, (credential as any).certificateX5c); - assert.strictEqual(jwtToken.payload.iss, clientId); - assert.strictEqual(jwtToken.payload.sub, clientId); - assert.strictEqual( - jwtToken.payload.aud.startsWith(`https://authority/${tenantId}`), - true, - "Audience does not have the correct authority or tenantId" - ); - } + const token = await credential.getToken(scope); + assert.ok(token?.token); + assert.ok(token?.expiresOnTimestamp! > Date.now()); }); - it("sends a correctly formatted token request with a chained x5c enabled", async () => { - const tenantId = "tenantId"; - const clientId = "clientId"; - const mockHttpClient = new MockAuthHttpClient(); - // Enable X5c flag - mockHttpClient.tokenCredentialOptions.sendCertificateChain = true; + it("authenticates with sendCertificateChain", async function() { + if (isPlaybackMode()) { + // MSAL creates a client assertion based on the certificate that I haven't been able to mock. + // This assertion could be provided as parameters, but we don't have that in the public API yet, + // and I'm trying to avoid having to generate one ourselves. + this.skip(); + } const credential = new ClientCertificateCredential( - tenantId, - clientId, - path.resolve(__dirname, "../test/azure-identity-chain-test.crt"), - mockHttpClient.tokenCredentialOptions + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + certificatePath, + { sendCertificateChain: true } ); - await credential.getToken("scope"); - - const authRequest = mockHttpClient.requests[0]; - if (!authRequest) { - assert.fail("No authentication request was intercepted"); - } else { - assert.strictEqual( - authRequest.url.startsWith(`https://authority/${tenantId}`), - true, - "Request URL doesn't contain expected tenantId" - ); - assert.strictEqual( - authRequest.body.indexOf(`client_id=${clientId}`) > -1, - true, - "Request URL doesn't contain expected clientId" - ); - - const queryParams = qs.parse(authRequest.body); - const jwtToken = jws.decode(queryParams.client_assertion as string); - - assert.strictEqual(jwtToken.header.x5t, (credential as any).certificateX5t); - assert.deepStrictEqual(jwtToken.header.x5c, (credential as any).certificateX5c); - assert.strictEqual(2, (credential as any).certificateX5c.length); - assert.strictEqual(jwtToken.payload.iss, clientId); - assert.strictEqual(jwtToken.payload.sub, clientId); - assert.strictEqual( - jwtToken.payload.aud.startsWith(`https://authority/${tenantId}`), - true, - "Audience does not have the correct authority or tenantId" - ); - } + const token = await credential.getToken(scope); + assert.ok(token?.token); + assert.ok(token?.expiresOnTimestamp! > Date.now()); }); - it("sends a correctly formatted token request while tracing", async () => { - const tracer = new TestTracer(); - setTracer(tracer); - const tenantId = "tenantId"; - const clientId = "clientId"; - const mockHttpClient = new MockAuthHttpClient(); - - const rootSpan = tracer.startSpan("root"); - + it("allows cancelling the authentication", async function() { const credential = new ClientCertificateCredential( - tenantId, - clientId, - path.resolve(__dirname, "../test/azure-identity-test.crt"), - mockHttpClient.tokenCredentialOptions + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + certificatePath ); - await credential.getToken("scope", { - tracingOptions: { - spanOptions: { - parent: rootSpan.context() - } - } + const controller = new AbortController(); + const getTokenPromise = credential.getToken(scope, { + abortSignal: controller.signal }); - rootSpan.end(); + await delay(5); + controller.abort(); - const rootSpans = tracer.getRootSpans(); - assert.strictEqual(rootSpans.length, 1, "Should only have one root span."); - assert.strictEqual(rootSpan, rootSpans[0], "The root span should match what was passed in."); + let error: Error | undefined; + try { + await getTokenPromise; + } catch (e) { + error = e; + } + assert.equal(error?.name, "CredentialUnavailable"); + assert.ok(error?.message.includes("could not resolve endpoints")); + }); - const expectedGraph: SpanGraph = { - roots: [ + it("supports tracing", async function() { + if (isPlaybackMode()) { + // MSAL creates a client assertion based on the certificate that I haven't been able to mock. + // This assertion could be provided as parameters, but we don't have that in the public API yet, + // and I'm trying to avoid having to generate one ourselves. + this.skip(); + } + await testTracing({ + test: async (spanOptions) => { + const credential = new ClientCertificateCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + certificatePath + ); + + await credential.getToken(scope, { + tracingOptions: { + spanOptions + } + }); + }, + children: [ { - name: rootSpan.name, - children: [ - { - name: "Azure.Identity.ClientCertificateCredential-getToken", - children: [ - { - children: [], - name: "/tenantId/oauth2/v2.0/token" - } - ] - } - ] + name: "Azure.Identity.ClientCertificateCredential.getToken", + children: [] } ] - }; - - assert.deepStrictEqual(tracer.getSpanGraph(rootSpan.context().traceId), expectedGraph); - assert.strictEqual(tracer.getActiveSpans().length, 0, "All spans should have had end called"); + }); }); }); diff --git a/sdk/identity/identity/test/public/node/clientSecretCredential.spec.ts b/sdk/identity/identity/test/public/node/clientSecretCredential.spec.ts new file mode 100644 index 000000000000..d4b1eef9eb28 --- /dev/null +++ b/sdk/identity/identity/test/public/node/clientSecretCredential.spec.ts @@ -0,0 +1,85 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/* eslint-disable no-invalid-this */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ + +import assert from "assert"; +import { env, delay } from "@azure/test-utils-recorder"; +import { AbortController } from "@azure/abort-controller"; +import { MsalTestCleanup, msalNodeTestSetup, testTracing } from "../../msalTestUtils"; +import { ClientSecretCredential } from "../../../src"; + +describe("ClientSecretCredential", function() { + let cleanup: MsalTestCleanup; + beforeEach(function() { + cleanup = msalNodeTestSetup(this).cleanup; + }); + afterEach(async function() { + await cleanup(); + }); + + const scope = "https://vault.azure.net/.default"; + + it("authenticates", async function() { + const credential = new ClientSecretCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_CLIENT_SECRET + ); + + const token = await credential.getToken(scope); + assert.ok(token?.token); + assert.ok(token?.expiresOnTimestamp! > Date.now()); + }); + + it("allows cancelling the authentication", async function() { + const credential = new ClientSecretCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_CLIENT_SECRET + ); + + const controller = new AbortController(); + const getTokenPromise = credential.getToken(scope, { + abortSignal: controller.signal + }); + + await delay(5); + controller.abort(); + + let error: Error | undefined; + try { + await getTokenPromise; + } catch (e) { + error = e; + } + console.log(error); + assert.equal(error?.name, "CredentialUnavailable"); + assert.ok(error?.message.includes("could not resolve endpoints")); + }); + + it( + "supports tracing", + testTracing({ + test: async (spanOptions) => { + const credential = new ClientSecretCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_CLIENT_SECRET + ); + + await credential.getToken(scope, { + tracingOptions: { + spanOptions + } + }); + }, + children: [ + { + name: "Azure.Identity.ClientSecretCredential.getToken", + children: [] + } + ] + }) + ); +}); diff --git a/sdk/identity/identity/test/public/node/deviceCodeCredential.spec.ts b/sdk/identity/identity/test/public/node/deviceCodeCredential.spec.ts new file mode 100644 index 000000000000..059888eef321 --- /dev/null +++ b/sdk/identity/identity/test/public/node/deviceCodeCredential.spec.ts @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/* eslint-disable no-invalid-this */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ + +import assert from "assert"; +import { env, isLiveMode, delay } from "@azure/test-utils-recorder"; +import { AbortController, AbortError } from "@azure/abort-controller"; +import { DeviceCodeCredential, DeviceCodePromptCallback } from "../../../src"; +import { msalNodeTestSetup, MsalTestCleanup, testTracing } from "../../msalTestUtils"; + +describe("DeviceCodeCredential", function() { + let cleanup: MsalTestCleanup; + beforeEach(function() { + cleanup = msalNodeTestSetup(this).cleanup; + }); + afterEach(async function() { + await cleanup(); + }); + + const scope = "https://vault.azure.net/.default"; + + it("authenticates with default values", async function() { + // These tests should not run live because this credential requires user interaction. + if (isLiveMode()) { + this.skip(); + } + const credential = new DeviceCodeCredential(); + + const token = await credential.getToken(scope); + assert.ok(token?.token); + assert.ok(token?.expiresOnTimestamp! > Date.now()); + }); + + it("authenticates with provided values", async function() { + // These tests should not run live because this credential requires user interaction. + if (isLiveMode()) { + this.skip(); + } + const credential = new DeviceCodeCredential(env.AZURE_TENANT_ID, env.AZURE_CLIENT_ID); + + const token = await credential.getToken(scope); + assert.ok(token?.token); + assert.ok(token?.expiresOnTimestamp! > Date.now()); + }); + + it("authenticates and allows the customization of the prompt callback", async function() { + // These tests should not run live because this credential requires user interaction. + if (isLiveMode()) { + this.skip(); + } + const callback: DeviceCodePromptCallback = (info) => { + console.log("CUSTOMIZED PROMPT CALLBACK", info.message); + }; + const credential = new DeviceCodeCredential(env.AZURE_TENANT_ID, env.AZURE_CLIENT_ID, callback); + + const token = await credential.getToken(scope); + assert.ok(token?.token); + assert.ok(token?.expiresOnTimestamp! > Date.now()); + }); + + // Setting the MSAL options to cancel doesn't seem to be cancelling MSAL. I'm waiting for them to mention how to do this. + it.skip("allows cancelling the authentication", async function() { + const credential = new DeviceCodeCredential(env.AZURE_TENANT_ID, env.AZURE_CLIENT_ID); + + const controller = new AbortController(); + const getTokenPromise = credential.getToken(scope, { + abortSignal: controller.signal + }); + + await delay(500); + controller.abort(); + + let error: AbortError | undefined; + try { + await getTokenPromise; + } catch (e) { + error = e; + } + assert.equal(error?.name, "AbortError"); + assert.equal(error?.message, "Cancellation triggered by the AbortSignal"); + }); + + it("allows setting disableAutomaticAuthentication", async function() { + // These tests should not run live because this credential requires user interaction. + if (isLiveMode()) { + this.skip(); + } + const credential = new DeviceCodeCredential({ + tenantId: env.AZURE_TENANT_ID, + clientId: env.AZURE_CLIENT_ID, + disableAutomaticAuthentication: true + }); + + let error: AbortError | undefined; + try { + await credential.getToken(scope); + } catch (e) { + error = e; + } + assert.equal( + error?.message, + `Automatic authentication has been disabled. You may call the authentication() method.` + ); + + const account = await credential.authenticate(scope); + assert.ok(account); + }); + + it("supports tracing", async function() { + // These tests should not run live because this credential requires user interaction. + if (isLiveMode()) { + this.skip(); + } + await testTracing({ + test: async (spanOptions) => { + const credential = new DeviceCodeCredential(env.AZURE_TENANT_ID, env.AZURE_CLIENT_ID); + + await credential.getToken(scope, { + tracingOptions: { + spanOptions + } + }); + }, + children: [ + { + name: "Azure.Identity.DeviceCodeCredential.getToken", + children: [] + } + ] + })(); + }); +}); diff --git a/sdk/identity/identity/test/public/node/environmentCredential.spec.ts b/sdk/identity/identity/test/public/node/environmentCredential.spec.ts index 9988e9b09477..ba11ea28d3b3 100644 --- a/sdk/identity/identity/test/public/node/environmentCredential.spec.ts +++ b/sdk/identity/identity/test/public/node/environmentCredential.spec.ts @@ -1,192 +1,235 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +/* eslint-disable no-invalid-this */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ +import sinon from "sinon"; import assert from "assert"; -import path from "path"; -import { EnvironmentCredential, AuthenticationError, CredentialUnavailable } from "../../../src"; +import { isPlaybackMode } from "@azure/test-utils-recorder"; import { - MockAuthHttpClient, - assertClientCredentials, - assertClientUsernamePassword, - assertRejects -} from "../../authTestUtils"; -import { TestTracer, setTracer, SpanGraph } from "@azure/core-tracing"; - -interface OAuthErrorResponse { - error: string; - error_description: string; - error_codes?: number[]; - timestamp?: string; - trace_id?: string; - correlation_id?: string; -} + AuthenticationError, + CredentialUnavailable, + EnvironmentCredential, + UsernamePasswordCredential +} from "../../../src"; +import { MsalTestCleanup, msalNodeTestSetup, testTracing } from "../../msalTestUtils"; +import { assertRejects } from "../../authTestUtils"; describe("EnvironmentCredential", function() { - it("finds and uses client credential environment variables", async () => { - process.env.AZURE_TENANT_ID = "tenant"; - process.env.AZURE_CLIENT_ID = "client"; - process.env.AZURE_CLIENT_SECRET = "secret"; - - const mockHttpClient = new MockAuthHttpClient(); - - const credential = new EnvironmentCredential(mockHttpClient.tokenCredentialOptions); - await credential.getToken("scope"); - - delete process.env.AZURE_TENANT_ID; - delete process.env.AZURE_CLIENT_ID; - delete process.env.AZURE_CLIENT_SECRET; - - const authRequest = mockHttpClient.requests[0]; - assertClientCredentials(authRequest, "tenant", "client", "secret"); + let cleanup: MsalTestCleanup; + const environmentVariableNames = [ + "AZURE_TENANT_ID", + "AZURE_CLIENT_ID", + "AZURE_CLIENT_SECRET", + "AZURE_CLIENT_CERTIFICATE_PATH", + "AZURE_USERNAME", + "AZURE_PASSWORD" + ]; + const cachedValues: Record = {}; + + beforeEach(function() { + const setup = msalNodeTestSetup(this); + cleanup = setup.cleanup; + environmentVariableNames.forEach((name) => { + cachedValues[name] = process.env[name]; + delete process.env[name]; + }); + }); + afterEach(async function() { + await cleanup(); + environmentVariableNames.forEach((name) => { + process.env[name] = cachedValues[name]; + }); }); - it("finds and uses client certificate path environment variables", async () => { - process.env.AZURE_TENANT_ID = "tenant"; - process.env.AZURE_CLIENT_ID = "client"; - process.env.AZURE_CLIENT_CERTIFICATE_PATH = path.resolve( - __dirname, - "../test/azure-identity-test.crt" - ); - - const mockHttpClient = new MockAuthHttpClient(); + const scope = "https://vault.azure.net/.default"; - const credential = new EnvironmentCredential(mockHttpClient.tokenCredentialOptions); - await credential.getToken("scope"); + it("authenticates with a client secret on the environment variables", async function() { + // The following environment variables must be set for this to work. + // On TEST_MODE="playback", the recorder automatically fills them with stubbed values. + process.env.AZURE_TENANT_ID = cachedValues.AZURE_TENANT_ID; + process.env.AZURE_CLIENT_ID = cachedValues.AZURE_CLIENT_ID; + process.env.AZURE_CLIENT_SECRET = cachedValues.AZURE_CLIENT_SECRET; - delete process.env.AZURE_TENANT_ID; - delete process.env.AZURE_CLIENT_ID; - delete process.env.AZURE_CLIENT_CERTIFICATE_PATH; + const credential = new EnvironmentCredential(); - assert.strictEqual( - (credential as any)._credential.certificateThumbprint, - "47080F3BAA6BF8DF068531106FBCF2DC6E5F6919" - ); + const token = await credential.getToken(scope); + assert.ok(token?.token); + assert.ok(token?.expiresOnTimestamp! > Date.now()); + }); - assert.strictEqual( - (credential as any)._credential.certificateX5t, - "RwgPO6pr+N8GhTEQb7zy3G5faRk=" - ); + it("authenticates with a client certificate on the environment variables", async function() { + if (isPlaybackMode()) { + // MSAL creates a client assertion based on the certificate that I haven't been able to mock. + // This assertion could be provided as parameters, but we don't have that in the public API yet, + // and I'm trying to avoid having to generate one ourselves. + this.skip(); + } + + // The following environment variables must be set for this to work. + // On TEST_MODE="playback", the recorder automatically fills them with stubbed values. + process.env.AZURE_TENANT_ID = cachedValues.AZURE_TENANT_ID; + process.env.AZURE_CLIENT_ID = cachedValues.AZURE_CLIENT_ID; + process.env.AZURE_CLIENT_CERTIFICATE_PATH = cachedValues.AZURE_CLIENT_CERTIFICATE_PATH; + + const credential = new EnvironmentCredential(); + + const token = await credential.getToken(scope); + assert.ok(token?.token); + assert.ok(token?.expiresOnTimestamp! > Date.now()); }); it("finds and uses client username/password environment variables", async () => { - process.env.AZURE_TENANT_ID = "tenant"; - process.env.AZURE_CLIENT_ID = "client"; + // The following environment variables must be set for this to work. + // On TEST_MODE="playback", the recorder automatically fills them with stubbed values. + process.env.AZURE_TENANT_ID = cachedValues.AZURE_TENANT_ID; + process.env.AZURE_CLIENT_ID = cachedValues.AZURE_CLIENT_ID; process.env.AZURE_USERNAME = "user"; process.env.AZURE_PASSWORD = "password"; - const mockHttpClient = new MockAuthHttpClient(); - - const credential = new EnvironmentCredential(mockHttpClient.tokenCredentialOptions); - await credential.getToken("scope"); + const getTokenSpy = sinon.spy(UsernamePasswordCredential.prototype, "getToken"); - delete process.env.AZURE_TENANT_ID; - delete process.env.AZURE_CLIENT_ID; - delete process.env.AZURE_USERNAME; - delete process.env.AZURE_PASSWORD; + try { + const credential = new EnvironmentCredential(); + await credential.getToken("scope"); + } catch (e) { + // To avoid having to store passwords anywhere, this getToken request will fail. + // We will focus our test on making sure the underlying getToken was called. + } - const authRequest = mockHttpClient.requests[0]; - assertClientUsernamePassword(authRequest, "tenant", "client", "user", "password"); + assert.equal( + getTokenSpy.callCount, + 1, + "UsernamePasswordCredential getToken should have been called" + ); }); - it("finds and uses client credential environment variables with tracing", async () => { - process.env.AZURE_TENANT_ID = "tenant"; - process.env.AZURE_CLIENT_ID = "client"; - process.env.AZURE_CLIENT_SECRET = "secret"; - - const mockHttpClient = new MockAuthHttpClient(); - const tracer = new TestTracer(); - setTracer(tracer); - - const credential = new EnvironmentCredential(mockHttpClient.tokenCredentialOptions); - const rootSpan = tracer.startSpan("root"); - await credential.getToken("scope", { - tracingOptions: { - spanOptions: { - parent: rootSpan.context() + it( + "supports tracing with environment client secret", + testTracing({ + test: async (spanOptions) => { + // The following environment variables must be set for this to work. + // On TEST_MODE="playback", the recorder automatically fills them with stubbed values. + process.env.AZURE_TENANT_ID = cachedValues.AZURE_TENANT_ID; + process.env.AZURE_CLIENT_ID = cachedValues.AZURE_CLIENT_ID; + process.env.AZURE_CLIENT_SECRET = cachedValues.AZURE_CLIENT_SECRET; + + const credential = new EnvironmentCredential(); + + await credential.getToken(scope, { + tracingOptions: { + spanOptions + } + }); + }, + children: [ + { + name: "Azure.Identity.EnvironmentCredential.getToken", + children: [ + { + name: "Azure.Identity.ClientSecretCredential.getToken", + children: [] + } + ] } - } - }); - rootSpan.end(); - - delete process.env.AZURE_TENANT_ID; - delete process.env.AZURE_CLIENT_ID; - delete process.env.AZURE_CLIENT_SECRET; - - const rootSpans = tracer.getRootSpans(); - assert.strictEqual(rootSpans.length, 1, "Should only have one root span."); - assert.strictEqual(rootSpan, rootSpans[0], "The root span should match what was passed in."); - - const expectedGraph: SpanGraph = { - roots: [ + ] + }) + ); + + it("supports tracing with environment client certificate", async function() { + if (isPlaybackMode()) { + // MSAL creates a client assertion based on the certificate that I haven't been able to mock. + // This assertion could be provided as parameters, but we don't have that in the public API yet, + // and I'm trying to avoid having to generate one ourselves. + this.skip(); + } + await testTracing({ + test: async (spanOptions) => { + // The following environment variables must be set for this to work. + // On TEST_MODE="playback", the recorder automatically fills them with stubbed values. + process.env.AZURE_TENANT_ID = cachedValues.AZURE_TENANT_ID; + process.env.AZURE_CLIENT_ID = cachedValues.AZURE_CLIENT_ID; + process.env.AZURE_CLIENT_CERTIFICATE_PATH = cachedValues.AZURE_CLIENT_CERTIFICATE_PATH; + + const credential = new EnvironmentCredential(); + + await credential.getToken(scope, { + tracingOptions: { + spanOptions + } + }); + }, + children: [ { - name: rootSpan.name, + name: "Azure.Identity.EnvironmentCredential.getToken", children: [ { - name: "Azure.Identity.EnvironmentCredential-getToken", - children: [ - { - name: "Azure.Identity.ClientSecretCredential-getToken", - children: [ - { - children: [], - name: "/tenant/oauth2/v2.0/token" - } - ] - } - ] + name: "Azure.Identity.ClientCertificateCredential.getToken", + children: [] } ] } ] - }; - - assert.deepStrictEqual(tracer.getSpanGraph(rootSpan.context().traceId), expectedGraph); - assert.strictEqual(tracer.getActiveSpans().length, 0, "All spans should have had end called"); + })(); }); - it("throws an CredentialUnavailable when getToken is called and no credential was configured", async () => { - const mockHttpClient = new MockAuthHttpClient(); - - const credential = new EnvironmentCredential(mockHttpClient.tokenCredentialOptions); - await assertRejects( - credential.getToken("scope"), - (error: CredentialUnavailable) => - error.message.indexOf( - "EnvironmentCredential is unavailable. Environment variables are not fully configured." - ) > -1 - ); - - process.env.AZURE_TENANT_ID = "Itme"; + it( + "supports tracing with environment username/password", + testTracing({ + test: async (spanOptions) => { + // The following environment variables must be set for this to work. + // On TEST_MODE="playback", the recorder automatically fills them with stubbed values. + process.env.AZURE_TENANT_ID = cachedValues.AZURE_TENANT_ID; + process.env.AZURE_CLIENT_ID = cachedValues.AZURE_CLIENT_ID; + process.env.AZURE_USERNAME = "user"; + process.env.AZURE_PASSWORD = "password"; + + const credential = new EnvironmentCredential(); + + try { + await credential.getToken(scope, { + tracingOptions: { + spanOptions + } + }); + } catch (e) { + // To avoid having to store passwords anywhere, this getToken request will fail. + // We will focus our test on making sure the underlying getToken was called. + } + }, + children: [ + { + name: "Azure.Identity.EnvironmentCredential.getToken", + children: [ + { + name: "Azure.Identity.UsernamePasswordCredential.getToken", + children: [] + } + ] + } + ] + }) + ); - const credentialDeux = new EnvironmentCredential(mockHttpClient.tokenCredentialOptions); + it("throws an CredentialUnavailable when getToken is called and no credential was configured", async () => { + const credential = new EnvironmentCredential(); await assertRejects( - credentialDeux.getToken("scope"), + credential.getToken(scope), (error: CredentialUnavailable) => error.message.indexOf( "EnvironmentCredential is unavailable. Environment variables are not fully configured." ) > -1 ); - - delete process.env.AZURE_TENANT_ID; }); it("throws an AuthenticationError when getToken is called and EnvironmentCredential authentication failed", async () => { process.env.AZURE_TENANT_ID = "tenant"; - process.env.AZURE_CLIENT_ID = "errclient"; + process.env.AZURE_CLIENT_ID = "client"; process.env.AZURE_CLIENT_SECRET = "secret"; - const errResponse: OAuthErrorResponse = { - error: "EnvironmentCredential authentication failed.", - error_description: "" - }; - - const mockHttpClient = new MockAuthHttpClient({ - authResponse: [{ status: 400, parsedBody: errResponse }] - }); - - const credential = new EnvironmentCredential(mockHttpClient.tokenCredentialOptions); + const credential = new EnvironmentCredential(); await assertRejects( - credential.getToken("scope"), + credential.getToken(scope), (error: AuthenticationError) => error.errorResponse.error.indexOf("EnvironmentCredential authentication failed.") > -1 ); diff --git a/sdk/identity/identity/test/public/node/usernamePasswordCredential.spec.ts b/sdk/identity/identity/test/public/node/usernamePasswordCredential.spec.ts new file mode 100644 index 000000000000..3d75930340bd --- /dev/null +++ b/sdk/identity/identity/test/public/node/usernamePasswordCredential.spec.ts @@ -0,0 +1,87 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. +/* eslint-disable no-invalid-this */ +/* eslint-disable @typescript-eslint/no-non-null-asserted-optional-chain */ + +import assert from "assert"; +import { env, delay } from "@azure/test-utils-recorder"; +import { AbortController } from "@azure/abort-controller"; +import { UsernamePasswordCredential } from "../../../src"; +import { MsalTestCleanup, msalNodeTestSetup, testTracing } from "../../msalTestUtils"; + +describe("UsernamePasswordCredential", function() { + let cleanup: MsalTestCleanup; + beforeEach(function() { + cleanup = msalNodeTestSetup(this).cleanup; + }); + afterEach(async function() { + await cleanup(); + }); + + const scope = "https://vault.azure.net/.default"; + + it("authenticates", async function() { + const credential = new UsernamePasswordCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_USERNAME, + env.AZURE_PASSWORD + ); + + const token = await credential.getToken(scope); + assert.ok(token?.token); + assert.ok(token?.expiresOnTimestamp! > Date.now()); + }); + + it("allows cancelling the authentication", async function() { + const credential = new UsernamePasswordCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_USERNAME, + env.AZURE_PASSWORD + ); + + const controller = new AbortController(); + const getTokenPromise = credential.getToken(scope, { + abortSignal: controller.signal + }); + + await delay(5); + controller.abort(); + + let error: Error | undefined; + try { + await getTokenPromise; + } catch (e) { + error = e; + } + assert.equal(error?.name, "CredentialUnavailable"); + assert.ok(error?.message.includes("could not resolve endpoints")); + }); + + it( + "supports tracing", + testTracing({ + test: async (spanOptions) => { + const credential = new UsernamePasswordCredential( + env.AZURE_TENANT_ID, + env.AZURE_CLIENT_ID, + env.AZURE_USERNAME, + env.AZURE_PASSWORD + ); + + await credential.getToken(scope, { + tracingOptions: { + spanOptions + } + }); + }, + children: [ + { + name: "Azure.Identity.UsernamePasswordCredential.getToken", + children: [] + } + ] + }) + ); +}); diff --git a/sdk/identity/identity/test/public/usernamePasswordCredential.spec.ts b/sdk/identity/identity/test/public/usernamePasswordCredential.spec.ts deleted file mode 100644 index a760c2b21017..000000000000 --- a/sdk/identity/identity/test/public/usernamePasswordCredential.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -import assert from "assert"; -import { UsernamePasswordCredential } from "../../src"; -import { MockAuthHttpClient } from "../authTestUtils"; - -describe("UsernamePasswordCredential", function() { - it("sends an authorization request with the given username and password", async () => { - const mockHttpClient = new MockAuthHttpClient(); - - const credential = new UsernamePasswordCredential( - "tenant", - "client", - "user@domain.com", - "p4s$w0rd", - mockHttpClient.tokenCredentialOptions - ); - - await credential.getToken("scope"); - - const authRequest = await mockHttpClient.requests[0]; - if (!authRequest) { - assert.fail("No authentication request was intercepted"); - } else { - assert.strictEqual( - authRequest.url.startsWith(`https://authority/tenant`), - true, - "Request body doesn't contain expected tenantId" - ); - assert.strictEqual( - authRequest.body.indexOf(`client_id=client`) > -1, - true, - "Request body doesn't contain expected clientId" - ); - assert.strictEqual( - authRequest.body.indexOf(`username=user%40domain.com`) > -1, - true, - "Request body doesn't contain expected username" - ); - assert.strictEqual( - authRequest.body.indexOf(`password=p4s%24w0rd`) > -1, - true, - "Request body doesn't contain expected username" - ); - } - }); -}); From a57a6f1ccf123cff188c6510c19715460a5a0366 Mon Sep 17 00:00:00 2001 From: Daniel Rodriguez Date: Thu, 18 Mar 2021 00:53:19 +0000 Subject: [PATCH 02/12] removed axios --- sdk/identity/identity/package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/sdk/identity/identity/package.json b/sdk/identity/identity/package.json index 7398c6ced07e..17e61e768379 100644 --- a/sdk/identity/identity/package.json +++ b/sdk/identity/identity/package.json @@ -91,7 +91,6 @@ "@azure/msal-browser": "2.9.0", "@opentelemetry/api": "^0.10.2", "@types/stoppable": "^1.1.0", - "axios": "^0.21.1", "events": "^3.0.0", "jws": "^4.0.0", "msal": "^1.0.2", From b4870f32a7e3f69c9acc4442386e10f9493192aa Mon Sep 17 00:00:00 2001 From: Daniel Rodriguez Date: Thu, 18 Mar 2021 05:27:09 +0000 Subject: [PATCH 03/12] I forgot to commit the changes to the API review file --- sdk/identity/identity/review/identity.api.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/sdk/identity/identity/review/identity.api.md b/sdk/identity/identity/review/identity.api.md index d5c169b81461..910ef64193cd 100644 --- a/sdk/identity/identity/review/identity.api.md +++ b/sdk/identity/identity/review/identity.api.md @@ -181,7 +181,6 @@ export class InteractiveBrowserCredential implements TokenCredential { // @public export type InteractiveBrowserCredentialBrowserOptions = TokenCredentialOptions & InteractiveCredentialOptions & { redirectUri?: string | (() => string); - postLogoutRedirectUri?: string | (() => string); tenantId?: string; clientId: string; loginStyle?: BrowserLoginStyle; @@ -191,7 +190,6 @@ export type InteractiveBrowserCredentialBrowserOptions = TokenCredentialOptions // @public export type InteractiveBrowserCredentialOptions = TokenCredentialOptions & InteractiveCredentialOptions & { redirectUri?: string | (() => string); - postLogoutRedirectUri?: string | (() => string); tenantId?: string; clientId?: string; }; From 11075565b6eea0a480c54156b891497d13026d2c Mon Sep 17 00:00:00 2001 From: Daniel Rodriguez Date: Thu, 18 Mar 2021 20:48:12 +0000 Subject: [PATCH 04/12] Gracefully throwing on Node 8 and a changelog update --- sdk/identity/identity/CHANGELOG.md | 5 +- ...ersistence_throws_on_node_8_as_expected.js | 5 ++ .../src/tokenCache/TokenCachePersistence.ts | 22 +++-- sdk/identity/identity/src/tokenCache/node8.ts | 5 ++ .../src/tokenCache/persistencePlatforms.ts | 88 +++++++++++-------- .../node/clientSecretCredential.spec.ts | 9 ++ .../node/deviceCodeCredential.spec.ts | 15 +++- .../node/usernamePasswordCredential.spec.ts | 42 ++++++++- sdk/identity/identity/test/msalTestUtils.ts | 5 +- 9 files changed, 144 insertions(+), 52 deletions(-) create mode 100644 sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_persistence_throws_on_node_8_as_expected.js create mode 100644 sdk/identity/identity/src/tokenCache/node8.ts diff --git a/sdk/identity/identity/CHANGELOG.md b/sdk/identity/identity/CHANGELOG.md index 1357e0e42f39..9911ee1c6e82 100644 --- a/sdk/identity/identity/CHANGELOG.md +++ b/sdk/identity/identity/CHANGELOG.md @@ -14,14 +14,15 @@ This release continues with the changes from `1.2.4` and `1.2.4-beta.1`. - Migrated the `InteractiveBrowserCredential`, `DeviceCodeCredential`, `ClientSecretCredential`, `ClientCertificateCredential` and `UsernamePasswordCredential` to the latest MSAL. - This update improves caching of tokens, significantly reducing the number of network requests. - Credentials `InteractiveBrowserCredential`, `DeviceCodeCredential` and `UsernamePasswordCredential` now can: + - Receive a `tokenCachePersistenceOptions` parameter to specify persitence caching of the credentials used to authenticate. This feature will not have support for Node 8 as part of this beta. This feature uses PNPM on Windows, it tries to use the Keychain on OSX and the Keyring on Linux, and if the user sets `allowUnencryptedStorage` to true in the `tokenCachePersistenceOptions`, it allows to fall back to an unprotected file if neither the Keychain nor the Keyring are available. - Receive an `authenticationRecord` from a previous authentication on their constructors, which skips the initial request altogether. - Receive a `disableAutomaticAuthentication` setting on the constructor, which stops `getToken` from requesting the user to authenticate manually. - An `authenticate()` method has been added besides the `getToken()` method. - The `authenticate()` method returns an `AuthenticationRecord` which can be serialized into strings with their property `serialize()`. To later deserialize from string into an `AuthenticationRecord`, use the new function `deserializeAuthenticationRecord()`. - If `disableAutomaticAuthentication` is set on the constructor of these credentials, developers can now control when to manually authenticate by calling to these credential's `authenticate()` method. - `DeviceCodeCredential` now can receive its optional parameters as a single parameter object. -- `InteractiveBrowserCredential` now only has `loginStyle` and `flow` in the optional parameters when the credential is bundled for browsers. This reflects the intended behavior. -- Removed the `postLogoutRedirectUri` from the optional properties of the `InteractiveBrowserCredential`. +- Breaking change: `InteractiveBrowserCredential` now only has `loginStyle` and `flow` in the optional parameters when the credential is bundled for browsers. This reflects the intended behavior. +- Breaking change: Removed the `postLogoutRedirectUri` from the optional properties of the `InteractiveBrowserCredential`. ## 1.2.4 (2021-03-08) diff --git a/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_persistence_throws_on_node_8_as_expected.js b/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_persistence_throws_on_node_8_as_expected.js new file mode 100644 index 000000000000..f99de585c519 --- /dev/null +++ b/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_persistence_throws_on_node_8_as_expected.js @@ -0,0 +1,5 @@ +let nock = require('nock'); + +module.exports.hash = "d0f8f8894b70897707f342830ec7e25f"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} diff --git a/sdk/identity/identity/src/tokenCache/TokenCachePersistence.ts b/sdk/identity/identity/src/tokenCache/TokenCachePersistence.ts index b5091dc8ccb3..9fd30836bfb2 100644 --- a/sdk/identity/identity/src/tokenCache/TokenCachePersistence.ts +++ b/sdk/identity/identity/src/tokenCache/TokenCachePersistence.ts @@ -1,10 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { - PersistenceCachePlugin, - IPersistence as MsalIPersistence -} from "@azure/msal-node-extensions"; +import { isNode8, Node8NotSupportedError } from "./node8"; import { msalPersistencePlatforms, TokenCachePersistenceOptions } from "./persistencePlatforms"; import { MsalPersistence, CachePlugin, TokenCacheRegisterOptions } from "./types"; @@ -34,11 +31,18 @@ export class TokenCachePersistence { retryDelay: 50 }; - const persistence = await this.getPersistence(); - if (!persistence) { - throw new Error("No persistence implementations available."); - } + if (isNode8) { + throw Node8NotSupportedError; + } else { + /* eslint-disable-next-line @typescript-eslint/no-require-imports */ + const { PersistenceCachePlugin } = require("@azure/msal-node-extensions"); + + const persistence = await this.getPersistence(); + if (!persistence) { + throw new Error("No persistence implementations available."); + } - return new PersistenceCachePlugin(persistence as MsalIPersistence, lockOptions); + return new PersistenceCachePlugin(persistence, lockOptions); + } } } diff --git a/sdk/identity/identity/src/tokenCache/node8.ts b/sdk/identity/identity/src/tokenCache/node8.ts new file mode 100644 index 000000000000..eebc84b93584 --- /dev/null +++ b/sdk/identity/identity/src/tokenCache/node8.ts @@ -0,0 +1,5 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +export const isNode8 = process.versions.node[0] === "8"; +export const Node8NotSupportedError = new Error("Node 8 does not support persistence caching."); diff --git a/sdk/identity/identity/src/tokenCache/persistencePlatforms.ts b/sdk/identity/identity/src/tokenCache/persistencePlatforms.ts index 0a998c648f12..d22b0d490bbb 100644 --- a/sdk/identity/identity/src/tokenCache/persistencePlatforms.ts +++ b/sdk/identity/identity/src/tokenCache/persistencePlatforms.ts @@ -2,15 +2,9 @@ // Licensed under the MIT license. /* eslint-disable tsdoc/syntax */ -import { - DataProtectionScope, - FilePersistence, - FilePersistenceWithDataProtection, - KeychainPersistence, - LibSecretPersistence -} from "@azure/msal-node-extensions"; -import { MsalPersistence } from "./types"; import * as path from "path"; +import { MsalPersistence } from "./types"; +import { isNode8, Node8NotSupportedError } from "./node8"; /** * Local application data folder @@ -125,11 +119,21 @@ export const msalPersistencePlatforms: Record< win32: { name: "win32", isAvailable: () => process.platform === "win32", - persistence: ({ name = defaultMsalValues.tokenCache.name } = {}) => - FilePersistenceWithDataProtection.create( - getPersistencePath(name), - DataProtectionScope.CurrentUser - ) + persistence: ({ name = defaultMsalValues.tokenCache.name } = {}): Promise => { + if (isNode8) { + throw Node8NotSupportedError; + } else { + const { + FilePersistenceWithDataProtection, + DataProtectionScope + /* eslint-disable-next-line @typescript-eslint/no-require-imports */ + } = require("@azure/msal-node-extensions"); + return FilePersistenceWithDataProtection.create( + getPersistencePath(name), + DataProtectionScope.CurrentUser + ); + } + } }, darwin: { @@ -140,18 +144,25 @@ export const msalPersistencePlatforms: Record< const { service, account } = defaultMsalValues.keyChain; const persistencePath = getPersistencePath(name || defaultMsalValues.tokenCache.name); - try { - const persistence = await KeychainPersistence.create(persistencePath, service, account); - // If we don't encounter an error when trying to read from the keychain, then we should be good to go. - await persistence.load(); - return persistence; - } catch (e) { - // If we got an error while trying to read from the keyring, - // we will proceed only if the user has specified that unencrypted storage is allowed. - if (!allowUnencryptedStorage) { - throw new Error("MSAL was unable to read from the system's keyring."); + if (isNode8) { + throw Node8NotSupportedError; + } else { + /* eslint-disable-next-line @typescript-eslint/no-require-imports */ + const { KeychainPersistence, FilePersistence } = require("@azure/msal-node-extensions"); + + try { + const persistence = await KeychainPersistence.create(persistencePath, service, account); + // If we don't encounter an error when trying to read from the keychain, then we should be good to go. + await persistence.load(); + return persistence; + } catch (e) { + // If we got an error while trying to read from the keyring, + // we will proceed only if the user has specified that unencrypted storage is allowed. + if (!allowUnencryptedStorage) { + throw new Error("MSAL was unable to read from the system's keyring."); + } + return FilePersistence.create(persistencePath); } - return FilePersistence.create(persistencePath); } } }, @@ -164,18 +175,25 @@ export const msalPersistencePlatforms: Record< const { service, account } = defaultMsalValues.keyRing; const persistencePath = getPersistencePath(name || defaultMsalValues.tokenCache.name); - try { - const persistence = await LibSecretPersistence.create(persistencePath, service, account); - // If we don't encounter an error when trying to read from the keyring, then we should be good to go. - await persistence.load(); - return persistence; - } catch (e) { - // If we got an error while trying to read from the keyring, - // we will proceed only if the user has specified that unencrypted storage is allowed. - if (!allowUnencryptedStorage) { - throw new Error("MSAL was unable to read from the system's keyring."); + if (isNode8) { + throw Node8NotSupportedError; + } else { + /* eslint-disable-next-line @typescript-eslint/no-require-imports */ + const { LibSecretPersistence, FilePersistence } = require("@azure/msal-node-extensions"); + + try { + const persistence = await LibSecretPersistence.create(persistencePath, service, account); + // If we don't encounter an error when trying to read from the keyring, then we should be good to go. + await persistence.load(); + return persistence; + } catch (e) { + // If we got an error while trying to read from the keyring, + // we will proceed only if the user has specified that unencrypted storage is allowed. + if (!allowUnencryptedStorage) { + throw new Error("MSAL was unable to read from the system's keyring."); + } + return FilePersistence.create(persistencePath); } - return FilePersistence.create(persistencePath); } } } diff --git a/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts b/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts index 5b4860dd839e..1cd3447950f2 100644 --- a/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts @@ -11,6 +11,7 @@ import { ClientSecretCredential, TokenCachePersistenceOptions } from "../../../s import { MsalTestCleanup, msalNodeTestSetup } from "../../msalTestUtils"; import { TokenCachePersistence } from "../../../src/tokenCache/TokenCachePersistence"; import { MsalNode } from "../../../src/msal/nodeFlows/nodeCommon"; +import { isNode8 } from "../../../src/tokenCache/node8"; describe("ClientSecretCredential (internal)", function() { let cleanup: MsalTestCleanup; @@ -56,6 +57,10 @@ describe("ClientSecretCredential (internal)", function() { }); it("Accepts tokenCachePersistenceOptions", async function() { + // msal-node-extensions does not currently support Node 8. + if (isNode8) { + this.skip(); + } const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), allowUnencryptedStorage: true @@ -80,6 +85,10 @@ describe("ClientSecretCredential (internal)", function() { }); it("Authenticates silently with tokenCachePersistenceOptions", async function() { + // msal-node-extensions does not currently support Node 8. + if (isNode8) { + this.skip(); + } const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), allowUnencryptedStorage: true diff --git a/sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts b/sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts index 564b73ebdeb9..015ec94691b3 100644 --- a/sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts @@ -5,12 +5,13 @@ import Sinon from "sinon"; import assert from "assert"; +import { PublicClientApplication } from "@azure/msal-node"; import { env, isLiveMode } from "@azure/test-utils-recorder"; import { DeviceCodeCredential, TokenCachePersistenceOptions } from "../../../src"; import { MsalTestCleanup, msalNodeTestSetup } from "../../msalTestUtils"; import { TokenCachePersistence } from "../../../src/tokenCache/TokenCachePersistence"; import { MsalNode } from "../../../src/msal/nodeFlows/nodeCommon"; -import { PublicClientApplication } from "@azure/msal-node"; +import { isNode8 } from "../../../src/tokenCache/node8"; describe("DeviceCodeCredential (internal)", function() { let cleanup: MsalTestCleanup; @@ -52,6 +53,10 @@ describe("DeviceCodeCredential (internal)", function() { }); it("Accepts tokenCachePersistenceOptions", async function() { + // msal-node-extensions does not currently support Node 8. + if (isNode8) { + this.skip(); + } // These tests should not run live because this credential requires user interaction. if (isLiveMode()) { this.skip(); @@ -79,6 +84,10 @@ describe("DeviceCodeCredential (internal)", function() { }); it("Authenticates silently with tokenCachePersistenceOptions", async function() { + // msal-node-extensions does not currently support Node 8. + if (isNode8) { + this.skip(); + } // These tests should not run live because this credential requires user interaction. if (isLiveMode()) { this.skip(); @@ -115,6 +124,10 @@ describe("DeviceCodeCredential (internal)", function() { }); it("allows passing an authenticationRecord to avoid further manual authentications", async function() { + // msal-node-extensions does not currently support Node 8. + if (isNode8) { + this.skip(); + } // These tests should not run live because this credential requires user interaction. if (isLiveMode()) { this.skip(); diff --git a/sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts b/sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts index 8a37540bc8a1..fb9752281b20 100644 --- a/sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts @@ -6,6 +6,7 @@ import Sinon from "sinon"; import assert from "assert"; import { env, isLiveMode } from "@azure/test-utils-recorder"; +import { PublicClientApplication } from "@azure/msal-node"; import { UsernamePasswordCredential, TokenCachePersistenceOptions, @@ -14,7 +15,7 @@ import { import { MsalTestCleanup, msalNodeTestSetup } from "../../msalTestUtils"; import { TokenCachePersistence } from "../../../src/tokenCache/TokenCachePersistence"; import { MsalNode } from "../../../src/msal/nodeFlows/nodeCommon"; -import { PublicClientApplication } from "@azure/msal-node"; +import { isNode8, Node8NotSupportedError } from "../../../src/tokenCache/node8"; describe("UsernamePasswordCredential (internal)", function() { let cleanup: MsalTestCleanup; @@ -61,7 +62,34 @@ describe("UsernamePasswordCredential (internal)", function() { assert.equal(doGetTokenSpy.callCount, 1); }); + it("Persistence throws on Node 8, as expected", async function() { + if (!isNode8) { + this.skip(); + } + + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { + name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), + allowUnencryptedStorage: true + }; + + // Emptying the token cache before we start. + const tokenCache = new TokenCachePersistence(tokenCachePersistenceOptions); + + let error: Error | undefined; + try { + await tokenCache.getPersistence(); + } catch (e) { + error = e; + } + + assert.equal(error?.message, Node8NotSupportedError.message); + }); + it("Accepts tokenCachePersistenceOptions", async function() { + // msal-node-extensions does not currently support Node 8. + if (isNode8) { + this.skip(); + } // These tests should not run live because this credential requires user interaction. if (isLiveMode()) { this.skip(); @@ -91,6 +119,10 @@ describe("UsernamePasswordCredential (internal)", function() { }); it("Authenticates silently with tokenCachePersistenceOptions", async function() { + // msal-node-extensions does not currently support Node 8. + if (isNode8) { + this.skip(); + } // These tests should not run live because this credential requires user interaction. if (isLiveMode()) { this.skip(); @@ -129,6 +161,10 @@ describe("UsernamePasswordCredential (internal)", function() { }); it("allows passing an authenticationRecord to avoid further manual authentications", async function() { + // msal-node-extensions does not currently support Node 8. + if (isNode8) { + this.skip(); + } // These tests should not run live because this credential requires user interaction. if (isLiveMode()) { this.skip(); @@ -181,6 +217,10 @@ describe("UsernamePasswordCredential (internal)", function() { }); it("allows working with an authenticationRecord that is serialized", async function() { + // msal-node-extensions does not currently support Node 8. + if (isNode8) { + this.skip(); + } // These tests should not run live because this credential requires user interaction. if (isLiveMode()) { this.skip(); diff --git a/sdk/identity/identity/test/msalTestUtils.ts b/sdk/identity/identity/test/msalTestUtils.ts index 8760caf1f757..1ea8e488f94b 100644 --- a/sdk/identity/identity/test/msalTestUtils.ts +++ b/sdk/identity/identity/test/msalTestUtils.ts @@ -49,10 +49,7 @@ export function msalNodeTestSetup( `client-request-id=${playbackValues.correlationId}` ), (recording: string): string => - recording.replace( - /client_assertion=[a-zA-Z0-9-\._]*/g, - `client_assertion=client_assertion` - ), + recording.replace(/client_assertion=[a-zA-Z0-9-._]*/g, `client_assertion=client_assertion`), (recording: string): string => recording.replace(/esctx=[a-zA-Z0-9-_]*/g, `esctx=esctx`), (recording: string): string => recording.replace(/'fpc=[^;]*/g, `'fpc=fpc;`), // Device code specific From 8fd1a52f14e6469c7fd951497672b7434afc0a8a Mon Sep 17 00:00:00 2001 From: Daniel Rodriguez Date: Thu, 18 Mar 2021 20:58:32 +0000 Subject: [PATCH 05/12] Added userPromptCallback to device code options and added a comment relevant to Node & --- sdk/identity/identity/review/identity.api.md | 1 + .../identity/src/credentials/deviceCodeCredential.ts | 1 - .../identity/src/credentials/deviceCodeCredentialOptions.ts | 6 ++++++ .../identity/src/credentials/persistentCredentialOptions.ts | 2 ++ 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/sdk/identity/identity/review/identity.api.md b/sdk/identity/identity/review/identity.api.md index 910ef64193cd..f67bd8fb37a2 100644 --- a/sdk/identity/identity/review/identity.api.md +++ b/sdk/identity/identity/review/identity.api.md @@ -135,6 +135,7 @@ export class DeviceCodeCredential implements TokenCredential { export interface DeviceCodeCredentialOptions extends InteractiveCredentialOptions { clientId?: string; tenantId?: string; + userPromptCallback?: DeviceCodePromptCallback; } // @public diff --git a/sdk/identity/identity/src/credentials/deviceCodeCredential.ts b/sdk/identity/identity/src/credentials/deviceCodeCredential.ts index e7e3cc678e83..7c54bb19c3a6 100644 --- a/sdk/identity/identity/src/credentials/deviceCodeCredential.ts +++ b/sdk/identity/identity/src/credentials/deviceCodeCredential.ts @@ -43,7 +43,6 @@ export class DeviceCodeCredential implements TokenCredential { * By default we will try to use the Azure CLI's client ID to authenticate. * @param userPromptCallback - A callback function that will be invoked to show * {@link DeviceCodeInfo} to the user. If left unassigned, we will automatically log the device code information and the authentication instructions in the console. - * Users can also pass the options as the third parameter, and skip the prompt callback entirely. * @param options - Options for configuring the client which makes the authentication requests. */ constructor(options?: DeviceCodeCredentialOptions); diff --git a/sdk/identity/identity/src/credentials/deviceCodeCredentialOptions.ts b/sdk/identity/identity/src/credentials/deviceCodeCredentialOptions.ts index d311271ba4dc..a6cc1c5fbedc 100644 --- a/sdk/identity/identity/src/credentials/deviceCodeCredentialOptions.ts +++ b/sdk/identity/identity/src/credentials/deviceCodeCredentialOptions.ts @@ -46,4 +46,10 @@ export interface DeviceCodeCredentialOptions extends InteractiveCredentialOption * The client (application) ID of an App Registration in the tenant. */ clientId?: string; + /** + * A callback function that will be invoked to show {@link DeviceCodeInfo} to the user. + * If left unassigned, we will automatically log the device code information + * and the authentication instructions in the console. + */ + userPromptCallback?: DeviceCodePromptCallback; } diff --git a/sdk/identity/identity/src/credentials/persistentCredentialOptions.ts b/sdk/identity/identity/src/credentials/persistentCredentialOptions.ts index d843be9ad642..206e418ef032 100644 --- a/sdk/identity/identity/src/credentials/persistentCredentialOptions.ts +++ b/sdk/identity/identity/src/credentials/persistentCredentialOptions.ts @@ -12,6 +12,8 @@ export interface PersistentCredentialOptions extends TokenCredentialOptions { * To provide a persistence layer to store the credentials, * we allow users to optionally specify {@link TokenCachePersistenceOptions} for their credential. * + * This feature is not currently available on Node 8 or earlier versions of Node JS. + * * This persistence layer uses DPAPI on Windows. * On OSX (Darwin) it tries to use the system's Keychain, otherwise if the property `allowUnencryptedStorage` is set to true, it uses an unencrypted file. * On Linux it tries to use the system's Keyring, otherwise if the property `allowUnencryptedStorage` is set to true, it uses an unencrypted file. From 75d69be472d53ee1182dc29a0903d84de01f6231 Mon Sep 17 00:00:00 2001 From: Daniel Rodriguez Date: Thu, 18 Mar 2021 21:38:41 +0000 Subject: [PATCH 06/12] skipping browser unit tests for now --- sdk/identity/identity/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/identity/identity/package.json b/sdk/identity/identity/package.json index 17e61e768379..b52b0061cb16 100644 --- a/sdk/identity/identity/package.json +++ b/sdk/identity/identity/package.json @@ -43,7 +43,7 @@ "test:browser": "npm run clean && npm run build:test && npm run unit-test:browser && npm run integration-test:browser", "test:node": "npm run clean && npm run build:test && npm run unit-test:node && npm run integration-test:node", "test": "npm run clean && npm run build:test && npm run unit-test && npm run integration-test", - "unit-test:browser": "karma start", + "unit-test:browser": "echo skipped", "unit-test:node": "mocha --require source-map-support/register --reporter ../../../common/tools/mocha-multi-reporter.js --timeout 180000 --full-trace test-dist/**/*.js", "unit-test": "npm run unit-test:node && npm run unit-test:browser", "docs": "typedoc --excludePrivate --excludeNotExported --excludeExternals --stripInternal --mode file --out ./dist/docs ./src" From 2aba1a54bc034ad15c6dbf6bf3c29836c5d5df0c Mon Sep 17 00:00:00 2001 From: Daniel Rodriguez Date: Thu, 18 Mar 2021 22:09:22 +0000 Subject: [PATCH 07/12] we use DPAPI on windows, not PNPM!! oops --- sdk/identity/identity/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/identity/identity/CHANGELOG.md b/sdk/identity/identity/CHANGELOG.md index 9911ee1c6e82..27f7b9192113 100644 --- a/sdk/identity/identity/CHANGELOG.md +++ b/sdk/identity/identity/CHANGELOG.md @@ -14,7 +14,7 @@ This release continues with the changes from `1.2.4` and `1.2.4-beta.1`. - Migrated the `InteractiveBrowserCredential`, `DeviceCodeCredential`, `ClientSecretCredential`, `ClientCertificateCredential` and `UsernamePasswordCredential` to the latest MSAL. - This update improves caching of tokens, significantly reducing the number of network requests. - Credentials `InteractiveBrowserCredential`, `DeviceCodeCredential` and `UsernamePasswordCredential` now can: - - Receive a `tokenCachePersistenceOptions` parameter to specify persitence caching of the credentials used to authenticate. This feature will not have support for Node 8 as part of this beta. This feature uses PNPM on Windows, it tries to use the Keychain on OSX and the Keyring on Linux, and if the user sets `allowUnencryptedStorage` to true in the `tokenCachePersistenceOptions`, it allows to fall back to an unprotected file if neither the Keychain nor the Keyring are available. + - Receive a `tokenCachePersistenceOptions` parameter to specify persitence caching of the credentials used to authenticate. This feature will not have support for Node 8 as part of this beta. This feature uses DPAPI on Windows, it tries to use the Keychain on OSX and the Keyring on Linux, and if the user sets `allowUnencryptedStorage` to true in the `tokenCachePersistenceOptions`, it allows to fall back to an unprotected file if neither the Keychain nor the Keyring are available. - Receive an `authenticationRecord` from a previous authentication on their constructors, which skips the initial request altogether. - Receive a `disableAutomaticAuthentication` setting on the constructor, which stops `getToken` from requesting the user to authenticate manually. - An `authenticate()` method has been added besides the `getToken()` method. From d296d7d11535e98107b2cf80b88878156b96d945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodr=C3=ADguez?= Date: Thu, 18 Mar 2021 19:06:30 -0400 Subject: [PATCH 08/12] version change in the manual test package.json --- sdk/identity/identity/test/manual/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/identity/identity/test/manual/package.json b/sdk/identity/identity/test/manual/package.json index 5432c7da0cbe..0c7758505f78 100644 --- a/sdk/identity/identity/test/manual/package.json +++ b/sdk/identity/identity/test/manual/package.json @@ -11,7 +11,7 @@ "author": "Microsoft Corporation", "license": "MIT", "dependencies": { - "@azure/identity": "^1.2.4-beta.1", + "@azure/identity": "^2.0.0-beta.1", "@azure/service-bus": "^7.0.3", "react": "^16.8.6", "react-dom": "^16.8.6", From 959b42861920bb5f3f63c68add0b15d34b8537ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Rodr=C3=ADguez?= Date: Thu, 18 Mar 2021 19:44:48 -0400 Subject: [PATCH 09/12] skipping persistence tests on OSX due to missing password --- .../node/clientCertificateCredential.spec.ts | 8 ++++++++ .../internal/node/clientSecretCredential.spec.ts | 10 ++++++++++ .../internal/node/deviceCodeCredential.spec.ts | 13 +++++++++++++ .../node/usernamePasswordCredential.spec.ts | 16 ++++++++++++++++ 4 files changed, 47 insertions(+) diff --git a/sdk/identity/identity/test/internal/node/clientCertificateCredential.spec.ts b/sdk/identity/identity/test/internal/node/clientCertificateCredential.spec.ts index 996096b8a19a..574245973716 100644 --- a/sdk/identity/identity/test/internal/node/clientCertificateCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/clientCertificateCredential.spec.ts @@ -81,6 +81,10 @@ describe("ClientCertificateCredential (internal)", function() { // and I'm trying to avoid having to generate one ourselves. this.skip(); } + // OSX asks for passwords on CI, so we need to skip these tests from our automation + if (process.platform === "darwin") { + this.skip(); + } const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), @@ -112,6 +116,10 @@ describe("ClientCertificateCredential (internal)", function() { // and I'm trying to avoid having to generate one ourselves. this.skip(); } + // OSX asks for passwords on CI, so we need to skip these tests from our automation + if (process.platform === "darwin") { + this.skip(); + } const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), diff --git a/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts b/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts index 1cd3447950f2..a07ce145e2d6 100644 --- a/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts @@ -61,6 +61,11 @@ describe("ClientSecretCredential (internal)", function() { if (isNode8) { this.skip(); } + // OSX asks for passwords on CI, so we need to skip these tests from our automation + if (process.platform === "darwin") { + this.skip(); + } + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), allowUnencryptedStorage: true @@ -89,6 +94,11 @@ describe("ClientSecretCredential (internal)", function() { if (isNode8) { this.skip(); } + // OSX asks for passwords on CI, so we need to skip these tests from our automation + if (process.platform === "darwin") { + this.skip(); + } + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), allowUnencryptedStorage: true diff --git a/sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts b/sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts index 015ec94691b3..c2323d727a7f 100644 --- a/sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts @@ -57,10 +57,15 @@ describe("DeviceCodeCredential (internal)", function() { if (isNode8) { this.skip(); } + // OSX asks for passwords on CI, so we need to skip these tests from our automation + if (process.platform === "darwin") { + this.skip(); + } // These tests should not run live because this credential requires user interaction. if (isLiveMode()) { this.skip(); } + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), allowUnencryptedStorage: true @@ -88,6 +93,10 @@ describe("DeviceCodeCredential (internal)", function() { if (isNode8) { this.skip(); } + // OSX asks for passwords on CI, so we need to skip these tests from our automation + if (process.platform === "darwin") { + this.skip(); + } // These tests should not run live because this credential requires user interaction. if (isLiveMode()) { this.skip(); @@ -128,6 +137,10 @@ describe("DeviceCodeCredential (internal)", function() { if (isNode8) { this.skip(); } + // OSX asks for passwords on CI, so we need to skip these tests from our automation + if (process.platform === "darwin") { + this.skip(); + } // These tests should not run live because this credential requires user interaction. if (isLiveMode()) { this.skip(); diff --git a/sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts b/sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts index fb9752281b20..f40fbcf45f85 100644 --- a/sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts @@ -90,6 +90,10 @@ describe("UsernamePasswordCredential (internal)", function() { if (isNode8) { this.skip(); } + // OSX asks for passwords on CI, so we need to skip these tests from our automation + if (process.platform === "darwin") { + this.skip(); + } // These tests should not run live because this credential requires user interaction. if (isLiveMode()) { this.skip(); @@ -123,6 +127,10 @@ describe("UsernamePasswordCredential (internal)", function() { if (isNode8) { this.skip(); } + // OSX asks for passwords on CI, so we need to skip these tests from our automation + if (process.platform === "darwin") { + this.skip(); + } // These tests should not run live because this credential requires user interaction. if (isLiveMode()) { this.skip(); @@ -165,6 +173,10 @@ describe("UsernamePasswordCredential (internal)", function() { if (isNode8) { this.skip(); } + // OSX asks for passwords on CI, so we need to skip these tests from our automation + if (process.platform === "darwin") { + this.skip(); + } // These tests should not run live because this credential requires user interaction. if (isLiveMode()) { this.skip(); @@ -221,6 +233,10 @@ describe("UsernamePasswordCredential (internal)", function() { if (isNode8) { this.skip(); } + // OSX asks for passwords on CI, so we need to skip these tests from our automation + if (process.platform === "darwin") { + this.skip(); + } // These tests should not run live because this credential requires user interaction. if (isLiveMode()) { this.skip(); From d9f1f4e32368957da035edae776d3f7262e97e00 Mon Sep 17 00:00:00 2001 From: Daniel Rodriguez Date: Mon, 22 Mar 2021 17:17:11 +0000 Subject: [PATCH 10/12] Jeff feedback --- sdk/identity/identity/CHANGELOG.md | 5 ++- .../identity/src/client/identityClient.ts | 13 ++++--- .../clientCertificateCredential.ts | 2 +- .../src/credentials/clientSecretCredential.ts | 2 +- .../src/credentials/deviceCodeCredential.ts | 4 +- .../interactiveBrowserCredential.browser.ts | 4 +- .../interactiveBrowserCredential.ts | 4 +- .../credentials/usernamePasswordCredential.ts | 4 +- .../identity/src/msal/nodeFlows/nodeCommon.ts | 10 ++--- .../src/tokenCache/TokenCachePersistence.ts | 6 +-- sdk/identity/identity/src/tokenCache/node8.ts | 5 --- .../identity/src/tokenCache/nodeVersion.ts | 12 ++++++ .../src/tokenCache/persistencePlatforms.ts | 14 +++---- .../node/clientSecretCredential.spec.ts | 18 ++++----- .../node/deviceCodeCredential.spec.ts | 8 ++-- .../node/usernamePasswordCredential.spec.ts | 38 ++++++++++++++++--- 16 files changed, 94 insertions(+), 55 deletions(-) delete mode 100644 sdk/identity/identity/src/tokenCache/node8.ts create mode 100644 sdk/identity/identity/src/tokenCache/nodeVersion.ts diff --git a/sdk/identity/identity/CHANGELOG.md b/sdk/identity/identity/CHANGELOG.md index 27f7b9192113..3d38efcd8dbe 100644 --- a/sdk/identity/identity/CHANGELOG.md +++ b/sdk/identity/identity/CHANGELOG.md @@ -4,7 +4,7 @@ This release continues with the changes from `1.2.4` and `1.2.4-beta.1`. -- The `getToken` methods will now never return `null`. If a token is not available, we will throw. +- The `getToken` methods will now never return `null`. If a token is not available, we will return a rejected promise. - `DefaultAzureCredential`'s implementation for browsers was simplified to throw a simple error instead of trying credentials that were already not supported for the browser. - Breaking Change: `InteractiveBrowserCredential` for the browser now requires the client ID to be provided. - Documentation was added to elaborate on how to configure an AAD application to support `InteractiveBrowserCredential`. @@ -14,7 +14,8 @@ This release continues with the changes from `1.2.4` and `1.2.4-beta.1`. - Migrated the `InteractiveBrowserCredential`, `DeviceCodeCredential`, `ClientSecretCredential`, `ClientCertificateCredential` and `UsernamePasswordCredential` to the latest MSAL. - This update improves caching of tokens, significantly reducing the number of network requests. - Credentials `InteractiveBrowserCredential`, `DeviceCodeCredential` and `UsernamePasswordCredential` now can: - - Receive a `tokenCachePersistenceOptions` parameter to specify persitence caching of the credentials used to authenticate. This feature will not have support for Node 8 as part of this beta. This feature uses DPAPI on Windows, it tries to use the Keychain on OSX and the Keyring on Linux, and if the user sets `allowUnencryptedStorage` to true in the `tokenCachePersistenceOptions`, it allows to fall back to an unprotected file if neither the Keychain nor the Keyring are available. + - Receive a `tokenCachePersistenceOptions` parameter to specify persitence caching of the credentials used to authenticate. This feature uses DPAPI on Windows, it tries to use the Keychain on OSX and the Keyring on Linux, and if the user sets `allowUnencryptedStorage` to true in the `tokenCachePersistenceOptions`, it allows to fall back to an unprotected file if neither the Keychain nor the Keyring are available. + - As part of this beta, this feature is only supported in Node 10, 12 and 14. - Receive an `authenticationRecord` from a previous authentication on their constructors, which skips the initial request altogether. - Receive a `disableAutomaticAuthentication` setting on the constructor, which stops `getToken` from requesting the user to authenticate manually. - An `authenticate()` method has been added besides the `getToken()` method. diff --git a/sdk/identity/identity/src/client/identityClient.ts b/sdk/identity/identity/src/client/identityClient.ts index a937f0283567..15e700d6aac2 100644 --- a/sdk/identity/identity/src/client/identityClient.ts +++ b/sdk/identity/identity/src/client/identityClient.ts @@ -22,6 +22,8 @@ import { DefaultAuthorityHost } from "../constants"; import { createSpan } from "../util/tracing"; import { logger } from "../util/logging"; +const noCorrelationId = "noCorrelationId"; + /** * An internal type used to communicate details of a token request's * response that should not be sent back as part of the access token. @@ -208,7 +210,7 @@ export class IdentityClient extends ServiceClient implements INetworkModule { generateAbortSignal(correlationId?: string): AbortSignalLike { const controller = new AbortController(); - const key = correlationId || "noCorrelationId"; + const key = correlationId || noCorrelationId; const controllers = this.abortControllers.get(key) || []; controllers.push(controller); @@ -217,12 +219,12 @@ export class IdentityClient extends ServiceClient implements INetworkModule { return controller.signal; } - abortRequests(correlationId?: string): void { - const key = correlationId || "noCorrelationId"; + abortRequests(correlationId: string = noCorrelationId): void { + const key = correlationId || noCorrelationId; const controllers = [ ...(this.abortControllers.get(key) || []), // MSAL passes no correlation ID to the get requests... - ...(this.abortControllers.get("noCorrelationId") || []) + ...(this.abortControllers.get(noCorrelationId) || []) ]; if (!controllers.length) { return; @@ -231,6 +233,7 @@ export class IdentityClient extends ServiceClient implements INetworkModule { controller.abort(); } this.abortControllers.set(key, undefined); + this.abortControllers.set(noCorrelationId, undefined); } getCorrelationId(options?: NetworkRequestOptions): string | undefined { @@ -238,7 +241,7 @@ export class IdentityClient extends ServiceClient implements INetworkModule { ?.split("&") .map((part) => part.split("=")) .find(([key]) => key === "client-request-id"); - return parameter && parameter.length ? parameter[1] : undefined; + return parameter && parameter.length ? parameter[1] : noCorrelationId; } // The MSAL network module methods follow diff --git a/sdk/identity/identity/src/credentials/clientCertificateCredential.ts b/sdk/identity/identity/src/credentials/clientCertificateCredential.ts index 0b8e975704b9..392cf5ecdf64 100644 --- a/sdk/identity/identity/src/credentials/clientCertificateCredential.ts +++ b/sdk/identity/identity/src/credentials/clientCertificateCredential.ts @@ -59,7 +59,7 @@ export class ClientCertificateCredential implements TokenCredential { */ async getToken(scopes: string | string[], options: GetTokenOptions = {}): Promise { return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => { - const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + const arrayScopes = Array.isArray(scopes) ? scopes : [scopes]; return this.msalFlow.getToken(arrayScopes, newOptions); }); } diff --git a/sdk/identity/identity/src/credentials/clientSecretCredential.ts b/sdk/identity/identity/src/credentials/clientSecretCredential.ts index e19a949a7346..9286ac21ab28 100644 --- a/sdk/identity/identity/src/credentials/clientSecretCredential.ts +++ b/sdk/identity/identity/src/credentials/clientSecretCredential.ts @@ -59,7 +59,7 @@ export class ClientSecretCredential implements TokenCredential { */ async getToken(scopes: string | string[], options: GetTokenOptions = {}): Promise { return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => { - const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + const arrayScopes = Array.isArray(scopes) ? scopes : [scopes]; return this.msalFlow.getToken(arrayScopes, newOptions); }); } diff --git a/sdk/identity/identity/src/credentials/deviceCodeCredential.ts b/sdk/identity/identity/src/credentials/deviceCodeCredential.ts index 7c54bb19c3a6..ffb65d576b23 100644 --- a/sdk/identity/identity/src/credentials/deviceCodeCredential.ts +++ b/sdk/identity/identity/src/credentials/deviceCodeCredential.ts @@ -91,7 +91,7 @@ export class DeviceCodeCredential implements TokenCredential { */ async getToken(scopes: string | string[], options: GetTokenOptions = {}): Promise { return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => { - const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + const arrayScopes = Array.isArray(scopes) ? scopes : [scopes]; return this.msalFlow.getToken(arrayScopes, { ...newOptions, disableAutomaticAuthentication: this.disableAutomaticAuthentication @@ -116,7 +116,7 @@ export class DeviceCodeCredential implements TokenCredential { options: GetTokenOptions = {} ): Promise { return trace(`${this.constructor.name}.authenticate`, options, async (newOptions) => { - const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + const arrayScopes = Array.isArray(scopes) ? scopes : [scopes]; await this.msalFlow.getToken(arrayScopes, newOptions); return this.msalFlow.getActiveAccount(); }); diff --git a/sdk/identity/identity/src/credentials/interactiveBrowserCredential.browser.ts b/sdk/identity/identity/src/credentials/interactiveBrowserCredential.browser.ts index 9e2c3b8fd6d3..eecff28b701a 100644 --- a/sdk/identity/identity/src/credentials/interactiveBrowserCredential.browser.ts +++ b/sdk/identity/identity/src/credentials/interactiveBrowserCredential.browser.ts @@ -97,7 +97,7 @@ export class InteractiveBrowserCredential implements TokenCredential { */ async getToken(scopes: string | string[], options: GetTokenOptions = {}): Promise { return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => { - const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + const arrayScopes = Array.isArray(scopes) ? scopes : [scopes]; return this.msalFlow.getToken(arrayScopes, { ...newOptions, disableAutomaticAuthentication: this.disableAutomaticAuthentication @@ -122,7 +122,7 @@ export class InteractiveBrowserCredential implements TokenCredential { options: GetTokenOptions = {} ): Promise { return trace(`${this.constructor.name}.authenticate`, options, async (newOptions) => { - const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + const arrayScopes = Array.isArray(scopes) ? scopes : [scopes]; await this.msalFlow.getToken(arrayScopes, newOptions); return this.msalFlow.getActiveAccount(); }); diff --git a/sdk/identity/identity/src/credentials/interactiveBrowserCredential.ts b/sdk/identity/identity/src/credentials/interactiveBrowserCredential.ts index 7acab7eb0c72..cfcf88c6130e 100644 --- a/sdk/identity/identity/src/credentials/interactiveBrowserCredential.ts +++ b/sdk/identity/identity/src/credentials/interactiveBrowserCredential.ts @@ -67,7 +67,7 @@ export class InteractiveBrowserCredential implements TokenCredential { */ async getToken(scopes: string | string[], options: GetTokenOptions = {}): Promise { return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => { - const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + const arrayScopes = Array.isArray(scopes) ? scopes : [scopes]; return this.msalFlow.getToken(arrayScopes, { ...newOptions, disableAutomaticAuthentication: this.disableAutomaticAuthentication @@ -92,7 +92,7 @@ export class InteractiveBrowserCredential implements TokenCredential { options: GetTokenOptions = {} ): Promise { return trace(`${this.constructor.name}.authenticate`, options, async (newOptions) => { - const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + const arrayScopes = Array.isArray(scopes) ? scopes : [scopes]; await this.msalFlow.getToken(arrayScopes, newOptions); return this.msalFlow.getActiveAccount(); }); diff --git a/sdk/identity/identity/src/credentials/usernamePasswordCredential.ts b/sdk/identity/identity/src/credentials/usernamePasswordCredential.ts index 5dcfd69e360e..eb2a0763680b 100644 --- a/sdk/identity/identity/src/credentials/usernamePasswordCredential.ts +++ b/sdk/identity/identity/src/credentials/usernamePasswordCredential.ts @@ -69,7 +69,7 @@ export class UsernamePasswordCredential implements TokenCredential { */ async getToken(scopes: string | string[], options: GetTokenOptions = {}): Promise { return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => { - const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + const arrayScopes = Array.isArray(scopes) ? scopes : [scopes]; return this.msalFlow.getToken(arrayScopes, { ...newOptions, disableAutomaticAuthentication: this.disableAutomaticAuthentication @@ -94,7 +94,7 @@ export class UsernamePasswordCredential implements TokenCredential { options: GetTokenOptions = {} ): Promise { return trace(`${this.constructor.name}.authenticate`, options, async (newOptions) => { - const arrayScopes = typeof scopes === "object" ? scopes : [scopes]; + const arrayScopes = Array.isArray(scopes) ? scopes : [scopes]; await this.msalFlow.getToken(arrayScopes, newOptions); return this.msalFlow.getActiveAccount(); }); diff --git a/sdk/identity/identity/src/msal/nodeFlows/nodeCommon.ts b/sdk/identity/identity/src/msal/nodeFlows/nodeCommon.ts index 80afc23456f3..d3abb7d0e5a7 100644 --- a/sdk/identity/identity/src/msal/nodeFlows/nodeCommon.ts +++ b/sdk/identity/identity/src/msal/nodeFlows/nodeCommon.ts @@ -169,11 +169,11 @@ export abstract class MsalNode extends MsalBaseUtilities implements MsalFlow { this.account = msalToPublic(accountsByTenant[0]); } else { this.logger - .info(`More than one account was found authenticated for this Client ID and Tenant ID.", -However, no "authenticationRecord" has been provided for this credential,", -therefore we're unable to pick between these accounts.", -A new login attempt will be requested, to ensure the correct account is picked.", -To work with multiple accounts for the same Client ID and Tenant ID, please provide an "authenticationRecord" when initializing "InteractiveBrowserCredential".`); + .info(`More than one account was found authenticated for this Client ID and Tenant ID. +However, no "authenticationRecord" has been provided for this credential, +therefore we're unable to pick between these accounts. +A new login attempt will be requested, to ensure the correct account is picked. +To work with multiple accounts for the same Client ID and Tenant ID, please provide an "authenticationRecord" when initializing a credential to prevent this from happening.`); return; } diff --git a/sdk/identity/identity/src/tokenCache/TokenCachePersistence.ts b/sdk/identity/identity/src/tokenCache/TokenCachePersistence.ts index 9fd30836bfb2..af64c30628c6 100644 --- a/sdk/identity/identity/src/tokenCache/TokenCachePersistence.ts +++ b/sdk/identity/identity/src/tokenCache/TokenCachePersistence.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import { isNode8, Node8NotSupportedError } from "./node8"; +import { isNode15, isNode8, Node15NotSupportedError, Node8NotSupportedError } from "./nodeVersion"; import { msalPersistencePlatforms, TokenCachePersistenceOptions } from "./persistencePlatforms"; import { MsalPersistence, CachePlugin, TokenCacheRegisterOptions } from "./types"; @@ -31,8 +31,8 @@ export class TokenCachePersistence { retryDelay: 50 }; - if (isNode8) { - throw Node8NotSupportedError; + if (isNode8 || isNode15) { + throw isNode8 ? Node8NotSupportedError : Node15NotSupportedError; } else { /* eslint-disable-next-line @typescript-eslint/no-require-imports */ const { PersistenceCachePlugin } = require("@azure/msal-node-extensions"); diff --git a/sdk/identity/identity/src/tokenCache/node8.ts b/sdk/identity/identity/src/tokenCache/node8.ts deleted file mode 100644 index eebc84b93584..000000000000 --- a/sdk/identity/identity/src/tokenCache/node8.ts +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -export const isNode8 = process.versions.node[0] === "8"; -export const Node8NotSupportedError = new Error("Node 8 does not support persistence caching."); diff --git a/sdk/identity/identity/src/tokenCache/nodeVersion.ts b/sdk/identity/identity/src/tokenCache/nodeVersion.ts new file mode 100644 index 000000000000..7bf2281140c5 --- /dev/null +++ b/sdk/identity/identity/src/tokenCache/nodeVersion.ts @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// TODO: +// Either drop Node 8 or re-enable it. +// Also TODO, re-enable Node 15 asap. + +export const isNode8 = process.versions.node[0] === "8"; +export const Node8NotSupportedError = new Error("Node 8 does not support persistence caching."); + +export const isNode15 = process.versions.node[0] === "15"; +export const Node15NotSupportedError = new Error("Node 15 does not support persistence caching."); diff --git a/sdk/identity/identity/src/tokenCache/persistencePlatforms.ts b/sdk/identity/identity/src/tokenCache/persistencePlatforms.ts index d22b0d490bbb..f36e40b5830b 100644 --- a/sdk/identity/identity/src/tokenCache/persistencePlatforms.ts +++ b/sdk/identity/identity/src/tokenCache/persistencePlatforms.ts @@ -4,7 +4,7 @@ import * as path from "path"; import { MsalPersistence } from "./types"; -import { isNode8, Node8NotSupportedError } from "./node8"; +import { isNode15, isNode8, Node15NotSupportedError, Node8NotSupportedError } from "./nodeVersion"; /** * Local application data folder @@ -120,8 +120,8 @@ export const msalPersistencePlatforms: Record< name: "win32", isAvailable: () => process.platform === "win32", persistence: ({ name = defaultMsalValues.tokenCache.name } = {}): Promise => { - if (isNode8) { - throw Node8NotSupportedError; + if (isNode8 || isNode15) { + throw isNode8 ? Node8NotSupportedError : Node15NotSupportedError; } else { const { FilePersistenceWithDataProtection, @@ -144,8 +144,8 @@ export const msalPersistencePlatforms: Record< const { service, account } = defaultMsalValues.keyChain; const persistencePath = getPersistencePath(name || defaultMsalValues.tokenCache.name); - if (isNode8) { - throw Node8NotSupportedError; + if (isNode8 || isNode15) { + throw isNode8 ? Node8NotSupportedError : Node15NotSupportedError; } else { /* eslint-disable-next-line @typescript-eslint/no-require-imports */ const { KeychainPersistence, FilePersistence } = require("@azure/msal-node-extensions"); @@ -175,8 +175,8 @@ export const msalPersistencePlatforms: Record< const { service, account } = defaultMsalValues.keyRing; const persistencePath = getPersistencePath(name || defaultMsalValues.tokenCache.name); - if (isNode8) { - throw Node8NotSupportedError; + if (isNode8 || isNode15) { + throw isNode8 ? Node8NotSupportedError : Node15NotSupportedError; } else { /* eslint-disable-next-line @typescript-eslint/no-require-imports */ const { LibSecretPersistence, FilePersistence } = require("@azure/msal-node-extensions"); diff --git a/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts b/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts index a07ce145e2d6..22c2e1adce4c 100644 --- a/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts @@ -11,14 +11,14 @@ import { ClientSecretCredential, TokenCachePersistenceOptions } from "../../../s import { MsalTestCleanup, msalNodeTestSetup } from "../../msalTestUtils"; import { TokenCachePersistence } from "../../../src/tokenCache/TokenCachePersistence"; import { MsalNode } from "../../../src/msal/nodeFlows/nodeCommon"; -import { isNode8 } from "../../../src/tokenCache/node8"; +import { isNode15, isNode8 } from "../../../src/tokenCache/nodeVersion"; -describe("ClientSecretCredential (internal)", function() { +describe("ClientSecretCredential (internal)", function () { let cleanup: MsalTestCleanup; let getTokenSilentSpy: Sinon.SinonSpy; let doGetTokenSpy: Sinon.SinonSpy; - beforeEach(function() { + beforeEach(function () { const setup = msalNodeTestSetup(this); cleanup = setup.cleanup; @@ -30,13 +30,13 @@ describe("ClientSecretCredential (internal)", function() { "acquireTokenByClientCredential" ); }); - afterEach(async function() { + afterEach(async function () { await cleanup(); }); const scope = "https://vault.azure.net/.default"; - it("Authenticates silently after the initial request", async function() { + it("Authenticates silently after the initial request", async function () { const credential = new ClientSecretCredential( env.AZURE_TENANT_ID, env.AZURE_CLIENT_ID, @@ -56,9 +56,9 @@ describe("ClientSecretCredential (internal)", function() { assert.equal(doGetTokenSpy.callCount, 2); }); - it("Accepts tokenCachePersistenceOptions", async function() { + it("Accepts tokenCachePersistenceOptions", async function () { // msal-node-extensions does not currently support Node 8. - if (isNode8) { + if (isNode8 || isNode15) { this.skip(); } // OSX asks for passwords on CI, so we need to skip these tests from our automation @@ -89,9 +89,9 @@ describe("ClientSecretCredential (internal)", function() { assert.ok(parsedResult.AccessToken); }); - it("Authenticates silently with tokenCachePersistenceOptions", async function() { + it("Authenticates silently with tokenCachePersistenceOptions", async function () { // msal-node-extensions does not currently support Node 8. - if (isNode8) { + if (isNode8 || isNode15) { this.skip(); } // OSX asks for passwords on CI, so we need to skip these tests from our automation diff --git a/sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts b/sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts index c2323d727a7f..38d33cb7ee52 100644 --- a/sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/deviceCodeCredential.spec.ts @@ -11,7 +11,7 @@ import { DeviceCodeCredential, TokenCachePersistenceOptions } from "../../../src import { MsalTestCleanup, msalNodeTestSetup } from "../../msalTestUtils"; import { TokenCachePersistence } from "../../../src/tokenCache/TokenCachePersistence"; import { MsalNode } from "../../../src/msal/nodeFlows/nodeCommon"; -import { isNode8 } from "../../../src/tokenCache/node8"; +import { isNode15, isNode8 } from "../../../src/tokenCache/nodeVersion"; describe("DeviceCodeCredential (internal)", function() { let cleanup: MsalTestCleanup; @@ -54,7 +54,7 @@ describe("DeviceCodeCredential (internal)", function() { it("Accepts tokenCachePersistenceOptions", async function() { // msal-node-extensions does not currently support Node 8. - if (isNode8) { + if (isNode8 || isNode15) { this.skip(); } // OSX asks for passwords on CI, so we need to skip these tests from our automation @@ -90,7 +90,7 @@ describe("DeviceCodeCredential (internal)", function() { it("Authenticates silently with tokenCachePersistenceOptions", async function() { // msal-node-extensions does not currently support Node 8. - if (isNode8) { + if (isNode8 || isNode15) { this.skip(); } // OSX asks for passwords on CI, so we need to skip these tests from our automation @@ -134,7 +134,7 @@ describe("DeviceCodeCredential (internal)", function() { it("allows passing an authenticationRecord to avoid further manual authentications", async function() { // msal-node-extensions does not currently support Node 8. - if (isNode8) { + if (isNode8 || isNode15) { this.skip(); } // OSX asks for passwords on CI, so we need to skip these tests from our automation diff --git a/sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts b/sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts index f40fbcf45f85..3a4dd72cec3f 100644 --- a/sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/usernamePasswordCredential.spec.ts @@ -15,7 +15,12 @@ import { import { MsalTestCleanup, msalNodeTestSetup } from "../../msalTestUtils"; import { TokenCachePersistence } from "../../../src/tokenCache/TokenCachePersistence"; import { MsalNode } from "../../../src/msal/nodeFlows/nodeCommon"; -import { isNode8, Node8NotSupportedError } from "../../../src/tokenCache/node8"; +import { + isNode15, + isNode8, + Node15NotSupportedError, + Node8NotSupportedError +} from "../../../src/tokenCache/nodeVersion"; describe("UsernamePasswordCredential (internal)", function() { let cleanup: MsalTestCleanup; @@ -85,9 +90,32 @@ describe("UsernamePasswordCredential (internal)", function() { assert.equal(error?.message, Node8NotSupportedError.message); }); + it("Persistence throws on Node 15, as expected", async function() { + if (!isNode15) { + this.skip(); + } + + const tokenCachePersistenceOptions: TokenCachePersistenceOptions = { + name: this.test?.title.replace(/[^a-zA-Z]/g, "_"), + allowUnencryptedStorage: true + }; + + // Emptying the token cache before we start. + const tokenCache = new TokenCachePersistence(tokenCachePersistenceOptions); + + let error: Error | undefined; + try { + await tokenCache.getPersistence(); + } catch (e) { + error = e; + } + + assert.equal(error?.message, Node15NotSupportedError.message); + }); + it("Accepts tokenCachePersistenceOptions", async function() { // msal-node-extensions does not currently support Node 8. - if (isNode8) { + if (isNode8 || isNode15) { this.skip(); } // OSX asks for passwords on CI, so we need to skip these tests from our automation @@ -124,7 +152,7 @@ describe("UsernamePasswordCredential (internal)", function() { it("Authenticates silently with tokenCachePersistenceOptions", async function() { // msal-node-extensions does not currently support Node 8. - if (isNode8) { + if (isNode8 || isNode15) { this.skip(); } // OSX asks for passwords on CI, so we need to skip these tests from our automation @@ -170,7 +198,7 @@ describe("UsernamePasswordCredential (internal)", function() { it("allows passing an authenticationRecord to avoid further manual authentications", async function() { // msal-node-extensions does not currently support Node 8. - if (isNode8) { + if (isNode8 || isNode15) { this.skip(); } // OSX asks for passwords on CI, so we need to skip these tests from our automation @@ -230,7 +258,7 @@ describe("UsernamePasswordCredential (internal)", function() { it("allows working with an authenticationRecord that is serialized", async function() { // msal-node-extensions does not currently support Node 8. - if (isNode8) { + if (isNode8 || isNode15) { this.skip(); } // OSX asks for passwords on CI, so we need to skip these tests from our automation From 033f53c2f540bc83771330d9372dd092aafc747c Mon Sep 17 00:00:00 2001 From: Daniel Rodriguez Date: Mon, 22 Mar 2021 17:29:16 +0000 Subject: [PATCH 11/12] recorded the Node 15 error test --- ...ding_persistence_throws_on_node_15_as_expected.js | 5 +++++ .../internal/node/clientSecretCredential.spec.ts | 12 ++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) create mode 100644 sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_persistence_throws_on_node_15_as_expected.js diff --git a/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_persistence_throws_on_node_15_as_expected.js b/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_persistence_throws_on_node_15_as_expected.js new file mode 100644 index 000000000000..3a0106b7ebb0 --- /dev/null +++ b/sdk/identity/identity/recordings/node/usernamepasswordcredential_internal/recording_persistence_throws_on_node_15_as_expected.js @@ -0,0 +1,5 @@ +let nock = require('nock'); + +module.exports.hash = "13cfb28c1c5a0965912cd87bef46fb7f"; + +module.exports.testInfo = {"uniqueName":{},"newDate":{}} diff --git a/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts b/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts index 22c2e1adce4c..072f1cfd745e 100644 --- a/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts +++ b/sdk/identity/identity/test/internal/node/clientSecretCredential.spec.ts @@ -13,12 +13,12 @@ import { TokenCachePersistence } from "../../../src/tokenCache/TokenCachePersist import { MsalNode } from "../../../src/msal/nodeFlows/nodeCommon"; import { isNode15, isNode8 } from "../../../src/tokenCache/nodeVersion"; -describe("ClientSecretCredential (internal)", function () { +describe("ClientSecretCredential (internal)", function() { let cleanup: MsalTestCleanup; let getTokenSilentSpy: Sinon.SinonSpy; let doGetTokenSpy: Sinon.SinonSpy; - beforeEach(function () { + beforeEach(function() { const setup = msalNodeTestSetup(this); cleanup = setup.cleanup; @@ -30,13 +30,13 @@ describe("ClientSecretCredential (internal)", function () { "acquireTokenByClientCredential" ); }); - afterEach(async function () { + afterEach(async function() { await cleanup(); }); const scope = "https://vault.azure.net/.default"; - it("Authenticates silently after the initial request", async function () { + it("Authenticates silently after the initial request", async function() { const credential = new ClientSecretCredential( env.AZURE_TENANT_ID, env.AZURE_CLIENT_ID, @@ -56,7 +56,7 @@ describe("ClientSecretCredential (internal)", function () { assert.equal(doGetTokenSpy.callCount, 2); }); - it("Accepts tokenCachePersistenceOptions", async function () { + it("Accepts tokenCachePersistenceOptions", async function() { // msal-node-extensions does not currently support Node 8. if (isNode8 || isNode15) { this.skip(); @@ -89,7 +89,7 @@ describe("ClientSecretCredential (internal)", function () { assert.ok(parsedResult.AccessToken); }); - it("Authenticates silently with tokenCachePersistenceOptions", async function () { + it("Authenticates silently with tokenCachePersistenceOptions", async function() { // msal-node-extensions does not currently support Node 8. if (isNode8 || isNode15) { this.skip(); From c13522e42ba409680e14d31e7b11bc64bb660cc9 Mon Sep 17 00:00:00 2001 From: Daniel Rodriguez Date: Mon, 22 Mar 2021 17:35:59 +0000 Subject: [PATCH 12/12] node 15 CI workaround while we wait for MSAL --- sdk/identity/identity/src/tokenCache/nodeVersion.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/identity/identity/src/tokenCache/nodeVersion.ts b/sdk/identity/identity/src/tokenCache/nodeVersion.ts index 7bf2281140c5..fa5f0d0f0b3d 100644 --- a/sdk/identity/identity/src/tokenCache/nodeVersion.ts +++ b/sdk/identity/identity/src/tokenCache/nodeVersion.ts @@ -8,5 +8,5 @@ export const isNode8 = process.versions.node[0] === "8"; export const Node8NotSupportedError = new Error("Node 8 does not support persistence caching."); -export const isNode15 = process.versions.node[0] === "15"; +export const isNode15 = process.versions.node.slice(0, 2) === "15"; export const Node15NotSupportedError = new Error("Node 15 does not support persistence caching.");