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.

274 lines
7.9 KiB

Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
7 years ago
  1. // Copyright 2014 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package internal
  5. import (
  6. "context"
  7. "encoding/json"
  8. "errors"
  9. "fmt"
  10. "io"
  11. "io/ioutil"
  12. "mime"
  13. "net/http"
  14. "net/url"
  15. "strconv"
  16. "strings"
  17. "time"
  18. "golang.org/x/net/context/ctxhttp"
  19. )
  20. // Token represents the credentials used to authorize
  21. // the requests to access protected resources on the OAuth 2.0
  22. // provider's backend.
  23. //
  24. // This type is a mirror of oauth2.Token and exists to break
  25. // an otherwise-circular dependency. Other internal packages
  26. // should convert this Token into an oauth2.Token before use.
  27. type Token struct {
  28. // AccessToken is the token that authorizes and authenticates
  29. // the requests.
  30. AccessToken string
  31. // TokenType is the type of token.
  32. // The Type method returns either this or "Bearer", the default.
  33. TokenType string
  34. // RefreshToken is a token that's used by the application
  35. // (as opposed to the user) to refresh the access token
  36. // if it expires.
  37. RefreshToken string
  38. // Expiry is the optional expiration time of the access token.
  39. //
  40. // If zero, TokenSource implementations will reuse the same
  41. // token forever and RefreshToken or equivalent
  42. // mechanisms for that TokenSource will not be used.
  43. Expiry time.Time
  44. // Raw optionally contains extra metadata from the server
  45. // when updating a token.
  46. Raw interface{}
  47. }
  48. // tokenJSON is the struct representing the HTTP response from OAuth2
  49. // providers returning a token in JSON form.
  50. type tokenJSON struct {
  51. AccessToken string `json:"access_token"`
  52. TokenType string `json:"token_type"`
  53. RefreshToken string `json:"refresh_token"`
  54. ExpiresIn expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
  55. Expires expirationTime `json:"expires"` // broken Facebook spelling of expires_in
  56. }
  57. func (e *tokenJSON) expiry() (t time.Time) {
  58. if v := e.ExpiresIn; v != 0 {
  59. return time.Now().Add(time.Duration(v) * time.Second)
  60. }
  61. if v := e.Expires; v != 0 {
  62. return time.Now().Add(time.Duration(v) * time.Second)
  63. }
  64. return
  65. }
  66. type expirationTime int32
  67. func (e *expirationTime) UnmarshalJSON(b []byte) error {
  68. var n json.Number
  69. err := json.Unmarshal(b, &n)
  70. if err != nil {
  71. return err
  72. }
  73. i, err := n.Int64()
  74. if err != nil {
  75. return err
  76. }
  77. *e = expirationTime(i)
  78. return nil
  79. }
  80. var brokenAuthHeaderProviders = []string{
  81. "https://accounts.google.com/",
  82. "https://api.codeswholesale.com/oauth/token",
  83. "https://api.dropbox.com/",
  84. "https://api.dropboxapi.com/",
  85. "https://api.instagram.com/",
  86. "https://api.netatmo.net/",
  87. "https://api.odnoklassniki.ru/",
  88. "https://api.pushbullet.com/",
  89. "https://api.soundcloud.com/",
  90. "https://api.twitch.tv/",
  91. "https://id.twitch.tv/",
  92. "https://app.box.com/",
  93. "https://api.box.com/",
  94. "https://connect.stripe.com/",
  95. "https://login.mailchimp.com/",
  96. "https://login.microsoftonline.com/",
  97. "https://login.salesforce.com/",
  98. "https://login.windows.net",
  99. "https://login.live.com/",
  100. "https://login.live-int.com/",
  101. "https://oauth.sandbox.trainingpeaks.com/",
  102. "https://oauth.trainingpeaks.com/",
  103. "https://oauth.vk.com/",
  104. "https://openapi.baidu.com/",
  105. "https://slack.com/",
  106. "https://test-sandbox.auth.corp.google.com",
  107. "https://test.salesforce.com/",
  108. "https://user.gini.net/",
  109. "https://www.douban.com/",
  110. "https://www.googleapis.com/",
  111. "https://www.linkedin.com/",
  112. "https://www.strava.com/oauth/",
  113. "https://www.wunderlist.com/oauth/",
  114. "https://api.patreon.com/",
  115. "https://sandbox.codeswholesale.com/oauth/token",
  116. "https://api.sipgate.com/v1/authorization/oauth",
  117. "https://api.medium.com/v1/tokens",
  118. "https://log.finalsurge.com/oauth/token",
  119. "https://multisport.todaysplan.com.au/rest/oauth/access_token",
  120. "https://whats.todaysplan.com.au/rest/oauth/access_token",
  121. "https://stackoverflow.com/oauth/access_token",
  122. "https://account.health.nokia.com",
  123. "https://accounts.zoho.com",
  124. }
  125. // brokenAuthHeaderDomains lists broken providers that issue dynamic endpoints.
  126. var brokenAuthHeaderDomains = []string{
  127. ".auth0.com",
  128. ".force.com",
  129. ".myshopify.com",
  130. ".okta.com",
  131. ".oktapreview.com",
  132. }
  133. func RegisterBrokenAuthHeaderProvider(tokenURL string) {
  134. brokenAuthHeaderProviders = append(brokenAuthHeaderProviders, tokenURL)
  135. }
  136. // providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
  137. // implements the OAuth2 spec correctly
  138. // See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
  139. // In summary:
  140. // - Reddit only accepts client secret in the Authorization header
  141. // - Dropbox accepts either it in URL param or Auth header, but not both.
  142. // - Google only accepts URL param (not spec compliant?), not Auth header
  143. // - Stripe only accepts client secret in Auth header with Bearer method, not Basic
  144. func providerAuthHeaderWorks(tokenURL string) bool {
  145. for _, s := range brokenAuthHeaderProviders {
  146. if strings.HasPrefix(tokenURL, s) {
  147. // Some sites fail to implement the OAuth2 spec fully.
  148. return false
  149. }
  150. }
  151. if u, err := url.Parse(tokenURL); err == nil {
  152. for _, s := range brokenAuthHeaderDomains {
  153. if strings.HasSuffix(u.Host, s) {
  154. return false
  155. }
  156. }
  157. }
  158. // Assume the provider implements the spec properly
  159. // otherwise. We can add more exceptions as they're
  160. // discovered. We will _not_ be adding configurable hooks
  161. // to this package to let users select server bugs.
  162. return true
  163. }
  164. func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values) (*Token, error) {
  165. bustedAuth := !providerAuthHeaderWorks(tokenURL)
  166. if bustedAuth {
  167. if clientID != "" {
  168. v.Set("client_id", clientID)
  169. }
  170. if clientSecret != "" {
  171. v.Set("client_secret", clientSecret)
  172. }
  173. }
  174. req, err := http.NewRequest("POST", tokenURL, strings.NewReader(v.Encode()))
  175. if err != nil {
  176. return nil, err
  177. }
  178. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  179. if !bustedAuth {
  180. req.SetBasicAuth(url.QueryEscape(clientID), url.QueryEscape(clientSecret))
  181. }
  182. r, err := ctxhttp.Do(ctx, ContextClient(ctx), req)
  183. if err != nil {
  184. return nil, err
  185. }
  186. defer r.Body.Close()
  187. body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
  188. if err != nil {
  189. return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
  190. }
  191. if code := r.StatusCode; code < 200 || code > 299 {
  192. return nil, &RetrieveError{
  193. Response: r,
  194. Body: body,
  195. }
  196. }
  197. var token *Token
  198. content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
  199. switch content {
  200. case "application/x-www-form-urlencoded", "text/plain":
  201. vals, err := url.ParseQuery(string(body))
  202. if err != nil {
  203. return nil, err
  204. }
  205. token = &Token{
  206. AccessToken: vals.Get("access_token"),
  207. TokenType: vals.Get("token_type"),
  208. RefreshToken: vals.Get("refresh_token"),
  209. Raw: vals,
  210. }
  211. e := vals.Get("expires_in")
  212. if e == "" {
  213. // TODO(jbd): Facebook's OAuth2 implementation is broken and
  214. // returns expires_in field in expires. Remove the fallback to expires,
  215. // when Facebook fixes their implementation.
  216. e = vals.Get("expires")
  217. }
  218. expires, _ := strconv.Atoi(e)
  219. if expires != 0 {
  220. token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
  221. }
  222. default:
  223. var tj tokenJSON
  224. if err = json.Unmarshal(body, &tj); err != nil {
  225. return nil, err
  226. }
  227. token = &Token{
  228. AccessToken: tj.AccessToken,
  229. TokenType: tj.TokenType,
  230. RefreshToken: tj.RefreshToken,
  231. Expiry: tj.expiry(),
  232. Raw: make(map[string]interface{}),
  233. }
  234. json.Unmarshal(body, &token.Raw) // no error checks for optional fields
  235. }
  236. // Don't overwrite `RefreshToken` with an empty value
  237. // if this was a token refreshing request.
  238. if token.RefreshToken == "" {
  239. token.RefreshToken = v.Get("refresh_token")
  240. }
  241. if token.AccessToken == "" {
  242. return token, errors.New("oauth2: server response missing access_token")
  243. }
  244. return token, nil
  245. }
  246. type RetrieveError struct {
  247. Response *http.Response
  248. Body []byte
  249. }
  250. func (r *RetrieveError) Error() string {
  251. return fmt.Sprintf("oauth2: cannot fetch token: %v\nResponse: %s", r.Response.Status, r.Body)
  252. }