Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sdk/identity/identity/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## 2.0.0-beta.3 (Unreleased)

- Removed `authenticationRecord`, `disableAutomaticAuthentication` and `authenticate()` from the credential `UsernamePasswordCredential`. While MSAL does support this, allowing `authenticationRecord` arguably could result in users authenticating through an account other than the one they're specifying with the username and the password.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets have this in a separate section Breaking changes from 2.0.0-beta.1

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

- The feature of persistence caching of credentials (introduced in 2.0.0-beta.1) is now supported on Node.js 15 as well.
- `AuthenticationRequiredError` (introduced in 2.0.0-beta.1) now has the same impact on `ChainedTokenCredential` as the `CredentialUnavailableError` which is to allow the next credential in the chain to be tried.

Expand Down
4 changes: 2 additions & 2 deletions sdk/identity/identity/review/identity.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,12 +232,12 @@ export interface TokenCredentialOptions extends PipelineOptions {
// @public
export class UsernamePasswordCredential implements TokenCredential {
constructor(tenantId: string, clientId: string, username: string, password: string, options?: UsernamePasswordCredentialOptions);
authenticate(scopes: string | string[], options?: GetTokenOptions): Promise<AuthenticationRecord | undefined>;
getToken(scopes: string | string[], options?: GetTokenOptions): Promise<AccessToken>;
}

// @public
export interface UsernamePasswordCredentialOptions extends InteractiveCredentialOptions {
export interface UsernamePasswordCredentialOptions extends TokenCredentialOptions {
tokenCachePersistenceOptions?: TokenCachePersistenceOptions;
}

// @public
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ 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");
Expand All @@ -21,7 +20,6 @@ const logger = credentialLogger("UsernamePasswordCredential");
// to reduce the number of times we send the password over the network.
export class UsernamePasswordCredential implements TokenCredential {
private msalFlow: MsalFlow;
private disableAutomaticAuthentication?: boolean;

/**
* Creates an instance of the UsernamePasswordCredential with the details
Expand Down Expand Up @@ -50,7 +48,6 @@ export class UsernamePasswordCredential implements TokenCredential {
password,
tokenCredentialOptions: options || {}
});
this.disableAutomaticAuthentication = options?.disableAutomaticAuthentication;
}

/**
Expand All @@ -70,33 +67,7 @@ export class UsernamePasswordCredential implements TokenCredential {
async getToken(scopes: string | string[], options: GetTokenOptions = {}): Promise<AccessToken> {
return trace(`${this.constructor.name}.getToken`, options, async (newOptions) => {
const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
return this.msalFlow.getToken(arrayScopes, {
...newOptions,
disableAutomaticAuthentication: this.disableAutomaticAuthentication
});
});
}

/**
* 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<AuthenticationRecord | undefined> {
return trace(`${this.constructor.name}.authenticate`, options, async (newOptions) => {
const arrayScopes = Array.isArray(scopes) ? scopes : [scopes];
await this.msalFlow.getToken(arrayScopes, newOptions);
return this.msalFlow.getActiveAccount();
return this.msalFlow.getToken(arrayScopes, newOptions);
});
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import { InteractiveCredentialOptions } from "./interactiveCredentialOptions";
import { TokenCredentialOptions } from "../client/identityClient";
import { TokenCachePersistenceOptions } from "../tokenCache/persistencePlatforms";

/**
* Defines options for the {@link UsernamePasswordCredential} class.
*/
export interface UsernamePasswordCredentialOptions extends InteractiveCredentialOptions {}
export interface UsernamePasswordCredentialOptions 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.
Comment on lines +18 to +19
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the file definitely unencrypted or just not protected by the native OS secret store?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file is unencrypted, only protected by file-system protections.

Here's one example in the source: https://github.com/Azure/azure-sdk-for-js/blob/master/sdk/identity/identity/src/tokenCache/persistencePlatforms.ts#L152-L155

MSAL for node offers a FilePersistenceWithDataProtection class, but it's only intended to be used on Windows.

*/
tokenCachePersistenceOptions?: TokenCachePersistenceOptions;
}