Skip to content

Commit ab362fe

Browse files
authored
[Docs] Add SupportsTokenInfo details to dev guide (#39931)
Added some details about the newer `SupportsTokenInfo` protocols into the CLIENT_LIBRARY_DEVELOPER.md file. Signed-off-by: Paul Van Eck <[email protected]>
1 parent 9af398d commit ab362fe

File tree

2 files changed

+138
-20
lines changed

2 files changed

+138
-20
lines changed

.vscode/cspell.json

+1
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@
403403
"rtsp",
404404
"rtype",
405405
"rwdlacu",
406+
"sansiohttppolicy",
406407
"scbedd",
407408
"sdist",
408409
"sdpath",

sdk/core/azure-core/CLIENT_LIBRARY_DEVELOPER.md

+137-20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,27 @@
1-
21
# Azure Core Library - Client library developer reference
32

3+
## Table of contents
4+
5+
- [Pipeline](#pipeline)
6+
- [Pipeline client configurations](#pipeline-client-configurations)
7+
- [Transport](#transport)
8+
- [Proxy Settings](#proxy-settings)
9+
- [HttpRequest and HttpResponse](#httprequest-and-httpresponse)
10+
- [PipelineRequest and PipelineResponse](#pipelinerequest-and-pipelineresponse)
11+
- [Policies](#policies)
12+
- [SansIOHTTPPolicy](#sansiohttppolicy)
13+
- [HTTPPolicy and AsyncHTTPPolicy](#httppolicy-and-asynchttppolicy)
14+
- [Available Policies](#available-policies)
15+
- [The Pipeline](#the-pipeline)
16+
- [Credentials](#credentials)
17+
- [Token Credential Protocols](#token-credential-protocols)
18+
- [SupportsTokenInfo and AsyncSupportsTokenInfo protocols (preferred)](#supportstokeninfo-and-asyncsupportstokeninfo-protocols-preferred)
19+
- [TokenCredential protocol (legacy)](#tokencredential-protocol-legacy)
20+
- [Implementation Guidance](#implementation-guidance)
21+
- [Known uses of token request parameters](#known-uses-of-token-request-parameters)
22+
- [BearerTokenCredentialPolicy and AsyncBearerTokenCredentialPolicy](#bearertokencredentialpolicy-and-asyncbearertokencredentialpolicy)
23+
- [Long-running operation (LRO) customization](#long-running-operation-lro-customization)
24+
425
## Pipeline
526

627
The Azure Core pipeline is a re-structuring of the msrest pipeline introduced in msrest 0.6.0.
@@ -544,10 +565,67 @@ class Pipeline:
544565

545566
## Credentials
546567

547-
### TokenCredential protocol
568+
### Token Credential Protocols
569+
570+
Clients from the Azure SDK often require a credential instance in their constructors. Azure Core offers several protocols for credential types that provide OAuth tokens. The main protocols are `SupportsTokenInfo` (preferred) and `TokenCredential` (legacy).
571+
572+
#### SupportsTokenInfo and AsyncSupportsTokenInfo protocols (preferred)
573+
574+
These protocols are the preferred way to implement new credential types in the Azure SDK. They are capable of providing enhanced token information and also provide a more structured approach to token requests compared to the legacy `TokenCredential` protocol. New credential implementations should aim to implement these protocols.
575+
576+
The `SupportsTokenInfo` protocol specifies a class that implements `get_token_info` which returns an `AccessTokenInfo` object which contains the token string, its expiration time, and additional properties such as `refresh_on` and `token_type`.
577+
578+
```python
579+
class AccessTokenInfo:
580+
"""Information about an OAuth access token.
581+
582+
This class is an alternative to `AccessToken` which provides additional
583+
information about the token.
584+
"""
585+
586+
token: str
587+
"""The token string."""
588+
expires_on: int
589+
"""The token's expiration time in Unix time."""
590+
token_type: str
591+
"""The type of access token."""
592+
refresh_on: Optional[int]
593+
"""Specifies the time, in Unix time, when the cached token should be proactively
594+
refreshed. Optional."""
595+
596+
597+
class SupportsTokenInfo(Protocol, ContextManager["SupportsTokenInfo"]):
598+
"""Protocol for classes able to provide OAuth access tokens with additional properties."""
599+
600+
def get_token_info(self, *scopes: str, options: Optional[TokenRequestOptions] = None) -> AccessTokenInfo:
601+
"""Request an access token for `scopes`.
602+
603+
:param str scopes: The type of access needed.
604+
:keyword options: A dictionary of options for the token request. Unknown options will be ignored.
605+
:paramtype options: TokenRequestOptions
606+
607+
:rtype: AccessTokenInfo
608+
:return: An AccessTokenInfo instance containing information about the token.
609+
"""
610+
611+
def close(self) -> None:
612+
...
613+
```
614+
615+
The async version `AsyncSupportsTokenInfo` provides the same functionality but with async/await support and requires implementation of the async context manager protocol (`__aenter__`, `__aexit__`, and `close` methods should be implemented).
616+
617+
The `get_token_info` methods use `TokenRequestOptions` to handle token request parameters in a more structured way:
618+
619+
```python
620+
class TokenRequestOptions(TypedDict, total=False):
621+
claims: str # Additional claims required in the token
622+
tenant_id: str # The tenant ID for the token request
623+
enable_cae: bool # Whether to enable Continuous Access Evaluation
624+
```
625+
626+
#### TokenCredential protocol (legacy)
548627

549-
Clients from the Azure SDK often require a `TokenCredential` instance in their constructors. A `TokenCredential` is
550-
meant to provide OAuth tokens to authenticate service requests and can be implemented in a number of ways.
628+
While still supported, the `TokenCredential` protocol is considered legacy as it has extensibility limitations. It is recommended to use `SupportsTokenInfo` for new credential implementations. Generally, to ensure compatibility with existing clients, new credential implementations should implement both `SupportsTokenInfo` and `TokenCredential`.
551629

552630
The `TokenCredential` protocol specifies a class that has a single method -- `get_token` -- which returns an
553631
`AccessToken`: a `NamedTuple` containing a `token` string and an `expires_on` integer (in Unix time).
@@ -559,7 +637,12 @@ class TokenCredential(Protocol):
559637
"""Protocol for classes able to provide OAuth tokens."""
560638

561639
def get_token(
562-
self, *scopes: str, claims: Optional[str] = None, tenant_id: Optional[str] = None, **kwargs: Any
640+
self,
641+
*scopes: str,
642+
claims: Optional[str] = None,
643+
tenant_id: Optional[str] = None,
644+
enable_cae: bool = False,
645+
**kwargs: Any,
563646
) -> AccessToken:
564647
"""Request an access token for `scopes`.
565648
@@ -576,22 +659,56 @@ class TokenCredential(Protocol):
576659
"""
577660
```
578661

579-
A `TokenCredential` implementation needs to implement the `get_token` method to these specifications and can optionally
580-
implement additional methods. The [`azure-identity`][identity_github] package has a number of `TokenCredential`
581-
implementations that can be used for reference. For example, the [`InteractiveCredential`][interactive_cred] is used as
582-
a base class for multiple credentials and uses `claims` and `tenant_id` in token requests.
662+
The async version `AsyncTokenCredential` provides the same functionality but with async/await support and also requires implementation of the async context manager protocol (`__aenter__`, `__aexit__`, and `close` methods should be implemented).
663+
664+
If a `TokenCredential` implementation doesn't have a use for a keyword argument in a given scenario, the documentation for the implementation should mention that this keyword argument will not be used when making token requests, as well as any potential consequences of this. For example, if a `TokenCredential` implementation doesn't use `tenant_id`, it should document that fetched tokens may not authorize requests made to the specified tenant.
665+
666+
When implementing the `get_token` method, ensure all keyword arguments are explicitly defined rather than using `**kwargs`. This approach prevents unintended keyword arguments from being passed to the HTTP transport layer, which could lead to unexpected behavior. The `get_token_info` method in `SupportsTokenInfo` does not have this issue, as it uses a `TokenRequestOptions` object to handle token request parameters.
667+
668+
### Implementation Guidance
583669

584-
If a `TokenCredential` implementation doesn't have a use for a keyword argument in a given scenario, the unused
585-
keyword argument should be removed from `kwargs` before getting passed elsewhere. The documentation for the
586-
implementation should mention that this keyword argument will not be used when making token requests, as well as any
587-
potential consequences of this. For example, if a `TokenCredential` implementation doesn't use `tenant_id`, it should
588-
document that fetched tokens may not authorize requests made to the specified tenant.
670+
When implementing a new credential type:
671+
672+
1. Implement `SupportsTokenInfo`/`AsyncSupportsTokenInfo` as your primary protocol
673+
2. If backwards compatibility is needed:
674+
- Implement `TokenCredential`/`AsyncTokenCredential` as a secondary protocol
675+
- Convert `get_token_info` results to `AccessToken` in the `get_token` implementation
676+
677+
Example structure for a new credential implementation:
678+
679+
```python
680+
from azure.core.credentials import AccessToken, AccessTokenInfo, TokenRequestOptions
681+
682+
683+
class MyNewCredential(SupportsTokenInfo):
684+
def get_token_info(self, *scopes: str, options: Optional[TokenRequestOptions] = None) -> AccessTokenInfo:
685+
# Primary implementation
686+
...
687+
688+
def get_token(
689+
self,
690+
*scopes: str,
691+
claims: Optional[str] = None,
692+
tenant_id: Optional[str] = None,
693+
enable_cae: bool = False,
694+
**kwargs: Any,
695+
) -> AccessToken:
696+
697+
# Secondary implementation for backwards compatibility
698+
options: TokenRequestOptions = {}
699+
if tenant_id:
700+
options["tenant_id"] = tenant_id
701+
if claims:
702+
options["claims"] = claims
703+
options["enable_cae"] = enable_cae
704+
705+
token_info = self.get_token_info(*scopes, options=options)
706+
return AccessToken(token_info.token, token_info.expires_on)
707+
```
589708

590-
There is also an async protocol -- the `AsyncTokenCredential` protocol -- that specifies a class with an async
591-
`get_token` method with the same arguments. An `AsyncTokenCredential` implementation additionally needs to be a context
592-
manager, with `__aenter__`, `__aexit__`, and `close` methods.
709+
The [`azure-identity`][identity_github] package has a number of credentials that implement both protocols, and serves as a good reference for implementing new credentials. For example, the [`InteractiveCredential`][interactive_cred] is used as a base class for multiple credentials and uses `claims` and `tenant_id` in token requests.
593710

594-
#### Known uses of `get_token` keyword-only parameters
711+
### Known uses of token request parameters
595712

596713
**`claims`**
597714

@@ -613,9 +730,9 @@ manager, with `__aenter__`, `__aexit__`, and `close` methods.
613730

614731
### BearerTokenCredentialPolicy and AsyncBearerTokenCredentialPolicy
615732

616-
`BearerTokenCredentialPolicy` and `AsyncBearerTokenCredentialPolicy` are HTTP policies that are used to authenticate requests to services that accept bearer tokens in their authorization headers. These credential policies take a `TokenCredential` instance and scopes as parameters in their constructors. The `TokenCredential` instance is used to get an access token for the scopes, and the policy adds the token to the request's authorization header.
733+
`BearerTokenCredentialPolicy` and `AsyncBearerTokenCredentialPolicy` are HTTP policies that are used to authenticate requests to services that accept bearer tokens in their authorization headers. These credential policies take `SupportsTokenInfo`/`TokenCredential` instances and scopes as parameters in their constructors. The `SupportsTokenInfo`/`TokenCredential` instance is used to get an access token for the scopes, and the policy adds the token to the request's authorization header.
617734

618-
Both of these policies also accept an `enable_cae` keyword argument that is passed to the `TokenCredential` instance's `get_token` method if set to `True`. This argument is used to indicate that the requested token should be [CAE-enabled][cae_doc]. If an SDK's service supports CAE, it should set this value to `True` when creating the policy.
735+
Both of these policies also accept an `enable_cae` keyword argument that is passed to the `SupportsTokenInfo`/`TokenCredential` instance's `get_token_info`/`get_token` method if set to `True`. This argument is used to indicate that the requested token should be [CAE-enabled][cae_doc]. If an SDK's service supports CAE, it should set this value to `True` when creating the policy.
619736

620737
```python
621738
from azure.core.pipeline.policies import BearerTokenCredentialPolicy

0 commit comments

Comments
 (0)