I added a custom TokenCredential
implementation called BearerTokenCredential
public class BearerTokenCredential : TokenCredential
{
private readonly IAccessTokenProvider _tokenProvider;
private readonly NavigationManager _navigation;
private readonly IEnumerable<string> _scopes;
public BearerTokenCredential(IAccessTokenProvider tokenProvider, NavigationManager navigation, IEnumerable<string> scopes)
{
_tokenProvider = tokenProvider;
_navigation = navigation;
_scopes = scopes;
}
public override Azure.Core.AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
var tokenResult = _tokenProvider.RequestAccessToken()
.AsTask()
.GetAwaiter()
.GetResult();
if (tokenResult.TryGetToken(out var token)) {
return new Azure.Core.AccessToken(token.Value, token.Expires);
}
else {
throw new AccessTokenNotAvailableException(_navigation, tokenResult, _scopes);
}
}
public override async ValueTask<Azure.Core.AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
{
var tokenResult = await _tokenProvider.RequestAccessToken()
.ConfigureAwait(false);
if (tokenResult.TryGetToken(out var token)) {
return new Azure.Core.AccessToken(token.Value, token.Expires);
}
else {
throw new AccessTokenNotAvailableException(_navigation, tokenResult, _scopes);
}
}
}
This service uses the IAccessTokenProvider
class to retrieve access tokens as described in the Blazor documentation for Security and Identity (Additional scenarios). The AccessTokenNotAvailableException
will redirect the user to be reauthenticated when thrown.
Then I register the ArmClient
service in my IServiceCollection
via the Dependency Injection extension methods as a singleton (as is recommended). I need to build the service provider here to access the IAccessTokenProvider
interface and NavigationManager
class.
var scopes = new List<string>() {
"https://management.azure.com/.default"
};
builder.Services.AddAzureClients(b => {
var sp = builder.Services.BuildServiceProvider();
var tokenProvider = sp.GetRequiredService<IAccessTokenProvider>();
var navigation = sp.GetRequiredService<NavigationManager>();
b.AddClient<ArmClient, ArmClientOptions>(o => {
return new ArmClient(new BearerTokenCredential(tokenProvider, navigation, scopes));
});
});
Then finally I can inject the ArmClient
in my pages and use it.
@inject ArmClient client;
@code {
private List<SubscriptionResource> subscriptions = new();
protected override async Task OnInitializedAsync()
{
subscriptions = await client.GetSubscriptions()
.GetAllAsync()
.ToListAsync();
}
}
However, I am unsure about the BearerTokenCredential
implementation, especially the synchronous GetToken
method and the building of the IServiceProvider
in Program.cs
, maybe someone else can weigh in on that.