本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

214 lines
5.6KB

  1. // Package dropbox implements the OAuth2 protocol for authenticating users through Dropbox.
  2. package dropbox
  3. import (
  4. "bytes"
  5. "encoding/json"
  6. "errors"
  7. "io"
  8. "io/ioutil"
  9. "net/http"
  10. "strings"
  11. "fmt"
  12. "github.com/markbates/goth"
  13. "golang.org/x/oauth2"
  14. )
  15. const (
  16. authURL = "https://www.dropbox.com/oauth2/authorize"
  17. tokenURL = "https://api.dropbox.com/oauth2/token"
  18. accountURL = "https://api.dropbox.com/2/users/get_current_account"
  19. )
  20. // Provider is the implementation of `goth.Provider` for accessing Dropbox.
  21. type Provider struct {
  22. ClientKey string
  23. Secret string
  24. CallbackURL string
  25. AccountURL string
  26. HTTPClient *http.Client
  27. config *oauth2.Config
  28. providerName string
  29. }
  30. // Session stores data during the auth process with Dropbox.
  31. type Session struct {
  32. AuthURL string
  33. Token string
  34. }
  35. // New creates a new Dropbox provider and sets up important connection details.
  36. // You should always call `dropbox.New` to get a new provider. Never try to
  37. // create one manually.
  38. func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
  39. p := &Provider{
  40. ClientKey: clientKey,
  41. Secret: secret,
  42. CallbackURL: callbackURL,
  43. AccountURL: accountURL,
  44. providerName: "dropbox",
  45. }
  46. p.config = newConfig(p, scopes)
  47. return p
  48. }
  49. // Name is the name used to retrieve this provider later.
  50. func (p *Provider) Name() string {
  51. return p.providerName
  52. }
  53. // SetName is to update the name of the provider (needed in case of multiple providers of 1 type)
  54. func (p *Provider) SetName(name string) {
  55. p.providerName = name
  56. }
  57. func (p *Provider) Client() *http.Client {
  58. return goth.HTTPClientWithFallBack(p.HTTPClient)
  59. }
  60. // Debug is a no-op for the dropbox package.
  61. func (p *Provider) Debug(debug bool) {}
  62. // BeginAuth asks Dropbox for an authentication end-point.
  63. func (p *Provider) BeginAuth(state string) (goth.Session, error) {
  64. return &Session{
  65. AuthURL: p.config.AuthCodeURL(state),
  66. }, nil
  67. }
  68. // FetchUser will go to Dropbox and access basic information about the user.
  69. func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
  70. s := session.(*Session)
  71. user := goth.User{
  72. AccessToken: s.Token,
  73. Provider: p.Name(),
  74. }
  75. if user.AccessToken == "" {
  76. // data is not yet retrieved since accessToken is still empty
  77. return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
  78. }
  79. req, err := http.NewRequest("POST", p.AccountURL, nil)
  80. if err != nil {
  81. return user, err
  82. }
  83. req.Header.Set("Authorization", "Bearer "+s.Token)
  84. resp, err := p.Client().Do(req)
  85. if err != nil {
  86. return user, err
  87. }
  88. defer resp.Body.Close()
  89. if resp.StatusCode != http.StatusOK {
  90. return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, resp.StatusCode)
  91. }
  92. bits, err := ioutil.ReadAll(resp.Body)
  93. if err != nil {
  94. return user, err
  95. }
  96. err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
  97. if err != nil {
  98. return user, err
  99. }
  100. err = userFromReader(bytes.NewReader(bits), &user)
  101. return user, err
  102. }
  103. // UnmarshalSession wil unmarshal a JSON string into a session.
  104. func (p *Provider) UnmarshalSession(data string) (goth.Session, error) {
  105. s := &Session{}
  106. err := json.NewDecoder(strings.NewReader(data)).Decode(s)
  107. return s, err
  108. }
  109. // GetAuthURL gets the URL set by calling the `BeginAuth` function on the Dropbox provider.
  110. func (s *Session) GetAuthURL() (string, error) {
  111. if s.AuthURL == "" {
  112. return "", errors.New("dropbox: missing AuthURL")
  113. }
  114. return s.AuthURL, nil
  115. }
  116. // Authorize the session with Dropbox and return the access token to be stored for future use.
  117. func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) {
  118. p := provider.(*Provider)
  119. token, err := p.config.Exchange(goth.ContextForClient(p.Client()), params.Get("code"))
  120. if err != nil {
  121. return "", err
  122. }
  123. if !token.Valid() {
  124. return "", errors.New("Invalid token received from provider")
  125. }
  126. s.Token = token.AccessToken
  127. return token.AccessToken, nil
  128. }
  129. // Marshal the session into a string
  130. func (s *Session) Marshal() string {
  131. b, _ := json.Marshal(s)
  132. return string(b)
  133. }
  134. func (s Session) String() string {
  135. return s.Marshal()
  136. }
  137. func newConfig(p *Provider, scopes []string) *oauth2.Config {
  138. c := &oauth2.Config{
  139. ClientID: p.ClientKey,
  140. ClientSecret: p.Secret,
  141. RedirectURL: p.CallbackURL,
  142. Endpoint: oauth2.Endpoint{
  143. AuthURL: authURL,
  144. TokenURL: tokenURL,
  145. },
  146. }
  147. return c
  148. }
  149. func userFromReader(r io.Reader, user *goth.User) error {
  150. u := struct {
  151. AccountID string `json:"account_id"`
  152. Name struct {
  153. GivenName string `json:"given_name"`
  154. Surname string `json:"surname"`
  155. DisplayName string `json:"display_name"`
  156. } `json:"name"`
  157. Country string `json:"country"`
  158. Email string `json:"email"`
  159. ProfilePhotoURL string `json:"profile_photo_url"`
  160. }{}
  161. err := json.NewDecoder(r).Decode(&u)
  162. if err != nil {
  163. return err
  164. }
  165. user.UserID = u.AccountID // The user's unique Dropbox ID.
  166. user.FirstName = u.Name.GivenName
  167. user.LastName = u.Name.Surname
  168. user.Name = strings.TrimSpace(fmt.Sprintf("%s %s", u.Name.GivenName, u.Name.Surname))
  169. user.Description = u.Name.DisplayName // Full name plus parenthetical team naem
  170. user.Email = u.Email
  171. user.NickName = u.Email // Email is the dropbox username
  172. user.Location = u.Country
  173. user.AvatarURL = u.ProfilePhotoURL // May be blank
  174. return nil
  175. }
  176. //RefreshToken refresh token is not provided by dropbox
  177. func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
  178. return nil, errors.New("Refresh token is not provided by dropbox")
  179. }
  180. //RefreshTokenAvailable refresh token is not provided by dropbox
  181. func (p *Provider) RefreshTokenAvailable() bool {
  182. return false
  183. }
上海开阖软件有限公司 沪ICP备12045867号-1