Go で gRPC サーバーを実装して、Azure AD B2Cで認証を試してみます。チュートリアルを参考に Azure AD B2C を準備します。
- チュートリアル:Azure Active Directory B2C テナントの作成
- チュートリアル:Azure Active Directory B2C に Web アプリケーションを登録する
- チュートリアル: 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
アクセスできました。