.NET (C#) で Azure系のSDKを使うときに認証をどうにかしたい

.NET系(C#)でAzure系の公式SDKを使う場合、最初にクライアントを生成してそこにCredentialを渡すと思います。今回はそのあたりをちょっと弄りたいと思います。

よくあるのは ApiKeyCredential でAPIキーの文字列を渡すタイプですね。例えば下のようなやつです。

var client =
    new AzureOpenAIClient(
        new Uri(openai_endpoint),
        new ApiKeyCredential(key)
    );

Azure系でManaged Identities使ったり、ローカルのAzure CLIの認証情報使ったりとそちらの挙動に任せたりする場合は下記みたいに DefaultAzureCredential を使ったりしますよね。

var client =
    new AzureOpenAIClient(
        new Uri(openai_endpoint),
        new DefaultAzureCredential()
    );

今回はそれらのシナリオに合わない時にどうにかする話です。具体的な例を挙げておきます。Azure OpenAI Serviceを使っていて、Azure API Management(APIM)でAPIの入り口を集約しているとします。またAPIMからAzure OpenAI ServiceへのアクセスそのものはManaged Identitiesを使います。
この状況で、ユーザー(クライアントアプリ)からAPIMへアクセスする際はアプリで取得したJWTなアクセストークンを使ってAPIMで検証、アクセス可否を判断したい場合を想定します。

アプリ内ではOpenAIとか関係なく認証して例えば社内のAPIサーバーにアクセスすることができるアクセストークンを持ってたりする状況で、それを使ってAPIMで管理されてるAzure OpenAIを呼び出したいみたいなケースです。(Azure OpenAI側のIAMですればいいのでは?とかはとりあえず無視しときます。アカウントのテナントとリソースのテナント違ってたら面倒だし、特権でもないタダの利用でIAMいじくりまわしたく無いです)

さてじゃぁ既にアクセストークンがあるような状況でどうしたらいいのか?という話ですが結論から言うと Azure.Core.TokenCredential を継承したカスタム用のTokenCredentialを作って実装すればOKです。

public class CustomCredential : Azure.Core.TokenCredential
{
    public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)
    {
        return new AccessToken("<独自トークン>", DateTimeOffset.UtcNow.AddHours(1));
    }

    public override async ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken)
    {
        return new AccessToken("<独自トークン>", DateTimeOffset.UtcNow.AddHours(1));
    }
}

みたいなクラスを作って他のCredentialの代わりに渡すだけです。

var client =
    new AzureOpenAIClient(
        new Uri(openai_endpoint),
        new CustomCredential()
    );

あとは必要な時にGetToken()/GetTokenAsync()が呼び出されるので、よしなにアクセストークンなり渡してあげればOKです。RefreshOn引数も渡したりしておけば適宜更新処理も(再呼び出しも)してくれると思います。

基本的にはSDKで提供されているTokenCredentialの派生やDefaultAzureCredentialを使うのが手間がないですが、こういうやり方も一応できるということで。

余談ですがDefaultAzureCredentialは資格情報をチェーンするので、ReleaseビルドでAzure上にデプロイするなど動作環境が確定している場合は ManagedIdentityCredential を渡すとかチェーンさせずに固定するほうがオーバーヘッドが少ないと思います。

コメントを残す