markbates/goth を使って Typetalk で OAuth2 認証をする

## 概要
[markbates/goth](https://github.com/markbates/goth) を使って Typetalk で OAuth2 認証をやってみた！というやつです。最近、 Typetalk もサポートしてもらうよう PR を送ったので認証情報を取得するサンプルを手順を紹介します。

## 前提知識

Typetalk って何？という方は [こちら](https://www.typetalk.com)
OAuth 2.0 って何？という方は [こちら](https://oauth.net/2/)

## markbates/goth とは

https://github.com/markbates/goth/blob/master/README.md
> Package goth provides a simple, clean, and idiomatic way to write authentication packages for Go web applications.

[markbates](https://github.com/markbates) という方が作ったライブラリで、[ominiauth](https://github.com/omniauth/omniauth) の Go 言語バージョンみたいな感じでしょうか。色々なサービスに対応する Provider が実装されておりますが、PR 送って Typetalk もサポートしてもらいました。


https://github.com/markbates/goth/blob/master/README.md
<img width="151" alt="Screen Shot 2019-01-05 at 22.52.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/27079/e99580e6-eea3-fcb7-de0a-5356366a82da.png">
*サポートしているサービスの一覧は README でみれます*

## 手順

1) Typetalk でアプリケーションを登録します。
https://typetalk.com/my/develop/applications/register

<img width="600" alt="アプリケーション登録画面" src="https://qiita-image-store.s3.amazonaws.com/0/27079/7eaee09f-f10b-2a7a-8cf7-7036a5cda1c1.png">

| 入力項目 | 値 |
|:--------|:--|
| アプリケーション名 | goth test |
| Grant Type | Authorization Code |
| ホームページ URL | http://localhost:3000 |
| リダイレクト URI | http://localhost:3000/auth/typetalk/callback |

登録すると、IDなどが発行されます。
 
<img width="600" alt="アプリケーション詳細画面" src="https://qiita-image-store.s3.amazonaws.com/0/27079/d5b5a123-1878-16bc-d499-b0e772115e91.png">
*スクリーンショットでは削ってますが、実際は "Client ID" と "Client Secret" のところに値が入ります。*

2) 環境変数を設定します。

```shell
$ export SESSION_SECRET=typetalk-goth-test # 本番で使う際は良い感じの値にしてください
$ export TYPETALK_KEY=XXXXXXXXXXX # Client ID の値を使ってください
$ export TYPETALK_SECRET=XXXXXXXXXXX # Client Secret の値を使ってください
```

3) サンプル用のサーバを実装します。

サンプルコード

```golang
package main

import (
	"fmt"
	"html/template"
	"net/http"
	"os"

	"log"

	"github.com/gorilla/pat"
	"github.com/markbates/goth"
	"github.com/markbates/goth/gothic"
	"github.com/markbates/goth/providers/typetalk"
)

func main() {
	goth.UseProviders(
		typetalk.New(os.Getenv("TYPETALK_KEY"), os.Getenv("TYPETALK_SECRET"), "http://localhost:3000/auth/typetalk/callback"),
	)

	p := pat.New()
	p.Get("/auth/{provider}/callback", func(res http.ResponseWriter, req *http.Request) {
		user, err := gothic.CompleteUserAuth(res, req)
		if err != nil {
			fmt.Fprintln(res, err)
			return
		}
		t, _ := template.New("foo").Parse(userTemplate)
		t.Execute(res, user)
	})

	p.Get("/logout/{provider}", func(res http.ResponseWriter, req *http.Request) {
		gothic.Logout(res, req)
		res.Header().Set("Location", "/")
		res.WriteHeader(http.StatusTemporaryRedirect)
	})

	p.Get("/auth/{provider}", func(res http.ResponseWriter, req *http.Request) {
		// try to get the user without re-authenticating
		if gothUser, err := gothic.CompleteUserAuth(res, req); err == nil {
			t, _ := template.New("foo").Parse(userTemplate)
			t.Execute(res, gothUser)
		} else {
			gothic.BeginAuthHandler(res, req)
		}
	})

	p.Get("/", func(res http.ResponseWriter, req *http.Request) {
		t, _ := template.New("foo").Parse(indexTemplate)
		t.Execute(res, nil)
	})
	log.Fatal(http.ListenAndServe(":3000", p))
}

var indexTemplate = `<p><a href="/auth/typetalk">Log in with Typetalk</a></p>`

var userTemplate = `
<p><a href="/logout/{{.Provider}}">logout</a></p>
<p>Name: {{.Name}} </p>
<p>Email: {{.Email}}</p>
<p>NickName: {{.NickName}}</p>
<p>AvatarURL: {{.AvatarURL}}</p>
<p>UserID: {{.UserID}}</p>
<p>AccessToken: {{.AccessToken}}</p>
<p>ExpiresAt: {{.ExpiresAt}}</p>
<p>RefreshToken: {{.RefreshToken}}</p>
`
```

4) サーバを起動します。

```
$ go run main.go
```

5) ブラウザを起動して、アクセスします。
http://localhost:3000/

<img width="157" alt="Screen Shot 2019-01-05 at 23.24.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/27079/5fd68b99-e08c-735f-574a-ec74be56eb30.png">

6) "Log in with Typetalk" をクリックし、Typetalk の認証画面に遷移します。

<img width="1672" alt="Screen Shot 2019-01-05 at 23.32.03.png" src="https://qiita-image-store.s3.amazonaws.com/0/27079/d7ec699d-fdb1-df62-f207-2998d154f141.png">

7) "許可" をクリックし、認証情報を取得します。


*スクリーンショットではトークンの値とか削ってますが、実際には表示されます*

<img width="661" alt="Screen Shot 2019-01-05 at 23.37.24.png" src="https://qiita-image-store.s3.amazonaws.com/0/27079/ef3f3719-0dbe-a31d-f78e-4db6fb92482c.png">

以上、認証情報を取得するまでのサンプルでした！フィードバック歓迎です :pray:

---
**備考**

scope の値は New メソッドの最後の引数として指定できます。省略すると "my" がデフォルトで入ります。

例：`topic.read` を指定

```golang
typetalk.New(os.Getenv("TYPETALK_KEY"), os.Getenv("TYPETALK_SECRET"), "http://localhost:3000/auth/typetalk/callback", "topic.read"),
```

複数指定するときは引数にそのまま複数渡してください。

例：`my` と `topic.read` を指定

```golang
typetalk.New(os.Getenv("TYPETALK_KEY"), os.Getenv("TYPETALK_SECRET"), "http://localhost:3000/auth/typetalk/callback", "my", "topic.read"),
```

## 変更履歴

**2019/01/19** Typetalk がスペース区切りのスコープ指定に対応したので修正しました。