🚪

gRPCとFirebase Authenticationで認証処理

2020/10/30に公開

はじめに

gRPCとFirebase Authenticationを使用したGoサーバでの認証処理のやり方を紹介します。認可処理についてはまた別記事で書く予定です。

Firebase Authentication

SDKの追加は以下のコマンドからできます。

go get firebase.google.com/go

Firebase Admin SDK for Go の初期化は以下を参考にします。
サーバーに Firebase Admin SDK を追加する

従来はoption.WithCredentialsFile()を使用した初期化が一般的だったようですが、GOOGLE_APPLICATION_CREDENTIALS 環境変数を使用する初期化を強くお勧めしているようです。

firebase.go
package middleware

import (
	"context"
	firebase "firebase.google.com/go"
	"firebase.google.com/go/auth"
)

type Firebase struct {
	Auth *auth.Client
}

func NewFirebase() (inst *Firebase, err error) {
	inst = new(Firebase)
	// GOOGLE_APPLICATION_CREDENTIALSで指定した認証情報ファイルを暗黙的に読み込む
	app, err := firebase.NewApp(context.Background(), nil)
	if err != nil {
		return
	}
	authInst, err := app.Auth(context.Background())
	if err != nil {
		return
	}

	inst.Auth = authInst
	return
}

認証処理

これは後から説明するgrpc_auth.UnaryServerInterceptorで引数に使用する認証メソッドを持つレシーバの定義です。先ほどのFirebaseをフィールドに持ちます。

package middleware

import (
	"context"
	grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
)

type Authentication struct {
	firebase Firebase
}

func NewAuthentication(firebase Firebase) *Authentication {
	authInst := new(Authentication)
	authInst.firebase = firebase
	return authInst
}

func (au *Authentication) Authenticate(ctx context.Context) (newCtx context.Context, err error) {

	idToken, err := grpc_auth.AuthFromMD(ctx, "bearer")

	// JWT の検証
	token, err := au.firebase.Auth.VerifyIDToken(context.Background(), idToken)
	if err != nil {
		return
	}
	// 引数のコンテキストにtokenというキーにJWTの情報を入れて返す
	newCtx = context.WithValue(ctx, "token", token.Claims)
	return
}

ちなみに、こちらの記事がわかりやすかったので参考として共有しておきます。Interceptorに関しての説明を読んでおくとこの後の処理の内容がスムーズにわかると思います。
gRPC-GoのMiddleware (Inerceptor) で認証/認可してみる

認証で使用するミドルウェアはGo gRPC Middlewareに定義してあるgrpc_authを使用します。

main.go
package main

import (
	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
	grpc_auth "github.com/grpc-ecosystem/go-grpc-middleware/auth"
	"google.golang.org/grpc"
	"google.golang.org/grpc/reflection"
	"log"
	pb "my-project/grpc"
	"my-project/middleware"
	"net"
)

func main() {

	listenPort, err := net.Listen("tcp", ":800")
	if err != nil {
		log.Fatalln(err)
	}

	// Firebaseインスタンスを生成
	firebase, err := NewFirebase()
	if err != nil {
		log.Fatalln(err)
	}
	// Authenticationインスタンスを生成
	auth = *NewAuthentication(*firebase)

	// serverに認証設定を追加
	server := grpc.NewServer(
		grpc_middleware.WithUnaryServerChain(
			grpc_auth.UnaryServerInterceptor(auth),
		),
		// Streamingを使用する場合は必要
		grpc_middleware.WithStreamServerChain(
			grpc_auth.StreamServerInterceptor(auth),
		))

	pb.MyProjectServer(server, &handler)

	reflection.Register(server)

	if err := server.Serve(listenPort); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

まとめ

簡単なgRPCとFirebase Authenticationを使用したGoサーバでの認証処理のやり方でした。認可についても似たようにInterceptorで実装できるので、また別の記事で描こうと思います。

Discussion