つくりおき

スタートアップで働くWebエンジニアのブログ

GinでTwitter OAuthでaccess token取得

バズるものを作れる瞬発力を身に付けたくてTwitterOAuth認証使ってツイートできるやつを作れるようになっておきたかった。アイデアはまだ無い。
GoのWebアプリケーションフレームワーク Ginを使ってTwitter OAuthでトークン取得するところまで試してみた。

Ginの採用理由

さくっと書きたいからsinatra likeなやつを探した。
その中で現時点でGitHub Starが一番多そうなGinを選んだ。

環境

前回のgo-twitterを使ってみた記事に引き続きCloud9(http://c9.io)さんを使いました。そろそろ有料プランも検討せねば。
92thunder.hatenablog.com

go getできない

前回は大丈夫だったがgo getでエラーが発生したのでこちらを参考に解決した。
go install時にno install location for directory ○○○ outside GOPATHのエラーが出る件について - Qiita

コード

main.go
Ginって標準でsession使えないのか…って思いつつ追加した。
contrib/sessions at master · gin-gonic/contrib · GitHub

package main

import (
	"flag"
	"log"
	"net/http"
	"os"

	"github.com/coreos/pkg/flagutil"
	gt "github.com/dghubble/go-twitter/twitter"
	"github.com/dghubble/oauth1"
	"github.com/dghubble/oauth1/twitter"
	"github.com/gin-gonic/contrib/sessions"
	"github.com/gin-gonic/gin"
)

func main() {
	// 環境変数からconsumer-key, consumer-secretを取得
	flags := flag.NewFlagSet("app-auth", flag.ExitOnError)
	consumerKey := flags.String("consumer-key", "", "Twitter Consumer Key")
	consumerSecret := flags.String("consumer-secret", "", "Twitter Consumer Secret")
	flags.Parse(os.Args[1:])
	flagutil.SetFlagsFromEnv(flags, "TWITTER")

	if *consumerKey == "" || *consumerSecret == "" {
		log.Fatal("Consumer key/secret required")
	}

	config := oauth1.Config{
		ConsumerKey:    *consumerKey,
		ConsumerSecret: *consumerSecret,
		Endpoint:       twitter.AuthorizeEndpoint,
	}

	// ginのrouter初期化
	router := gin.Default()
	store := sessions.NewCookieStore([]byte("secret"))

	// セッションの設定
	router.Use(sessions.Sessions("oauth-tweet-test-session", store))
	// templatesディレクトリを登録
	router.LoadHTMLGlob("templates/*")

	// GET /
	// アクセストークンがセッションに入っていれば表示
	// 入ってなければrequestTokenを取得してOAuth認証ページにリダイレクト
	router.GET("/", func(c *gin.Context) {
		session := sessions.Default(c)
		accessToken := session.Get("access_token")
		accessSecret := session.Get("access_secret")
		if accessToken == nil || accessSecret == nil {
			requestToken, requestSecret, _ := config.RequestToken()
			session.Set("request_secret", requestSecret)
			session.Save()
			c.Redirect(http.StatusFound, "https://api.twitter.com/oauth/authenticate?oauth_token="+requestToken)
		} else {
			c.HTML(http.StatusOK, "index.tmpl", gin.H{
				"test": "hello world",
			})
		}
	})

	// GET /callback
	// OAuth認証ページからこのアプリを許可したらリダイレクトされてくる
	//  このあたりはTwitter Application Managementで設定
	// URLに入っているoauth_token, oauth_verifierと
	//  GET /で取得していたrequestSecretを使ってアクセストークン取得
	router.GET("/callback", func(c *gin.Context) {
		oauthToken := c.Query("oauth_token")
		oauthVerifier := c.Query("oauth_verifier")
		session := sessions.Default(c)
		requestSecret := session.Get("request_secret").(string)
		accessToken, accessSecret, _ := config.AccessToken(oauthToken, requestSecret, oauthVerifier)
		session.Set("access_token", accessToken)
		session.Set("access_secret", accessSecret)
		session.Save()
		c.Redirect(http.StatusFound, "/")
	})

	// POST /post
	// access token/secret を使ってツイート投稿する
	router.POST("/post", func(c *gin.Context) {
		session := sessions.Default(c)
		accessToken := session.Get("access_token")
		accessSecret := session.Get("access_secret")
		if accessToken == nil || accessSecret == nil {
			log.Fatal("Invalid access token/secret.")
		}
		config := oauth1.NewConfig(*consumerKey, *consumerSecret)
		token := oauth1.NewToken(accessToken.(string), accessSecret.(string))
		httpClient := config.Client(oauth1.NoContext, token)

		client := gt.NewClient(httpClient)
		client.Statuses.Update("oauth test", nil)
		c.Redirect(http.StatusFound, "/")
	})

	router.Run()
}

templates/index.tmpl

<html>
    <head>
    </head>
    <body>
        <h3>OAuth Tweet Test</h3>
        <form method="post" action="/post">
            <button type="submit">Post</button>
        </form>
    </body>
</html>

実行

export TWITTER_CONSUMER_KEY=xxx
export TWITTER_CONSUMER_SECRET=xxx
go get .
go run main.go

認証ページにリダイレクトされてツイートするとこまでできた。
f:id:daryo:20170911021054p:plain

所感

  • さくっとOAuthでトークン取得試せた
  • Mac Bookのスペースキーに何か挟まってるのか打ちにくい