Go言語でGoogle,Twitter,FacebookのOauth認証をしてメールアドレスを取得するまで

# 概要
Go言語で、こういうのやりたい！
![ダウンロード.png](https://qiita-image-store.s3.amazonaws.com/0/170824/d76e8481-6e6e-2469-5da5-ce7f3a38d71e.png)

# github
今回作ったサンプルは、こちらにあります
https://github.com/wheatandcat/example-golang-oauth2

**※動作確認時はconf/app_template.conf → conf/app.confに変更して、 必要な設定を追加して下さい**


# 全体の手順
 - 各APIが使用できるようにサービス登録
 - Go言語からAPIを実装して認証ユーザのメールアドレスを取得

ここまでやってみる

# Go言語でOauth認証

## golang/oauth2
公式で提供しているOauth2パッケージを使う
https://github.com/golang/oauth2/

## github.com/garyburd/go-oauth/oauth
twitterはOauth1.0認証を使用
https://github.com/garyburd/go-oauth

# Google API
## 事前準備 
 1. [Google Cloud Platform](https://console.developers.google.com)
 2. プロジェクトを作成し、認証情報追加

![ダウンロード (2).png](https://qiita-image-store.s3.amazonaws.com/0/170824/2056a3d0-c515-d116-5fb3-98c692ea34f0.png)
 3. **ドメイン**と**リダイレクトURL**を設定

![ダウンロード (3).png](https://qiita-image-store.s3.amazonaws.com/0/170824/f12f6ba4-626a-dd9f-3506-591632c57b5b.png)
 4. **クライアントID**と**シークレット**を確認

![ダウンロード (4).png](https://qiita-image-store.s3.amazonaws.com/0/170824/ac6f18d8-dc4f-aa00-bd4a-0cdd3c3521dd.png)

## 実装
- **クライアントID**と**シークレット**を設定

```example-golang-oauth2/conf/app.conf
googleClientID = "xxxxxxxx"
googleClientSecret = "xxxxxxxx"
```

- 接続設定

```example-golang-oauth2/lib/google/main.go
const (
	authorizeEndpoint = "https://accounts.google.com/o/oauth2/v2/auth"
	tokenEndpoint     = "https://www.googleapis.com/oauth2/v4/token"
)

// GetConnect 接続を取得する
func GetConnect() *oauth2.Config {
	config := &oauth2.Config{
		ClientID:     beego.AppConfig.String("googleClientID"),
		ClientSecret: beego.AppConfig.String("googleClientSecret"),
		Endpoint: oauth2.Endpoint{
			AuthURL:  authorizeEndpoint,
			TokenURL: tokenEndpoint,
		},
		Scopes:      []string{"openid", "email", "profile"},
		RedirectURL: "http://localhost:8080/google/callback",
	}

	return config
}
```
- コールバック

```example-golang-oauth2/controllers/google/callback.go
	config := google.GetConnect()

	context := context.Background()

	tok, err := config.Exchange(context, request.Code)
	if err != nil {
		panic(err)
	}

	if tok.Valid() == false {
		panic(errors.New("vaild token"))
	}

	service, _ := v2.New(config.Client(context, tok))
	tokenInfo, _ := service.Tokeninfo().AccessToken(tok.AccessToken).Context(context).Do()

	c.Data["ID"] = tokenInfo.UserId
	c.Data["Email"] = tokenInfo.Email
```

# Twitter API
## 事前準備
1. [Twitter Apps](https://apps.twitter.com/)
2. プロジェクト作成
3. **Callback URL**を設定

![ダウンロード (5).png](https://qiita-image-store.s3.amazonaws.com/0/170824/c543d137-8896-8a57-cb1e-db593d10b0c8.png)
4. メールアドレスを取得できるようにするために**Privacy Policy URL**と**Terms of Service URL**に設定

![ダウンロード (6).png](https://qiita-image-store.s3.amazonaws.com/0/170824/586f9e75-0490-ac46-eb2d-87dd506f652c.png)
5. permissionsのAccessを**Read, Write and Access direct messages**に変更

![ダウンロード (7).png](https://qiita-image-store.s3.amazonaws.com/0/170824/92a2e864-5d9f-031f-8323-b32e6586a93e.png)
6. **Consumer Key**と**Consumer Secret**を確認

![ダウンロード (8).png](https://qiita-image-store.s3.amazonaws.com/0/170824/1e6f0bc6-1aad-852d-a410-d3b3dd4998cc.png)

# 実装
- **Consumer Key**と**Consumer Secret**を設定

```example-golang-oauth2/conf/app.conf
twitterConsumerKey = "xxxxxxxx"
twitterConsumerSecret = "xxxxxxxx"
```
- 接続設定

```example-golang-oauth2/lib/twitter/main.go
// GetConnect 接続を取得する
func GetConnect() *oauth.Client {
	return &oauth.Client{
		TemporaryCredentialRequestURI: "https://api.twitter.com/oauth/request_token",
		ResourceOwnerAuthorizationURI: "https://api.twitter.com/oauth/authorize",
		TokenRequestURI:               "https://api.twitter.com/oauth/access_token",
		Credentials: oauth.Credentials{
			Token:  tempCredKey,
			Secret: tokenCredKey,
		},
	}
}

// GetAccessToken アクセストークンを取得する
func GetAccessToken(rt *oauth.Credentials, oauthVerifier string) (*oauth.Credentials, error) {
	oc := GetConnect()
	at, _, err := oc.RequestToken(nil, rt, oauthVerifier)

	return at, err
}

// GetMe 自身を取得する
func GetMe(at *oauth.Credentials, user *Account) error {
	oc := GetConnect()

	v := url.Values{}
	v.Set("include_email", "true")

	resp, err := oc.Get(nil, at, "https://api.twitter.com/1.1/account/verify_credentials.json", v)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 500 {
		return errors.New("Twitter is unavailable")
	}

	if resp.StatusCode >= 400 {
		return errors.New("Twitter request is invalid")
	}

	err = json.NewDecoder(resp.Body).Decode(user)
	if err != nil {
		return err
	}

	return nil
}
```
- コールバック

```example-golang-oauth2/controllers/twitter/callback.go
	at, err := twitter.GetAccessToken(
		&oauth.Credentials{
			Token:  c.CruSession.Get("request_token").(string),
			Secret: c.CruSession.Get("request_token_secret").(string),
		},
		request.Verifier,
	)
	if err != nil {
		panic(err)
	}

	c.CruSession.Set("oauth_secret", at.Secret)
	c.CruSession.Set("oauth_token", at.Token)

	account := twitter.Account{}
	if err = twitter.GetMe(at, &account); err != nil {
		panic(err)
	}

	c.Data["ID"] = account.ID
	c.Data["ProfileImageURL"] = account.ProfileImageURL
	c.Data["ScreenName"] = account.ScreenName
	c.Data["Email"] = account.Email
```
# Facebook API
## 事前準備
 1, [Facebook Developers](https://developers.facebook.com/apps)
 2. プロジェクト作成
 3. **有効なOAuthリダイレクトURI**を設定

![ダウンロード (9).png](https://qiita-image-store.s3.amazonaws.com/0/170824/ab8c42bc-6d28-854d-be39-6abc0db0f7f8.png)
 4. **アプリID**と**app secret**を確認

![ダウンロード (17).png](https://qiita-image-store.s3.amazonaws.com/0/170824/1983a6eb-1598-af37-5e86-22467720d801.png)



## 実装
- **アプリID**と**app secret**を設定

```example-golang-oauth2/conf/app.conf
twitterConsumerKey = "xxxxxxxx"
twitterConsumerSecret = "xxxxxxxx"
```

- 接続設定

```example-golang-oauth2/lib/facebook/main.go
const (
	authorizeEndpoint = "https://www.facebook.com/dialog/oauth"
	tokenEndpoint     = "https://graph.facebook.com/oauth/access_token"
)

// GetConnect 接続を取得する
func GetConnect() *oauth2.Config {
	config := &oauth2.Config{
		ClientID:     beego.AppConfig.String("facebookClientID"),
		ClientSecret: beego.AppConfig.String("facebookClientSecret"),
		Endpoint: oauth2.Endpoint{
			AuthURL:  authorizeEndpoint,
			TokenURL: tokenEndpoint,
		},
		Scopes:      []string{"email"},
		RedirectURL: "http://localhost:8080/facebook/callback",
	}

	return config
}
```

- コールバック

```example-golang-oauth2/controllers/twitter/callback.go
	config := facebook.GetConnect()

	tok, err := config.Exchange(oauth2.NoContext, request.Code)
	if err != nil {
		panic(err)
	}

	if tok.Valid() == false {
		panic(errors.New("vaild token"))
	}

	client := config.Client(oauth2.NoContext, tok)
	session := &fb.Session{
		Version:    "v2.8",
		HttpClient: client,
	}

	res, err := session.Get("/me?fields=id,name,email", nil)
	if err != nil {
		panic(err)
	}

	c.Data["ID"] = res["id"]
	c.Data["Name"] = res["name"]
	c.Data["Email"] = res["email"]
```
# サンプル
https://github.com/wheatandcat/example-golang-oauth2

 - githubのソースを実行すると以下が表示されます

![ダウンロード (11).png](https://qiita-image-store.s3.amazonaws.com/0/170824/d39d6314-254f-b67e-5463-b60f214d7e65.png)

 - twitterログインする

![ダウンロード (13).png](https://qiita-image-store.s3.amazonaws.com/0/170824/094f052d-0374-beab-d0ca-690d793e7d31.png)

 - 認証成功！アカウント情報が取得できます

![ダウンロード (14).png](https://qiita-image-store.s3.amazonaws.com/0/170824/9c3a0f9e-71cd-85a5-9580-1f8f93bc9aa2.png)

# 蛇足
- 実際にサイトにもアカウントログインを追加してみました
    - 構成：サーバーはGo言語、フロントはreact + redux
- 実装を確認したい場合は、githubから確認できます
    - https://github.com/wheatandcat/dotstamp_server
    - https://github.com/wheatandcat/dotstamp_client
 
## 実装サイト
https://dotstamp.com/login/login

![ダウンロード (16).png](https://qiita-image-store.s3.amazonaws.com/0/170824/edf60630-d9d6-32f0-b6cd-7bef9b54b6a4.png)