コード例 #1
0
ファイル: login_test.go プロジェクト: gooops/gologin
func TestGoogleHandler(t *testing.T) {
	jsonData := `{"id": "900913", "name": "Ben Bitdiddle"}`
	expectedUser := &google.Userinfoplus{Id: "900913", Name: "Ben Bitdiddle"}
	proxyClient, server := newGoogleTestServer(jsonData)
	defer server.Close()
	// oauth2 Client will use the proxy client's base Transport
	ctx := context.WithValue(context.Background(), oauth2.HTTPClient, proxyClient)
	anyToken := &oauth2.Token{AccessToken: "any-token"}
	ctx = oauth2Login.WithToken(ctx, anyToken)

	config := &oauth2.Config{}
	success := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		googleUser, err := UserFromContext(ctx)
		assert.Nil(t, err)
		// assert required fields; Userinfoplus contains other raw response info
		assert.Equal(t, expectedUser.Id, googleUser.Id)
		assert.Equal(t, expectedUser.Id, googleUser.Id)
		fmt.Fprintf(w, "success handler called")
	}
	failure := testutils.AssertFailureNotCalled(t)

	// GoogleHandler assert that:
	// - Token is read from the ctx and passed to the Google API
	// - google Userinfoplus is obtained from the Google API
	// - success handler is called
	// - google Userinfoplus is added to the ctx of the success handler
	googleHandler := googleHandler(config, ctxh.ContextHandlerFunc(success), failure)
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/", nil)
	googleHandler.ServeHTTP(ctx, w, req)
	assert.Equal(t, "success handler called", w.Body.String())
}
コード例 #2
0
ファイル: login_test.go プロジェクト: gooops/gologin
func TestWebHandler(t *testing.T) {
	proxyClient, _, server := newDigitsTestServer(testAccountJSON)
	defer server.Close()

	config := &Config{
		ConsumerKey: testConsumerKey,
		Client:      proxyClient,
	}
	success := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		account, err := AccountFromContext(ctx)
		assert.Nil(t, err)
		assert.Equal(t, testDigitsToken, account.AccessToken.Token)
		assert.Equal(t, testDigitsSecret, account.AccessToken.Secret)
		assert.Equal(t, "0123456789", account.PhoneNumber)

		endpoint, header, err := EchoFromContext(ctx)
		assert.Nil(t, err)
		assert.Equal(t, testAccountEndpoint, endpoint)
		assert.Equal(t, testAccountRequestHeader, header)
	}
	handler := LoginHandler(config, ctxh.ContextHandlerFunc(success), testutils.AssertFailureNotCalled(t))
	ts := httptest.NewServer(ctxh.NewHandler(handler))
	// POST OAuth Echo to server under test
	resp, err := http.PostForm(ts.URL, url.Values{accountEndpointField: {testAccountEndpoint}, accountRequestHeaderField: {testAccountRequestHeader}})
	assert.Nil(t, err)
	if assert.NotNil(t, resp) {
		assert.Equal(t, http.StatusOK, resp.StatusCode)
	}
}
コード例 #3
0
ファイル: login_test.go プロジェクト: gooops/gologin
func TestCallbackHandler_ExchangeError(t *testing.T) {
	_, server := testutils.NewErrorServer("OAuth2 Service Down", http.StatusInternalServerError)
	defer server.Close()

	config := &oauth2.Config{
		Endpoint: oauth2.Endpoint{
			TokenURL: server.URL,
		},
	}
	success := testutils.AssertSuccessNotCalled(t)
	failure := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		err := gologin.ErrorFromContext(ctx)
		if assert.NotNil(t, err) {
			// error from golang.org/x/oauth2 config.Exchange as provider is down
			assert.True(t, strings.HasPrefix(err.Error(), "oauth2: cannot fetch token"))
		}
		fmt.Fprintf(w, "failure handler called")
	}

	// CallbackHandler cannot exchange for an Access Token, assert that:
	// - failure handler is called
	// - error with the reason the exchange failed is added to the ctx
	callbackHandler := CallbackHandler(config, success, ctxh.ContextHandlerFunc(failure))
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/?code=any_code&state=d4e5f6", nil)
	ctx := WithState(context.Background(), "d4e5f6")
	callbackHandler.ServeHTTP(ctx, w, req)
	assert.Equal(t, "failure handler called", w.Body.String())
}
コード例 #4
0
ファイル: token.go プロジェクト: gooops/gologin
// TokenHandler receives a Digits access token/secret and calls the Digits
// accounts endpoint to get the corresponding Account. If successful, the
// access token/secret and Account are added to the ctx and the success handler
// is called. Otherwise, the failure handler is called.
func TokenHandler(config *oauth1.Config, success, failure ctxh.ContextHandler) ctxh.ContextHandler {
	success = digitsHandler(config, success, failure)
	if failure == nil {
		failure = gologin.DefaultFailureHandler
	}
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		if req.Method != "POST" {
			ctx = gologin.WithError(ctx, fmt.Errorf("Method not allowed"))
			failure.ServeHTTP(ctx, w, req)
			return
		}
		req.ParseForm()
		accessToken := req.PostForm.Get(accessTokenField)
		accessSecret := req.PostForm.Get(accessTokenSecretField)
		err := validateToken(accessToken, accessSecret)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		ctx = oauth1Login.WithAccessToken(ctx, accessToken, accessSecret)
		success.ServeHTTP(ctx, w, req)
	}
	return ctxh.ContextHandlerFunc(fn)
}
コード例 #5
0
ファイル: login_test.go プロジェクト: gooops/gologin
func TestLoginHandler_RequestTokenError(t *testing.T) {
	_, server := testutils.NewErrorServer("OAuth1 Service Down", http.StatusInternalServerError)
	defer server.Close()

	config := &oauth1.Config{
		Endpoint: oauth1.Endpoint{
			RequestTokenURL: server.URL,
		},
	}
	success := testutils.AssertSuccessNotCalled(t)
	failure := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		err := gologin.ErrorFromContext(ctx)
		if assert.NotNil(t, err) {
			// first validation in OAuth1 impl failed
			assert.Equal(t, "oauth1: oauth_callback_confirmed was not true", err.Error())
		}
		fmt.Fprintf(w, "failure handler called")
	}

	// LoginHandler cannot get the OAuth1 request token, assert that:
	// - failure handler is called
	// - error is added to the ctx of the failure handler
	loginHandler := LoginHandler(config, success, ctxh.ContextHandlerFunc(failure))
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/", nil)
	loginHandler.ServeHTTP(context.Background(), w, req)
	assert.Equal(t, "failure handler called", w.Body.String())
}
コード例 #6
0
ファイル: login_test.go プロジェクト: gooops/gologin
func TestGithubHandler_ErrorGettingUser(t *testing.T) {
	proxyClient, server := testutils.NewErrorServer("Github Service Down", http.StatusInternalServerError)
	defer server.Close()
	// oauth2 Client will use the proxy client's base Transport
	ctx := context.WithValue(context.Background(), oauth2.HTTPClient, proxyClient)
	anyToken := &oauth2.Token{AccessToken: "any-token"}
	ctx = oauth2Login.WithToken(ctx, anyToken)

	config := &oauth2.Config{}
	success := testutils.AssertSuccessNotCalled(t)
	failure := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		err := gologin.ErrorFromContext(ctx)
		if assert.NotNil(t, err) {
			assert.Equal(t, ErrUnableToGetGithubUser, err)
		}
		fmt.Fprintf(w, "failure handler called")
	}

	// GithubHandler cannot get Github User, assert that:
	// - failure handler is called
	// - error cannot get Github User added to the failure handler ctx
	githubHandler := githubHandler(config, success, ctxh.ContextHandlerFunc(failure))
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/", nil)
	githubHandler.ServeHTTP(ctx, w, req)
	assert.Equal(t, "failure handler called", w.Body.String())
}
コード例 #7
0
ファイル: token_test.go プロジェクト: gooops/gologin
func TestTokenHandler(t *testing.T) {
	proxyClient, _, server := newDigitsTestServer(testAccountJSON)
	defer server.Close()
	// oauth1 Client will use the proxy client's base Transport
	ctx := context.WithValue(context.Background(), oauth1.HTTPClient, proxyClient)

	config := &oauth1.Config{}
	success := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		account, err := AccountFromContext(ctx)
		assert.Nil(t, err)
		assert.Equal(t, testDigitsToken, account.AccessToken.Token)
		assert.Equal(t, testDigitsSecret, account.AccessToken.Secret)
		assert.Equal(t, "0123456789", account.PhoneNumber)

		accessToken, accessSecret, err := oauth1Login.AccessTokenFromContext(ctx)
		assert.Nil(t, err)
		assert.Equal(t, testDigitsToken, accessToken)
		assert.Equal(t, testDigitsSecret, accessSecret)
	}
	handler := TokenHandler(config, ctxh.ContextHandlerFunc(success), testutils.AssertFailureNotCalled(t))
	ts := httptest.NewServer(ctxh.NewHandlerWithContext(ctx, handler))
	// POST Digits access token to server under test
	resp, err := http.PostForm(ts.URL, url.Values{accessTokenField: {testDigitsToken}, accessTokenSecretField: {testDigitsSecret}})
	assert.Nil(t, err)
	if assert.NotNil(t, resp) {
		assert.Equal(t, resp.StatusCode, http.StatusOK)
	}
}
コード例 #8
0
ファイル: login.go プロジェクト: gooops/gologin
// getAccountViaEcho is a ContextHandler that gets the Digits Echo endpoint and
// OAuth header from the ctx and calls the endpoint to get the corresponding
// Digits Account. If successful, the Account is added to the ctx and the
// success handler is called. Otherwise, the failure handler is called.
func getAccountViaEcho(config *Config, success, failure ctxh.ContextHandler) ctxh.ContextHandler {
	if failure == nil {
		failure = gologin.DefaultFailureHandler
	}
	client := config.Client
	if client == nil {
		client = http.DefaultClient
	}
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		endpoint, header, err := EchoFromContext(ctx)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		// fetch the Digits Account
		account, resp, err := requestAccount(client, endpoint, header)
		// validate the Digits Account response
		err = validateResponse(account, resp, err)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		ctx = WithAccount(ctx, account)
		success.ServeHTTP(ctx, w, req)
	}
	return ctxh.ContextHandlerFunc(fn)
}
コード例 #9
0
ファイル: login.go プロジェクト: gooops/gologin
// LoginHandler receives a Digits OAuth Echo endpoint and OAuth header,
// validates the echo, and calls the endpoint to get the corresponding Digits
// Account. If successful, the Digits Account is added to the ctx and the
// success handler is called. Otherwise, the failure handler is called.
func LoginHandler(config *Config, success, failure ctxh.ContextHandler) ctxh.ContextHandler {
	success = getAccountViaEcho(config, success, failure)
	if failure == nil {
		failure = gologin.DefaultFailureHandler
	}
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		if req.Method != "POST" {
			ctx = gologin.WithError(ctx, fmt.Errorf("Method not allowed"))
			failure.ServeHTTP(ctx, w, req)
			return
		}
		req.ParseForm()
		accountEndpoint := req.PostForm.Get(accountEndpointField)
		accountRequestHeader := req.PostForm.Get(accountRequestHeaderField)
		// validate POST'ed Digits OAuth Echo data
		err := validateEcho(accountEndpoint, accountRequestHeader, config.ConsumerKey)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		ctx = WithEcho(ctx, accountEndpoint, accountRequestHeader)
		success.ServeHTTP(ctx, w, req)
	}
	return ctxh.ContextHandlerFunc(fn)
}
コード例 #10
0
ファイル: login.go プロジェクト: gooops/gologin
// facebookHandler is a ContextHandler that gets the OAuth2 Token from the ctx
// to get the corresponding Facebook User. If successful, the user is added to
// the ctx and the success handler is called. Otherwise, the failure handler
// is called.
func facebookHandler(config *oauth2.Config, success, failure ctxh.ContextHandler) ctxh.ContextHandler {
	if failure == nil {
		failure = gologin.DefaultFailureHandler
	}
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		token, err := oauth2Login.TokenFromContext(ctx)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		httpClient := config.Client(ctx, token)
		facebookService := newClient(httpClient)
		user, resp, err := facebookService.Me()
		err = validateResponse(user, resp, err)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		ctx = WithUser(ctx, user)
		success.ServeHTTP(ctx, w, req)
	}
	return ctxh.ContextHandlerFunc(fn)
}
コード例 #11
0
ファイル: login.go プロジェクト: gooops/gologin
// googleHandler is a ContextHandler that gets the OAuth2 Token from the ctx
// to get the corresponding Google Userinfoplus. If successful, the user info
// is added to the ctx and the success handler is called. Otherwise, the
// failure handler is called.
func googleHandler(config *oauth2.Config, success, failure ctxh.ContextHandler) ctxh.ContextHandler {
	if failure == nil {
		failure = gologin.DefaultFailureHandler
	}
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		token, err := oauth2Login.TokenFromContext(ctx)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		httpClient := config.Client(ctx, token)
		googleService, err := google.New(httpClient)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		userInfoPlus, err := googleService.Userinfo.Get().Do()
		err = validateResponse(userInfoPlus, err)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		ctx = WithUser(ctx, userInfoPlus)
		success.ServeHTTP(ctx, w, req)
	}
	return ctxh.ContextHandlerFunc(fn)
}
コード例 #12
0
ファイル: login_test.go プロジェクト: gooops/gologin
func TestGithubHandler(t *testing.T) {
	jsonData := `{"id": 917408, "name": "Alyssa Hacker"}`
	expectedUser := &github.User{ID: github.Int(917408), Name: github.String("Alyssa Hacker")}
	proxyClient, server := newGithubTestServer(jsonData)
	defer server.Close()
	// oauth2 Client will use the proxy client's base Transport
	ctx := context.WithValue(context.Background(), oauth2.HTTPClient, proxyClient)
	anyToken := &oauth2.Token{AccessToken: "any-token"}
	ctx = oauth2Login.WithToken(ctx, anyToken)

	config := &oauth2.Config{}
	success := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		githubUser, err := UserFromContext(ctx)
		assert.Nil(t, err)
		assert.Equal(t, expectedUser, githubUser)
		fmt.Fprintf(w, "success handler called")
	}
	failure := testutils.AssertFailureNotCalled(t)

	// GithubHandler assert that:
	// - Token is read from the ctx and passed to the Github API
	// - github User is obtained from the Github API
	// - success handler is called
	// - github User is added to the ctx of the success handler
	githubHandler := githubHandler(config, ctxh.ContextHandlerFunc(success), failure)
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/", nil)
	githubHandler.ServeHTTP(ctx, w, req)
	assert.Equal(t, "success handler called", w.Body.String())
}
コード例 #13
0
ファイル: login_test.go プロジェクト: gooops/gologin
func TestCallbackHandler(t *testing.T) {
	expectedToken := "acces_token"
	expectedSecret := "access_secret"
	requestSecret := "request_secret"
	data := url.Values{}
	data.Add("oauth_token", expectedToken)
	data.Add("oauth_token_secret", expectedSecret)
	server := NewAccessTokenServer(t, data)
	defer server.Close()

	config := &oauth1.Config{
		Endpoint: oauth1.Endpoint{
			AccessTokenURL: server.URL,
		},
	}
	success := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		accessToken, accessSecret, err := AccessTokenFromContext(ctx)
		assert.Equal(t, expectedToken, accessToken)
		assert.Equal(t, expectedSecret, accessSecret)
		assert.Nil(t, err)
		fmt.Fprintf(w, "success handler called")
	}
	failure := testutils.AssertFailureNotCalled(t)

	// CallbackHandler gets OAuth1 access token, assert that:
	// - success handler is called
	// - access token and secret added to the ctx of the success handler
	// - failure handler is not called
	callbackHandler := CallbackHandler(config, ctxh.ContextHandlerFunc(success), failure)
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/?oauth_token=any_token&oauth_verifier=any_verifier", nil)
	ctx := WithRequestToken(context.Background(), "", requestSecret)
	callbackHandler.ServeHTTP(ctx, w, req)
	assert.Equal(t, "success handler called", w.Body.String())
}
コード例 #14
0
ファイル: login.go プロジェクト: gooops/gologin
// twitterHandler is a ContextHandler that gets the OAuth1 access token from
// the ctx and calls Twitter verify_credentials to get the corresponding User.
// If successful, the User is added to the ctx and the success handler is
// called. Otherwise, the failure handler is called.
func twitterHandler(config *oauth1.Config, success, failure ctxh.ContextHandler) ctxh.ContextHandler {
	if failure == nil {
		failure = gologin.DefaultFailureHandler
	}
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		accessToken, accessSecret, err := oauth1Login.AccessTokenFromContext(ctx)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		httpClient := config.Client(ctx, oauth1.NewToken(accessToken, accessSecret))
		twitterClient := twitter.NewClient(httpClient)
		accountVerifyParams := &twitter.AccountVerifyParams{
			IncludeEntities: twitter.Bool(false),
			SkipStatus:      twitter.Bool(true),
			IncludeEmail:    twitter.Bool(false),
		}
		user, resp, err := twitterClient.Accounts.VerifyCredentials(accountVerifyParams)
		err = validateResponse(user, resp, err)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		ctx = WithUser(ctx, user)
		success.ServeHTTP(ctx, w, req)
	}
	return ctxh.ContextHandlerFunc(fn)
}
コード例 #15
0
ファイル: login.go プロジェクト: gooops/gologin
// tumblrHandler is a ContextHandler that gets the OAuth1 access token from
// the ctx and obtains the Tumblr User. If successful, the User is added to
// the ctx and the success handler is called. Otherwise, the failure handler
// is called.
func tumblrHandler(config *oauth1.Config, success, failure ctxh.ContextHandler) ctxh.ContextHandler {
	if failure == nil {
		failure = gologin.DefaultFailureHandler
	}
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		accessToken, accessSecret, err := oauth1Login.AccessTokenFromContext(ctx)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		httpClient := config.Client(ctx, oauth1.NewToken(accessToken, accessSecret))
		tumblrClient := newClient(httpClient)
		user, resp, err := tumblrClient.UserInfo()
		err = validateResponse(user, resp, err)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		ctx = WithUser(ctx, user)
		success.ServeHTTP(ctx, w, req)
	}
	return ctxh.ContextHandlerFunc(fn)
}
コード例 #16
0
ファイル: token_test.go プロジェクト: noscripter/gologin
func TestTokenHandler(t *testing.T) {
	proxyClient, _, server := newTwitterTestServer(testTwitterUserJSON)
	defer server.Close()
	// oauth1 Client will use the proxy client's base Transport
	ctx := context.WithValue(context.Background(), oauth1.HTTPClient, proxyClient)

	config := &oauth1.Config{}
	success := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		accessToken, accessSecret, err := oauth1Login.AccessTokenFromContext(ctx)
		assert.Nil(t, err)
		assert.Equal(t, testTwitterToken, accessToken)
		assert.Equal(t, testTwitterTokenSecret, accessSecret)

		user, err := UserFromContext(ctx)
		assert.Nil(t, err)
		assert.Equal(t, expectedUserID, user.ID)
		assert.Equal(t, "1234", user.IDStr)
	}
	handler := TokenHandler(config, ctxh.ContextHandlerFunc(success), assertFailureNotCalled(t))
	ts := httptest.NewServer(ctxh.NewHandlerWithContext(ctx, handler))
	// POST token to server under test
	resp, err := http.PostForm(ts.URL, url.Values{accessTokenField: {testTwitterToken}, accessTokenSecretField: {testTwitterTokenSecret}})
	assert.Nil(t, err)
	if assert.NotNil(t, resp) {
		assert.Equal(t, resp.StatusCode, http.StatusOK)
	}
}
コード例 #17
0
ファイル: login_test.go プロジェクト: gooops/gologin
func TestLoginHandler(t *testing.T) {
	expectedToken := "request_token"
	expectedSecret := "request_secret"
	data := url.Values{}
	data.Add("oauth_token", expectedToken)
	data.Add("oauth_token_secret", expectedSecret)
	data.Add("oauth_callback_confirmed", "true")
	server := NewRequestTokenServer(t, data)
	defer server.Close()

	config := &oauth1.Config{
		Endpoint: oauth1.Endpoint{
			RequestTokenURL: server.URL,
		},
	}
	success := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		requestToken, requestSecret, err := RequestTokenFromContext(ctx)
		assert.Equal(t, expectedToken, requestToken)
		assert.Equal(t, expectedSecret, requestSecret)
		assert.Nil(t, err)
		fmt.Fprintf(w, "success handler called")
	}
	failure := testutils.AssertFailureNotCalled(t)

	// LoginHandler gets OAuth1 request token, assert that:
	// - success handler is called
	// - request token added to the ctx of the success handler
	// - request secret added to the ctx of the success handler
	// - failure handler is not called
	loginHandler := LoginHandler(config, ctxh.ContextHandlerFunc(success), failure)
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/", nil)
	loginHandler.ServeHTTP(context.Background(), w, req)
	assert.Equal(t, "success handler called", w.Body.String())
}
コード例 #18
0
ファイル: login.go プロジェクト: gooops/gologin
// CallbackHandler handles OAuth2 redirection URI requests by parsing the auth
// code and state, comparing with the state value from the ctx, and obtaining
// an OAuth2 Token.
func CallbackHandler(config *oauth2.Config, success, failure ctxh.ContextHandler) ctxh.ContextHandler {
	if failure == nil {
		failure = gologin.DefaultFailureHandler
	}
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		authCode, state, err := parseCallback(req)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		ownerState, err := StateFromContext(ctx)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		if state != ownerState || state == "" {
			ctx = gologin.WithError(ctx, ErrInvalidState)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		// use the authorization code to get a Token
		token, err := config.Exchange(ctx, authCode)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		ctx = WithToken(ctx, token)
		success.ServeHTTP(ctx, w, req)
	}
	return ctxh.ContextHandlerFunc(fn)
}
コード例 #19
0
ファイル: login.go プロジェクト: gooops/gologin
// CallbackHandler handles OAuth1 callback requests by parsing the oauth token
// and verifier, reading the request token secret from the ctx, then obtaining
// an access token and adding it to the ctx.
func CallbackHandler(config *oauth1.Config, success, failure ctxh.ContextHandler) ctxh.ContextHandler {
	if failure == nil {
		failure = gologin.DefaultFailureHandler
	}
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		requestToken, verifier, err := oauth1.ParseAuthorizationCallback(req)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}

		// upstream handler should add the request token secret from the login step
		_, requestSecret, err := RequestTokenFromContext(ctx)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}

		accessToken, accessSecret, err := config.AccessToken(requestToken, requestSecret, verifier)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		ctx = WithAccessToken(ctx, accessToken, accessSecret)
		success.ServeHTTP(ctx, w, req)
	}
	return ctxh.ContextHandlerFunc(fn)
}
コード例 #20
0
ファイル: login_test.go プロジェクト: gooops/gologin
func TestCallbackHandler_AccessTokenError(t *testing.T) {
	requestSecret := "request_secret"
	_, server := testutils.NewErrorServer("OAuth1 Service Down", http.StatusInternalServerError)
	defer server.Close()

	config := &oauth1.Config{
		Endpoint: oauth1.Endpoint{
			AccessTokenURL: server.URL,
		},
	}
	success := testutils.AssertSuccessNotCalled(t)
	failure := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		err := gologin.ErrorFromContext(ctx)
		if assert.NotNil(t, err) {
			assert.Equal(t, "oauth1: Response missing oauth_token or oauth_token_secret", err.Error())
		}
		fmt.Fprintf(w, "failure handler called")
	}

	// CallbackHandler cannot get the OAuth1 access token, assert that:
	// - failure handler is called
	// - error about missing oauth_token and oauth_token_secret is added to the ctx
	callbackHandler := CallbackHandler(config, success, ctxh.ContextHandlerFunc(failure))
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/?oauth_token=any_token&oauth_verifier=any_verifier", nil)
	ctx := WithRequestToken(context.Background(), "", requestSecret)
	callbackHandler.ServeHTTP(ctx, w, req)
	assert.Equal(t, "failure handler called", w.Body.String())
}
コード例 #21
0
ファイル: login_test.go プロジェクト: gooops/gologin
func TestAuthRedirectHandler_AuthorizationURL(t *testing.T) {
	requestToken := "request_token"
	config := &oauth1.Config{
		Endpoint: oauth1.Endpoint{
			AuthorizeURL: "%gh&%ij", // always causes AuthorizationURL parse error
		},
	}
	failure := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		err := gologin.ErrorFromContext(ctx)
		if assert.NotNil(t, err) {
			assert.Equal(t, "parse %gh&%ij: invalid URL escape \"%gh\"", err.Error())
		}
		fmt.Fprintf(w, "failure handler called")
	}

	// AuthRedirectHandler cannot construct the AuthorizationURL, assert that:
	// - failure handler is called
	// - error about authorization URL is added to the ctx
	authRedirectHandler := AuthRedirectHandler(config, ctxh.ContextHandlerFunc(failure))
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/", nil)
	ctx := WithRequestToken(context.Background(), requestToken, "")
	authRedirectHandler.ServeHTTP(ctx, w, req)
	assert.Equal(t, "failure handler called", w.Body.String())
}
コード例 #22
0
ファイル: login_test.go プロジェクト: gooops/gologin
func TestCallbackHandler_ParseCallbackError(t *testing.T) {
	config := &oauth2.Config{}
	success := testutils.AssertSuccessNotCalled(t)
	failure := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		err := gologin.ErrorFromContext(ctx)
		if assert.NotNil(t, err) {
			assert.Equal(t, "oauth2: Request missing code or state", err.Error())
		}
		fmt.Fprintf(w, "failure handler called")
	}

	// CallbackHandler called without code or state, assert that:
	// - failure handler is called
	// - error about missing code or state is added to the ctx
	callbackHandler := CallbackHandler(config, success, ctxh.ContextHandlerFunc(failure))
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/?code=any_code", nil)
	callbackHandler.ServeHTTP(context.Background(), w, req)
	assert.Equal(t, "failure handler called", w.Body.String())

	w = httptest.NewRecorder()
	req, _ = http.NewRequest("GET", "/?state=any_state", nil)
	callbackHandler.ServeHTTP(context.Background(), w, req)
	assert.Equal(t, "failure handler called", w.Body.String())
}
コード例 #23
0
ファイル: login_test.go プロジェクト: gooops/gologin
func TestBitbucketHandler(t *testing.T) {
	jsonData := `{"username": "******", "display_name": "Atlas Ian"}`
	expectedUser := &User{Username: "******", DisplayName: "Atlas Ian"}
	proxyClient, server := newBitbucketTestServer(jsonData)
	defer server.Close()
	// oauth2 Client will use the proxy client's base Transport
	ctx := context.WithValue(context.Background(), oauth2.HTTPClient, proxyClient)
	anyToken := &oauth2.Token{AccessToken: "any-token"}
	ctx = oauth2Login.WithToken(ctx, anyToken)

	config := &oauth2.Config{}
	success := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		bitbucketUser, err := UserFromContext(ctx)
		assert.Nil(t, err)
		assert.Equal(t, expectedUser, bitbucketUser)
		fmt.Fprintf(w, "success handler called")
	}
	failure := testutils.AssertFailureNotCalled(t)

	// BitbucketHandler assert that:
	// - Token is read from the ctx and passed to the Bitbucket API
	// - bitbucket User is obtained from the Bitbucket API
	// - success handler is called
	// - bitbucket User is added to the ctx of the success handler
	bitbucketHandler := bitbucketHandler(config, ctxh.ContextHandlerFunc(success), failure)
	w := httptest.NewRecorder()
	req, _ := http.NewRequest("GET", "/", nil)
	bitbucketHandler.ServeHTTP(ctx, w, req)
	assert.Equal(t, "success handler called", w.Body.String())
}
コード例 #24
0
ファイル: login.go プロジェクト: gooops/gologin
// EmptyTempHandler adds an empty request token secret to the ctx if none is
// present to support OAuth1 providers which do not require temp secrets to
// be kept between the login phase and callback phase.
func EmptyTempHandler(success ctxh.ContextHandler) ctxh.ContextHandler {
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		_, _, err := RequestTokenFromContext(ctx)
		if err != nil {
			ctx = WithRequestToken(ctx, "", "")
		}
		success.ServeHTTP(ctx, w, req)
	}
	return ctxh.ContextHandlerFunc(fn)
}
コード例 #25
0
ファイル: main.go プロジェクト: gooops/gologin
// issueSession issues a cookie session after successful Twitter login
func issueSession() ctxh.ContextHandler {
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		twitterUser, err := twitter.UserFromContext(ctx)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		// 2. Implement a success handler to issue some form of session
		session := sessionStore.New(sessionName)
		session.Values[sessionUserKey] = twitterUser.ID
		session.Save(w)
		http.Redirect(w, req, "/profile", http.StatusFound)
	}
	return ctxh.ContextHandlerFunc(fn)
}
コード例 #26
0
ファイル: login_test.go プロジェクト: gooops/gologin
func checkSuccess(t *testing.T) ctxh.ContextHandler {
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		account, err := AccountFromContext(ctx)
		assert.Nil(t, err)
		assert.Equal(t, testDigitsToken, account.AccessToken.Token)
		assert.Equal(t, testDigitsSecret, account.AccessToken.Secret)
		assert.Equal(t, "0123456789", account.PhoneNumber)

		endpoint, header, err := EchoFromContext(ctx)
		assert.Nil(t, err)
		assert.Equal(t, testAccountEndpoint, endpoint)
		assert.Equal(t, testAccountRequestHeader, header)
	}
	return ctxh.ContextHandlerFunc(fn)
}
コード例 #27
0
ファイル: login.go プロジェクト: gooops/gologin
// LoginHandler handles OAuth2 login requests by reading the state value from
// the ctx and redirecting requests to the AuthURL with that state value.
func LoginHandler(config *oauth2.Config, failure ctxh.ContextHandler) ctxh.ContextHandler {
	if failure == nil {
		failure = gologin.DefaultFailureHandler
	}
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		state, err := StateFromContext(ctx)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		authURL := config.AuthCodeURL(state)
		http.Redirect(w, req, authURL, http.StatusFound)
	}
	return ctxh.ContextHandlerFunc(fn)
}
コード例 #28
0
ファイル: login.go プロジェクト: gooops/gologin
// LoginHandler handles OAuth1 login requests by obtaining a request token and
// secret (temporary credentials) and adding it to the ctx. If successful,
// handling delegates to the success handler, otherwise to the failure handler.
//
// Typically, the success handler is an AuthRedirectHandler or a handler which
// stores the request token secret.
func LoginHandler(config *oauth1.Config, success, failure ctxh.ContextHandler) ctxh.ContextHandler {
	if failure == nil {
		failure = gologin.DefaultFailureHandler
	}
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		requestToken, requestSecret, err := config.RequestToken()
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		ctx = WithRequestToken(ctx, requestToken, requestSecret)
		success.ServeHTTP(ctx, w, req)
	}
	return ctxh.ContextHandlerFunc(fn)
}
コード例 #29
0
ファイル: login.go プロジェクト: gooops/gologin
// StateHandler checks for a state cookie. If found, the state value is read
// and added to the ctx. Otherwise, a non-guessable value is added to the ctx
// and to a (short-lived) state cookie issued to the requester.
//
// Implements OAuth 2 RFC 6749 10.12 CSRF Protection. If you wish to issue
// state params differently, write a ContextHandler which sets the ctx state,
// using oauth2 WithState(ctx, state) since it is required by LoginHandler
// and CallbackHandler.
func StateHandler(config gologin.CookieConfig, success ctxh.ContextHandler) ctxh.ContextHandler {
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		cookie, err := req.Cookie(config.Name)
		if err == nil {
			// add the cookie state to the ctx
			ctx = WithState(ctx, cookie.Value)
		} else {
			// add Cookie with a random state
			val := randomState()
			http.SetCookie(w, internal.NewCookie(config, val))
			ctx = WithState(ctx, val)
		}
		success.ServeHTTP(ctx, w, req)
	}
	return ctxh.ContextHandlerFunc(fn)
}
コード例 #30
0
ファイル: token_test.go プロジェクト: noscripter/gologin
func TestTokenHandler_UnauthorizedPassesError(t *testing.T) {
	proxyClient, server := testutils.UnauthorizedTestServer()
	defer server.Close()
	// oauth1 Client will use the proxy client's base Transport
	ctx := context.WithValue(context.Background(), oauth1.HTTPClient, proxyClient)

	config := &oauth1.Config{}
	failure := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		// assert that error passed through ctx
		err := gologin.ErrorFromContext(ctx)
		if assert.Error(t, err) {
			assert.Equal(t, err, ErrUnableToGetTwitterUser)
		}
	}
	handler := TokenHandler(config, assertSuccessNotCalled(t), ctxh.ContextHandlerFunc(failure))
	ts := httptest.NewServer(ctxh.NewHandlerWithContext(ctx, handler))
	http.PostForm(ts.URL, url.Values{accessTokenField: {testTwitterToken}, accessTokenSecretField: {testTwitterTokenSecret}})
}