Edited at

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


はじめに

こんな感じのものを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を中心に勉強しようと思います。


参考記事