|
| 1 | +# Client side user authentication |
| 2 | + |
| 3 | +Client side applications often need to authenticate users to interact with resources in Azure. Some examples of this might be a command line tool which fetches secrets a user has access to from a key vault to setup a local test environment, or a GUI based application which allows a user to browse storage blobs they have access to. This sample demonstrates authenticating users with the `Azure.Identity` library. |
| 4 | + |
| 5 | +## Interactive user authentication |
| 6 | + |
| 7 | +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. |
| 8 | + |
| 9 | + |
| 10 | +## Authenticating users with the InteractiveBrowserCredential |
| 11 | + |
| 12 | +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`. |
| 13 | + |
| 14 | +```C# Snippet:Identity_ClientSideUserAuthentication_SimpleInteractiveBrowser |
| 15 | +var client = new SecretClient(new Uri("https://myvault.azure.vaults.net/"), new InteractiveBrowserCredential()); |
| 16 | +``` |
| 17 | +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. |
| 18 | + |
| 19 | + |
| 20 | +## Authenticating users with the DeviceCodeCredential |
| 21 | + |
| 22 | +For terminal clients without an available web browser, or clients with limited UI capabilities the `DeviceCodeCredential` provides the ability to authenticate any client using a device code. The next sample shows authenticating a `BlobClient` using the `DeviceCodeCredential`. |
| 23 | + |
| 24 | +```C# Snippet:Identity_ClientSideUserAuthentication_SimpleDeviceCode |
| 25 | +var credential = new DeviceCodeCredential(); |
| 26 | + |
| 27 | +var client = new BlobClient(new Uri("https://myaccount.blob.core.windows.net/mycontainer/myblob"), credential); |
| 28 | +``` |
| 29 | +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. |
| 30 | + |
| 31 | + |
| 32 | +## Controlling user interaction |
| 33 | + |
| 34 | +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. |
| 35 | + |
| 36 | +```C# Snippet:Identity_ClientSideUserAuthentication_DisableAutomaticAuthentication |
| 37 | +var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { DisableAutomaticAuthentication = true }); |
| 38 | + |
| 39 | +await credential.AuthenticateAsync(); |
| 40 | + |
| 41 | +var client = new SecretClient(new Uri("https://myvault.azure.vaults.net/"), credential); |
| 42 | +``` |
| 43 | +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`. |
| 44 | + |
| 45 | +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. |
| 46 | + |
| 47 | +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. |
| 48 | + |
| 49 | +```C# Snippet:Identity_ClientSideUserAuthentication_DisableAutomaticAuthentication_ExHandling |
| 50 | +try |
| 51 | +{ |
| 52 | + client.GetSecret("secret"); |
| 53 | +} |
| 54 | +catch (AuthenticationRequiredException e) |
| 55 | +{ |
| 56 | + await EnsureAnimationCompleteAsync(); |
| 57 | + |
| 58 | + await credential.AuthenticateAsync(e.TokenRequestContext); |
| 59 | + |
| 60 | + client.GetSecret("secret"); |
| 61 | +} |
| 62 | +``` |
| 63 | + |
| 64 | +## Persisting user authentication data |
| 65 | + |
| 66 | +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`. |
| 67 | + |
| 68 | +### Persisting the TokenCache |
| 69 | + |
| 70 | +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. |
| 71 | + |
| 72 | +To use the `PersistentTokenCache` to persist the cache of any credential simply set the `TokenCache` option. |
| 73 | + |
| 74 | +```C# Snippet:Identity_ClientSideUserAuthentication_Persist_TokenCache |
| 75 | +var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = new PersistentTokenCache() }); |
| 76 | +``` |
| 77 | + |
| 78 | +### Persisting the AuthenticationRecord |
| 79 | + |
| 80 | +The `AuthenticationRecord` which is returned from the `Authenticate` and `AuthenticateAsync`, contains data identifying an authenticated account. It is needed to identify the appropriate entry in the `TokenCache` to silently authenticate on subsequent executions. There is no sensitive data in the `AuthenticationRecord` so it can be persisted in a non-protected state. |
| 81 | + |
| 82 | +Here is an example of an application storing the `AuthenticationRecord` to the local file system after authenticating the user. |
| 83 | + |
| 84 | +```C# Snippet:Identity_ClientSideUserAuthentication_Persist_AuthRecord |
| 85 | +AuthenticationRecord authRecord = await credential.AuthenticateAsync(); |
| 86 | + |
| 87 | +using var authRecordStream = new FileStream(AUTH_RECORD_PATH, FileMode.Create, FileAccess.Write); |
| 88 | + |
| 89 | +await authRecord.SerializeAsync(authRecordStream); |
| 90 | + |
| 91 | +await authRecordStream.FlushAsync(); |
| 92 | +``` |
| 93 | +### Silent authentication with AuthenticationRecord and PersistentTokenCache |
| 94 | + |
| 95 | +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. |
| 96 | + |
| 97 | +```C# Snippet:Identity_ClientSideUserAuthentication_Persist_SilentAuth |
| 98 | +using var authRecordStream = new FileStream(AUTH_RECORD_PATH, FileMode.Open, FileAccess.Read); |
| 99 | + |
| 100 | +AuthenticationRecord authRecord = await AuthenticationRecord.DeserializeAsync(authRecordStream); |
| 101 | + |
| 102 | +var credential = new InteractiveBrowserCredential(new InteractiveBrowserCredentialOptions { TokenCache = new PersistentTokenCache(), AuthenticationRecord = authRecord }); |
| 103 | +``` |
| 104 | + |
| 105 | +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. |
0 commit comments