327
324

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vue.js + Firebase + golangでログインシステムを作ってみた備忘録

Last updated at Posted at 2019-01-18

はじめに

こんな感じのものをvue.js + Firebase + golangで作ってみました。
demo2.mov.gif

golangは多少慣れていますが、vueとfirebaseは初めてのだったので備忘録を残しておきます。
特にフロントはほとんど経験もなく、vueも今回が初めてなので参考資料のソースにはすごく助けられました。

何を作ったのか?

ログイン(メール認証・Google認証・Twitter認証・Facebook認証)認証ができます。
発行したJWTをHeader情報に詰め込んでAPIサーバにリクエストを投げます。
APIサーバは許可されたJWTかの確認を行い、問題なければレスポンスを返すといったものです。

個人でWEBサービスを作った時にログイン画面っているだろうから、ちょこっと触って慣れておこう
と思い学んでみました。なので作りは雑です。

ソースコードはgithubに載せています。

JWT

JSON Web Tokenの略で、電子署名が出来るURL-safeなトークンです。
めっちゃ大雑把に言うと**「改竄できない鍵」**みたいなもの。

今回のシステムでどう使われているかというと、
クライアント側でJWTを発行し、サーバ側で改竄チェックをしています。

{Base64エンコードされたヘッダ}.{Base64エンコードされた JSON の中身}.{電子署名}

JWTの中身はこのようになっているので、クライアントからID情報などをJWTに詰めたりすることも出来ます。

以下、参考資料

クライアント

Firebaseの設定

FirebaseコンソールのWEB用のKEYを取得して、config/dev.env.jsに記載。

module.exports = merge(prodEnv, {
  NODE_ENV: '"xxx"',
  YOUR_KEY: '"xxx"',
  YOUR_DOMAIN: '"xxx"',
  YOUR_ID: '"xxx"',
  YOUR_BUCKET_ID: '"xxx"',
  YOUR_SENDER_ID: '"xxx"',
})

main.jsで設定値を読み込みFirebaseを使えるようにする。

const config = {
  apiKey: process.env.YOUR_KEY,
  authDomain: process.env.YOUR_DOMAIN + '.firebaseapp.com',
  databaseURL: process.env.YOUR_DOMAIN + '.firebaseio.com',
  projectId: process.env.YOUR_ID,
  storageBucket: process.env.YOUR_BUCKET_ID + '.appspot.com',
  messagingSenderId: process.env.YOUR_SENDER_ID
}
firebase.initializeApp(config)

これだけでFirebaseが使えるようになるってすごい。

認証方法

Firebaseが提供しているプロバイダー認証は多くありますが、その中でも以下の4つを今回は採用し、
実装してみました。

メール認証

Sign inの時
signInWithEmailAndPasswordでemailとpasswordを送るだけでログインできます。
ログイン後にローカルストレージにjwtを保存し、home画面にリダイレクトしています。

firebase.auth().signInWithEmailAndPassword(this.email, this.password).then(res => {
    res.user.getIdToken().then(idToken => {
        localStorage.setItem('jwt', idToken.toString())
    })
    this.$router.push('/')
}

Sign upの時
createUserWithEmailAndPasswordでemailとpasswordを送るだけで登録されます。
以下の実装では登録後にSign inし、ローカルストレージにjwtを保存し、home画面にリダイレクトしています。

firebase.auth().createUserWithEmailAndPassword(this.email, this.password).then(res => {
    console.log('Create account: ', res.user.email)
    firebase.auth().signInWithEmailAndPassword(this.email, this.password).then(r => {
        r.user.getIdToken().then(idToken => {
        localStorage.setItem('jwt', idToken.toString())
        })
        this.$router.push('/')
    }, err => {
        alert(err.message)
    })
})

Google認証

const provider = new firebase.auth.GoogleAuthProvider()
firebase.auth().signInWithPopup(provider).then(res => {
res.user.getIdToken().then(idToken => {
    localStorage.setItem('jwt', idToken.toString())
})
this.$router.push('/')
}

Facebook認証

Facebookのdeveloper画面から各種設定や登録する必要があります。
https://developers.facebook.com/

const provider = new firebase.auth.FacebookAuthProvider()
firebase.auth().signInWithPopup(provider).then(res => {
res.user.getIdToken().then(idToken => {
    localStorage.setItem('jwt', idToken.toString())
})
this.$router.push('/')
}

Twitter認証

Twitterのdeveloper画面から各種設定や登録する必要があります。
https://developer.twitter.com/en.html

const provider = new firebase.auth.TwitterAuthProvider()
firebase.auth().signInWithPopup(provider).then(res => {
res.user.getIdToken().then(idToken => {
    localStorage.setItem('jwt', idToken.toString())
})
this.$router.push('/')
}

JWTをサーバへ送信

privateの時のみログインや登録時にローカルストレージに保存したjwtを取得しヘッダ情報に付与し、リクエストしています。

apiPublic: async function () {
    let res = await axios.get('http://localhost:8000/public')
    this.msg = res.data
},
apiPrivate: async function () {
    let res = await axios.get('http://localhost:8000/private', {
    headers: {'Authorization': `Bearer ${localStorage.getItem('jwt')}`}
    })
    this.msg = res.data
}

その他

Vuetifyを使ってデザインは作ってみました。ブートストラップみたいにコンポーネントが色々用意されているので簡単に使えました。

JWTをCookie、ローカルストレージのどちらに保存するか迷いましたが、
調べてもどっちでも問題ないような情報が多かったため、そこまで考えずにローカルストレージにしました。
どっちがいいいのかは自分なりの答えをどこかで考えたいです。

サーバ

CORS設定

最初フロントからAPIを呼び出してもうまく動きませんでした。
理由はCORS (Cross-Origin Resource Sharing)制約によるものでした。
APIサーバ側で許可をしてあげる必要があります。

e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
    AllowOrigins: []string{"http://localhost:8081"},
    AllowHeaders: []string{echo.HeaderAuthorization},
    AllowMethods: []string{echo.GET},
}))

今回はechoを使ったためこんな感じで設定しました。

以下、参考資料

JWTの取得&有効確認

FirebaseのSDKを使用し、JWTの改竄チェックを行いました。

// FirebaseのSDKを使用するためのkeyを読み込み
opt := option.WithCredentialsFile("/Users/yoshinori.hisakawa/keys/firebase-key.json")
app, err := fire.NewApp(context.Background(), nil, opt)
if err != nil {
    fmt.Printf("error: %v\n", err)
    os.Exit(1)
}
auth, err := app.Auth(context.Background())
if err != nil {
    fmt.Printf("error: %v\n", err)
    os.Exit(1)
}

// クライアントから送られてきた JWT 取得
authHeader := c.Request().Header.Get("Authorization")
idToken := strings.Replace(authHeader, "Bearer ", "", 1)

// JWT の検証
token, err := auth.VerifyIDToken(context.Background(), idToken)
if err != nil {
    u := fmt.Sprintf("error verifying ID token: %v\n", err)
    return c.JSON(http.StatusBadRequest, u)
}

最後に

1日かけて色々試してみました。
作りこむ必要はありますが、なんとなくログイン機能を実装する場合のイメージが湧きました。
フロント書けるようになればもっと出来ること広がるな-と思ったのでvueを中心に勉強しようと思います。

参考記事

327
324
3

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
327
324

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?