1923

都内と業界の隅っこで生活しているエンジニアのノート

Go の gRPC サーバーで Azure AD B2C の JWT 使ってみる

Go で gRPC サーバーを実装して、Azure AD B2Cで認証を試してみます。チュートリアルを参考に Azure AD B2C を準備します。

  1. チュートリアル:Azure Active Directory B2C テナントの作成
  2. チュートリアル:Azure Active Directory B2C に Web アプリケーションを登録する
  3. チュートリアル: Azure Active Directory B2C でユーザー フローとカスタム ポリシーを作成する

実装です。gRPCサーバー作成時に認証のInterceptor設定します。

   server := grpc.NewServer(grpc.UnaryInterceptor(grpc_auth.UnaryServerInterceptor(authenticate)))

認証処理でJWTトークンを取得。JWTの処理は jwt-go を使用しています。

func authenticate(ctx context.Context) (context.Context, error) {
    bearer, err := grpc_auth.AuthFromMD(ctx, "bearer")
    if err != nil {
        return nil, err
    }

    token, err := jwt.Parse(bearer, getKey)
    if err != nil {
        return nil, err
    }

    claims := token.Claims.(jwt.MapClaims)
    for key, val := range claims {
        fmt.Printf("%s\t%v\n", key, val)
    }

    return ctx, nil
}

jwkの処理は jwx を使って対応します。urlの「TENANT-NAME」と「POLICY-NAME」は適切な値に修正が必要です。

func getKey(token *jwt.Token) (interface{}, error) {
    url := "https://TENANT-NAME.b2clogin.com/TENANT-NAME.onmicrosoft.com/POLICY-NAME/discovery/v2.0/keys"
    set, err := jwk.Fetch(context.Background(), url)
    if err != nil {
        return nil, err
    }

    keyID, ok := token.Header["kid"].(string)
    if !ok {
        return nil, errors.New("kid が Header にありません。")
    }
    key, ok := set.LookupKeyID(keyID)
    if ok {
        var rawKey interface{}
        if err := key.Raw(&rawKey); err != nil {
            return nil, err
        }
        return rawKey, nil
    }

    return nil, fmt.Errorf("key(%q) が見つかりません。", keyID)
}

実装が終わりましたので、grpcurlで実行してみます。トークンは、Azureポータルでユーザーフローを実行して https://jwt.ms で確認も可能です。ユーザー フローをテストする が参考になります。

grpcurl -plaintext  -H "Authorization: Bearer **トークン**" localhost:9000 example.EchoService.Test 

アクセスできました。

f:id:taka1923:20210907202651p:plain