diff --git a/sdk/identity/Azure.Identity/samples/ClientSideUserAuthentication.md b/sdk/identity/Azure.Identity/samples/ClientSideUserAuthentication.md index 4059a091a909..4f337e4a30bc 100644 --- a/sdk/identity/Azure.Identity/samples/ClientSideUserAuthentication.md +++ b/sdk/identity/Azure.Identity/samples/ClientSideUserAuthentication.md @@ -6,16 +6,18 @@ Client side applications often need to authenticate users to interact with resou Most often authenticating users requires some user interaction. Properly handling this user interaction for OAuth2 authorization code or device code authentication can be challenging. To simplify this for client side applications the `Azure.Identity` library provides the `InteractiveBrowserCredential` and the `DeviceCodeCredential`. These credentials are designed to handle the user interactions needed to authenticate via these two client side authentication flows, so the application developer can simply create the credential and authenticate clients with it. - ## Authenticating users with the InteractiveBrowserCredential For clients which have a default browser available, the `InteractiveBrowserCredential` provides the most simple user authentication experience. In the sample below an application authenticates a `SecretClient` using the `InteractiveBrowserCredential`. ```C# Snippet:Identity_ClientSideUserAuthentication_SimpleInteractiveBrowser -var client = new SecretClient(new Uri("https://myvault.azure.vaults.net/"), new InteractiveBrowserCredential()); +var client = new SecretClient( + new Uri("https://myvault.azure.vaults.net/"), + new InteractiveBrowserCredential() +); ``` -As code uses the `SecretClient` in the above sample, the `InteractiveBrowserCredential` will automatically authenticate the user by launching the default system browser prompting the user to login. In this case the user interaction happens on demand as is necessary to authenticate calls from the client. +As code uses the `SecretClient` in the above sample, the `InteractiveBrowserCredential` will automatically authenticate the user by launching the default system browser prompting the user to login. In this case the user interaction happens on demand as is necessary to authenticate calls from the client. ## Authenticating users with the DeviceCodeCredential @@ -24,25 +26,32 @@ For terminal clients without an available web browser, or clients with limited U ```C# Snippet:Identity_ClientSideUserAuthentication_SimpleDeviceCode var credential = new DeviceCodeCredential(); -var client = new BlobClient(new Uri("https://myaccount.blob.core.windows.net/mycontainer/myblob"), credential); +var client = new BlobClient( + new Uri("https://myaccount.blob.core.windows.net/mycontainer/myblob"), + credential +); ``` -Similarly to the `InteractiveBrowserCredential` the `DeviceCodeCredential` will also initiate the user interaction automatically as needed. To instantiate the `DeviceCodeCredential` the application must provide a callback which is called to display the device code along with details on how to authenticate to the user. In the above sample a lambda is provided which prints the full device code message to the console. +Similarly to the `InteractiveBrowserCredential` the `DeviceCodeCredential` will also initiate the user interaction automatically as needed. To instantiate the `DeviceCodeCredential` the application must provide a callback which is called to display the device code along with details on how to authenticate to the user. In the above sample a lambda is provided which prints the full device code message to the console. ## Controlling user interaction In many cases applications require tight control over user interaction. In these applications automatically blocking on required user interaction is often undesired or impractical. For this reason, credentials in the `Azure.Identity` library which interact with the user offer mechanisms to fully control user interaction. ```C# Snippet:Identity_ClientSideUserAuthentication_DisableAutomaticAuthentication -var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { DisableAutomaticAuthentication = true }); +var credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { + DisableAutomaticAuthentication = true + }); await credential.AuthenticateAsync(); var client = new SecretClient(new Uri("https://myvault.azure.vaults.net/"), credential); ``` -In this sample the application is again using the `InteractiveBrowserCredential` to authenticate a `SecretClient`, but with two major differences from our first example. First, in this example the application is explicitly forcing any user interaction to happen before the credential is given to the client by calling `AuthenticateAsync`. -The second difference is here the application is preventing the credential from automatically initiating user interaction. Even though the application authenticates the user before the credential is used, further interaction might still be needed, for instance in the case that the user's refresh token expires, or a specific method require additional consent or authentication. +In this sample the application is again using the `InteractiveBrowserCredential` to authenticate a `SecretClient`, but with two major differences from our first example. First, in this example the application is explicitly forcing any user interaction to happen before the credential is given to the client by calling `AuthenticateAsync`. + +The second difference is here the application is preventing the credential from automatically initiating user interaction. Even though the application authenticates the user before the credential is used, further interaction might still be needed, for instance in the case that the user's refresh token expires, or a specific method require additional consent or authentication. By setting the option `DisableAutomaticAuthentication` to `true` the credential will fail to automatically authenticate calls where user interaction is necessary. Instead, the credential will throw an `AuthenticationRequiredException`. The following example demonstrates an application handling such an exception to prompt the user to authenticate only after some application logic has completed. @@ -63,16 +72,19 @@ catch (AuthenticationRequiredException e) ## Persisting user authentication data -Quite often applications desire the ability to be run multiple times without having to reauthenticate the user on each execution. This requires that data from the original authentication be persisted outside of the application memory, so that it can authenticate silently on subsequent executions. Specifically two pieces of data need to be persisted, the `TokenCache` and the `AuthenticationRecord`. +Quite often applications desire the ability to be run multiple times without having to reauthenticate the user on each execution. This requires that data from the original authentication be persisted outside of the application memory, so that it can authenticate silently on subsequent executions. Specifically two pieces of data need to be persisted, the `TokenCache` and the `AuthenticationRecord`. ### Persisting the TokenCache -The `TokenCache` contains all the data needed to silently authenticate, one or many accounts. It contains sensitive data such as refresh tokens, and access tokens and must be protected to prevent compromising the accounts it houses tokens for. The `Azure.Identity` library provides the `PersistentTokenCache` class which by default will protect and persist the cache using available platform data protection. +The `TokenCache` contains all the data needed to silently authenticate, one or many accounts. It contains sensitive data such as refresh tokens, and access tokens and must be protected to prevent compromising the accounts it houses tokens for. The `Azure.Identity` library provides the `PersistentTokenCache` class which by default will protect and persist the cache using available platform data protection. To use the `PersistentTokenCache` to persist the cache of any credential simply set the `TokenCache` option. ```C# Snippet:Identity_ClientSideUserAuthentication_Persist_TokenCache -var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = new PersistentTokenCache() }); +var credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { + TokenCache = new PersistentTokenCache() + }); ``` ### Persisting the AuthenticationRecord @@ -81,25 +93,36 @@ The `AuthenticationRecord` which is returned from the `Authenticate` and `Authen Here is an example of an application storing the `AuthenticationRecord` to the local file system after authenticating the user. +```C# Snippet:Identity_ClientSideUserAuthentication_Persist_TokenCache_AuthRecordPath +private const string AUTH_RECORD_PATH = "./tokencache.bin"; +``` + ```C# Snippet:Identity_ClientSideUserAuthentication_Persist_AuthRecord AuthenticationRecord authRecord = await credential.AuthenticateAsync(); -using var authRecordStream = new FileStream(AUTH_RECORD_PATH, FileMode.Create, FileAccess.Write); - -await authRecord.SerializeAsync(authRecordStream); - -await authRecordStream.FlushAsync(); +using (var authRecordStream = new FileStream(AUTH_RECORD_PATH, FileMode.Create, FileAccess.Write)) +{ + await authRecord.SerializeAsync(authRecordStream); +} ``` + ### Silent authentication with AuthenticationRecord and PersistentTokenCache Once an application has persisted both the `TokenCache` and the `AuthenticationRecord` this data can be used to silently authenticate. This example demonstrates an application using the `PersistentTokenCache` and retrieving an `AuthenticationRecord` from the local file system to create an `InteractiveBrowserCredential` capable of silent authentication. ```C# Snippet:Identity_ClientSideUserAuthentication_Persist_SilentAuth -using var authRecordStream = new FileStream(AUTH_RECORD_PATH, FileMode.Open, FileAccess.Read); +AuthenticationRecord authRecord; -AuthenticationRecord authRecord = await AuthenticationRecord.DeserializeAsync(authRecordStream); +using (var authRecordStream = new FileStream(AUTH_RECORD_PATH, FileMode.Open, FileAccess.Read)) +{ + authRecord = await AuthenticationRecord.DeserializeAsync(authRecordStream); +} -var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = new PersistentTokenCache(), AuthenticationRecord = authRecord }); +var credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { + TokenCache = new PersistentTokenCache(), + AuthenticationRecord = authRecord + }); ``` The credential created in this example will silently authenticate given that a valid token for corresponding to the `AuthenticationRecord` still exists in the `TokenCache`. There are some cases where interaction will still be required such as on token expiry, or when additional authentication is required for a particular resource. diff --git a/sdk/identity/Azure.Identity/samples/TokenCache.md b/sdk/identity/Azure.Identity/samples/TokenCache.md index 6741aada3d2c..6b639826ba58 100644 --- a/sdk/identity/Azure.Identity/samples/TokenCache.md +++ b/sdk/identity/Azure.Identity/samples/TokenCache.md @@ -1,5 +1,6 @@ # Persisting the credential TokenCache -Many credential implementations in the Azure.Identity library have an underlying `TokenCache` which caches sensitive authentication data such as account information, access tokens, and refresh tokens. By default this `TokenCache` instance is an in memory cache which is specific to the credential instance. However, there are scenarios where an application needs to share the token cache across credentials, and persist it across executions. To accomplish this the Azure.Identity provides the `TokenCache` and `PeristantTokenCache` classes. + +Many credential implementations in the Azure.Identity library have an underlying `TokenCache` which caches sensitive authentication data such as account information, access tokens, and refresh tokens. By default this `TokenCache` instance is an in memory cache which is specific to the credential instance. However, there are scenarios where an application needs to share the token cache across credentials, and persist it across executions. To accomplish this the Azure.Identity provides the `TokenCache` and `PersistentTokenCache` classes. >IMPORTANT! The `TokenCache` contains sensitive data and **MUST** be protected to prevent compromising accounts. All application decisions regarding the storage of the `TokenCache` must consider that a breach of its content will fully compromise all the accounts it contains. @@ -8,7 +9,10 @@ Many credential implementations in the Azure.Identity library have an underlying The simplest way to persist the `TokenCache` of a credential is to to use the default `PersistentTokenCache`. This will persist and read the `TokenCache` from a shared persisted token cache protected to the current account. ```C# Snippet:Identity_TokenCache_PersistentDefault -var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = new PersistentTokenCache() }); +var credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { + TokenCache = new PersistentTokenCache() + }); ``` ## Using a named PersistentTokenCache @@ -16,66 +20,91 @@ var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredenti Some applications may prefer to isolate the `PersistentTokenCache` they user rather than using the shared instance. To accomplish this they can specify a `PersistentTokenCacheOptions` when creating the `PersistentTokenCache` and provide a `Name` for the persisted cache instance. ```C# Snippet:Identity_TokenCache_PersistentNamed -var tokenCache = new PersistentTokenCache(new PersistentTokenCacheOptions { Name = "my_application_name" }); +var tokenCache = new PersistentTokenCache( + new PersistentTokenCacheOptions { Name = "my_application_name" } +); -var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = tokenCache }); +var credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { TokenCache = tokenCache } +); ``` ## Allowing unencrypted storage + By default the `PersistentTokenCache` will protect any data which is persisted using the user data protection APIs available on the current platform. However, there are cases where no data protection is available, and applications may choose to still persist the token cache in an unencrypted state. This is accomplished with the `AllowUnencryptedStorage` option. ```C# Snippet:Identity_TokenCache_PersistentUnencrypted -var tokenCache = new PersistentTokenCache(new PersistentTokenCacheOptions { AllowUnencryptedStorage = true }); +var tokenCache = new PersistentTokenCache( + new PersistentTokenCacheOptions { AllowUnencryptedStorage = true } +); -var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = tokenCache}); +var credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { TokenCache = tokenCache} +); ``` + By setting `AllowUnencryptedStorage` to `true`, the `PersistentTokenCache` will encrypt the contents of the `TokenCache` before persisting it if data protection is available on the current platform, otherwise it will write and read the `TokenCache` data to an unencrypted local file ACL'd to the current account. If `AllowUnencryptedStorage` is `false` (the default) a `CredentialUnavailableException` will be raised in the case no data protection is available. -## Implementing custom TokenCache persistence -Some applications may require complete control of how the `TokenCache` is persisted. To enable this the `TokenCache` provides the methods `Serialize`, `SerializeAsync`, `Deserialize` and `DeserializeAsync` methods so applications can write the `TokenCache` to any stream. The following samples illustrate how to use these serialization methods to write and read the cache from a stream. +## Implementing custom TokenCache persistence + +Some applications may require complete control of how the `TokenCache` is persisted. To enable this the `TokenCache` provides the methods `Serialize`, `SerializeAsync`, `Deserialize` and `DeserializeAsync` methods so applications can write the `TokenCache` to any stream. The following samples illustrate how to use these serialization methods to write and read the cache from a stream. > IMPORTANT! This sample assumes the location of the file it is using for storage is secure. The `Serialize` and `SerializeAsync` methods will write the unencrypted content of the `TokenCache` to the provide stream. It is the responsibility the implementer to properly protect the `TokenCache` data. The `Serialize` or `SerializeAsync` methods can be used to write out content of a `TokenCache` to any writeable stream. -```C# Snippet:Identity_TokenCache_CustomPersistence_Write -using var cacheStream = new FileStream(TokenCachePath, FileMode.Create, FileAccess.Write); +```C# Snippet:Identity_TokenCache_CustomPersistence_Usage_TokenCachePath +private const string TOKEN_CACHE_PATH = "./tokencache.bin"; +``` -await tokenCache.SerializeAsync(cacheStream); +```C# Snippet:Identity_TokenCache_CustomPersistence_Write +using (var cacheStream = new FileStream(TOKEN_CACHE_PATH, FileMode.Create, FileAccess.Write)) +{ + await tokenCache.SerializeAsync(cacheStream); +} ``` The `Deserialize` or `DeserializeAsync` methods can be used to read the content of a `TokenCache` from any readable stream. ```C# Snippet:Identity_TokenCache_CustomPersistence_Read -using var cacheStream = new FileStream(TokenCachePath, FileMode.OpenOrCreate, FileAccess.Read); +TokenCache tokenCache; -var tokenCache = await TokenCache.DeserializeAsync(cacheStream); +using (var cacheStream = new FileStream(TOKEN_CACHE_PATH, FileMode.OpenOrCreate, FileAccess.Read)) +{ + tokenCache = await TokenCache.DeserializeAsync(cacheStream); +} ``` Applications can combine these methods along with the `Updated` event to automatically persist and read the token from a storage solution of their choice. + ```C# Snippet:Identity_TokenCache_CustomPersistence_Usage public static async Task ReadTokenCacheAsync() { - using var cacheStream = new FileStream(TokenCachePath, FileMode.OpenOrCreate, FileAccess.Read); + TokenCache tokenCache; - var tokenCache = await TokenCache.DeserializeAsync(cacheStream); - - tokenCache.Updated += WriteCacheOnUpdateAsync; + using (var cacheStream = new FileStream(TOKEN_CACHE_PATH, FileMode.OpenOrCreate, FileAccess.Read)) + { + tokenCache = await TokenCache.DeserializeAsync(cacheStream); + tokenCache.Updated += WriteCacheOnUpdateAsync; + } return tokenCache; } public static async Task WriteCacheOnUpdateAsync(TokenCacheUpdatedArgs args) { - using var cacheStream = new FileStream(TokenCachePath, FileMode.Create, FileAccess.Write); - - await args.Cache.SerializeAsync(cacheStream); + using (var cacheStream = new FileStream(TOKEN_CACHE_PATH, FileMode.Create, FileAccess.Write)) + { + await args.Cache.SerializeAsync(cacheStream); + } } public static async Task Main() { var tokenCache = await ReadTokenCacheAsync(); - var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = tokenCache }); + var credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { TokenCache = tokenCache } + ); } -``` \ No newline at end of file +``` diff --git a/sdk/identity/Azure.Identity/tests/samples/TokenCacheSnippets.cs b/sdk/identity/Azure.Identity/tests/samples/TokenCacheSnippets.cs index 617cb979ea3e..b06ec37a7c75 100644 --- a/sdk/identity/Azure.Identity/tests/samples/TokenCacheSnippets.cs +++ b/sdk/identity/Azure.Identity/tests/samples/TokenCacheSnippets.cs @@ -12,37 +12,55 @@ namespace Azure.Identity.Samples { public class TokenCacheSnippets { + #region Snippet:Identity_TokenCache_CustomPersistence_Usage_TokenCachePath + private const string TOKEN_CACHE_PATH = "./tokencache.bin"; + #endregion + public void Identity_TokenCache_PersistentDefault() { #region Snippet:Identity_TokenCache_PersistentDefault - var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = new PersistentTokenCache() }); + var credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { + TokenCache = new PersistentTokenCache() + }); #endregion } public void Identity_TokenCache_PersistentNamed() { #region Snippet:Identity_TokenCache_PersistentNamed - var tokenCache = new PersistentTokenCache(new PersistentTokenCacheOptions { Name = "my_application_name" }); + var tokenCache = new PersistentTokenCache( + new PersistentTokenCacheOptions { Name = "my_application_name" } + ); - var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = tokenCache }); + var credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { TokenCache = tokenCache } + ); #endregion } public void Identity_TokenCache_PersistentUnencrypted() { #region Snippet:Identity_TokenCache_PersistentUnencrypted - var tokenCache = new PersistentTokenCache(new PersistentTokenCacheOptions { AllowUnencryptedStorage = true }); + var tokenCache = new PersistentTokenCache( + new PersistentTokenCacheOptions { AllowUnencryptedStorage = true } + ); - var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = tokenCache}); + var credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { TokenCache = tokenCache} + ); #endregion } public async Task Identity_TokenCache_CustomPersistence_Read() { #region Snippet:Identity_TokenCache_CustomPersistence_Read - using var cacheStream = new FileStream(TokenCachePath, FileMode.OpenOrCreate, FileAccess.Read); + TokenCache tokenCache; - var tokenCache = await TokenCache.DeserializeAsync(cacheStream); + using (var cacheStream = new FileStream(TOKEN_CACHE_PATH, FileMode.OpenOrCreate, FileAccess.Read)) + { + tokenCache = await TokenCache.DeserializeAsync(cacheStream); + } #endregion } @@ -51,39 +69,43 @@ public async Task Identity_TokenCache_CustomPersistence_Write() var tokenCache = new TokenCache(); #region Snippet:Identity_TokenCache_CustomPersistence_Write - using var cacheStream = new FileStream(TokenCachePath, FileMode.Create, FileAccess.Write); - - await tokenCache.SerializeAsync(cacheStream); + using (var cacheStream = new FileStream(TOKEN_CACHE_PATH, FileMode.Create, FileAccess.Write)) + { + await tokenCache.SerializeAsync(cacheStream); + } #endregion } - private const string TokenCachePath = "./tokencache.bin"; - #region Snippet:Identity_TokenCache_CustomPersistence_Usage public static async Task ReadTokenCacheAsync() { - using var cacheStream = new FileStream(TokenCachePath, FileMode.OpenOrCreate, FileAccess.Read); - - var tokenCache = await TokenCache.DeserializeAsync(cacheStream); + TokenCache tokenCache; - tokenCache.Updated += WriteCacheOnUpdateAsync; + using (var cacheStream = new FileStream(TOKEN_CACHE_PATH, FileMode.OpenOrCreate, FileAccess.Read)) + { + tokenCache = await TokenCache.DeserializeAsync(cacheStream); + tokenCache.Updated += WriteCacheOnUpdateAsync; + } return tokenCache; } public static async Task WriteCacheOnUpdateAsync(TokenCacheUpdatedArgs args) { - using var cacheStream = new FileStream(TokenCachePath, FileMode.Create, FileAccess.Write); - - await args.Cache.SerializeAsync(cacheStream); + using (var cacheStream = new FileStream(TOKEN_CACHE_PATH, FileMode.Create, FileAccess.Write)) + { + await args.Cache.SerializeAsync(cacheStream); + } } public static async Task Main() { var tokenCache = await ReadTokenCacheAsync(); - var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = tokenCache }); + var credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { TokenCache = tokenCache } + ); } #endregion } -} +} \ No newline at end of file diff --git a/sdk/identity/Azure.Identity/tests/samples/UserAuthenticationSnippets.cs b/sdk/identity/Azure.Identity/tests/samples/UserAuthenticationSnippets.cs index e050fb06e799..cbbe5ca9ef34 100644 --- a/sdk/identity/Azure.Identity/tests/samples/UserAuthenticationSnippets.cs +++ b/sdk/identity/Azure.Identity/tests/samples/UserAuthenticationSnippets.cs @@ -14,10 +14,17 @@ namespace Azure.Identity.Samples { public class UserAuthenticationSnippets { + #region Snippet:Identity_ClientSideUserAuthentication_Persist_TokenCache_AuthRecordPath + private const string AUTH_RECORD_PATH = "./tokencache.bin"; + #endregion + public void Identity_ClientSideUserAuthentication_SimpleInteractiveBrowser() { #region Snippet:Identity_ClientSideUserAuthentication_SimpleInteractiveBrowser - var client = new SecretClient(new Uri("https://myvault.azure.vaults.net/"), new InteractiveBrowserCredential()); + var client = new SecretClient( + new Uri("https://myvault.azure.vaults.net/"), + new InteractiveBrowserCredential() + ); #endregion } @@ -26,14 +33,20 @@ public void Identity_ClientSideUserAuthentication_SimpleDeviceCode() #region Snippet:Identity_ClientSideUserAuthentication_SimpleDeviceCode var credential = new DeviceCodeCredential(); - var client = new BlobClient(new Uri("https://myaccount.blob.core.windows.net/mycontainer/myblob"), credential); + var client = new BlobClient( + new Uri("https://myaccount.blob.core.windows.net/mycontainer/myblob"), + credential + ); #endregion } public async Task Identity_ClientSideUserAuthentication_DisableAutomaticAuthentication() { #region Snippet:Identity_ClientSideUserAuthentication_DisableAutomaticAuthentication - var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { DisableAutomaticAuthentication = true }); + var credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { + DisableAutomaticAuthentication = true + }); await credential.AuthenticateAsync(); @@ -58,24 +71,25 @@ public async Task Identity_ClientSideUserAuthentication_DisableAutomaticAuthenti private Task EnsureAnimationCompleteAsync() => Task.CompletedTask; - private const string AUTH_RECORD_PATH = @".\Data\authrecord.bin"; - public static async Task GetUserCredentialAsync() { if (!File.Exists(AUTH_RECORD_PATH)) { #region Snippet:Identity_ClientSideUserAuthentication_Persist_TokenCache - var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = new PersistentTokenCache() }); + var credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { + TokenCache = new PersistentTokenCache() + }); #endregion #region Snippet:Identity_ClientSideUserAuthentication_Persist_AuthRecord AuthenticationRecord authRecord = await credential.AuthenticateAsync(); - using var authRecordStream = new FileStream(AUTH_RECORD_PATH, FileMode.Create, FileAccess.Write); - - await authRecord.SerializeAsync(authRecordStream); + using (var authRecordStream = new FileStream(AUTH_RECORD_PATH, FileMode.Create, FileAccess.Write)) + { + await authRecord.SerializeAsync(authRecordStream); + } - await authRecordStream.FlushAsync(); #endregion return credential; @@ -83,11 +97,18 @@ public static async Task GetUserCredentialAsync() else { #region Snippet:Identity_ClientSideUserAuthentication_Persist_SilentAuth - using var authRecordStream = new FileStream(AUTH_RECORD_PATH, FileMode.Open, FileAccess.Read); - - AuthenticationRecord authRecord = await AuthenticationRecord.DeserializeAsync(authRecordStream); - - var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = new PersistentTokenCache(), AuthenticationRecord = authRecord }); + AuthenticationRecord authRecord; + + using (var authRecordStream = new FileStream(AUTH_RECORD_PATH, FileMode.Open, FileAccess.Read)) + { + authRecord = await AuthenticationRecord.DeserializeAsync(authRecordStream); + } + + var credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { + TokenCache = new PersistentTokenCache(), + AuthenticationRecord = authRecord + }); #endregion return credential; @@ -100,26 +121,35 @@ public static async Task Main() if (!File.Exists(AUTH_RECORD_PATH)) { - credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = new PersistentTokenCache() }); + credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { + TokenCache = new PersistentTokenCache() + }); AuthenticationRecord authRecord = await credential.AuthenticateAsync(); - using var authRecordStream = new FileStream(AUTH_RECORD_PATH, FileMode.Create, FileAccess.Write); - - await authRecord.SerializeAsync(authRecordStream); - - await authRecordStream.FlushAsync(); + using (var authRecordStream = new FileStream(AUTH_RECORD_PATH, FileMode.Create, FileAccess.Write)) + { + await authRecord.SerializeAsync(authRecordStream); + } } else { - using var authRecordStream = new FileStream(AUTH_RECORD_PATH, FileMode.Open, FileAccess.Read); - - AuthenticationRecord authRecord = await AuthenticationRecord.DeserializeAsync(authRecordStream); - - credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = new PersistentTokenCache(), AuthenticationRecord = authRecord }); + AuthenticationRecord authRecord; + + using (var authRecordStream = new FileStream(AUTH_RECORD_PATH, FileMode.Open, FileAccess.Read)) + { + authRecord = await AuthenticationRecord.DeserializeAsync(authRecordStream); + } + + credential = new InteractiveBrowserCredential( + new InteractiveBrowserCredentialOptions { + TokenCache = new PersistentTokenCache(), + AuthenticationRecord = authRecord + }); } var client = new SecretClient(new Uri("https://myvault.azure.vaults.net/"), credential); } } -} +} \ No newline at end of file