GAE/Goでuser APIを使ってユーザー認証をする

GAE/Goでuser APIを使ってユーザー認証をする

user APIを使うと何ができるのか

公式のAPIドキュメントを見ると以下のように記載されている

The Users API allows an application to:

  • Detect whether the current user has signed in.
  • Redirect the user to the appropriate sign-in page to sign in.
  • Request that your application user create a new Google account if they don’t have one already.

以下の公式ドキュメントから引用
https://cloud.google.com/appengine/docs/standard/go/users/

上記を意訳してみた(間違っていたら、ご指摘してください)
* 現在のユーザーが既にサインイン済みか確認する
* ユーザーをGoogleアカウントの適切なサインインページにリダイレクトさせることができる
* ユーザーがまだGoogleアカウントを取得していない場合は、Googleアカウントを取得するように要求することができる

実装してみた

公式のドキュメントとAPIリファレンスにしたがい、実装してみた

package main

import (
    "net/http"

    "google.golang.org/appengine"
    "google.golang.org/appengine/user"
    "html/template"
    "fmt"
)
// エントリポイント
func init() {
    http.HandleFunc("/", Entry)
    http.HandleFunc("/loggedin", LoggedIn)
}

func Entry(w http.ResponseWriter, r *http.Request) {
    // コンテキスト生成
    c := appengine.NewContext(r)

    // ログイン用URL
    // ログイン後ユーザーを引数で渡したURLへリダイレクト
    _, err := user.LoginURL(c, "/loggined")

    // エラーハンドリング
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }

    // テンプレートを表示
    tmpl := template.Must(template.New("enrty").Parse(entyTmpl))
    tmpl.Execute(w, nil)
}

// エントリーページのテンプレート
var entyTmpl = `
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ログイン前</title>
</head>
<body>
   <a href="/loggedin">ログインしてね</a>
</body>
</html>
`

// ログイン後のハンドラ
func LoggedIn(w http.ResponseWriter, r *http.Request) {
    // コンテキスト生成
    c := appengine.NewContext(r)

    // 現在ログインしているユーザーの情報を取得する
    u := user.Current(c)

    // 現在ログインしているユーザーがいない場合
    if u == nil {
        // ユーザーにサインインするように促すためのページのURLを返す
        // ログイン後ユーザーを引数で与えたURLにリダイレクトさせる
        url, _ := user.LoginURL(c, "/")
        fmt.Fprintf(w, `<a href="%s">ログイン用のサイトに移る</a>`, url)
        return
    }

    // ログアウト用のURL
    // ログインアウト後ユーザーを引数で渡したURLへリダイレクト
    _, err := user.LogoutURL(c, "/")

    // エラーハンドリング
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
    }

    // テンプレートを表示
    tmpl := template.Must(template.New("loggedIn").Parse(loggedInTmpl))
    tmpl.Execute(w, nil)
}

// ログイン後のページのテンプレート
var loggedInTmpl = `
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>ログイン後</title>
</head>
<body>
   <p>ログインできたよ</p>
   <a href="/">ログアウト</a>
</body>
</html>
`

サイトの遷移

サイトの遷移は、大まかに言うと以下のような流れだ
1 リクエストを送ると実装したアプリケーションのページがレスポンスとして返って来る
->これは、go http.HandleFunc("/", Entry) で登録したハンドラが発動している

2 go http.HandleFunc("/", Entry) のハンドラによって表示されたHTMLページの「ログインしてね」をクリックする

3 「ログインしてね」をクリックすると go http.HandleFunc("/loggedin", LoggedIn) で登録したハンドラが発動する

4 ログインしていない場合は、 以下の部分が発動して、『ログイン用のサイトに移る』という表示が現れる

        // 現在ログインしているユーザーの情報を取得する
    u := user.Current(c)

    // 現在ログインしているユーザーがいない場合
    if u == nil {
        // ユーザーにサインインするように促すためのページのURLを返す
        // ログイン後ユーザーを引数で与えたURLにリダイレクトさせる
        url, _ := user.LoginURL(c, "/")
        fmt.Fprintf(w, `<a href="%s">ログイン用のサイトに移る</a>`, url)
        return
    } 

5 『ログイン用のサイトに移る』をクリックするとGoogleアカウントの認証用のページ(外部のGoogleのサイト)にリダイレクトする

6 リダイレクト先の認証用のページで認証が完了したら、ログインが完了し go http.HandleFunc("/loggedin", LoggedIn) 内のHTMLテンプレートが表示される(「ログインできたよ」が表示される)

以下の公式のドキュメントがわかりやすい
https://cloud.google.com/appengine/docs/standard/go/users/ https://cloud.google.com/appengine/docs/standard/go/users/reference

参考にさせていただいたサイト

user APIについて
https://cloud.google.com/appengine/docs/standard/go/users/ https://cloud.google.com/appengine/docs/standard/go/users/reference

Basic認証について
http://qiita.com/r7kamura/items/69904137ea20b6b86822

IPAのOAuth 2.0の説明
https://www.ipa.go.jp/security/awareness/vendor/programmingv2/contents/709.html

参考文献

茨木 隆彰 (2012/02)『はじめてのGoogle App Engine Go言語編 (I・O BOOKS)』 工学社

※ Qiitaでも同一記事を投稿している

qiita.com