Go
linebot
LineLogin

Go言語でLineLoginしてmidとアクセストークンを取得する(ライブラリ書いた)

More than 1 year has passed since last update.

(エピソード0)
もともとはLineBotでTwitter情報を流してみようと思いたったところからはじまる。
LineアカウントとTwitterアカウントの連携が必要だな。
TwitterのGo OAuthクライアントはふむふむAnacondaで使ってるやつでいいか。
Lineは、、あれ、見つからないな。
あー、LineのOAuth仕様って楽ちんだからみんなイチから書いてるのか。
うーんでも少しでも楽したい人はいるだろうしサンプルにもなるよね。
みんGo読んで意識高くなったしGithubに公開だ。
カキカキ。
コード量すくねぇーー
けどせっかくだし誰かの役に立つことを祈って公開しよう。キータも書こう。。

githubはここ

https://github.com/matsuoky/linein

やりたいこと

  • LineでOAuthログインしたい
  • REST API用のアクセストークンを取得したい

LineでOAuthログイン

Lineの画像を拾ってくるなりして適当なログインボタンを作る。
ログインボタンをポチったときのリクエスト先でこんな感じにハンドリングする。

func (f GetLineOauthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        l := linein.NewLinein(clientId, clientSecret)
        url, values, err := l.GetWebLoginURL("http://"+r.Host+"/line_auth_callback", "") 
        if err != nil {
                fmt.Fprintf(w, "%v", err)
                return
        }   
        http.Redirect(w, r, url+"?"+values.Encode(), http.StatusFound)
}

あ、lineinパッケージはimportしてある前提です。
import "github.com/matsuoky/linein"

clientId clientSecret はLineLoginのチャネルを作成した際に表示される Channel ID, Channel Secret をセットする。
ID, Secretを未取得の方は こちら のリンクからどうぞ

話もどって、、するとLineさんが用意してくれているログイン画面にリダイレクトされる。
遷移後のログイン画面でログインすると l.GetWebLoginURL("http://"+r.Host+"/line_auth_callback", "") でセットしたコールバックURLにさらにリダイレクトされる。

コールバック先でのハンドリングはこんな感じ。

func (f GetLineAuthCallbackHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        code := r.FormValue("code")
        if code == "" {
                fmt.Fprintf(w, "code is not found")
                return
        }
        l := linein.NewLinein(clientId, clientSecret)
        url, values, err := l.GetAccessTokenURL(code, "")
        if err != nil {
                fmt.Fprintf(w, "GetAccessTokenURL: %v", err)
                return
        }
        c := appengine.NewContext(r)
        client := urlfetch.Client(c)
        res, err := linein.Post(client, url, values)
        if err != nil {
                fmt.Fprintf(w, "line auth post err: %v", err)
                return
        }
        defer res.Body.Close()
        lUser := new(LineUser)
        if err := json.NewDecoder(res.Body).Decode(lUser); err != nil {
                fmt.Fprintf(w, "decode err: %v", err)
                return
        }
        ...

ここに来た時点ではまだREST API用のアクセストークンもユーザ情報も入っていない。
これらを取得するために必要な一時的なトークン(code)が入っている。
ではアクセストークンを取得するためのURLとパラメータを取得。

url, values, err := l.GetAccessTokenURL(code, "")

valuesはnet/url.Values型なのでこれをそのままPostすればOK。
一応Postのラッパーも用意しているので今回はこれを使う。

res, err := linein.Post(nil, url, values)

appengineユーザはここでHTTPクライアントを入れ替える。

c := appengine.NewContext(r)
client := urlfetch.Client(c)
res, err := linein.Post(client, url, values)

レスポンスをデコードする際に使う型の例。

type LineUser struct {
        Mid          string `json:"mid"`
        AccessToken  string `json:"access_token"`
        ExpiresIn    int    `json:"expires_in"`
        RefreshToken string `json:"refresh_token"`
}

AccessToken の値を利用すればLineの提供するREST APIを使える。
Mid の値を保存してログイン用の識別子として使いログイン機能を実装できる。
また、Messaging API で使用されるUserIdとMidは一致するはず。