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

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

## githubはここ ##
https://github.com/matsuoky/linein

## やりたいこと
- LineでOAuthログインしたい
- REST API用のアクセストークンを取得したい

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

```go
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を未取得の方は [こちら](https://business.line.me/ja/) のリンクからどうぞ

話もどって、、すると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)
```

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

```go
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は一致するはず。