LoginSignup
20
12

More than 3 years have passed since last update.

【Go】ginでgothを使ってチョー簡単にOAuth認証

Last updated at Posted at 2019-02-21

gothとは

gothはチョー簡単にGoでOAuth認証できる便利パッケージ。
https://github.com/markbates/goth

goth x gin の問題点

gothのチョー便利関数CompleteUserAuth BeginAuthHandlerを使うと爆速でOAuthを実装できる。
がしかし、そのままではginと良い感じに組み合わせて使うことができない。
具体的には、gothではOAuthのプロバイダー名(要はサービス名。Twitterとかfacebookとか)を取得するためにGetProviderName関数を使っているが、デフォルトでは

gothic.go#L262
    // try to get it from the url param "provider"
    if p := req.URL.Query().Get("provider"); p != "" {
        return p, nil
    }

    // try to get it from the url param ":provider"
    if p := req.URL.Query().Get(":provider"); p != "" {
        return p, nil
    }

    // try to get it from the context's value of "provider" key
    if p, ok := mux.Vars(req)["provider"]; ok {
        return p, nil
    }

    //  try to get it from the go-context's value of "provider" key
    if p, ok := req.Context().Value("provider").(string); ok {
        return p, nil
    }

こんな感じの実装をしてる。URLパラメータでプロバイダー名を渡したい時、このままだとhttp.Requestもラップした独自のコンテキストを使って頑張るginでは対応できない。 (gin.Context.Param(string)

解決方法

ここ

gothic.go#L277
    //  try to get it from the go-context's value of "provider" key
    if p, ok := req.Context().Value("provider").(string); ok {
        return p, nil
    }

コンテキストバリューから取ってこようとしてるので、これに乗っかります

実装

ヘルパー関数として

func contextWithProviderName(c *gin.Context, provider string) (*http.Request){
    return  c.Request.WithContext(context.WithValue(c.Request.Context(), "provider", provider))
}

こんなのを用意して、http.Requestにバリューコンテキストをくっつます。
そしてら、ハンドラを

    r.GET("/auth/:provider", func(c *gin.Context) {
        provider := c.Param("provider")
        c.Request = contextWithProviderName(c, provider)

        gothic.BeginAuthHandler(c.Writer, c.Request)
    })
r.GET("/auth/:provider/callback", func(c *gin.Context) {
        provider := c.Param("provider")
        c.Request = contextWithProviderName(c, provider)

        user, err := gothic.CompleteUserAuth(c.Writer, c.Request)
        if err != nil {
            fmt.Fprintln(c.Writer, err)
            return
        }
        log.Printf("%#v", user)
    })

こんな感じで作りましょう。これで完璧です。

補足

プロバイダ、Twitterしか使わねぇぜ!ってなったら

gothic.go#L239
// GetProviderName is a function used to get the name of a provider
// for a given request. By default, this provider is fetched from
// the URL query string. If you provide it in a different way,
// assign your own function to this variable that returns the provider
// name for your request.
var GetProviderName = getProviderName

これを使いましょう。
このようにGetProviderNameをカスタムできるようにパブリックにしてくれている!

と言うわけでinitとかで

func init() {
    gothic.GetProviderName = func(req *http.Request) (string, error) {
        return "twitter", nil
    }
}

こうしてGetProviderNameをオーバーライドすればおk

まとめ

もともとあるgothのexampleにならってginバージョンで実装したものが

これです。よかったら眺めてみてください。

20
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
12