Пример #1
0
// 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)
}
Пример #2
0
// 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)
}
Пример #3
0
// 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)
}
Пример #4
0
// 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)
}
Пример #5
0
// 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)
}
Пример #6
0
// 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)
}
Пример #7
0
// 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)
}
Пример #8
0
// AuthRedirectHandler reads the request token from the ctx and redirects
// to the authorization URL.
func AuthRedirectHandler(config *oauth1.Config, failure ctxh.ContextHandler) ctxh.ContextHandler {
	if failure == nil {
		failure = gologin.DefaultFailureHandler
	}
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		requestToken, _, err := RequestTokenFromContext(ctx)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		authorizationURL, err := config.AuthorizationURL(requestToken)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		http.Redirect(w, req, authorizationURL.String(), http.StatusFound)
	}
	return ctxh.ContextHandlerFunc(fn)
}
Пример #9
0
// 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)
}
Пример #10
0
// 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)
}
Пример #11
0
// 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)
}
Пример #12
0
// 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)
}
Пример #13
0
// 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)
}
Пример #14
0
// 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)
}
Пример #15
0
// CookieTempHandler persists or retrieves the request token secret (temporary
// credentials). If the request token can be read from the ctx (login phase),
// the secret is set in a short-lived cookie to be read later. Otherwise
// (callback phase) the cookie is read to retrieve the request token secret
// and add it to the ctx.
// If the ctx contains no request token and the request has no temp cookie,
// the failure handler is called.
//
// Some OAuth1 providers (Twitter, Digits) do NOT require temp secrets to be
// kept between the login phase and callback phase. To implement those
// providers, use the EmptyTempHandler instead.
func CookieTempHandler(config gologin.CookieConfig, success, failure ctxh.ContextHandler) ctxh.ContextHandler {
	if failure == nil {
		failure = gologin.DefaultFailureHandler
	}
	fn := func(ctx context.Context, w http.ResponseWriter, req *http.Request) {
		_, requestSecret, err := RequestTokenFromContext(ctx)
		if err == nil {
			// add request secret  to a short-lived cookie
			http.SetCookie(w, internal.NewCookie(config, requestSecret))
			success.ServeHTTP(ctx, w, req)
			return
		}
		// read request secret from the short-lived cookie to add to ctx
		cookie, err := req.Cookie(config.Name)
		if err != nil {
			ctx = gologin.WithError(ctx, err)
			failure.ServeHTTP(ctx, w, req)
			return
		}
		ctx = WithRequestToken(ctx, "", cookie.Value)
		success.ServeHTTP(ctx, w, req)
	}
	return ctxh.ContextHandlerFunc(fn)
}