diff --git a/Documentation/connectors/bitbucketcloud.md b/Documentation/connectors/bitbucketcloud.md
new file mode 100644
index 0000000000000000000000000000000000000000..fbf8a5891613f020c3fcbfd6de3c07e1c227e774
--- /dev/null
+++ b/Documentation/connectors/bitbucketcloud.md
@@ -0,0 +1,32 @@
+# Authentication through Bitbucket Cloud
+
+## Overview
+
+One of the login options for dex uses the Bitbucket OAuth2 flow to identify the end user through their Bitbucket account.
+
+When a client redeems a refresh token through dex, dex will re-query Bitbucket to update user information in the ID Token. To do this, __dex stores a readonly Bitbucket access token in its backing datastore.__ Users that reject dex's access through Bitbucket will also revoke all dex clients which authenticated them through Bitbucket.
+
+## Configuration
+
+Register a new OAuth consumer with [Bitbucket](https://confluence.atlassian.com/bitbucket/oauth-on-bitbucket-cloud-238027431.html) ensuring the callback URL is `(dex issuer)/callback`. For example if dex is listening at the non-root path `https://auth.example.com/dex` the callback would be `https://auth.example.com/dex/callback`.
+
+The following is an example of a configuration for `examples/config-dev.yaml`:
+
+```yaml
+connectors:
+- type: bitbucket-cloud
+  # Required field for connector id.
+  id: bitbucket-cloud
+  # Required field for connector name.
+  name: Bitbucket Cloud
+  config:
+    # Credentials can be string literals or pulled from the environment.
+    clientID: $BITBUCKET_CLIENT_ID
+    clientSecret: $BITBUCKET_CLIENT_SECRET
+    redirectURI: http://127.0.0.1:5556/dex/callback
+    # Optional teams whitelist, communicated through the "groups" scope.
+    # If `teams` is omitted, all of the user's Bitbucket teams are returned when the groups scope is present.
+    # If `teams` is provided, this acts as a whitelist - only the user's Bitbucket teams that are in the configured `teams` below will go into the groups claim.  Conversely, if the user is not in any of the configured `teams`, the user will not be authenticated.
+    teams:
+    - my-team
+```
diff --git a/README.md b/README.md
index 33a941ffa36a02ee0f65926f1cae613c9358a306..d6d558f856a387d6b35dfad78ae01e51e9c279f8 100644
--- a/README.md
+++ b/README.md
@@ -73,6 +73,7 @@ Dex implements the following connectors:
 | [LinkedIn](Documentation/connectors/linkedin.md) | yes | no | beta | |
 | [Microsoft](Documentation/connectors/microsoft.md) | yes | yes | beta | |
 | [AuthProxy](Documentation/connectors/authproxy.md) | no | no | alpha | Authentication proxies such as Apache2 mod_auth, etc. |
+| [Bitbucket Cloud](Documentation/connectors/bitbucket.md) | yes | yes | alpha | |
 
 Stable, beta, and alpha are defined as:
 
diff --git a/connector/bitbucketcloud/bitbucketcloud.go b/connector/bitbucketcloud/bitbucketcloud.go
new file mode 100644
index 0000000000000000000000000000000000000000..27a63c4684169d54550f55168fac040f589b5b1e
--- /dev/null
+++ b/connector/bitbucketcloud/bitbucketcloud.go
@@ -0,0 +1,444 @@
+// Package bitbucketcloud provides authentication strategies using Bitbucket Cloud.
+package bitbucketcloud
+
+import (
+	"context"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"sync"
+	"time"
+
+	"golang.org/x/oauth2"
+	"golang.org/x/oauth2/bitbucket"
+
+	"github.com/sirupsen/logrus"
+
+	"github.com/dexidp/dex/connector"
+)
+
+const (
+	apiURL = "https://api.bitbucket.org/2.0"
+
+	// Bitbucket requires this scope to access '/user' API endpoints.
+	scopeAccount = "account"
+	// Bitbucket requires this scope to access '/user/emails' API endpoints.
+	scopeEmail = "email"
+	// Bitbucket requires this scope to access '/teams' API endpoints
+	// which are used when a client includes the 'groups' scope.
+	scopeTeams = "team"
+)
+
+// Config holds configuration options for Bitbucket logins.
+type Config struct {
+	ClientID     string   `json:"clientID"`
+	ClientSecret string   `json:"clientSecret"`
+	RedirectURI  string   `json:"redirectURI"`
+	Teams        []string `json:"teams"`
+}
+
+// Open returns a strategy for logging in through Bitbucket.
+func (c *Config) Open(id string, logger logrus.FieldLogger) (connector.Connector, error) {
+
+	b := bitbucketConnector{
+		redirectURI:  c.RedirectURI,
+		teams:        c.Teams,
+		clientID:     c.ClientID,
+		clientSecret: c.ClientSecret,
+		apiURL:       apiURL,
+		logger:       logger,
+	}
+
+	return &b, nil
+}
+
+type connectorData struct {
+	AccessToken  string    `json:"accessToken"`
+	RefreshToken string    `json:"refreshToken"`
+	Expiry       time.Time `json:"expiry"`
+}
+
+var (
+	_ connector.CallbackConnector = (*bitbucketConnector)(nil)
+	_ connector.RefreshConnector  = (*bitbucketConnector)(nil)
+)
+
+type bitbucketConnector struct {
+	redirectURI  string
+	teams        []string
+	clientID     string
+	clientSecret string
+	logger       logrus.FieldLogger
+	apiURL       string
+
+	// the following are used only for tests
+	hostName   string
+	httpClient *http.Client
+}
+
+// groupsRequired returns whether dex requires Bitbucket's 'team' scope.
+func (b *bitbucketConnector) groupsRequired(groupScope bool) bool {
+	return len(b.teams) > 0 || groupScope
+}
+
+func (b *bitbucketConnector) oauth2Config(scopes connector.Scopes) *oauth2.Config {
+	bitbucketScopes := []string{scopeAccount, scopeEmail}
+	if b.groupsRequired(scopes.Groups) {
+		bitbucketScopes = append(bitbucketScopes, scopeTeams)
+	}
+
+	endpoint := bitbucket.Endpoint
+	if b.hostName != "" {
+		endpoint = oauth2.Endpoint{
+			AuthURL:  "https://" + b.hostName + "/site/oauth2/authorize",
+			TokenURL: "https://" + b.hostName + "/site/oauth2/access_token",
+		}
+	}
+
+	return &oauth2.Config{
+		ClientID:     b.clientID,
+		ClientSecret: b.clientSecret,
+		Endpoint:     endpoint,
+		Scopes:       bitbucketScopes,
+	}
+}
+
+func (b *bitbucketConnector) LoginURL(scopes connector.Scopes, callbackURL, state string) (string, error) {
+	if b.redirectURI != callbackURL {
+		return "", fmt.Errorf("expected callback URL %q did not match the URL in the config %q", callbackURL, b.redirectURI)
+	}
+
+	return b.oauth2Config(scopes).AuthCodeURL(state), nil
+}
+
+type oauth2Error struct {
+	error            string
+	errorDescription string
+}
+
+func (e *oauth2Error) Error() string {
+	if e.errorDescription == "" {
+		return e.error
+	}
+	return e.error + ": " + e.errorDescription
+}
+
+func (b *bitbucketConnector) HandleCallback(s connector.Scopes, r *http.Request) (identity connector.Identity, err error) {
+	q := r.URL.Query()
+	if errType := q.Get("error"); errType != "" {
+		return identity, &oauth2Error{errType, q.Get("error_description")}
+	}
+
+	oauth2Config := b.oauth2Config(s)
+
+	ctx := r.Context()
+	if b.httpClient != nil {
+		ctx = context.WithValue(r.Context(), oauth2.HTTPClient, b.httpClient)
+	}
+
+	token, err := oauth2Config.Exchange(ctx, q.Get("code"))
+	if err != nil {
+		return identity, fmt.Errorf("bitbucket: failed to get token: %v", err)
+	}
+
+	client := oauth2Config.Client(ctx, token)
+
+	user, err := b.user(ctx, client)
+	if err != nil {
+		return identity, fmt.Errorf("bitbucket: get user: %v", err)
+	}
+
+	identity = connector.Identity{
+		UserID:        user.UUID,
+		Username:      user.Username,
+		Email:         user.Email,
+		EmailVerified: true,
+	}
+
+	if b.groupsRequired(s.Groups) {
+		groups, err := b.getGroups(ctx, client, s.Groups, user.Username)
+		if err != nil {
+			return identity, err
+		}
+		identity.Groups = groups
+	}
+
+	if s.OfflineAccess {
+		data := connectorData{
+			AccessToken:  token.AccessToken,
+			RefreshToken: token.RefreshToken,
+			Expiry:       token.Expiry,
+		}
+		connData, err := json.Marshal(data)
+		if err != nil {
+			return identity, fmt.Errorf("bitbucket: marshal connector data: %v", err)
+		}
+		identity.ConnectorData = connData
+	}
+
+	return identity, nil
+}
+
+// Refreshing tokens
+// https://github.com/golang/oauth2/issues/84#issuecomment-332860871
+type tokenNotifyFunc func(*oauth2.Token) error
+
+// notifyRefreshTokenSource is essentially `oauth2.ReuseTokenSource` with `TokenNotifyFunc` added.
+type notifyRefreshTokenSource struct {
+	new oauth2.TokenSource
+	mu  sync.Mutex // guards t
+	t   *oauth2.Token
+	f   tokenNotifyFunc // called when token refreshed so new refresh token can be persisted
+}
+
+// Token returns the current token if it's still valid, else will
+// refresh the current token (using r.Context for HTTP client
+// information) and return the new one.
+func (s *notifyRefreshTokenSource) Token() (*oauth2.Token, error) {
+	s.mu.Lock()
+	defer s.mu.Unlock()
+	if s.t.Valid() {
+		return s.t, nil
+	}
+	t, err := s.new.Token()
+	if err != nil {
+		return nil, err
+	}
+	s.t = t
+	return t, s.f(t)
+}
+
+func (b *bitbucketConnector) Refresh(ctx context.Context, s connector.Scopes, identity connector.Identity) (connector.Identity, error) {
+	if len(identity.ConnectorData) == 0 {
+		return identity, errors.New("bitbucket: no upstream access token found")
+	}
+
+	var data connectorData
+	if err := json.Unmarshal(identity.ConnectorData, &data); err != nil {
+		return identity, fmt.Errorf("bitbucket: unmarshal access token: %v", err)
+	}
+
+	tok := &oauth2.Token{
+		AccessToken:  data.AccessToken,
+		RefreshToken: data.RefreshToken,
+		Expiry:       data.Expiry,
+	}
+
+	client := oauth2.NewClient(ctx, &notifyRefreshTokenSource{
+		new: b.oauth2Config(s).TokenSource(ctx, tok),
+		t:   tok,
+		f: func(tok *oauth2.Token) error {
+			data := connectorData{
+				AccessToken:  tok.AccessToken,
+				RefreshToken: tok.RefreshToken,
+				Expiry:       tok.Expiry,
+			}
+			connData, err := json.Marshal(data)
+			if err != nil {
+				return fmt.Errorf("bitbucket: marshal connector data: %v", err)
+			}
+			identity.ConnectorData = connData
+			return nil
+		},
+	})
+
+	user, err := b.user(ctx, client)
+	if err != nil {
+		return identity, fmt.Errorf("bitbucket: get user: %v", err)
+	}
+
+	identity.Username = user.Username
+	identity.Email = user.Email
+
+	if b.groupsRequired(s.Groups) {
+		groups, err := b.getGroups(ctx, client, s.Groups, user.Username)
+		if err != nil {
+			return identity, err
+		}
+		identity.Groups = groups
+	}
+
+	return identity, nil
+}
+
+// Bitbucket pagination wrapper
+type pagedResponse struct {
+	Size     int     `json:"size"`
+	Page     int     `json:"page"`
+	PageLen  int     `json:"pagelen"`
+	Next     *string `json:"next"`
+	Previous *string `json:"previous"`
+}
+
+// user holds Bitbucket user information (relevant to dex) as defined by
+// https://developer.atlassian.com/bitbucket/api/2/reference/resource/user
+type user struct {
+	Username string `json:"username"`
+	UUID     string `json:"uuid"`
+	Email    string `json:"email"`
+}
+
+// user queries the Bitbucket API for profile information using the provided client.
+//
+// The HTTP client is expected to be constructed by the golang.org/x/oauth2 package,
+// which inserts a bearer token as part of the request.
+func (b *bitbucketConnector) user(ctx context.Context, client *http.Client) (user, error) {
+	// https://developer.atlassian.com/bitbucket/api/2/reference/resource/user
+	var (
+		u   user
+		err error
+	)
+
+	if err = get(ctx, client, b.apiURL+"/user", &u); err != nil {
+		return user{}, err
+	}
+
+	if u.Email, err = b.userEmail(ctx, client); err != nil {
+		return user{}, err
+	}
+
+	return u, nil
+}
+
+// userEmail holds Bitbucket user email information as defined by
+// https://developer.atlassian.com/bitbucket/api/2/reference/resource/user/emails
+type userEmail struct {
+	IsPrimary   bool   `json:"is_primary"`
+	IsConfirmed bool   `json:"is_confirmed"`
+	Email       string `json:"email"`
+}
+
+type userEmailResponse struct {
+	pagedResponse
+	Values []userEmail
+}
+
+// userEmail returns the users primary, confirmed email
+//
+// The HTTP client is expected to be constructed by the golang.org/x/oauth2 package,
+// which inserts a bearer token as part of the request.
+func (b *bitbucketConnector) userEmail(ctx context.Context, client *http.Client) (string, error) {
+	apiURL := b.apiURL + "/user/emails"
+	for {
+		// https://developer.atlassian.com/bitbucket/api/2/reference/resource/user/emails
+		var response userEmailResponse
+
+		if err := get(ctx, client, apiURL, &response); err != nil {
+			return "", err
+		}
+
+		for _, email := range response.Values {
+			if email.IsConfirmed && email.IsPrimary {
+				return email.Email, nil
+			}
+		}
+
+		if response.Next == nil {
+			break
+		}
+	}
+
+	return "", errors.New("bitbucket: user has no confirmed, primary email")
+}
+
+// getGroups retrieves Bitbucket teams a user is in, if any.
+func (b *bitbucketConnector) getGroups(ctx context.Context, client *http.Client, groupScope bool, userLogin string) ([]string, error) {
+	bitbucketTeams, err := b.userTeams(ctx, client)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(b.teams) > 0 {
+		filteredTeams := filterTeams(bitbucketTeams, b.teams)
+		if len(filteredTeams) == 0 {
+			return nil, fmt.Errorf("bitbucket: user %q is not in any of the required teams", userLogin)
+		}
+		return filteredTeams, nil
+	} else if groupScope {
+		return bitbucketTeams, nil
+	}
+
+	return nil, nil
+}
+
+// Filter the users' team memberships by 'teams' from config.
+func filterTeams(userTeams, configTeams []string) []string {
+	teams := []string{}
+	teamFilter := make(map[string]struct{})
+	for _, team := range configTeams {
+		teamFilter[team] = struct{}{}
+	}
+	for _, team := range userTeams {
+		if _, ok := teamFilter[team]; ok {
+			teams = append(teams, team)
+		}
+	}
+	return teams
+}
+
+type team struct {
+	Name string `json:"username"` // The "username" from Bitbucket Cloud is actually the team name here
+}
+
+type userTeamsResponse struct {
+	pagedResponse
+	Values []team
+}
+
+func (b *bitbucketConnector) userTeams(ctx context.Context, client *http.Client) ([]string, error) {
+
+	var teams []string
+	apiURL := b.apiURL + "/teams?role=member"
+
+	for {
+		// https://developer.atlassian.com/bitbucket/api/2/reference/resource/teams
+		var response userTeamsResponse
+
+		if err := get(ctx, client, apiURL, &response); err != nil {
+			return nil, fmt.Errorf("bitbucket: get user teams: %v", err)
+		}
+
+		for _, team := range response.Values {
+			teams = append(teams, team.Name)
+		}
+
+		if response.Next == nil {
+			break
+		}
+	}
+
+	return teams, nil
+}
+
+// get creates a "GET `apiURL`" request with context, sends the request using
+// the client, and decodes the resulting response body into v.
+// Any errors encountered when building requests, sending requests, and
+// reading and decoding response data are returned.
+func get(ctx context.Context, client *http.Client, apiURL string, v interface{}) error {
+	req, err := http.NewRequest("GET", apiURL, nil)
+	if err != nil {
+		return fmt.Errorf("bitbucket: new req: %v", err)
+	}
+	req = req.WithContext(ctx)
+	resp, err := client.Do(req)
+	if err != nil {
+		return fmt.Errorf("bitbucket: get URL %v", err)
+	}
+	defer resp.Body.Close()
+
+	if resp.StatusCode != http.StatusOK {
+		body, err := ioutil.ReadAll(resp.Body)
+		if err != nil {
+			return fmt.Errorf("bitbucket: read body: %s: %v", resp.Status, err)
+		}
+		return fmt.Errorf("%s: %s", resp.Status, body)
+	}
+
+	if err := json.NewDecoder(resp.Body).Decode(v); err != nil {
+		return fmt.Errorf("bitbucket: failed to decode response: %v", err)
+	}
+
+	return nil
+}
diff --git a/connector/bitbucketcloud/bitbucketcloud_test.go b/connector/bitbucketcloud/bitbucketcloud_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..b9f4ba08f822857280f3e34b63dcbb16046254bc
--- /dev/null
+++ b/connector/bitbucketcloud/bitbucketcloud_test.go
@@ -0,0 +1,124 @@
+package bitbucketcloud
+
+import (
+	"context"
+	"crypto/tls"
+	"encoding/json"
+	"net/http"
+	"net/http/httptest"
+	"net/url"
+	"reflect"
+	"testing"
+
+	"github.com/dexidp/dex/connector"
+)
+
+func TestUserGroups(t *testing.T) {
+
+	teamsResponse := userTeamsResponse{
+		pagedResponse: pagedResponse{
+			Size:    3,
+			Page:    1,
+			PageLen: 10,
+		},
+		Values: []team{
+			{Name: "team-1"},
+			{Name: "team-2"},
+			{Name: "team-3"},
+		},
+	}
+
+	s := newTestServer(map[string]interface{}{
+		"/teams?role=member": teamsResponse,
+	})
+
+	connector := bitbucketConnector{apiURL: s.URL}
+	groups, err := connector.userTeams(context.Background(), newClient())
+
+	expectNil(t, err)
+	expectEquals(t, groups, []string{
+		"team-1",
+		"team-2",
+		"team-3",
+	})
+
+	s.Close()
+}
+
+func TestUserWithoutTeams(t *testing.T) {
+
+	s := newTestServer(map[string]interface{}{
+		"/teams?role=member": userTeamsResponse{},
+	})
+
+	connector := bitbucketConnector{apiURL: s.URL}
+	groups, err := connector.userTeams(context.Background(), newClient())
+
+	expectNil(t, err)
+	expectEquals(t, len(groups), 0)
+
+	s.Close()
+}
+
+func TestUsernameIncludedInFederatedIdentity(t *testing.T) {
+
+	s := newTestServer(map[string]interface{}{
+		"/user": user{Username: "some-login"},
+		"/user/emails": userEmailResponse{
+			pagedResponse: pagedResponse{
+				Size:    1,
+				Page:    1,
+				PageLen: 10,
+			},
+			Values: []userEmail{{
+				Email:       "some@email.com",
+				IsConfirmed: true,
+				IsPrimary:   true,
+			}},
+		},
+		"/site/oauth2/access_token": map[string]interface{}{
+			"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9",
+			"expires_in":   "30",
+		},
+	})
+
+	hostURL, err := url.Parse(s.URL)
+	expectNil(t, err)
+
+	req, err := http.NewRequest("GET", hostURL.String(), nil)
+	expectNil(t, err)
+
+	bitbucketConnector := bitbucketConnector{apiURL: s.URL, hostName: hostURL.Host, httpClient: newClient()}
+	identity, err := bitbucketConnector.HandleCallback(connector.Scopes{}, req)
+
+	expectNil(t, err)
+	expectEquals(t, identity.Username, "some-login")
+
+	s.Close()
+}
+
+func newTestServer(responses map[string]interface{}) *httptest.Server {
+	return httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		w.Header().Add("Content-Type", "application/json")
+		json.NewEncoder(w).Encode(responses[r.URL.String()])
+	}))
+}
+
+func newClient() *http.Client {
+	tr := &http.Transport{
+		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+	}
+	return &http.Client{Transport: tr}
+}
+
+func expectNil(t *testing.T, a interface{}) {
+	if a != nil {
+		t.Fatalf("Expected %+v to equal nil", a)
+	}
+}
+
+func expectEquals(t *testing.T, a interface{}, b interface{}) {
+	if !reflect.DeepEqual(a, b) {
+		t.Fatalf("Expected %+v to equal %+v", a, b)
+	}
+}
diff --git a/glide.lock b/glide.lock
index 82e1f2a804a77ee4b92574e660018f87f823f069..d646231b561bd4dc2d15a773c95190710f0e8251 100644
--- a/glide.lock
+++ b/glide.lock
@@ -1,5 +1,5 @@
 hash: 12d0ad2fc0df4ab221e45c1ba7821708b908033c82741e250cc46dcd445b67eb
-updated: 2018-09-18T23:51:30.787348994+02:00
+updated: 2018-09-30T14:07:57.901347233-04:00
 imports:
 - name: github.com/beevik/etree
   version: 4cd0dd976db869f817248477718071a28e978df0
@@ -124,6 +124,7 @@ imports:
 - name: golang.org/x/oauth2
   version: 08c8d727d2392d18286f9f88ad775ad98f09ab33
   subpackages:
+  - bitbucket
   - github
   - internal
 - name: golang.org/x/sys
diff --git a/server/server.go b/server/server.go
index d96f562d24b97da5f4c0876a268b41232c1e2862..adf872eb7df8294a0ae123d72775f5612c506295 100644
--- a/server/server.go
+++ b/server/server.go
@@ -24,6 +24,7 @@ import (
 
 	"github.com/dexidp/dex/connector"
 	"github.com/dexidp/dex/connector/authproxy"
+	"github.com/dexidp/dex/connector/bitbucketcloud"
 	"github.com/dexidp/dex/connector/github"
 	"github.com/dexidp/dex/connector/gitlab"
 	"github.com/dexidp/dex/connector/ldap"
@@ -429,16 +430,17 @@ type ConnectorConfig interface {
 // ConnectorsConfig variable provides an easy way to return a config struct
 // depending on the connector type.
 var ConnectorsConfig = map[string]func() ConnectorConfig{
-	"mockCallback": func() ConnectorConfig { return new(mock.CallbackConfig) },
-	"mockPassword": func() ConnectorConfig { return new(mock.PasswordConfig) },
-	"ldap":         func() ConnectorConfig { return new(ldap.Config) },
-	"github":       func() ConnectorConfig { return new(github.Config) },
-	"gitlab":       func() ConnectorConfig { return new(gitlab.Config) },
-	"oidc":         func() ConnectorConfig { return new(oidc.Config) },
-	"saml":         func() ConnectorConfig { return new(saml.Config) },
-	"authproxy":    func() ConnectorConfig { return new(authproxy.Config) },
-	"linkedin":     func() ConnectorConfig { return new(linkedin.Config) },
-	"microsoft":    func() ConnectorConfig { return new(microsoft.Config) },
+	"mockCallback":    func() ConnectorConfig { return new(mock.CallbackConfig) },
+	"mockPassword":    func() ConnectorConfig { return new(mock.PasswordConfig) },
+	"ldap":            func() ConnectorConfig { return new(ldap.Config) },
+	"github":          func() ConnectorConfig { return new(github.Config) },
+	"gitlab":          func() ConnectorConfig { return new(gitlab.Config) },
+	"oidc":            func() ConnectorConfig { return new(oidc.Config) },
+	"saml":            func() ConnectorConfig { return new(saml.Config) },
+	"authproxy":       func() ConnectorConfig { return new(authproxy.Config) },
+	"linkedin":        func() ConnectorConfig { return new(linkedin.Config) },
+	"microsoft":       func() ConnectorConfig { return new(microsoft.Config) },
+	"bitbucket-cloud": func() ConnectorConfig { return new(bitbucketcloud.Config) },
 	// Keep around for backwards compatibility.
 	"samlExperimental": func() ConnectorConfig { return new(saml.Config) },
 }
diff --git a/vendor/golang.org/x/oauth2/bitbucket/bitbucket.go b/vendor/golang.org/x/oauth2/bitbucket/bitbucket.go
new file mode 100644
index 0000000000000000000000000000000000000000..44af1f1a9cddfa837cd5b160f383959bc6ead3ce
--- /dev/null
+++ b/vendor/golang.org/x/oauth2/bitbucket/bitbucket.go
@@ -0,0 +1,16 @@
+// Copyright 2015 The oauth2 Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package bitbucket provides constants for using OAuth2 to access Bitbucket.
+package bitbucket
+
+import (
+	"golang.org/x/oauth2"
+)
+
+// Endpoint is Bitbucket's OAuth 2.0 endpoint.
+var Endpoint = oauth2.Endpoint{
+	AuthURL:  "https://bitbucket.org/site/oauth2/authorize",
+	TokenURL: "https://bitbucket.org/site/oauth2/access_token",
+}