Skip to content

Commit 7b9fe7a

Browse files
committed
proposal: app engine support
App Engine unfortunately can't make outbound requests without using the appengine urlfetch library and a request-specific context. I don't see a way around this other than to change the signature of the Provider and Session interfaces. This really has a huge amount of downsides obviously, but before I go and fix stuff for providers besides Facebook, G+, and Twitter (Twitter support requires mrjones/oauth#60 to get merged), I figured I'd open a pull request and discuss the tradeoffs. Unfortunately I think we need to do something like this to support App Engine. Perhaps this is an opportunity to version goth using gopkg.in or something?
1 parent a185bfa commit 7b9fe7a

File tree

10 files changed

+191
-46
lines changed

10 files changed

+191
-46
lines changed

client.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// +build !appengine
2+
3+
package goth
4+
5+
import (
6+
"net/http"
7+
8+
"golang.org/x/net/context"
9+
)
10+
11+
// Provider implementations should use this method for making outbound HTTP
12+
// requests.
13+
var HTTPClient = func(ctx context.Context) (*http.Client, error) {
14+
return http.DefaultClient, nil
15+
}

client_appengine.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// +build appengine
2+
3+
package goth
4+
5+
import (
6+
"net/http"
7+
8+
"golang.org/x/net/context"
9+
"google.golang.org/appengine/urlfetch"
10+
)
11+
12+
// Provider implementations should use this method for making outbound HTTP
13+
// requests.
14+
var HTTPClient = func(ctx context.Context) (*http.Client, error) {
15+
return urlfetch.Client(ctx), nil
16+
}

provider.go

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,34 @@
11
package goth
22

3-
import "fmt"
4-
import "golang.org/x/oauth2"
3+
import (
4+
"fmt"
5+
6+
"golang.org/x/net/context"
7+
"golang.org/x/oauth2"
8+
)
59

610
// Provider needs to be implemented for each 3rd party authentication provider
711
// e.g. Facebook, Twitter, etc...
812
type Provider interface {
13+
// When implementing a provider, these methods should not make outbound
14+
// requests.
915
Name() string
10-
BeginAuth(state string) (Session, error)
1116
UnmarshalSession(string) (Session, error)
12-
FetchUser(Session) (User, error)
1317
Debug(bool)
14-
RefreshToken(refreshToken string) (*oauth2.Token, error) //Get new access token based on the refresh token
15-
RefreshTokenAvailable() bool //Refresh token is provided by auth provider or not
18+
// Refresh token is provided by auth provider or not
19+
RefreshTokenAvailable() bool
20+
21+
// These three methods are deprecated. See the appropriate *Ctx replacement.
22+
BeginAuth(state string) (Session, error)
23+
FetchUser(Session) (User, error)
24+
RefreshToken(refreshToken string) (*oauth2.Token, error)
25+
26+
// These methods are now preferred.
27+
BeginAuthCtx(ctx context.Context, state string) (Session, error)
28+
FetchUserCtx(context.Context, Session) (User, error)
29+
// Get new access token based on the refresh token.
30+
// Only works if RefreshTokenAvailable() is true
31+
RefreshTokenCtx(ctx context.Context, refreshToken string) (*oauth2.Token, error)
1632
}
1733

1834
// Providers is list of known/available providers.

providers/facebook/facebook.go

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import (
88
"errors"
99
"io"
1010
"io/ioutil"
11-
"net/http"
1211
"net/url"
1312

1413
"github.com/markbates/goth"
14+
"golang.org/x/net/context"
1515
"golang.org/x/oauth2"
1616
)
1717

@@ -50,25 +50,39 @@ func (p *Provider) Name() string {
5050
// Debug is a no-op for the facebook package.
5151
func (p *Provider) Debug(debug bool) {}
5252

53-
// BeginAuth asks Facebook for an authentication end-point.
5453
func (p *Provider) BeginAuth(state string) (goth.Session, error) {
54+
return p.BeginAuthCtx(context.TODO(), state)
55+
}
56+
57+
// BeginAuthCtx asks Facebook for an authentication end-point.
58+
func (p *Provider) BeginAuthCtx(ctx context.Context, state string) (goth.Session, error) {
5559
url := p.config.AuthCodeURL(state)
5660
session := &Session{
5761
AuthURL: url,
5862
}
5963
return session, nil
6064
}
6165

62-
// FetchUser will go to Facebook and access basic information about the user.
6366
func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
67+
return p.FetchUserCtx(context.TODO(), session)
68+
}
69+
70+
// FetchUserCtx will go to Facebook and access basic information about the user.
71+
func (p *Provider) FetchUserCtx(ctx context.Context, session goth.Session) (
72+
goth.User, error) {
6473
sess := session.(*Session)
6574
user := goth.User{
6675
AccessToken: sess.AccessToken,
6776
Provider: p.Name(),
6877
ExpiresAt: sess.ExpiresAt,
6978
}
7079

71-
response, err := http.Get(endpointProfile + "&access_token=" + url.QueryEscape(sess.AccessToken))
80+
client, err := goth.HTTPClient(ctx)
81+
if err != nil {
82+
return user, err
83+
}
84+
85+
response, err := client.Get(endpointProfile + "&access_token=" + url.QueryEscape(sess.AccessToken))
7286
if err != nil {
7387
return user, err
7488
}
@@ -152,12 +166,16 @@ func newConfig(provider *Provider, scopes []string) *oauth2.Config {
152166
return c
153167
}
154168

155-
//RefreshToken refresh token is not provided by facebook
156169
func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
170+
return p.RefreshTokenCtx(context.TODO(), refreshToken)
171+
}
172+
173+
// RefreshTokenCtx refresh token is not provided by facebook
174+
func (p *Provider) RefreshTokenCtx(ctx context.Context, refreshToken string) (*oauth2.Token, error) {
157175
return nil, errors.New("Refresh token is not provided by facebook")
158176
}
159177

160-
//RefreshTokenAvailable refresh token is not provided by facebook
178+
// RefreshTokenAvailable refresh token is not provided by facebook
161179
func (p *Provider) RefreshTokenAvailable() bool {
162180
return false
163181
}

providers/facebook/session.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ package facebook
33
import (
44
"encoding/json"
55
"errors"
6-
"github.com/markbates/goth"
7-
"golang.org/x/oauth2"
86
"strings"
97
"time"
8+
9+
"github.com/markbates/goth"
10+
"golang.org/x/net/context"
1011
)
1112

1213
// Session stores data during the auth process with Facebook.
@@ -24,10 +25,14 @@ func (s Session) GetAuthURL() (string, error) {
2425
return s.AuthURL, nil
2526
}
2627

27-
// Authorize the session with Facebook and return the access token to be stored for future use.
2828
func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) {
29+
return s.AuthorizeCtx(context.TODO(), provider, params)
30+
}
31+
32+
// AuthorizeCtx the session with Facebook and return the access token to be stored for future use.
33+
func (s *Session) AuthorizeCtx(ctx context.Context, provider goth.Provider, params goth.Params) (string, error) {
2934
p := provider.(*Provider)
30-
token, err := p.config.Exchange(oauth2.NoContext, params.Get("code"))
35+
token, err := p.config.Exchange(ctx, params.Get("code"))
3136
if err != nil {
3237
return "", err
3338
}

providers/gplus/gplus.go

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import (
77
"encoding/json"
88
"io"
99
"io/ioutil"
10-
"net/http"
1110
"net/url"
1211
"strings"
1312

1413
"github.com/markbates/goth"
14+
"golang.org/x/net/context"
1515
"golang.org/x/oauth2"
1616
)
1717

@@ -51,8 +51,12 @@ func (p *Provider) Name() string {
5151
// Debug is a no-op for the gplus package.
5252
func (p *Provider) Debug(debug bool) {}
5353

54-
// BeginAuth asks Google+ for an authentication end-point.
5554
func (p *Provider) BeginAuth(state string) (goth.Session, error) {
55+
return p.BeginAuthCtx(context.TODO(), state)
56+
}
57+
58+
// BeginAuthCtx asks Google+ for an authentication end-point.
59+
func (p *Provider) BeginAuthCtx(ctx context.Context, state string) (goth.Session, error) {
5660
var opts []oauth2.AuthCodeOption
5761
if p.prompt != nil {
5862
opts = append(opts, p.prompt)
@@ -64,8 +68,14 @@ func (p *Provider) BeginAuth(state string) (goth.Session, error) {
6468
return session, nil
6569
}
6670

67-
// FetchUser will go to Google+ and access basic information about the user.
68-
func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
71+
func (p *Provider) FetchUser(session goth.Session) (
72+
goth.User, error) {
73+
return p.FetchUserCtx(context.TODO(), session)
74+
}
75+
76+
// FetchUserCtx will go to Google+ and access basic information about the user.
77+
func (p *Provider) FetchUserCtx(ctx context.Context, session goth.Session) (
78+
goth.User, error) {
6979
sess := session.(*Session)
7080
user := goth.User{
7181
AccessToken: sess.AccessToken,
@@ -74,7 +84,12 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
7484
ExpiresAt: sess.ExpiresAt,
7585
}
7686

77-
response, err := http.Get(endpointProfile + "?access_token=" + url.QueryEscape(sess.AccessToken))
87+
client, err := goth.HTTPClient(ctx)
88+
if err != nil {
89+
return user, err
90+
}
91+
92+
response, err := client.Get(endpointProfile + "?access_token=" + url.QueryEscape(sess.AccessToken))
7893
if err != nil {
7994
return user, err
8095
}
@@ -145,15 +160,21 @@ func newConfig(provider *Provider, scopes []string) *oauth2.Config {
145160
return c
146161
}
147162

148-
//RefreshTokenAvailable refresh token is provided by auth provider or not
163+
// RefreshTokenAvailable refresh token is provided by auth provider or not
149164
func (p *Provider) RefreshTokenAvailable() bool {
150165
return true
151166
}
152167

153-
//RefreshToken get new access token based on the refresh token
154-
func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
168+
func (p *Provider) RefreshToken(refreshToken string) (
169+
*oauth2.Token, error) {
170+
return p.RefreshTokenCtx(context.TODO(), refreshToken)
171+
}
172+
173+
// RefreshTokenCtx get new access token based on the refresh token
174+
func (p *Provider) RefreshTokenCtx(ctx context.Context, refreshToken string) (
175+
*oauth2.Token, error) {
155176
token := &oauth2.Token{RefreshToken: refreshToken}
156-
ts := p.config.TokenSource(oauth2.NoContext, token)
177+
ts := p.config.TokenSource(ctx, token)
157178
newToken, err := ts.Token()
158179
if err != nil {
159180
return nil, err

providers/gplus/session.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ package gplus
33
import (
44
"encoding/json"
55
"errors"
6-
"github.com/markbates/goth"
7-
"golang.org/x/oauth2"
86
"strings"
97
"time"
8+
9+
"github.com/markbates/goth"
10+
"golang.org/x/net/context"
1011
)
1112

1213
// Session stores data during the auth process with Facebook.
@@ -25,10 +26,14 @@ func (s Session) GetAuthURL() (string, error) {
2526
return s.AuthURL, nil
2627
}
2728

28-
// Authorize the session with Google+ and return the access token to be stored for future use.
2929
func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) {
30+
return s.AuthorizeCtx(context.TODO(), provider, params)
31+
}
32+
33+
// AuthorizeCtx the session with Google+ and return the access token to be stored for future use.
34+
func (s *Session) AuthorizeCtx(ctx context.Context, provider goth.Provider, params goth.Params) (string, error) {
3035
p := provider.(*Provider)
31-
token, err := p.config.Exchange(oauth2.NoContext, params.Get("code"))
36+
token, err := p.config.Exchange(ctx, params.Get("code"))
3237
if err != nil {
3338
return "", err
3439
}

providers/twitter/session.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package twitter
33
import (
44
"encoding/json"
55
"errors"
6+
"strings"
7+
68
"github.com/markbates/goth"
79
"github.com/mrjones/oauth"
8-
"strings"
10+
"golang.org/x/net/context"
911
)
1012

1113
// Session stores data during the auth process with Twitter.
@@ -23,10 +25,15 @@ func (s Session) GetAuthURL() (string, error) {
2325
return s.AuthURL, nil
2426
}
2527

26-
// Authorize the session with Twitter and return the access token to be stored for future use.
2728
func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) {
29+
return s.AuthorizeCtx(context.TODO(), provider, params)
30+
}
31+
32+
// AuthorizeCtx the session with Twitter and return the access token to be stored for future use.
33+
func (s *Session) AuthorizeCtx(ctx context.Context, provider goth.Provider, params goth.Params) (string, error) {
2834
p := provider.(*Provider)
29-
accessToken, err := p.consumer.AuthorizeToken(s.RequestToken, params.Get("oauth_verifier"))
35+
accessToken, err := p.consumer.AuthorizeTokenWithParamsCtx(
36+
ctx, s.RequestToken, params.Get("oauth_verifier"), p.consumer.AdditionalParams)
3037
if err != nil {
3138
return "", err
3239
}

0 commit comments

Comments
 (0)