Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Google App Engine for GoでOAuth2による認証を用意する

More than 3 years have passed since last update.

要点

Google App EngineでGoogle AccountによるOAuth2認証を用意します。
実例のあるOpenID Connectを使用します。
App Engineは基本無料なので安心して使えます。
Enable Billingのようなものが出てきたら速攻で脱出してください :scream:

OAuth2 for App Engine

GoでのOAuth認証手順。
https://cloud.google.com/appengine/docs/go/oauth/

実例があるOpenID Connectで用意します
https://developers.google.com/identity/protocols/OpenIDConnect

Google Developer ConsoleでAPI Credentialを取得

  1. https://console.cloud.google.com
  2. サイドバーのAPI Managerをクリック
  3. Credentialsをクリック
  4. Create credentialsをクリック
  5. OAuth client IDをクリック
  6. Web applicationを選択後、createをクリック
  7. 作成されたclientをクリックしてClient IDClient secretをメモ
  8. 念のためAuthorized redirect URIsにエンドポイントを指定する

実装

使用したLibraryはこちらになります。
未インストールのものはgo get -u -vしてください。

import (
    "fmt"
    "net/http"
    "strings"

    "github.com/satori/go.uuid"
    "golang.org/x/oauth2"
    v2 "google.golang.org/api/oauth2/v2"
    "google.golang.org/appengine"
    appenginelog "google.golang.org/appengine/log"
)

まずは、OAuth2用のConfigを作成します。

var (
    conf = oauth2.Config{
        ClientID:     "先ほどのClient ID",
        ClientSecret: "先ほどのClient secret",
        Scopes:       []string{"openid", "email", "profile"}, // 6/19 update: openidのscopeが漏れていたので追加
        Endpoint: oauth2.Endpoint{
            AuthURL:  "https://accounts.google.com/o/oauth2/v2/auth",
            TokenURL: "https://www.googleapis.com/oauth2/v4/token",
        },
    }
    // 後で使います
    // 6/11 update:globalで持つのは誤りでした
    state string
)

次に認証用のhandlerを作成します。

func init() {
    http.HandleFunc("/api/oauth2", oauth2Handler)
    http.HandleFunc("/oauth2callback", tokenHandler)
}

func oauth2Handler(w http.ResponseWriter, r *http.Request) {
    // ランダムな文字列作成に
    // github.com/satori/go.uuid
    // を使用しています
    state = uuid.NewV4().String()

    // 6/11 update:stateをredirect後と比較する場合はcookieに入れるのが無難です
    sc := &http.Cookie{
        Name:   "hogehoge",
        Value:  state,
        MaxAge: 60,
        Path:   "/",
    }
    redierctURL := getRedirectURL(r.URL.Host)
    conf.RedirectURL = redierctURL
    url := conf.AuthCodeURL(state)
    http.Redirect(w, r, url, 302)
}

func getRedirectURL(host string) string {
    return fmt.Sprintf("https://%s/oauth2callback", host)
}

次に、トークン用のhandlerを作成します。

func tokenHandler(w http.ResponseWriter, r *http.Request) {
    // redirectされたstateと生成したstateが等しいかを確認します
    // 6/11 update:cookieと比較するようにします
    sc, err := r.Cookie("hogehoge")
    if err != nil ||  sc.Value != r.FormValue("state") {
        http.Error(w, "state is invalid.", http.StatusUnauthorized)
        return
    }

    // 認証コードを取得します
    code := r.FormValue("code")
    // appengineのcontextを取得します
    context := appengine.NewContext(r)

    // 認証コードからtokenを取得します
    tok, err := conf.Exchange(context, code)
    if err != nil {
        http.Error(w, err.Error(), http.StatusUnauthorized)
        return
    }

    // tokenが正しいことを確認します
    if tok.Valid() == false {
        http.Error(w, "token is invalid.", http.StatusUnauthorized)
        return
    }

    // oauth2 clinet serviceを取得します
    // 特にuserの情報が必要ない場合はスルーです
    service, err := v2.New(conf.Client(context, tok))
    if err != nil {
        http.Error(w, err.Error(), http.StatusUnauthorized)
        return
    }

    // token情報を取得します
    // ここにEmailやUser IDなどが入っています
    // 特にuserの情報が必要ない場合はスルーです
    tokenInfo, err := service.Tokeninfo().AccessToken(tok.AccessToken).Context(context).Do()
    if err != nil {
        http.Error(w, err.Error(), http.StatusUnauthorized)
        return
    }

    // indexページにリダイレクトします
    // 6/11 update: ハードコードではなく定数を使用するようにした
    http.Redirect(w, r, "/", http.StatusMovedPermanently)
}

これでGoogle Accountが登録されているユーザであることは認証されました。
以上になります。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away