gothとは
gothはチョー簡単にGoでOAuth認証できる便利パッケージ。
https://github.com/markbates/goth
goth x gin の問題点
gothのチョー便利関数CompleteUserAuth
BeginAuthHandler
を使うと爆速でOAuthを実装できる。
がしかし、そのままではginと良い感じに組み合わせて使うことができない。
具体的には、gothではOAuthのプロバイダー名(要はサービス名。Twitterとかfacebookとか)を取得するためにGetProviderName
関数を使っているが、デフォルトでは
// 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)
)
解決方法
ここ
// 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しか使わねぇぜ!ってなったら
// 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バージョンで実装したものが
これです。よかったら眺めてみてください。