Dockerでローカル環境を楽に整えつつ、GAEをベースにしたアプリ開発ができれば最高だなあ。と常々思っていたので、今回はそれについて書きつつ、実践的なGoogleOAuth認証のアプリを作っていきたいと思います。

すぐにでもコードを見たい方はこちらをどうぞ。

https://github.com/gotokatsuya/gae-google

GAEとdepのDocker環境を整える

まずは DockerCompose をインストールしましょう。

公式サイトを参考にインストールしてください。

インストールできたら次はDockerイメージとComposeファイルを用意します。

今回はGAEベースのアプリケーションを作りたいので、mercariさんが提供してくれているGEAのDockerイメージを利用します。

加えて、ベンダリングのツールには「dep」を利用するので、いい感じのDockerイメージを探してきてComposeファイルに追加します。

docker-compose.yaml
version: '2'
services:
  app:
    image: mercari/appengine-go:1.8
    working_dir: /go/src/github.com/gotokatsuya/gae-google
    command: make serve-dev
    ports:
      - 8080:8080
      - 8000:8000
    volumes:
      - .:/go/src/github.com/gotokatsuya/gae-google
  dep:
    image: instrumentisto/dep:0.3.2
    working_dir: /go/src/github.com/gotokatsuya/gae-google
    volumes:
      - .:/go/src/github.com/gotokatsuya/gae-google

このようなdocker-compose.ymlを用意するだけでローカル開発が最高に楽になります。

GoogleOAuth認証アプリを作る

GoogleOAuth認証設定

コードを書く前にGoogleOAuthの設定をして、ClientIDとClientSecretを取得、そしてRedirectURLをセットします。設定方法は公式サイトが参考になります。

今回のアプリケーションではRedirectURLはhttp://localhost:8080/auth/google/callbackを指定してください。ClientIDとClientSecretはapp.yamlに環境変数として記載します。

app.yml
application: gae-google
version: 1
runtime: go
api_version: go1.8
handlers:
- url: /.*
  script: _go_app
  secure: always
env_variables:
  AUTH_GOOGLE_CLIENT_ID: "xxx"
  AUTH_GOOGLE_CLIENT_SECRET: "xxx"
  AUTH_GOOGLE_REDIRECT_URL: "http://localhost:8080/auth/google/callback"

実際にコードを書いていく

まずはRoutingから作ります。

routes.go
package routes

import (
    "net/http"

    "github.com/gorilla/mux"
    "github.com/urfave/negroni"

    "github.com/gotokatsuya/gae-google/app/controllers"
)

var (
    App = negroni.New(negroni.NewRecovery())
)

func init() {
    router := mux.NewRouter()

    authRouter := router.PathPrefix("/auth").Subrouter()
    authRouter.Path("/login").HandlerFunc(controllers.AuthLoginHandler).Methods(http.MethodGet)
    authRouter.Path("/google").HandlerFunc(controllers.AuthGoogleHandler).Methods(http.MethodGet)
    authRouter.Path("/google/callback").HandlerFunc(controllers.AuthGoogleCallbackHandler).Methods(http.MethodGet)

    App.UseHandler(router)
}

Contollerを作っていきます。

controller.go
package controllers

import (
    "net/http"

    "google.golang.org/appengine"

    "github.com/gotokatsuya/gae-google/lib/template"

    "github.com/gotokatsuya/gae-google/app/services/auth"
)

// AuthLoginHandler HTMLをレンダリングする
func AuthLoginHandler(w http.ResponseWriter, r *http.Request) {
    template.Render.HTML(w, http.StatusOK, "auth/login", nil)
}

// AuthGoogleHandler Googleログイン
func AuthGoogleHandler(w http.ResponseWriter, r *http.Request) {
    svc := auth.NewService()
    // ランダム値を生成する
    state := svc.GenerateState()
    // CallbackするURLを生成する
    url := svc.GoogleAuthURL(state)
    // 生成したランダム値ををセッションに保存する
    svc.SaveState(state, r, w)
    http.Redirect(w, r, url, http.StatusFound)
}

// AuthGoogleCallbackHandler Googleログイン認証結果
func AuthGoogleCallbackHandler(w http.ResponseWriter, r *http.Request) {
    ctx := appengine.NewContext(r)
    svc := auth.NewService()
    // セッションに保存されているランダム値と比較して一致するかどうか確認
    if r.URL.Query().Get("state") != svc.State(r) {
        http.Error(w, "invalid state", http.StatusUnauthorized)
        return
    }
    // GoogleログインしたユーザーのメールアドレスやIDを取得する
    if err := svc.GoogleLogin(ctx, r.URL.Query().Get("code")); err != nil {
        http.Error(w, err.Error(), http.StatusUnauthorized)
        return
    }

    // 適当な画面に飛ばす
    http.Redirect(w, r, "https://eure.jp", http.StatusFound)
}

DockerComposeを使って起動する

最初に記載したように今回はdepを使って依存ライブラリをベンダリングします。今回はvendorフォルダをgit管理しているので実行する必要はないですが、initを実行するとvendorフォルダが作られ、ライブラリをベンダリングしてくれるようになります。

// $ docker-compose run --rm dep init

GAEに載せるアプリケーションを起動します。

$ docker-compose up app

起動後に http://localhost:8080/auth/login へアクセスするとこのようなシンプルな画面がレンダリングされるはずです。

スクリーンショット 2017-12-09 0.17.14.png

「Google Login」ボタンを押下するとログイン画面が立ち上がります。ログインに成功すると、適当に設定したeurekaのHPに遷移します。

スクリーンショット 2017-12-09 0.35.47.png