oauth2.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. // Copyright 2014 The oauth2 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 oauth2 provides support for making
  5. // OAuth2 authorized and authenticated HTTP requests.
  6. // It can additionally grant authorization with Bearer JWT.
  7. package oauth2 // import "golang.org/x/oauth2"
  8. import (
  9. "bytes"
  10. "encoding/json"
  11. "errors"
  12. "fmt"
  13. "io"
  14. "io/ioutil"
  15. "mime"
  16. "net/http"
  17. "net/url"
  18. "strconv"
  19. "strings"
  20. "sync"
  21. "time"
  22. "golang.org/x/net/context"
  23. )
  24. // NoContext is the default context you should supply if not using
  25. // your own context.Context (see https://golang.org/x/net/context).
  26. var NoContext = context.TODO()
  27. // Config describes a typical 3-legged OAuth2 flow, with both the
  28. // client application information and the server's endpoint URLs.
  29. type Config struct {
  30. // ClientID is the application's ID.
  31. ClientID string
  32. // ClientSecret is the application's secret.
  33. ClientSecret string
  34. // Endpoint contains the resource server's token endpoint
  35. // URLs. These are constants specific to each server and are
  36. // often available via site-specific packages, such as
  37. // google.Endpoint or github.Endpoint.
  38. Endpoint Endpoint
  39. // RedirectURL is the URL to redirect users going through
  40. // the OAuth flow, after the resource owner's URLs.
  41. RedirectURL string
  42. // Scope specifies optional requested permissions.
  43. Scopes []string
  44. }
  45. // A TokenSource is anything that can return a token.
  46. type TokenSource interface {
  47. // Token returns a token or an error.
  48. // Token must be safe for concurrent use by multiple goroutines.
  49. // The returned Token must not be modified.
  50. Token() (*Token, error)
  51. }
  52. // Endpoint contains the OAuth 2.0 provider's authorization and token
  53. // endpoint URLs.
  54. type Endpoint struct {
  55. AuthURL string
  56. TokenURL string
  57. }
  58. var (
  59. // AccessTypeOnline and AccessTypeOffline are options passed
  60. // to the Options.AuthCodeURL method. They modify the
  61. // "access_type" field that gets sent in the URL returned by
  62. // AuthCodeURL.
  63. //
  64. // Online is the default if neither is specified. If your
  65. // application needs to refresh access tokens when the user
  66. // is not present at the browser, then use offline. This will
  67. // result in your application obtaining a refresh token the
  68. // first time your application exchanges an authorization
  69. // code for a user.
  70. AccessTypeOnline AuthCodeOption = SetParam("access_type", "online")
  71. AccessTypeOffline AuthCodeOption = SetParam("access_type", "offline")
  72. // ApprovalForce forces the users to view the consent dialog
  73. // and confirm the permissions request at the URL returned
  74. // from AuthCodeURL, even if they've already done so.
  75. ApprovalForce AuthCodeOption = SetParam("approval_prompt", "force")
  76. )
  77. // An AuthCodeOption is passed to Config.AuthCodeURL.
  78. type AuthCodeOption interface {
  79. setValue(url.Values)
  80. }
  81. type setParam struct{ k, v string }
  82. func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) }
  83. // SetParam builds an AuthCodeOption which passes key/value parameters
  84. // to a provider's authorization endpoint.
  85. func SetParam(key, value string) AuthCodeOption {
  86. return setParam{key, value}
  87. }
  88. // AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
  89. // that asks for permissions for the required scopes explicitly.
  90. //
  91. // State is a token to protect the user from CSRF attacks. You must
  92. // always provide a non-zero string and validate that it matches the
  93. // the state query parameter on your redirect callback.
  94. // See http://tools.ietf.org/html/rfc6749#section-10.12 for more info.
  95. //
  96. // Opts may include AccessTypeOnline or AccessTypeOffline, as well
  97. // as ApprovalForce.
  98. func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
  99. var buf bytes.Buffer
  100. buf.WriteString(c.Endpoint.AuthURL)
  101. v := url.Values{
  102. "response_type": {"code"},
  103. "client_id": {c.ClientID},
  104. "redirect_uri": condVal(c.RedirectURL),
  105. "scope": condVal(strings.Join(c.Scopes, " ")),
  106. "state": condVal(state),
  107. }
  108. for _, opt := range opts {
  109. opt.setValue(v)
  110. }
  111. if strings.Contains(c.Endpoint.AuthURL, "?") {
  112. buf.WriteByte('&')
  113. } else {
  114. buf.WriteByte('?')
  115. }
  116. buf.WriteString(v.Encode())
  117. return buf.String()
  118. }
  119. // PasswordCredentialsToken converts a resource owner username and password
  120. // pair into a token.
  121. //
  122. // Per the RFC, this grant type should only be used "when there is a high
  123. // degree of trust between the resource owner and the client (e.g., the client
  124. // is part of the device operating system or a highly privileged application),
  125. // and when other authorization grant types are not available."
  126. // See https://tools.ietf.org/html/rfc6749#section-4.3 for more info.
  127. //
  128. // The HTTP client to use is derived from the context.
  129. // If nil, http.DefaultClient is used.
  130. func (c *Config) PasswordCredentialsToken(ctx context.Context, username, password string) (*Token, error) {
  131. return retrieveToken(ctx, c, url.Values{
  132. "grant_type": {"password"},
  133. "username": {username},
  134. "password": {password},
  135. "scope": condVal(strings.Join(c.Scopes, " ")),
  136. })
  137. }
  138. // Exchange converts an authorization code into a token.
  139. //
  140. // It is used after a resource provider redirects the user back
  141. // to the Redirect URI (the URL obtained from AuthCodeURL).
  142. //
  143. // The HTTP client to use is derived from the context.
  144. // If a client is not provided via the context, http.DefaultClient is used.
  145. //
  146. // The code will be in the *http.Request.FormValue("code"). Before
  147. // calling Exchange, be sure to validate FormValue("state").
  148. func (c *Config) Exchange(ctx context.Context, code string) (*Token, error) {
  149. return retrieveToken(ctx, c, url.Values{
  150. "grant_type": {"authorization_code"},
  151. "code": {code},
  152. "redirect_uri": condVal(c.RedirectURL),
  153. "scope": condVal(strings.Join(c.Scopes, " ")),
  154. })
  155. }
  156. // contextClientFunc is a func which tries to return an *http.Client
  157. // given a Context value. If it returns an error, the search stops
  158. // with that error. If it returns (nil, nil), the search continues
  159. // down the list of registered funcs.
  160. type contextClientFunc func(context.Context) (*http.Client, error)
  161. var contextClientFuncs []contextClientFunc
  162. func registerContextClientFunc(fn contextClientFunc) {
  163. contextClientFuncs = append(contextClientFuncs, fn)
  164. }
  165. func contextClient(ctx context.Context) (*http.Client, error) {
  166. for _, fn := range contextClientFuncs {
  167. c, err := fn(ctx)
  168. if err != nil {
  169. return nil, err
  170. }
  171. if c != nil {
  172. return c, nil
  173. }
  174. }
  175. if hc, ok := ctx.Value(HTTPClient).(*http.Client); ok {
  176. return hc, nil
  177. }
  178. return http.DefaultClient, nil
  179. }
  180. func contextTransport(ctx context.Context) http.RoundTripper {
  181. hc, err := contextClient(ctx)
  182. if err != nil {
  183. // This is a rare error case (somebody using nil on App Engine),
  184. // so I'd rather not everybody do an error check on this Client
  185. // method. They can get the error that they're doing it wrong
  186. // later, at client.Get/PostForm time.
  187. return errorTransport{err}
  188. }
  189. return hc.Transport
  190. }
  191. // Client returns an HTTP client using the provided token.
  192. // The token will auto-refresh as necessary. The underlying
  193. // HTTP transport will be obtained using the provided context.
  194. // The returned client and its Transport should not be modified.
  195. func (c *Config) Client(ctx context.Context, t *Token) *http.Client {
  196. return NewClient(ctx, c.TokenSource(ctx, t))
  197. }
  198. // TokenSource returns a TokenSource that returns t until t expires,
  199. // automatically refreshing it as necessary using the provided context.
  200. //
  201. // Most users will use Config.Client instead.
  202. func (c *Config) TokenSource(ctx context.Context, t *Token) TokenSource {
  203. tkr := &tokenRefresher{
  204. ctx: ctx,
  205. conf: c,
  206. }
  207. if t != nil {
  208. tkr.refreshToken = t.RefreshToken
  209. }
  210. return &reuseTokenSource{
  211. t: t,
  212. new: tkr,
  213. }
  214. }
  215. // tokenRefresher is a TokenSource that makes "grant_type"=="refresh_token"
  216. // HTTP requests to renew a token using a RefreshToken.
  217. type tokenRefresher struct {
  218. ctx context.Context // used to get HTTP requests
  219. conf *Config
  220. refreshToken string
  221. }
  222. // WARNING: Token is not safe for concurrent access, as it
  223. // updates the tokenRefresher's refreshToken field.
  224. // Within this package, it is used by reuseTokenSource which
  225. // synchronizes calls to this method with its own mutex.
  226. func (tf *tokenRefresher) Token() (*Token, error) {
  227. if tf.refreshToken == "" {
  228. return nil, errors.New("oauth2: token expired and refresh token is not set")
  229. }
  230. tk, err := retrieveToken(tf.ctx, tf.conf, url.Values{
  231. "grant_type": {"refresh_token"},
  232. "refresh_token": {tf.refreshToken},
  233. })
  234. if err != nil {
  235. return nil, err
  236. }
  237. if tf.refreshToken != tk.RefreshToken {
  238. tf.refreshToken = tk.RefreshToken
  239. }
  240. return tk, err
  241. }
  242. // reuseTokenSource is a TokenSource that holds a single token in memory
  243. // and validates its expiry before each call to retrieve it with
  244. // Token. If it's expired, it will be auto-refreshed using the
  245. // new TokenSource.
  246. type reuseTokenSource struct {
  247. new TokenSource // called when t is expired.
  248. mu sync.Mutex // guards t
  249. t *Token
  250. }
  251. // Token returns the current token if it's still valid, else will
  252. // refresh the current token (using r.Context for HTTP client
  253. // information) and return the new one.
  254. func (s *reuseTokenSource) Token() (*Token, error) {
  255. s.mu.Lock()
  256. defer s.mu.Unlock()
  257. if s.t.Valid() {
  258. return s.t, nil
  259. }
  260. t, err := s.new.Token()
  261. if err != nil {
  262. return nil, err
  263. }
  264. s.t = t
  265. return t, nil
  266. }
  267. func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
  268. hc, err := contextClient(ctx)
  269. if err != nil {
  270. return nil, err
  271. }
  272. v.Set("client_id", c.ClientID)
  273. bustedAuth := !providerAuthHeaderWorks(c.Endpoint.TokenURL)
  274. if bustedAuth && c.ClientSecret != "" {
  275. v.Set("client_secret", c.ClientSecret)
  276. }
  277. req, err := http.NewRequest("POST", c.Endpoint.TokenURL, strings.NewReader(v.Encode()))
  278. if err != nil {
  279. return nil, err
  280. }
  281. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  282. if !bustedAuth {
  283. req.SetBasicAuth(c.ClientID, c.ClientSecret)
  284. }
  285. r, err := hc.Do(req)
  286. if err != nil {
  287. return nil, err
  288. }
  289. defer r.Body.Close()
  290. body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
  291. if err != nil {
  292. return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
  293. }
  294. if code := r.StatusCode; code < 200 || code > 299 {
  295. return nil, fmt.Errorf("oauth2: cannot fetch token: %v\nResponse: %s", r.Status, body)
  296. }
  297. var token *Token
  298. content, _, _ := mime.ParseMediaType(r.Header.Get("Content-Type"))
  299. switch content {
  300. case "application/x-www-form-urlencoded", "text/plain":
  301. vals, err := url.ParseQuery(string(body))
  302. if err != nil {
  303. return nil, err
  304. }
  305. token = &Token{
  306. AccessToken: vals.Get("access_token"),
  307. TokenType: vals.Get("token_type"),
  308. RefreshToken: vals.Get("refresh_token"),
  309. raw: vals,
  310. }
  311. e := vals.Get("expires_in")
  312. if e == "" {
  313. // TODO(jbd): Facebook's OAuth2 implementation is broken and
  314. // returns expires_in field in expires. Remove the fallback to expires,
  315. // when Facebook fixes their implementation.
  316. e = vals.Get("expires")
  317. }
  318. expires, _ := strconv.Atoi(e)
  319. if expires != 0 {
  320. token.Expiry = time.Now().Add(time.Duration(expires) * time.Second)
  321. }
  322. default:
  323. var tj tokenJSON
  324. if err = json.Unmarshal(body, &tj); err != nil {
  325. return nil, err
  326. }
  327. token = &Token{
  328. AccessToken: tj.AccessToken,
  329. TokenType: tj.TokenType,
  330. RefreshToken: tj.RefreshToken,
  331. Expiry: tj.expiry(),
  332. raw: make(map[string]interface{}),
  333. }
  334. json.Unmarshal(body, &token.raw) // no error checks for optional fields
  335. }
  336. // Don't overwrite `RefreshToken` with an empty value
  337. // if this was a token refreshing request.
  338. if token.RefreshToken == "" {
  339. token.RefreshToken = v.Get("refresh_token")
  340. }
  341. return token, nil
  342. }
  343. // tokenJSON is the struct representing the HTTP response from OAuth2
  344. // providers returning a token in JSON form.
  345. type tokenJSON struct {
  346. AccessToken string `json:"access_token"`
  347. TokenType string `json:"token_type"`
  348. RefreshToken string `json:"refresh_token"`
  349. ExpiresIn expirationTime `json:"expires_in"` // at least PayPal returns string, while most return number
  350. Expires expirationTime `json:"expires"` // broken Facebook spelling of expires_in
  351. }
  352. func (e *tokenJSON) expiry() (t time.Time) {
  353. if v := e.ExpiresIn; v != 0 {
  354. return time.Now().Add(time.Duration(v) * time.Second)
  355. }
  356. if v := e.Expires; v != 0 {
  357. return time.Now().Add(time.Duration(v) * time.Second)
  358. }
  359. return
  360. }
  361. type expirationTime int32
  362. func (e *expirationTime) UnmarshalJSON(b []byte) error {
  363. var n json.Number
  364. err := json.Unmarshal(b, &n)
  365. if err != nil {
  366. return err
  367. }
  368. i, err := n.Int64()
  369. if err != nil {
  370. return err
  371. }
  372. *e = expirationTime(i)
  373. return nil
  374. }
  375. func condVal(v string) []string {
  376. if v == "" {
  377. return nil
  378. }
  379. return []string{v}
  380. }
  381. var brokenAuthHeaderProviders = []string{
  382. "https://accounts.google.com/",
  383. "https://www.googleapis.com/",
  384. "https://github.com/",
  385. "https://api.instagram.com/",
  386. "https://www.douban.com/",
  387. "https://api.dropbox.com/",
  388. "https://api.soundcloud.com/",
  389. "https://www.linkedin.com/",
  390. "https://api.twitch.tv/",
  391. "https://oauth.vk.com/",
  392. "https://api.odnoklassniki.ru/",
  393. "https://connect.stripe.com/",
  394. "https://api.pushbullet.com/",
  395. "https://oauth.sandbox.trainingpeaks.com/",
  396. "https://oauth.trainingpeaks.com/",
  397. "https://www.strava.com/oauth/",
  398. }
  399. // providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
  400. // implements the OAuth2 spec correctly
  401. // See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
  402. // In summary:
  403. // - Reddit only accepts client secret in the Authorization header
  404. // - Dropbox accepts either it in URL param or Auth header, but not both.
  405. // - Google only accepts URL param (not spec compliant?), not Auth header
  406. // - Stripe only accepts client secret in Auth header with Bearer method, not Basic
  407. func providerAuthHeaderWorks(tokenURL string) bool {
  408. for _, s := range brokenAuthHeaderProviders {
  409. if strings.HasPrefix(tokenURL, s) {
  410. // Some sites fail to implement the OAuth2 spec fully.
  411. return false
  412. }
  413. }
  414. // Assume the provider implements the spec properly
  415. // otherwise. We can add more exceptions as they're
  416. // discovered. We will _not_ be adding configurable hooks
  417. // to this package to let users select server bugs.
  418. return true
  419. }
  420. // HTTPClient is the context key to use with golang.org/x/net/context's
  421. // WithValue function to associate an *http.Client value with a context.
  422. var HTTPClient contextKey
  423. // contextKey is just an empty struct. It exists so HTTPClient can be
  424. // an immutable public variable with a unique type. It's immutable
  425. // because nobody else can create a contextKey, being unexported.
  426. type contextKey struct{}
  427. // NewClient creates an *http.Client from a Context and TokenSource.
  428. // The returned client is not valid beyond the lifetime of the context.
  429. //
  430. // As a special case, if src is nil, a non-OAuth2 client is returned
  431. // using the provided context. This exists to support related OAuth2
  432. // packages.
  433. func NewClient(ctx context.Context, src TokenSource) *http.Client {
  434. if src == nil {
  435. c, err := contextClient(ctx)
  436. if err != nil {
  437. return &http.Client{Transport: errorTransport{err}}
  438. }
  439. return c
  440. }
  441. return &http.Client{
  442. Transport: &Transport{
  443. Base: contextTransport(ctx),
  444. Source: ReuseTokenSource(nil, src),
  445. },
  446. }
  447. }
  448. // ReuseTokenSource returns a TokenSource which repeatedly returns the
  449. // same token as long as it's valid, starting with t.
  450. // When its cached token is invalid, a new token is obtained from src.
  451. //
  452. // ReuseTokenSource is typically used to reuse tokens from a cache
  453. // (such as a file on disk) between runs of a program, rather than
  454. // obtaining new tokens unnecessarily.
  455. //
  456. // The initial token t may be nil, in which case the TokenSource is
  457. // wrapped in a caching version if it isn't one already. This also
  458. // means it's always safe to wrap ReuseTokenSource around any other
  459. // TokenSource without adverse effects.
  460. func ReuseTokenSource(t *Token, src TokenSource) TokenSource {
  461. // Don't wrap a reuseTokenSource in itself. That would work,
  462. // but cause an unnecessary number of mutex operations.
  463. // Just build the equivalent one.
  464. if rt, ok := src.(*reuseTokenSource); ok {
  465. if t == nil {
  466. // Just use it directly.
  467. return rt
  468. }
  469. src = rt.new
  470. }
  471. return &reuseTokenSource{
  472. t: t,
  473. new: src,
  474. }
  475. }