まとめ
- Node.js実装をそのままGoにポートした
- FCM周りはサーバKeyだけ取得すればOK
前提
- Projectを取得していること
- GCMのAPIを有効化していること
- Projectを取得していること
Client
ここを見てその通りに実装します。
注) p256dhとauthはclientかserverのどちらかでsafe URLに置換する必要があります。
概要は以下のような感じです
- Event Listenerを登録する
- Server Workerでsubscriptionを取得する
- GAEにsubscriptionを送信する
- GAEのデータストアにsubscription情報を保存する
Server
とりあえずPUSH通知だけしたい場合
AuthorizationヘッダにFCMのServer Keyを設定してFCMサーバ(https://fcm.googleapis.com/fcm/send) にPOSTするだけ
Payload付きでPUSH通知したい場合
Payloadを暗号化する必要があります
Node.jsの暗号化実装
https://github.com/GoogleChrome/web-push-encryption
自分はGoで使いたかったのですが見当たらなかったので用意しました
https://github.com/koichiroo/webpushencrypto/blob/master/encrypto.go
paddingが0固定になっているのでその内直します。。
以下使い方です。環境はGAEを想定しています。
import "github.com/koichiroo/webpushencrypto"
const (
fcm = "https://fcm.googleapis.com/fcm/send"
gcm = "https://android.googleapis.com/gcm/send"
serverKey = "プロジェクトによる"
)
func webpush() error {
encrypt, err := webpushencrypto.GetEncryptoMessage(key, auth, payload)
if err != nil {
return err
}
safeSalt := convertSafeURL(base64.URLEncoding.EncodeToString(encrypt.Salt))
safeKey := convertSafeURL(base64.URLEncoding.EncodeToString(encrypt.PublickKey))
endpoint := strings.Replace(sub.Endpoint, gcm, fcm, -1)
req, err := http.NewRequest("POST", endpoint, bytes.NewReader(encrypt.Payload))
if e != nil {
return
}
defer req.Body.Close()
req.Header.Set("Authorization", strings.Join([]string{"key", serverKey}, "="))
req.Header.Set("Encryption", fmt.Sprintf("salt=%s", safeSalt))
req.Header.Set("Crypto-Key", fmt.Sprintf("dh=%s", safeKey))
req.Header.Set("Content-Encoding", "aesgcm")
req.Header.Set("Content-Type", "application/json")
req.Header.Set("TTL", "0")
res, err := urlfetch.Client(ctx).Do(req)
if err != nil {
return
}
defer res.Body.Close()
}
func convertToSafeURL(s string) string {
s = strings.Replace(s, "+", "-", -1)
s = strings.Replace(s, "/", "_", -1)
s = strings.Replace(s, "=", "", -1)
return s
}
keyはsubscriptionのp256dh、authはsubscriptionのauthです。
Server KeyはFirebaseのプロジェクトを作成すれば自動的に取得されているものです。
endpointはService WorkerがandroidのURLを取得してくるのでFCM用に置換しています。
payloadは何でもいいですが、今回はMarshal済のJSONを想定しています。
感想
わかってしまえば簡単なのですが、意外とハマりました。
Crypto-Key
ヘッダのdh=
をdf=
でずっと送信していた。。typoは怖い