LoginSignup
7
6

More than 3 years have passed since last update.

Golang によるfirebase AuthenticationにおけるJWT解析

Last updated at Posted at 2019-11-03

概要

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
main.go

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に埋め込んだ関数を使用できる。

jwtDecode.go

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型で抽出される。

jwtFB.go

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
}
7
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6