要点
Google App EngineでGoogle AccountによるOAuth2認証を用意します。
実例のあるOpenID Connectを使用します。
App Engineは基本無料なので安心して使えます。
Enable Billing
のようなものが出てきたら速攻で脱出してください
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を取得
- https://console.cloud.google.com
- サイドバーのAPI Managerをクリック
- Credentialsをクリック
- Create credentialsをクリック
- OAuth client IDをクリック
- Web applicationを選択後、createをクリック
- 作成されたclientをクリックして
Client ID
とClient secret
をメモ - 念のため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が登録されているユーザであることは認証されました。
以上になります。