Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DefaultAzureCredential #4409

Merged
merged 9 commits into from
Mar 8, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 4 additions & 4 deletions sdk/identity/azure-identity/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
# Release History

## 1.5.0-beta.1 (Unreleased)
LarryOsterman marked this conversation as resolved.
Show resolved Hide resolved
## 1.5.0-beta.1 (2023-03-07)

### Features Added

### Breaking Changes

### Bugs Fixed
- Added `DefaultAzureCredential`.

### Other Changes

- Improved log messages.

## 1.4.0 (2023-02-07)

### Features Added
Expand Down
2 changes: 2 additions & 0 deletions sdk/identity/azure-identity/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ set(
inc/azure/identity/chained_token_credential.hpp
inc/azure/identity/client_certificate_credential.hpp
inc/azure/identity/client_secret_credential.hpp
inc/azure/identity/default_azure_credential.hpp
inc/azure/identity/dll_import_export.hpp
inc/azure/identity/environment_credential.hpp
inc/azure/identity/managed_identity_credential.hpp
Expand All @@ -69,6 +70,7 @@ set(
src/client_certificate_credential.cpp
src/client_credential_core.cpp
src/client_secret_credential.cpp
src/default_azure_credential.cpp
src/environment_credential.cpp
src/managed_identity_credential.cpp
src/managed_identity_source.cpp
Expand Down
175 changes: 94 additions & 81 deletions sdk/identity/azure-identity/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,113 +22,125 @@ find_package(azure-identity-cpp CONFIG REQUIRED)
target_link_libraries(<your project name> PRIVATE Azure::azure-identity)
```

### Prerequisites

* An [Azure subscription][azure_sub].
* The [Azure CLI][azure_cli] can also be useful for authenticating in a development environment, creating accounts, and managing account roles.

### Authenticate the client

When debugging and executing code locally it is typical for a developer to use their own account for authenticating calls to Azure services. There are several developer tools which can be used to perform this authentication in your development environment.

#### Authenticate via the Azure CLI

Developers can use the [Azure CLI][azure_cli] to authenticate. Applications using the `DefaultAzureCredential` or the `AzureCliCredential` can then use this account to authenticate calls in their application when running locally.

To authenticate with the [Azure CLI][azure_cli], users can run the command `az login`. For users running on a system with a default web browser, the Azure CLI will launch the browser to authenticate the user.

## Key concepts
### Credentials

A credential is a class which contains or can obtain the data needed for a service client to authenticate requests. Service clients across Azure SDK accept credentials when they are constructed, and service clients use those credentials to authenticate requests to the service.

The Azure Identity library focuses on OAuth authentication with Azure Active directory, and it offers a variety of credential classes capable of acquiring an AAD token to authenticate service requests. All of the credential classes in this library are implementations of the `TokenCredential` abstract class in [azure-core][azure_core_library], and any of them can be used by to construct service clients capable of authenticating with a `TokenCredential`.

### Authenticating Service Principals

<table border="1" width="100%">
<thead>
<tr>
<th>credential class</th>
<th>usage</th>
<th>configuration</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>ClientSecretCredential</code></td>
<td>authenticates a service principal using a secret</td>
<td><a href="https://docs.microsoft.com/azure/active-directory/develop/app-objects-and-service-principals">Service principal authentication</a></td>
</tr>
<tr>
<td><code>ClientCertificateCredential</code></td>
<td>authenticates a service principal using a certificate</td>
<td><a href="https://docs.microsoft.com/azure/active-directory/develop/app-objects-and-service-principals">Service principal authentication</a></td>
</tr>
</tbody>
</table>
See [Credential Classes](#credential-classes) for a complete listing of available credential types.

## Environment Variables
`EnvironmentCredential` can be configured with environment variables. Each type of authentication requires values for specific variables:
### DefaultAzureCredential

#### Service principal with secret
<table border="1" width="100%">
<thead>
<tr>
<th>variable name</th>
<th>value</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>AZURE_CLIENT_ID</code></td>
<td>id of an Azure Active Directory application</td>
</tr>
<tr>
<td><code>AZURE_TENANT_ID</code></td>
<td>id of the application's Azure Active Directory tenant</td>
</tr>
<tr>
<td><code>AZURE_CLIENT_SECRET</code></td>
<td>one of the application's client secrets</td>
</tr>
</tbody>
</table>
`DefaultAzureCredential` combines credentials commonly used to authenticate when deployed, with credentials used to authenticate in a development environment.

#### Service principal with certificate
<table border="1" width="100%">
<thead>
<tr>
<th>variable name</th>
<th>value</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>AZURE_CLIENT_ID</code></td>
<td>id of an Azure Active Directory application</td>
</tr>
<tr>
<td><code>AZURE_TENANT_ID</code></td>
<td>id of the application's Azure Active Directory tenant</td>
</tr>
<tr>
<td><code>AZURE_CLIENT_CERTIFICATE_PATH</code></td>
<td>path to a PEM-encoded certificate file including private key (without password protection)</td>
</tr>
</tbody>
</table>
> Note: `DefaultAzureCredential` is intended to simplify getting started with the SDK by handling common scenarios with reasonable default behaviors. It is not recommended to use it in production. Developers who want more control or whose scenario isn't served by the default settings should use other credential types.

Configuration is attempted in the above order. For example, if values for a client secret and certificate are both present, the client secret will be used.
The `DefaultAzureCredential` attempts to authenticate via the following mechanisms, in this order, stopping when one succeeds:

## Managed Identity Support
The [Managed identity authentication](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview) is supported via the `ManagedIdentityCredential` for the following Azure Services:
* [Azure Virtual Machines](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token)
* [Azure Cloud Shell](https://docs.microsoft.com/azure/cloud-shell/msi-authorization)
* [Azure Arc](https://docs.microsoft.com/azure/azure-arc/servers/managed-identity-authentication)
1. **Environment** - The `DefaultAzureCredential` will read account information specified via [environment variables](#environment-variables) and use it to authenticate.
1. **Azure CLI** - If the developer has authenticated an account via the Azure CLI `az login` command, the `DefaultAzureCredential` will authenticate with that account.
1. **Managed Identity** - If the application is deployed to an Azure host with Managed Identity enabled, the `DefaultAzureCredential` will authenticate with that account.

`DefaultAzureCredential` uses [`ChainedTokenCredential`](#chained-token-credential) that consists of a chain of `EnvironmentCredential`, `AzureCliCredential`, and `ManagedIdentityCredential`. Implementation, including the order in which credentials are applied is documented, but it may change from release to release.

`DefaultAzureCredential` intends to provide a credential that "just works out of the box and without requiring any information", if only the environment is set up sufficiently for the credential to work.
Therefore, it could be simple to use, but since it uses a chain of credentials, it could be a bit complicated to diagnose if the environment setup is not sufficient.
TO help with this, `DefaultAzureCredential` code paths are instrumented with [log messages](#troubleshooting).

## Examples

See the [code samples](https://github.com/Azure/azure-sdk-for-cpp/tree/main/sdk/identity/azure-identity/samples).

## Chained Token Credential
`ChainedTokenCredential` allows users to customize the credentials considered when authenticating.

`ChainedTokenCredential` allows users to set up custom authentication flow consisting of multiple credentials.

An example below demonstrates using `ChainedTokenCredential` which will attempt to authenticate using `EnvironmentCredential`, and fall back to authenticate using `ManagedIdentityCredential`.
```cpp
// Authenticate using environment credential if it is available; otherwise use the managed identity credential to authenticate.
// A configuration demonstrated below would authenticate using EnvironmentCredential if it is
// available, and if it is not available, would fall back to use AzureCliCredential, and then to
// ManagedIdentityCredential.
auto chainedTokenCredential = std::make_shared<Azure::Identity::ChainedTokenCredential>(
Azure::Identity::ChainedTokenCredential::Sources{
std::make_shared<Azure::Identity::EnvironmentCredential>(),
std::make_shared<Azure::Identity::AzureCliCredential>(),
std::make_shared<Azure::Identity::ManagedIdentityCredential>()});

Azure::Service::Client azureServiceClient("serviceUrl", chainedTokenCredential);
```

## Managed Identity Support

The [Managed identity authentication](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/overview) is supported via the `ManagedIdentityCredential` for the following Azure Services:
* [Azure App Service and Azure Functions](https://docs.microsoft.com/azure/app-service/overview-managed-identity)
* [Azure Cloud Shell](https://docs.microsoft.com/azure/cloud-shell/msi-authorization)
* [Azure Arc](https://docs.microsoft.com/azure/azure-arc/servers/managed-identity-authentication)
* [Azure Virtual Machines](https://docs.microsoft.com/azure/active-directory/managed-identities-azure-resources/how-to-use-vm-token)

## Environment Variables

`DefaultAzureCredential` and `EnvironmentCredential` can be configured with environment variables. Each type of authentication requires values for specific variables:

#### Service principal with secret
|Variable name|Value
|-|-
|`AZURE_TENANT_ID`|ID of the application's Azure AD tenant
|`AZURE_CLIENT_ID`|ID of an Azure AD application
|`AZURE_CLIENT_SECRET`|one of the application's client secrets
|`AZURE_AUTHORITY_HOST`|(optional) [authentication authority URL](https://docs.microsoft.com/azure/active-directory/develop/authentication-national-cloud)

#### Service principal with certificate
|variable name|Value
|-|-
|`AZURE_CLIENT_ID`|ID of an Azure AD application
|`AZURE_TENANT_ID`|ID of the application's Azure AD tenant
|`AZURE_CLIENT_CERTIFICATE_PATH`|path to a PFX or PEM-encoded certificate file including private key
|`AZURE_AUTHORITY_HOST`|(optional) [authentication authority URL](https://docs.microsoft.com/azure/active-directory/develop/authentication-national-cloud)

Configuration is attempted in the above order. For example, if values for a client secret and certificate are both present, the client secret will be used.

## Credential classes

### Authenticate Azure-hosted applications
|Credential | Usage
|-|-
|`DefaultAzureCredential`|Provides a simplified authentication experience to quickly start developing applications run in Azure.
|`ChainedTokenCredential`|Allows users to define custom authentication flows composing multiple credentials.
|`ManagedIdentityCredential`|Authenticates the managed identity of an Azure resource.
|`EnvironmentCredential`|Authenticates a service principal or user via credential information specified in environment variables.

### Authenticate service principals
|Credential | Usage
|-|-
|`ClientSecretCredential`|Authenticates a service principal [using a secret](https://learn.microsoft.com/azure/active-directory/develop/app-objects-and-service-principals).
|`ClientCertificateCredential`|Authenticates a service principal [using a certificate](https://learn.microsoft.com/azure/active-directory/develop/app-objects-and-service-principals).

### Authenticate via development tools
|Credential | Usage
|-|-
|`AzureCliCredential`|Authenticates in a development environment [with the Azure CLI](https://learn.microsoft.com/cli/azure/authenticate-azure-cli).

## Troubleshooting
Credentials raise exceptions either when they fail to authenticate or cannot execute authentication.
When credentials fail to authenticate, the `AuthenticationException` is thrown and it has the `what()` functions returning the description why authentication failed.

1. Azure Identity [SDK log messages](https://github.com/Azure/azure-sdk-for-cpp/tree/main/sdk/core/azure-core#sdk-log-messages) contain disgnostic information, and start with "`Identity: `".
1. Credentials raise exceptions either when they fail to authenticate or cannot execute authentication. When a credential fails to authenticate, an `AuthenticationException` is thrown. The exception has the `what()` function that provides more information about the failure.
antkmsft marked this conversation as resolved.
Show resolved Hide resolved

## Contributing
For details on contributing to this repository, see the [contributing guide][azure_sdk_for_cpp_contributing].
Expand Down Expand Up @@ -163,6 +175,7 @@ Security issues and bugs should be reported privately, via email, to the Microso
Azure SDK for C++ is licensed under the [MIT](https://github.com/Azure/azure-sdk-for-cpp/blob/main/LICENSE.txt) license.

<!-- LINKS -->
[azure_cli]: https://learn.microsoft.com/cli/azure
[azsdk_vcpkg_install]: https://github.com/Azure/azure-sdk-for-cpp#download--install-the-sdk
[azure_sdk_for_cpp_contributing]: https://github.com/Azure/azure-sdk-for-cpp/blob/main/CONTRIBUTING.md
[azure_sdk_for_cpp_contributing_developer_guide]: https://github.com/Azure/azure-sdk-for-cpp/blob/main/CONTRIBUTING.md#developer-guide
Expand Down
1 change: 1 addition & 0 deletions sdk/identity/azure-identity/inc/azure/identity.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "azure/identity/chained_token_credential.hpp"
#include "azure/identity/client_certificate_credential.hpp"
#include "azure/identity/client_secret_credential.hpp"
#include "azure/identity/default_azure_credential.hpp"
#include "azure/identity/dll_import_export.hpp"
#include "azure/identity/environment_credential.hpp"
#include "azure/identity/managed_identity_credential.hpp"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,23 @@
#include <azure/core/credentials/credentials.hpp>

#include <memory>
#include <string>
#include <vector>

namespace Azure { namespace Identity {
class DefaultAzureCredential;

/**
* @brief Chained Token Credential provides a token credential implementation which chains
* multiple Azure::Core::Credentials::TokenCredential implementations to be tried in order until
* one of the GetToken() methods returns an access token.
*
*/
class ChainedTokenCredential final : public Core::Credentials::TokenCredential {
// Friend declaration is needed for DefaultAzureCredential to access ChainedTokenCredential's
// private constructor built to be used specifically by it.
friend class DefaultAzureCredential;
antkmsft marked this conversation as resolved.
Show resolved Hide resolved

public:
/**
* @brief A container type to store the ordered chain of credentials.
Expand Down Expand Up @@ -55,7 +62,14 @@ namespace Azure { namespace Identity {
Core::Context const& context) const override;

private:
explicit ChainedTokenCredential(
Sources sources,
std::string const& enclosingCredential,
std::vector<std::string> sourcesFriendlyNames);

Sources m_sources;
std::vector<std::string> m_sourcesFriendlyNames;
std::string m_logPrefix;
};

}} // namespace Azure::Identity
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT

/**
* @file
* @brief Default Azure Credential.
*/

#pragma once

#include <azure/core/credentials/token_credential_options.hpp>

antkmsft marked this conversation as resolved.
Show resolved Hide resolved
#include <azure/identity/chained_token_credential.hpp>

#include <memory>

namespace Azure { namespace Identity {

/**
* @brief Default Azure Credential combines multiple credentials that depend on the setup
* environment and require no parameters into a single chain. If the environment is set up
* sufficiently for at least one of such credentials to work, `DefaultAzureCredential` will work
* as well.
*
* @details This credential is using the #ChainedTokenCredential of 3 credentials in the order:
* #EnvironmentCredential, #AzureCliCredential, and #ManagedIdentityCredential. Even though the
* credentials being used and their order is documented, it may be changed in the future versions
* of the SDK, potentially bringing breaking changes in its behavior.
*
* @note This credential is intended to be used at the early stages of development, to allow the
* developer some time to work with the other aspects of the SDK, and later to replace this
* credential with the exact credential that is the best fit for the application. It is not
* intended to be used in a production environment.
antkmsft marked this conversation as resolved.
Show resolved Hide resolved
*
*/
class DefaultAzureCredential final : public Core::Credentials::TokenCredential {
public:
/**
* @brief Constructs `%DefaultAzureCredential`.
*
*/
explicit DefaultAzureCredential()
: DefaultAzureCredential(Core::Credentials::TokenCredentialOptions{}){};

/**
* @brief Constructs `%DefaultAzureCredential`.
*
* @param options Generic Token Credential Options.
*/
explicit DefaultAzureCredential(Core::Credentials::TokenCredentialOptions const& options);
antkmsft marked this conversation as resolved.
Show resolved Hide resolved

/**
* @brief Destructs `%DefaultAzureCredential`.
*
*/
~DefaultAzureCredential() override;

/**
* @brief Gets an authentication token.
*
* @param tokenRequestContext A context to get the token in.
* @param context A context to control the request lifetime.
*
* @throw Azure::Core::Credentials::AuthenticationException Authentication error occurred.
*/
Core::Credentials::AccessToken GetToken(
Core::Credentials::TokenRequestContext const& tokenRequestContext,
Core::Context const& context) const override;

private:
std::shared_ptr<ChainedTokenCredential> m_credentials;
};

}} // namespace Azure::Identity
5 changes: 5 additions & 0 deletions sdk/identity/azure-identity/samples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ target_link_libraries(client_secret_credential_sample PRIVATE azure-identity ser
target_include_directories(client_secret_credential_sample PRIVATE .)
create_per_service_target_build_for_sample(identity client_secret_credential_sample)

add_executable(default_azure_credential_sample default_azure_credential.cpp)
target_link_libraries(default_azure_credential_sample PRIVATE azure-identity service)
target_include_directories(default_azure_credential_sample PRIVATE .)
create_per_service_target_build_for_sample(identity default_azure_credential_sample)

add_executable(environment_credential_sample environment_credential.cpp)
target_link_libraries(environment_credential_sample PRIVATE azure-identity service)
target_include_directories(environment_credential_sample PRIVATE .)
Expand Down
Loading