概要
Firebase Authenticationの機能を使用した際、フロントエンドでJWTを取得できる。このJWTは、改ざんがされていないことを検知可能なので、バックエンドでは、このJWTを用いてユーザ情報を取得できる。
今回紹介するライブラリは、サーバーに送られてきた、以下の形式のJWTを分解し、そして、デコードすることでFirebaseにおけるユーザー情報を取得する処理をGolangで実装した。
{base64エンコードしたhead1er}.{base64エンコードしたclaims}.{署名}
発行者(今回は、Firebase)が鍵を使用して、JSONに署名することでトークンとして扱うことができる。また、鍵を使用して検証することで改善を検知できる。
claims部には、ユーザー名などの任意の情報を含めることができる。
git: k-washi/jwt-decode
インストール
go get -u github.com/k-washi/jwt-decode/jwtdecode
使用例
以下のプログラムを次のようなコマンドで実行した結果、firebaseに格納されたユーザIDとメールアドレスが取得できる。
> go run main.go
User ID: qZhsF2HfuWZEBghFa4nl2Kidyp22 ,Email: test@test.com
package main
import (
"fmt"
"log"
"github.com/k-washi/jwt-decode/jwtdecode"
)
func main() {
jwt := "eyJhbGciOiJSUzI1NiIsImtpZCI6ImEw ...." //自分で取得したJWTを記載する
//JWTをheader, claims, 署名に分解する。
hCS, err := jwtdecode.JwtDecode.DecomposeFB(jwt)
if err != nil {
log.Fatalln("Error : ", err)
}
//hCS[1] ユーザー情報が入ったclaims部分をユーザー情報に変換する。
/*
//FireBaseCustomToken auth.token add email
type FireBaseCustomToken struct {
auth.Token
Email string `json:"email"`
}
の構造にJWTを変換する。
auth.TokenにEmailを入れて拡張した構造体。
*/
payload, err := jwtdecode.JwtDecode.DecodeClaimFB(hCS[1])
if err != nil {
log.Fatalln("Error :", err)
}
//ユーザーIDと, メールアドレスを表示
user := payload.Subject
email := payload.Email
fmt.Printf("User ID: " + user + " ,Email: " + email + "\n")
解説
パッケージの型をjwtDecodeInterfaceで定義し、JwtDecodeとして公開。そのため、上記のようにjwtdecode.JwtDecodeでアクセスできる。
関数を使用する時、公開しているJwtDecodeに構造体jwtDecodeを格納することで、jwtDecodeに埋め込んだ関数を使用できる。
package jwtdecode
type jwtDecodeInterface interface {
DecomposeFB(string) ([]string, error)
DecodeClaimFB(string) (*FireBaseCustomToken, error)
}
var (
//JwtDecode jwt decode and parse user info in firebase
JwtDecode jwtDecodeInterface
)
type jwtDecode struct{}
func init() {
JwtDecode = &jwtDecode{}
}
以下は、jwtDecodeの構造体に埋め込んだ関数である。JWTを分解するDecomposeFBと、claims部分をデコードするDecodeClaimFBを実装している。デコードの結果が、FireBaseCustomToken型で抽出される。
package jwtdecode
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"strings"
"firebase.google.com/go/auth"
)
//FireBaseCustomToken auth.tokenをemailで拡張
/*
type Token struct{
Issuer string "json:\"iss\"";
Audience string "json:\"aud\"";
Expires int64 "json:\"exp\"";
IssuedAt int64 "json:\"iat\"";
Subject string "json:\"sub,omitempty\"";
UID string "json:\"uid,omitempty\"";
Claims map[string]interface{}
"json:\"-\""
}
*/
type FireBaseCustomToken struct {
auth.Token
Email string `json:"email"`
}
//DecomposeFB JWTをHeader, claims, 署名に分解
func (s *jwtDecode) DecomposeFB(jwt string) ([]string, error) {
hCS := strings.Split(jwt, ".")
if len(hCS) == 3 {
return hCS, nil
}
return nil, errors.New("Error jwt str decompose: inccorrect number of segments")
}
//DecodeClaimFB JWTのclaims部分をFireBaseCustomTokenの構造体にデコード
func (s *jwtDecode) DecodeClaimFB(payload string) (*FireBaseCustomToken, error) {
payloadByte, err := base64.RawURLEncoding.DecodeString(payload)
if err != nil {
return nil, errors.New("Error jwt token decode: " + err.Error())
}
var tokenJSON FireBaseCustomToken
err = json.Unmarshal(payloadByte, &tokenJSON)
if err != nil {
fmt.Println("Error: ", err)
return nil, errors.New("Error jwt token unmarshal: " + err.Error())
}
return &tokenJSON, nil
}