🗂

【Go】gRPCの認証Inerceptorで取得した情報を後続処理で使用する

2022/04/20に公開

概要

gRPCで特定のサービスに対し、認証処理を挟みたい場合は、Inerceptorを使うことが多いと思います。実装の概要等はgRPC-GoのMiddleware (Inerceptor) で認証/認可してみるの記事が参考になると思います。
今回はこのInerceptorで認証時に取得した情報を、後続で使いたい場合どうするかというのを書きたいと思います。

前提など

  • 今回はJwtトークンを検証する方法での認証とします。Jwtトークンの中に含めているuserIdを、後続処理に引き渡します。
  • Contextを介して、取得したデータを後続に引き渡します。

実装サンプル

まずはmainのコードです。grpcwebで起動していますが、別に使う必然性はないです。

main.go
func main() {
	// 後述の認証用Inerceptorを設定
	grpcServer := grpc.NewServer(
		grpc.UnaryInterceptor(
			grpc_middleware.ChainUnaryServer(
				service.AuthInterceptor,
			),
		),
	)
	// サービスの登録
	pb.RegisterAuthenticationUserServiceServer(grpcServer, service.NewAuthenticationUserService(dbEngine))
	pb.RegisterGeographicPointServiceServer(grpcServer, service.NewGeographicPointService(dbEngine))

	// grpcwebでの起動
	wrappedServer := grpcweb.WrapServer(
		grpcServer,
		grpcweb.WithOriginFunc(func(origin string) bool {
			return origin == os.Getenv("VIEW_DOMAIN")
		}),
	)
	mux := http.NewServeMux()
	mux.Handle("/", http.HandlerFunc(wrappedServer.ServeHTTP))
	hs := &http.Server{
		Addr:    ":8080",
		Handler: mux,
	}
	log.Fatal(hs.ListenAndServe())
}

以下が認証用のInerceptorです。後続でuserIdを使用できるように、 Contextにセットしてreturnしています。

AuthInterceptor.go
func AuthInterceptor(ctx context.Context,
	req interface{},
	info *grpc.UnaryServerInfo,
	handler grpc.UnaryHandler) (interface{}, error) {
	// 認証をスキップするパス
	if info.FullMethod == "/pb.AuthenticationUserService/Login" {
		return handler(ctx, req)
	} else {
	       	// 認証がOKならContextを返す
		userIdSetCtx, err := authorize(ctx)
		if err != nil {
			return nil, err
		}
		return handler(userIdSetCtx, req)
	}
}

// トークンから情報を取得してContextを返す
func authorize(ctx context.Context) (context.Context, error) {
	// ヘッダーのトークンからユーザIDを取得
	token, err := grpc_auth.AuthFromMD(ctx, "Bearer")
	if err != nil {
		return nil, err
	}
	// jwtのトークンを検証してuserIdを取得(処理の記載は割愛)
	userId, err := GetUserIdInfoFromJwtToken(token)
	if err != nil {
		return nil, err
	}

	// contextにユーザIDをセット
	return context.WithValue(ctx, "userId", userId)
}

以下がサービスでuserIdを使用する箇所です。Contextから取得します。

geographicPointService.go
func (s *GeographicPointService) AddGeographicPoint(ctx context.Context, r *pb.AddGeographicPointRequest) (*pb.RegsiterGeographicPointResponse, error) {
	if ctx.Err() == context.Canceled {
		return &pb.RegsiterGeographicPointResponse{}, fmt.Errorf("client cancelled: abandoning")
	}
	// contextからユーザID取得
	userId := ctx.Value("userId").(string)
	// 後続処理は記載割愛・・
	・
	・
	・
}

Discussion