GolangでFirebase Admin SDKの認証周りを試しました

## はじめに
[ｋ．ｓ．ロジャース](https://www.wantedly.com/companies/ks-rogers)の西谷です。

担当のプロジェクトでFirebase Authenticationを使うことになりました。
社内で利用しているプロジェクトは多数あるのですが、私自身利用するのが初めてになります。
知見がほぼ無いため今回は実際に手を動かしてFirebase Authenticationを使ってみようと思います。

間違い等があれば、コメントにてご指摘頂けたらと思います。

## 環境構築
1. Go言語の開発環境を構築します(こちらは他解説に任せます)
2. `go get firebase.google.com/go`でSDKを導入します
3. Firebaseのプロジェクトを作成します
4. プロジェクトの設定から秘密鍵を生成します
<img width="1016" alt="スクリーンショット 2019-10-16 0.52.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/274674/deb517e5-9b81-35ba-6596-23b73850e069.png">

5. 秘密鍵はSDKで利用しますので保存します


## Firebaseを使うための初期化
Firebase利用のための初期化です。以降の処理で`app, client`を利用します。

```golang
ctx := context.Background()

opt := option.WithCredentialsFile("secretkey.json")
app, err := firebase.NewApp(context.Background(), nil, opt)
if err != nil {
    log.Panic(fmt.Errorf("error initializing app: %v", err))
}

client, err := app.Auth(ctx)
if err != nil {
    log.Fatalf("error getting Auth client: %v\n", err)
}
```
## ユーザのCRUD
サンプルコードは[こちら](https://github.com/firebase/firebase-admin-go/blob/master/snippets/auth.go)でほぼ網羅されています。

本記事ではよく使う機能と実行結果について記載しようと思います。
### ユーザ作成
サンプルの`createUser`を実行します。レスポンスの`UserInfo`に登録情報が入っています。

```golang
func createUser(ctx context.Context, client *auth.Client) *auth.UserRecord {
	params := (&auth.UserToCreate{}).
		Email("user@example.com").
		EmailVerified(false).
		PhoneNumber("+15555550100").
		Password("secretPassword").
		DisplayName("John Doe").
		PhotoURL("http://www.example.com/12345678/photo.png").
		Disabled(false)
	u, err := client.CreateUser(ctx, params)
	if err != nil {
		log.Fatalf("error creating user: %v\n", err)
	}
	log.Printf("Successfully created user: %#v\n", u.UserInfo)

	return u
}
```

```bash:result
2019/10/16 02:20:39 UserInfo: &auth.UserInfo{DisplayName:"John Doe", Email:"user@example.com", PhoneNumber:"+15555550100", PhotoURL:"http://www.example.com/12345678/photo.png", ProviderID:"firebase", UID:"nlF8TIkKwYg0upBbfdJ8dDVJtkJ3"}
```

### ユーザ取得
`UID`を入れるとユーザデータを取得できます。

```golang
func getUser(ctx context.Context, client *auth.Client, uid string) *auth.UserRecord {
	u, err := client.GetUser(ctx, uid)
	if err != nil {
		log.Fatalf("error getting user %s: %v\n", uid, err)
	}
	log.Printf("Successfully fetched user data: %#v\n", u.UserInfo)

	return u
}
```

```bash:result
2019/10/16 02:26:07 Successfully fetched user data: &auth.UserInfo{DisplayName:"John Doe", Email:"user@example.com", PhoneNumber:"+15555550100", PhotoURL:"http://www.example.com/12345678/photo.png", ProviderID:"firebase", UID:"nlF8TIkKwYg0upBbfdJ8dDVJtkJ3"}
```

`getUserByEmail`を使うことでメールアドレス検索も出来ます。

### ユーザ更新

```golang
func updateUser(ctx context.Context, client *auth.Client, uid string) {
	params := (&auth.UserToUpdate{}).
		Email("updated@example.com").
		EmailVerified(true).
		PhoneNumber("+15555550100").
		Password("newPassword").
		DisplayName("John Doe").
		PhotoURL("http://www.example.com/12345678/photo.png").
		Disabled(true)
	u, err := client.UpdateUser(ctx, uid, params)
	if err != nil {
		log.Fatalf("error updating user: %v\n", err)
	}
	log.Printf("Successfully fetched user data: %#v\n", u.UserInfo)
}
```

```bash:result
2019/10/16 02:41:55 Successfully fetched user data: &auth.UserInfo{DisplayName:"John Doe", Email:"updated@example.com", PhoneNumber:"+15555550100", PhotoURL:"http://www.example.com/12345678/photo.png", ProviderID:"firebase", UID:"nlF8TIkKwYg0upBbfdJ8dDVJtkJ3"}
```

### ユーザ削除
```golang
func deleteUser(ctx context.Context, client *auth.Client, uid string) {
	err := client.DeleteUser(ctx, uid)
	if err != nil {
		log.Fatalf("error deleting user: %v\n", err)
	}
	log.Printf("Successfully deleted user: %s\n", uid)
}
```

```bash:result
2019/10/16 02:49:28 Successfully deleted user: nlF8TIkKwYg0upBbfdJ8dDVJtkJ3
```

## カスタムクレーム
ユーザにカスタムクレームを持たせる必要があったため、こちらも試しました。

````golang
func customClaimsSet(ctx context.Context, client *auth.Client, uid string) {
	claims := map[string]interface{}{"admin": true}
	err := client.SetCustomUserClaims(ctx, uid, claims)
	if err != nil {
		log.Fatalf("error setting custom claims %v\n", err)
	}
}
```

`user.CustomClaims["admin"]`でカスタムクレームを取得することが出来ます。

また、カスタムトークンにクレームを埋め込む場合は次のようにします。

```golang
func createCustomTokenWithClaims(ctx context.Context, client *auth.Client, uid string) string {
	claims := map[string]interface{}{
		"premiumAccount": true,
	}

	token, err := client.CustomTokenWithClaims(ctx, uid, claims)
	if err != nil {
		log.Fatalf("error minting custom token: %v\n", err)
	}

	log.Printf("Got custom token: %v\n", token)
	return token
}
```

実行後にJavaScriptから設定したクレームを確認できます。

```js
firebase.auth().signInWithCustomToken(token)
.then( user => {
    firebase.auth().currentUser.getIdTokenResult().then((idTokenResult) => {
        console.log(idTokenResult.claims);
    })
});
```


## おわりに
今回はFirebase Admin SDKの認証周りについて試しました。
他にも多数機能があり、今後使う機会も多くなると思うので次の機会に試せたらと思います。

間違い等あればご指摘頂けたらと思います。

### Wantedlyでもブログ投稿してます
Techブログに加えて会社ブログなどもやっているので、気になった方はぜひ覗いてみてください。
https://www.wantedly.com/companies/ks-rogers