Firebase Authentication生成JWTをGoサーバでバリデーションする
はじめに
現在(2018年11月12日時点)のFirebase Authenticationが提供する認証用のトークンはJWTです。
公式ドキュメントにもあるように、Firebaseでは各種言語のSDKが用意されており、Go言語用のSDKも提供されています。
本記事では上記SDKを利用しない、Goの代表的なJWT用サードパーティのjwt-goを利用したバリデーション実装方法を示します。
そもそもJWTとは何なのか、どう使うのかに関しては公式ドキュメントや他記事をご参照ください。
環境
- Go: go version 1.11
- jwt-go: v3.2.0
- MacOS: High Sierra 10.13.6
Firebase AuthenticationのJWT用公開鍵利用について
JWTのバリデーションをするには発行元が提供する公開鍵が必要です。
こちらに記載されているように、Firebase発行の公開鍵はこのリンク上にJSON形式で複数あり、検証側は始めに利用するべき公開鍵を特定するためのkid(Key ID)をJWTから取得する必要があります。
実装の流れとしては以下のようになります。
- リクエストにあるJWTをJSONとしてパースしkidの値を得る。
- Firebaseの公開鍵一覧から取得したkidに対応する公開鍵を特定する。
- 公開鍵を利用してJWTを検証する。
実際のコード
動くHTTPサーバを作りました。
ファイル構成は以下になります。
.
├── auth.go <= JWTバリデーションをする。
├── helpers.go <= googleapis_key.jsonから公開鍵リストを取得する。
├── main.go <= HTTPサーバを立てる。
└── publicKeys
└── googleapis_keys.json
package main
import (
"log"
"net/http"
"github.com/gorilla/pat"
)
type regularHandler struct{}
func (h *regularHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("200 - OK!"))
}
func main() {
// 公開鍵JSONからkid対応したmapを取得。
publicKeys := LoadRSAPublicKeyFromJsonFile("./publicKeys/googleapis_keys.json")
// Route setting
p := pat.New()
p.Add("GET", "/", MustAuth(®ularHandler{}, publicKeys))
// Launch web server
log.Fatal(http.ListenAndServe(":5000", p))
}
package main
import (
"net/http"
"crypto/rsa"
"log"
"fmt"
"github.com/dgrijalva/jwt-go"
"github.com/dgrijalva/jwt-go/request"
)
func MustAuth(handler http.Handler, publicKeys map[string]*rsa.PublicKey) http.Handler {
return &authHandler{next: handler, publicKeys: publicKeys}
}
type authHandler struct {
next http.Handler
publicKeys map[string]*rsa.PublicKey
}
func (h *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// サンプルのトークンを利用する。【注意】↓のJWTのトークンはダミーなので実際にFirebaseから発行されたJWTを利用ください。
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
// Authorizationヘッダーにトークンが付与されている想定。
r.Header.Set("Authorization", fmt.Sprintf("Bearer %v", tokenString))
// JWTをパースしてFirebase定義のkidの値を得る。この時点ではJWTとしての検証は行わない(行えない)。
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
return []byte("DummyBytes"), nil
})
if err.Error() != "key is of invalid type" {
log.Println("Can't parse tokenString correctly. Error message:", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
kid, ok := token.Header["kid"].(string)
if !ok {
log.Println("Can't get token.Header['kid']")
w.WriteHeader(http.StatusInternalServerError)
return
}
// 取得したkidに対応する公開鍵を利用してJWTを検証する。
token, err = request.ParseFromRequest(r, request.AuthorizationHeaderExtractor, func(*jwt.Token) (interface{}, error) {
return h.publicKeys[kid], nil
})
if err != nil {
log.Println("Can't Parse from request. Error message:", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
if !token.Valid {
log.Println("Request token is invalid. token.Header:", token.Header, "token.Claims", token.Claims)
w.WriteHeader(http.StatusUnauthorized)
return
}
log.Println("Got an valid token. token.Header:", token.Header, "token.Claims", token.Claims)
// 検証OKならば本来のHTTPハンドリングへ。
h.next.ServeHTTP(w, r)
}
package main
import (
"crypto/rsa"
"io/ioutil"
"encoding/json"
"github.com/dgrijalva/jwt-go"
)
func LoadRSAPublicKeyFromJsonFile(location string) map[string]*rsa.PublicKey {
jsonData, err := ioutil.ReadFile(location)
if err != nil {
panic(err.Error())
}
var objMap map[string]string
err = json.Unmarshal(jsonData, &objMap)
if err != nil {
panic(err.Error())
}
result := make(map[string]*rsa.PublicKey)
for k, v := range objMap {
key, err := jwt.ParseRSAPublicKeyFromPEM([]byte(v))
if err != nil {
panic(err.Error())
}
result[k] = key
}
return result
}
参考
Author And Source
この問題について(Firebase Authentication生成JWTをGoサーバでバリデーションする), 我々は、より多くの情報をここで見つけました https://qiita.com/momotaro98/items/9407dd2ef66256761a7f著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .