Switch to using the dep tool and update all the dependencies

This commit is contained in:
Nick Craig-Wood
2017-05-11 15:39:54 +01:00
parent 5135ff73cb
commit 98c2d2c41b
5321 changed files with 4483197 additions and 5918 deletions
+9
View File
@@ -63,3 +63,12 @@ with the `oauth2` package.
client.Get("...")
}
## Contributing
We appreciate your help!
To contribute, please read the contribution guidelines:
https://golang.org/doc/contribute.html
Note that the Go project does not use GitHub pull requests but
uses Gerrit for code reviews. See the contribution guide for details.
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2017 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 amazon provides constants for using OAuth2 to access Amazon.
package amazon
import (
"golang.org/x/oauth2"
)
// Endpoint is Amazon's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://www.amazon.com/ap/oa",
TokenURL: "https://api.amazon.com/auth/o2/token",
}
+16
View File
@@ -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",
}
+104
View File
@@ -0,0 +1,104 @@
// Copyright 2014 The Go 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 clientcredentials implements the OAuth2.0 "client credentials" token flow,
// also known as the "two-legged OAuth 2.0".
//
// This should be used when the client is acting on its own behalf or when the client
// is the resource owner. It may also be used when requesting access to protected
// resources based on an authorization previously arranged with the authorization
// server.
//
// See https://tools.ietf.org/html/rfc6749#section-4.4
package clientcredentials // import "golang.org/x/oauth2/clientcredentials"
import (
"fmt"
"net/http"
"net/url"
"strings"
"golang.org/x/net/context"
"golang.org/x/oauth2"
"golang.org/x/oauth2/internal"
)
// Config describes a 2-legged OAuth2 flow, with both the
// client application information and the server's endpoint URLs.
type Config struct {
// ClientID is the application's ID.
ClientID string
// ClientSecret is the application's secret.
ClientSecret string
// TokenURL is the resource server's token endpoint
// URL. This is a constant specific to each server.
TokenURL string
// Scope specifies optional requested permissions.
Scopes []string
// EndpointParams specifies additional parameters for requests to the token endpoint.
EndpointParams url.Values
}
// Token uses client credentials to retrieve a token.
// The HTTP client to use is derived from the context.
// If nil, http.DefaultClient is used.
func (c *Config) Token(ctx context.Context) (*oauth2.Token, error) {
return c.TokenSource(ctx).Token()
}
// Client returns an HTTP client using the provided token.
// The token will auto-refresh as necessary. The underlying
// HTTP transport will be obtained using the provided context.
// The returned client and its Transport should not be modified.
func (c *Config) Client(ctx context.Context) *http.Client {
return oauth2.NewClient(ctx, c.TokenSource(ctx))
}
// TokenSource returns a TokenSource that returns t until t expires,
// automatically refreshing it as necessary using the provided context and the
// client ID and client secret.
//
// Most users will use Config.Client instead.
func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource {
source := &tokenSource{
ctx: ctx,
conf: c,
}
return oauth2.ReuseTokenSource(nil, source)
}
type tokenSource struct {
ctx context.Context
conf *Config
}
// Token refreshes the token by using a new client credentials request.
// tokens received this way do not include a refresh token
func (c *tokenSource) Token() (*oauth2.Token, error) {
v := url.Values{
"grant_type": {"client_credentials"},
"scope": internal.CondVal(strings.Join(c.conf.Scopes, " ")),
}
for k, p := range c.conf.EndpointParams {
if _, ok := v[k]; ok {
return nil, fmt.Errorf("oauth2: cannot overwrite parameter %q", k)
}
v[k] = p
}
tk, err := internal.RetrieveToken(c.ctx, c.conf.ClientID, c.conf.ClientSecret, c.conf.TokenURL, v)
if err != nil {
return nil, err
}
t := &oauth2.Token{
AccessToken: tk.AccessToken,
TokenType: tk.TokenType,
RefreshToken: tk.RefreshToken,
Expiry: tk.Expiry,
}
return t.WithExtra(tk.Raw), nil
}
+97
View File
@@ -0,0 +1,97 @@
// Copyright 2014 The Go 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 clientcredentials
import (
"context"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"testing"
)
func newConf(serverURL string) *Config {
return &Config{
ClientID: "CLIENT_ID",
ClientSecret: "CLIENT_SECRET",
Scopes: []string{"scope1", "scope2"},
TokenURL: serverURL + "/token",
EndpointParams: url.Values{"audience": {"audience1"}},
}
}
type mockTransport struct {
rt func(req *http.Request) (resp *http.Response, err error)
}
func (t *mockTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
return t.rt(req)
}
func TestTokenRequest(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.String() != "/token" {
t.Errorf("authenticate client request URL = %q; want %q", r.URL, "/token")
}
headerAuth := r.Header.Get("Authorization")
if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" {
t.Errorf("Unexpected authorization header, %v is found.", headerAuth)
}
if got, want := r.Header.Get("Content-Type"), "application/x-www-form-urlencoded"; got != want {
t.Errorf("Content-Type header = %q; want %q", got, want)
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
r.Body.Close()
}
if err != nil {
t.Errorf("failed reading request body: %s.", err)
}
if string(body) != "audience=audience1&grant_type=client_credentials&scope=scope1+scope2" {
t.Errorf("payload = %q; want %q", string(body), "grant_type=client_credentials&scope=scope1+scope2")
}
w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&token_type=bearer"))
}))
defer ts.Close()
conf := newConf(ts.URL)
tok, err := conf.Token(context.Background())
if err != nil {
t.Error(err)
}
if !tok.Valid() {
t.Fatalf("token invalid. got: %#v", tok)
}
if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
t.Errorf("Access token = %q; want %q", tok.AccessToken, "90d64460d14870c08c81352a05dedd3465940a7c")
}
if tok.TokenType != "bearer" {
t.Errorf("token type = %q; want %q", tok.TokenType, "bearer")
}
}
func TestTokenRefreshRequest(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.String() == "/somethingelse" {
return
}
if r.URL.String() != "/token" {
t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL)
}
headerContentType := r.Header.Get("Content-Type")
if headerContentType != "application/x-www-form-urlencoded" {
t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
}
body, _ := ioutil.ReadAll(r.Body)
if string(body) != "audience=audience1&grant_type=client_credentials&scope=scope1+scope2" {
t.Errorf("Unexpected refresh token payload, %v is found.", string(body))
}
}))
defer ts.Close()
conf := newConf(ts.URL)
c := conf.Client(context.Background())
c.Get(ts.URL + "/somethingelse")
}
+71
View File
@@ -0,0 +1,71 @@
// Copyright 2014 The Go 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 oauth2_test
import (
"context"
"fmt"
"log"
"net/http"
"time"
"golang.org/x/oauth2"
)
func ExampleConfig() {
ctx := context.Background()
conf := &oauth2.Config{
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
Scopes: []string{"SCOPE1", "SCOPE2"},
Endpoint: oauth2.Endpoint{
AuthURL: "https://provider.com/o/oauth2/auth",
TokenURL: "https://provider.com/o/oauth2/token",
},
}
// Redirect user to consent page to ask for permission
// for the scopes specified above.
url := conf.AuthCodeURL("state", oauth2.AccessTypeOffline)
fmt.Printf("Visit the URL for the auth dialog: %v", url)
// Use the authorization code that is pushed to the redirect
// URL. Exchange will do the handshake to retrieve the
// initial access token. The HTTP Client returned by
// conf.Client will refresh the token as necessary.
var code string
if _, err := fmt.Scan(&code); err != nil {
log.Fatal(err)
}
tok, err := conf.Exchange(ctx, code)
if err != nil {
log.Fatal(err)
}
client := conf.Client(ctx, tok)
client.Get("...")
}
func ExampleHTTPClient() {
hc := &http.Client{Timeout: 2 * time.Second}
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, hc)
conf := &oauth2.Config{
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
Scopes: []string{"SCOPE1", "SCOPE2"},
Endpoint: oauth2.Endpoint{
AuthURL: "https://provider.com/o/oauth2/auth",
TokenURL: "https://provider.com/o/oauth2/token",
},
}
// Exchange request will be made by the custom
// HTTP client, hc.
_, err := conf.Exchange(ctx, "foo")
if err != nil {
log.Fatal(err)
}
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2015 The Go 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 facebook provides constants for using OAuth2 to access Facebook.
package facebook // import "golang.org/x/oauth2/facebook"
import (
"golang.org/x/oauth2"
)
// Endpoint is Facebook's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://www.facebook.com/dialog/oauth",
TokenURL: "https://graph.facebook.com/oauth/access_token",
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2016 The Go 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 fitbit provides constants for using OAuth2 to access the Fitbit API.
package fitbit // import "golang.org/x/oauth2/fitbit"
import (
"golang.org/x/oauth2"
)
// Endpoint is the Fitbit API's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://www.fitbit.com/oauth2/authorize",
TokenURL: "https://api.fitbit.com/oauth2/token",
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2016 The Go 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 foursquare provides constants for using OAuth2 to access Foursquare.
package foursquare // import "golang.org/x/oauth2/foursquare"
import (
"golang.org/x/oauth2"
)
// Endpoint is Foursquare's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://foursquare.com/oauth2/authorize",
TokenURL: "https://foursquare.com/oauth2/access_token",
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2014 The Go 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 github provides constants for using OAuth2 to access Github.
package github // import "golang.org/x/oauth2/github"
import (
"golang.org/x/oauth2"
)
// Endpoint is Github's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://github.com/login/oauth/authorize",
TokenURL: "https://github.com/login/oauth/access_token",
}
+2 -2
View File
@@ -14,8 +14,8 @@ import (
"golang.org/x/oauth2"
)
// Set at init time by appenginevm_hook.go. If true, we are on App Engine Managed VMs.
var appengineVM bool
// appengineFlex is set at init time by appengineflex_hook.go. If true, we are on App Engine Flex.
var appengineFlex bool
// Set at init time by appengine_hook.go. If nil, we're not on App Engine.
var appengineTokenFunc func(c context.Context, scopes ...string) (token string, expiry time.Time, err error)
+1 -1
View File
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build appengine
// +build appengine appenginevm
package google
+11
View File
@@ -0,0 +1,11 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build appenginevm
package google
func init() {
appengineFlex = true // Flex doesn't support appengine.AccessToken; depend on metadata server.
}
-15
View File
@@ -1,15 +0,0 @@
// 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.
// +build appenginevm
package google
import "google.golang.org/appengine"
func init() {
appengineVM = true
appengineTokenFunc = appengine.AccessToken
appengineAppIDFunc = appengine.AppID
}
+1 -1
View File
@@ -81,7 +81,7 @@ func FindDefaultCredentials(ctx context.Context, scope ...string) (*DefaultCrede
}
// Third, if we're on Google App Engine use those credentials.
if appengineTokenFunc != nil && !appengineVM {
if appengineTokenFunc != nil && !appengineFlex {
return &DefaultCredentials{
ProjectID: appengineAppIDFunc(ctx),
TokenSource: AppEngineTokenSource(ctx, scope...),
+150
View File
@@ -0,0 +1,150 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build appenginevm appengine
package google_test
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"golang.org/x/oauth2/jwt"
"google.golang.org/appengine"
"google.golang.org/appengine/urlfetch"
)
func ExampleDefaultClient() {
client, err := google.DefaultClient(oauth2.NoContext,
"https://www.googleapis.com/auth/devstorage.full_control")
if err != nil {
log.Fatal(err)
}
client.Get("...")
}
func Example_webServer() {
// Your credentials should be obtained from the Google
// Developer Console (https://console.developers.google.com).
conf := &oauth2.Config{
ClientID: "YOUR_CLIENT_ID",
ClientSecret: "YOUR_CLIENT_SECRET",
RedirectURL: "YOUR_REDIRECT_URL",
Scopes: []string{
"https://www.googleapis.com/auth/bigquery",
"https://www.googleapis.com/auth/blogger",
},
Endpoint: google.Endpoint,
}
// Redirect user to Google's consent page to ask for permission
// for the scopes specified above.
url := conf.AuthCodeURL("state")
fmt.Printf("Visit the URL for the auth dialog: %v", url)
// Handle the exchange code to initiate a transport.
tok, err := conf.Exchange(oauth2.NoContext, "authorization-code")
if err != nil {
log.Fatal(err)
}
client := conf.Client(oauth2.NoContext, tok)
client.Get("...")
}
func ExampleJWTConfigFromJSON() {
// Your credentials should be obtained from the Google
// Developer Console (https://console.developers.google.com).
// Navigate to your project, then see the "Credentials" page
// under "APIs & Auth".
// To create a service account client, click "Create new Client ID",
// select "Service Account", and click "Create Client ID". A JSON
// key file will then be downloaded to your computer.
data, err := ioutil.ReadFile("/path/to/your-project-key.json")
if err != nil {
log.Fatal(err)
}
conf, err := google.JWTConfigFromJSON(data, "https://www.googleapis.com/auth/bigquery")
if err != nil {
log.Fatal(err)
}
// Initiate an http.Client. The following GET request will be
// authorized and authenticated on the behalf of
// your service account.
client := conf.Client(oauth2.NoContext)
client.Get("...")
}
func ExampleSDKConfig() {
// The credentials will be obtained from the first account that
// has been authorized with `gcloud auth login`.
conf, err := google.NewSDKConfig("")
if err != nil {
log.Fatal(err)
}
// Initiate an http.Client. The following GET request will be
// authorized and authenticated on the behalf of the SDK user.
client := conf.Client(oauth2.NoContext)
client.Get("...")
}
func Example_serviceAccount() {
// Your credentials should be obtained from the Google
// Developer Console (https://console.developers.google.com).
conf := &jwt.Config{
Email: "xxx@developer.gserviceaccount.com",
// The contents of your RSA private key or your PEM file
// that contains a private key.
// If you have a p12 file instead, you
// can use `openssl` to export the private key into a pem file.
//
// $ openssl pkcs12 -in key.p12 -passin pass:notasecret -out key.pem -nodes
//
// The field only supports PEM containers with no passphrase.
// The openssl command will convert p12 keys to passphrase-less PEM containers.
PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."),
Scopes: []string{
"https://www.googleapis.com/auth/bigquery",
"https://www.googleapis.com/auth/blogger",
},
TokenURL: google.JWTTokenURL,
// If you would like to impersonate a user, you can
// create a transport with a subject. The following GET
// request will be made on the behalf of user@example.com.
// Optional.
Subject: "user@example.com",
}
// Initiate an http.Client, the following GET request will be
// authorized and authenticated on the behalf of user@example.com.
client := conf.Client(oauth2.NoContext)
client.Get("...")
}
func ExampleAppEngineTokenSource() {
var req *http.Request // from the ServeHTTP handler
ctx := appengine.NewContext(req)
client := &http.Client{
Transport: &oauth2.Transport{
Source: google.AppEngineTokenSource(ctx, "https://www.googleapis.com/auth/bigquery"),
Base: &urlfetch.Transport{
Context: ctx,
},
},
}
client.Get("...")
}
func ExampleComputeTokenSource() {
client := &http.Client{
Transport: &oauth2.Transport{
// Fetch from Google Compute Engine's metadata server to retrieve
// an access token for the provided account.
// If no account is specified, "default" is used.
Source: google.ComputeTokenSource(""),
},
}
client.Get("...")
}
+1 -1
View File
@@ -12,7 +12,7 @@
// https://developers.google.com/accounts/docs/OAuth2
// and
// https://developers.google.com/accounts/docs/application-default-credentials.
package google
package google // import "golang.org/x/oauth2/google"
import (
"encoding/json"
+116
View File
@@ -0,0 +1,116 @@
// Copyright 2015 The Go 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 google
import (
"strings"
"testing"
)
var webJSONKey = []byte(`
{
"web": {
"auth_uri": "https://google.com/o/oauth2/auth",
"client_secret": "3Oknc4jS_wA2r9i",
"token_uri": "https://google.com/o/oauth2/token",
"client_email": "222-nprqovg5k43uum874cs9osjt2koe97g8@developer.gserviceaccount.com",
"redirect_uris": ["https://www.example.com/oauth2callback"],
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/222-nprqovg5k43uum874cs9osjt2koe97g8@developer.gserviceaccount.com",
"client_id": "222-nprqovg5k43uum874cs9osjt2koe97g8.apps.googleusercontent.com",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"javascript_origins": ["https://www.example.com"]
}
}`)
var installedJSONKey = []byte(`{
"installed": {
"client_id": "222-installed.apps.googleusercontent.com",
"redirect_uris": ["https://www.example.com/oauth2callback"]
}
}`)
var jwtJSONKey = []byte(`{
"private_key_id": "268f54e43a1af97cfc71731688434f45aca15c8b",
"private_key": "super secret key",
"client_email": "gopher@developer.gserviceaccount.com",
"client_id": "gopher.apps.googleusercontent.com",
"token_uri": "https://accounts.google.com/o/gophers/token",
"type": "service_account"
}`)
var jwtJSONKeyNoTokenURL = []byte(`{
"private_key_id": "268f54e43a1af97cfc71731688434f45aca15c8b",
"private_key": "super secret key",
"client_email": "gopher@developer.gserviceaccount.com",
"client_id": "gopher.apps.googleusercontent.com",
"type": "service_account"
}`)
func TestConfigFromJSON(t *testing.T) {
conf, err := ConfigFromJSON(webJSONKey, "scope1", "scope2")
if err != nil {
t.Error(err)
}
if got, want := conf.ClientID, "222-nprqovg5k43uum874cs9osjt2koe97g8.apps.googleusercontent.com"; got != want {
t.Errorf("ClientID = %q; want %q", got, want)
}
if got, want := conf.ClientSecret, "3Oknc4jS_wA2r9i"; got != want {
t.Errorf("ClientSecret = %q; want %q", got, want)
}
if got, want := conf.RedirectURL, "https://www.example.com/oauth2callback"; got != want {
t.Errorf("RedictURL = %q; want %q", got, want)
}
if got, want := strings.Join(conf.Scopes, ","), "scope1,scope2"; got != want {
t.Errorf("Scopes = %q; want %q", got, want)
}
if got, want := conf.Endpoint.AuthURL, "https://google.com/o/oauth2/auth"; got != want {
t.Errorf("AuthURL = %q; want %q", got, want)
}
if got, want := conf.Endpoint.TokenURL, "https://google.com/o/oauth2/token"; got != want {
t.Errorf("TokenURL = %q; want %q", got, want)
}
}
func TestConfigFromJSON_Installed(t *testing.T) {
conf, err := ConfigFromJSON(installedJSONKey)
if err != nil {
t.Error(err)
}
if got, want := conf.ClientID, "222-installed.apps.googleusercontent.com"; got != want {
t.Errorf("ClientID = %q; want %q", got, want)
}
}
func TestJWTConfigFromJSON(t *testing.T) {
conf, err := JWTConfigFromJSON(jwtJSONKey, "scope1", "scope2")
if err != nil {
t.Fatal(err)
}
if got, want := conf.Email, "gopher@developer.gserviceaccount.com"; got != want {
t.Errorf("Email = %q, want %q", got, want)
}
if got, want := string(conf.PrivateKey), "super secret key"; got != want {
t.Errorf("PrivateKey = %q, want %q", got, want)
}
if got, want := conf.PrivateKeyID, "268f54e43a1af97cfc71731688434f45aca15c8b"; got != want {
t.Errorf("PrivateKeyID = %q, want %q", got, want)
}
if got, want := strings.Join(conf.Scopes, ","), "scope1,scope2"; got != want {
t.Errorf("Scopes = %q; want %q", got, want)
}
if got, want := conf.TokenURL, "https://accounts.google.com/o/gophers/token"; got != want {
t.Errorf("TokenURL = %q; want %q", got, want)
}
}
func TestJWTConfigFromJSONNoTokenURL(t *testing.T) {
conf, err := JWTConfigFromJSON(jwtJSONKeyNoTokenURL, "scope1", "scope2")
if err != nil {
t.Fatal(err)
}
if got, want := conf.TokenURL, "https://accounts.google.com/o/oauth2/token"; got != want {
t.Errorf("TokenURL = %q; want %q", got, want)
}
}
+91
View File
@@ -0,0 +1,91 @@
// Copyright 2016 The Go 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 google
import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"strings"
"testing"
"time"
"golang.org/x/oauth2/jws"
)
func TestJWTAccessTokenSourceFromJSON(t *testing.T) {
// Generate a key we can use in the test data.
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatal(err)
}
// Encode the key and substitute into our example JSON.
enc := pem.EncodeToMemory(&pem.Block{
Type: "PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
})
enc, err = json.Marshal(string(enc))
if err != nil {
t.Fatalf("json.Marshal: %v", err)
}
jsonKey := bytes.Replace(jwtJSONKey, []byte(`"super secret key"`), enc, 1)
ts, err := JWTAccessTokenSourceFromJSON(jsonKey, "audience")
if err != nil {
t.Fatalf("JWTAccessTokenSourceFromJSON: %v\nJSON: %s", err, string(jsonKey))
}
tok, err := ts.Token()
if err != nil {
t.Fatalf("Token: %v", err)
}
if got, want := tok.TokenType, "Bearer"; got != want {
t.Errorf("TokenType = %q, want %q", got, want)
}
if got := tok.Expiry; tok.Expiry.Before(time.Now()) {
t.Errorf("Expiry = %v, should not be expired", got)
}
err = jws.Verify(tok.AccessToken, &privateKey.PublicKey)
if err != nil {
t.Errorf("jws.Verify on AccessToken: %v", err)
}
claim, err := jws.Decode(tok.AccessToken)
if err != nil {
t.Fatalf("jws.Decode on AccessToken: %v", err)
}
if got, want := claim.Iss, "gopher@developer.gserviceaccount.com"; got != want {
t.Errorf("Iss = %q, want %q", got, want)
}
if got, want := claim.Sub, "gopher@developer.gserviceaccount.com"; got != want {
t.Errorf("Sub = %q, want %q", got, want)
}
if got, want := claim.Aud, "audience"; got != want {
t.Errorf("Aud = %q, want %q", got, want)
}
// Finally, check the header private key.
parts := strings.Split(tok.AccessToken, ".")
hdrJSON, err := base64.RawURLEncoding.DecodeString(parts[0])
if err != nil {
t.Fatalf("base64 DecodeString: %v\nString: %q", err, parts[0])
}
var hdr jws.Header
if err := json.Unmarshal([]byte(hdrJSON), &hdr); err != nil {
t.Fatalf("json.Unmarshal: %v (%q)", err, hdrJSON)
}
if got, want := hdr.KeyID, "268f54e43a1af97cfc71731688434f45aca15c8b"; got != want {
t.Errorf("Header KeyID = %q, want %q", got, want)
}
}
+46
View File
@@ -0,0 +1,46 @@
// Copyright 2015 The Go 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 google
import "testing"
func TestSDKConfig(t *testing.T) {
sdkConfigPath = func() (string, error) {
return "testdata/gcloud", nil
}
tests := []struct {
account string
accessToken string
err bool
}{
{"", "bar_access_token", false},
{"foo@example.com", "foo_access_token", false},
{"bar@example.com", "bar_access_token", false},
{"baz@serviceaccount.example.com", "", true},
}
for _, tt := range tests {
c, err := NewSDKConfig(tt.account)
if got, want := err != nil, tt.err; got != want {
if !tt.err {
t.Errorf("got %v, want nil", err)
} else {
t.Errorf("got nil, want error")
}
continue
}
if err != nil {
continue
}
tok := c.initialToken
if tok == nil {
t.Errorf("got nil, want %q", tt.accessToken)
continue
}
if tok.AccessToken != tt.accessToken {
t.Errorf("got %q, want %q", tok.AccessToken, tt.accessToken)
}
}
}
+122
View File
@@ -0,0 +1,122 @@
{
"data": [
{
"credential": {
"_class": "OAuth2Credentials",
"_module": "oauth2client.client",
"access_token": "foo_access_token",
"client_id": "foo_client_id",
"client_secret": "foo_client_secret",
"id_token": {
"at_hash": "foo_at_hash",
"aud": "foo_aud",
"azp": "foo_azp",
"cid": "foo_cid",
"email": "foo@example.com",
"email_verified": true,
"exp": 1420573614,
"iat": 1420569714,
"id": "1337",
"iss": "accounts.google.com",
"sub": "1337",
"token_hash": "foo_token_hash",
"verified_email": true
},
"invalid": false,
"refresh_token": "foo_refresh_token",
"revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
"token_expiry": "2015-01-09T00:51:51Z",
"token_response": {
"access_token": "foo_access_token",
"expires_in": 3600,
"id_token": "foo_id_token",
"token_type": "Bearer"
},
"token_uri": "https://accounts.google.com/o/oauth2/token",
"user_agent": "Cloud SDK Command Line Tool"
},
"key": {
"account": "foo@example.com",
"clientId": "foo_client_id",
"scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting",
"type": "google-cloud-sdk"
}
},
{
"credential": {
"_class": "OAuth2Credentials",
"_module": "oauth2client.client",
"access_token": "bar_access_token",
"client_id": "bar_client_id",
"client_secret": "bar_client_secret",
"id_token": {
"at_hash": "bar_at_hash",
"aud": "bar_aud",
"azp": "bar_azp",
"cid": "bar_cid",
"email": "bar@example.com",
"email_verified": true,
"exp": 1420573614,
"iat": 1420569714,
"id": "1337",
"iss": "accounts.google.com",
"sub": "1337",
"token_hash": "bar_token_hash",
"verified_email": true
},
"invalid": false,
"refresh_token": "bar_refresh_token",
"revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
"token_expiry": "2015-01-09T00:51:51Z",
"token_response": {
"access_token": "bar_access_token",
"expires_in": 3600,
"id_token": "bar_id_token",
"token_type": "Bearer"
},
"token_uri": "https://accounts.google.com/o/oauth2/token",
"user_agent": "Cloud SDK Command Line Tool"
},
"key": {
"account": "bar@example.com",
"clientId": "bar_client_id",
"scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting",
"type": "google-cloud-sdk"
}
},
{
"credential": {
"_class": "ServiceAccountCredentials",
"_kwargs": {},
"_module": "oauth2client.client",
"_private_key_id": "00000000000000000000000000000000",
"_private_key_pkcs8_text": "-----BEGIN RSA PRIVATE KEY-----\nMIICWwIBAAKBgQCt3fpiynPSaUhWSIKMGV331zudwJ6GkGmvQtwsoK2S2LbvnSwU\nNxgj4fp08kIDR5p26wF4+t/HrKydMwzftXBfZ9UmLVJgRdSswmS5SmChCrfDS5OE\nvFFcN5+6w1w8/Nu657PF/dse8T0bV95YrqyoR0Osy8WHrUOMSIIbC3hRuwIDAQAB\nAoGAJrGE/KFjn0sQ7yrZ6sXmdLawrM3mObo/2uI9T60+k7SpGbBX0/Pi6nFrJMWZ\nTVONG7P3Mu5aCPzzuVRYJB0j8aldSfzABTY3HKoWCczqw1OztJiEseXGiYz4QOyr\nYU3qDyEpdhS6q6wcoLKGH+hqRmz6pcSEsc8XzOOu7s4xW8kCQQDkc75HjhbarCnd\nJJGMe3U76+6UGmdK67ltZj6k6xoB5WbTNChY9TAyI2JC+ppYV89zv3ssj4L+02u3\nHIHFGxsHAkEAwtU1qYb1tScpchPobnYUFiVKJ7KA8EZaHVaJJODW/cghTCV7BxcJ\nbgVvlmk4lFKn3lPKAgWw7PdQsBTVBUcCrQJATPwoIirizrv3u5soJUQxZIkENAqV\nxmybZx9uetrzP7JTrVbFRf0SScMcyN90hdLJiQL8+i4+gaszgFht7sNMnwJAAbfj\nq0UXcauQwALQ7/h2oONfTg5S+MuGC/AxcXPSMZbMRGGoPh3D5YaCv27aIuS/ukQ+\n6dmm/9AGlCb64fsIWQJAPaokbjIifo+LwC5gyK73Mc4t8nAOSZDenzd/2f6TCq76\nS1dcnKiPxaED7W/y6LJiuBT2rbZiQ2L93NJpFZD/UA==\n-----END RSA PRIVATE KEY-----\n",
"_revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
"_scopes": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting",
"_service_account_email": "baz@serviceaccount.example.com",
"_service_account_id": "baz.serviceaccount.example.com",
"_token_uri": "https://accounts.google.com/o/oauth2/token",
"_user_agent": "Cloud SDK Command Line Tool",
"access_token": null,
"assertion_type": null,
"client_id": null,
"client_secret": null,
"id_token": null,
"invalid": false,
"refresh_token": null,
"revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
"service_account_name": "baz@serviceaccount.example.com",
"token_expiry": null,
"token_response": null,
"user_agent": "Cloud SDK Command Line Tool"
},
"key": {
"account": "baz@serviceaccount.example.com",
"clientId": "baz_client_id",
"scope": "https://www.googleapis.com/auth/appengine.admin https://www.googleapis.com/auth/bigquery https://www.googleapis.com/auth/compute https://www.googleapis.com/auth/devstorage.full_control https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/ndev.cloudman https://www.googleapis.com/auth/cloud-platform https://www.googleapis.com/auth/sqlservice.admin https://www.googleapis.com/auth/prediction https://www.googleapis.com/auth/projecthosting",
"type": "google-cloud-sdk"
}
}
],
"file_version": 1
}
+2
View File
@@ -0,0 +1,2 @@
[core]
account = bar@example.com
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2016 The Go 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 heroku provides constants for using OAuth2 to access Heroku.
package heroku // import "golang.org/x/oauth2/heroku"
import (
"golang.org/x/oauth2"
)
// Endpoint is Heroku's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://id.heroku.com/oauth/authorize",
TokenURL: "https://id.heroku.com/oauth/token",
}
+60
View File
@@ -0,0 +1,60 @@
// Copyright 2016 The Go 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 hipchat provides constants for using OAuth2 to access HipChat.
package hipchat // import "golang.org/x/oauth2/hipchat"
import (
"encoding/json"
"errors"
"golang.org/x/oauth2"
"golang.org/x/oauth2/clientcredentials"
)
// Endpoint is HipChat's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://www.hipchat.com/users/authorize",
TokenURL: "https://api.hipchat.com/v2/oauth/token",
}
// ServerEndpoint returns a new oauth2.Endpoint for a HipChat Server instance
// running on the given domain or host.
func ServerEndpoint(host string) oauth2.Endpoint {
return oauth2.Endpoint{
AuthURL: "https://" + host + "/users/authorize",
TokenURL: "https://" + host + "/v2/oauth/token",
}
}
// ClientCredentialsConfigFromCaps generates a Config from a HipChat API
// capabilities descriptor. It does not verify the scopes against the
// capabilities document at this time.
//
// For more information see: https://www.hipchat.com/docs/apiv2/method/get_capabilities
func ClientCredentialsConfigFromCaps(capsJSON []byte, clientID, clientSecret string, scopes ...string) (*clientcredentials.Config, error) {
var caps struct {
Caps struct {
Endpoint struct {
TokenURL string `json:"tokenUrl"`
} `json:"oauth2Provider"`
} `json:"capabilities"`
}
if err := json.Unmarshal(capsJSON, &caps); err != nil {
return nil, err
}
// Verify required fields.
if caps.Caps.Endpoint.TokenURL == "" {
return nil, errors.New("oauth2/hipchat: missing OAuth2 token URL in the capabilities descriptor JSON")
}
return &clientcredentials.Config{
ClientID: clientID,
ClientSecret: clientSecret,
Scopes: scopes,
TokenURL: caps.Caps.Endpoint.TokenURL,
}, nil
}
+62
View File
@@ -0,0 +1,62 @@
// Copyright 2014 The Go 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 internal contains support packages for oauth2 package.
package internal
import (
"reflect"
"strings"
"testing"
)
func TestParseINI(t *testing.T) {
tests := []struct {
ini string
want map[string]map[string]string
}{
{
`root = toor
[foo]
bar = hop
ini = nin
`,
map[string]map[string]string{
"": {"root": "toor"},
"foo": {"bar": "hop", "ini": "nin"},
},
},
{
`[empty]
[section]
empty=
`,
map[string]map[string]string{
"": {},
"empty": {},
"section": {"empty": ""},
},
},
{
`ignore
[invalid
=stuff
;comment=true
`,
map[string]map[string]string{
"": {},
},
},
}
for _, tt := range tests {
result, err := ParseINI(strings.NewReader(tt.ini))
if err != nil {
t.Errorf("ParseINI(%q) error %v, want: no error", tt.ini, err)
continue
}
if !reflect.DeepEqual(result, tt.want) {
t.Errorf("ParseINI(%q) = %#v, want: %#v", tt.ini, result, tt.want)
}
}
}
+24 -3
View File
@@ -120,6 +120,15 @@ var brokenAuthHeaderProviders = []string{
"https://www.wunderlist.com/oauth/",
"https://api.patreon.com/",
"https://sandbox.codeswholesale.com/oauth/token",
"https://api.sipgate.com/v1/authorization/oauth",
}
// brokenAuthHeaderDomains lists broken providers that issue dynamic endpoints.
var brokenAuthHeaderDomains = []string{
".force.com",
".myshopify.com",
".okta.com",
".oktapreview.com",
}
func RegisterBrokenAuthHeaderProvider(tokenURL string) {
@@ -142,6 +151,14 @@ func providerAuthHeaderWorks(tokenURL string) bool {
}
}
if u, err := url.Parse(tokenURL); err == nil {
for _, s := range brokenAuthHeaderDomains {
if strings.HasSuffix(u.Host, s) {
return false
}
}
}
// Assume the provider implements the spec properly
// otherwise. We can add more exceptions as they're
// discovered. We will _not_ be adding configurable hooks
@@ -155,9 +172,13 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
return nil, err
}
bustedAuth := !providerAuthHeaderWorks(tokenURL)
if bustedAuth && clientSecret != "" {
v.Set("client_id", clientID)
v.Set("client_secret", clientSecret)
if bustedAuth {
if clientID != "" {
v.Set("client_id", clientID)
}
if clientSecret != "" {
v.Set("client_secret", clientSecret)
}
}
req, err := http.NewRequest("POST", tokenURL, strings.NewReader(v.Encode()))
if err != nil {
+81
View File
@@ -0,0 +1,81 @@
// Copyright 2014 The Go 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 internal contains support packages for oauth2 package.
package internal
import (
"fmt"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"golang.org/x/net/context"
)
func TestRegisterBrokenAuthHeaderProvider(t *testing.T) {
RegisterBrokenAuthHeaderProvider("https://aaa.com/")
tokenURL := "https://aaa.com/token"
if providerAuthHeaderWorks(tokenURL) {
t.Errorf("got %q as unbroken; want broken", tokenURL)
}
}
func TestRetrieveTokenBustedNoSecret(t *testing.T) {
const clientID = "client-id"
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if got, want := r.FormValue("client_id"), clientID; got != want {
t.Errorf("client_id = %q; want %q", got, want)
}
if got, want := r.FormValue("client_secret"), ""; got != want {
t.Errorf("client_secret = %q; want empty", got)
}
}))
defer ts.Close()
RegisterBrokenAuthHeaderProvider(ts.URL)
_, err := RetrieveToken(context.Background(), clientID, "", ts.URL, url.Values{})
if err != nil {
t.Errorf("RetrieveToken = %v; want no error", err)
}
}
func Test_providerAuthHeaderWorks(t *testing.T) {
for _, p := range brokenAuthHeaderProviders {
if providerAuthHeaderWorks(p) {
t.Errorf("got %q as unbroken; want broken", p)
}
p := fmt.Sprintf("%ssomesuffix", p)
if providerAuthHeaderWorks(p) {
t.Errorf("got %q as unbroken; want broken", p)
}
}
p := "https://api.not-in-the-list-example.com/"
if !providerAuthHeaderWorks(p) {
t.Errorf("got %q as unbroken; want broken", p)
}
}
func TestProviderAuthHeaderWorksDomain(t *testing.T) {
tests := []struct {
tokenURL string
wantWorks bool
}{
{"https://dev-12345.okta.com/token-url", false},
{"https://dev-12345.oktapreview.com/token-url", false},
{"https://dev-12345.okta.org/token-url", true},
{"https://foo.bar.force.com/token-url", false},
{"https://foo.force.com/token-url", false},
{"https://force.com/token-url", true},
}
for _, test := range tests {
got := providerAuthHeaderWorks(test.tokenURL)
if got != test.wantWorks {
t.Errorf("providerAuthHeaderWorks(%q) = %v; want %v", test.tokenURL, got, test.wantWorks)
}
}
}
+38
View File
@@ -0,0 +1,38 @@
// Copyright 2015 The Go 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 internal
import (
"net/http"
"testing"
"golang.org/x/net/context"
)
func TestContextClient(t *testing.T) {
rc := &http.Client{}
RegisterContextClientFunc(func(context.Context) (*http.Client, error) {
return rc, nil
})
c := &http.Client{}
ctx := context.WithValue(context.Background(), HTTPClient, c)
hc, err := ContextClient(ctx)
if err != nil {
t.Fatalf("want valid client; got err = %v", err)
}
if hc != c {
t.Fatalf("want context client = %p; got = %p", c, hc)
}
hc, err = ContextClient(context.TODO())
if err != nil {
t.Fatalf("want valid client; got err = %v", err)
}
if hc != rc {
t.Fatalf("want registered client = %p; got = %p", c, hc)
}
}
+1 -1
View File
@@ -12,7 +12,7 @@
// removed in the future. It exists for internal use only.
// Please switch to another JWS package or copy this package into your own
// source tree.
package jws
package jws // import "golang.org/x/oauth2/jws"
import (
"bytes"
+46
View File
@@ -0,0 +1,46 @@
// Copyright 2016 The Go 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 jws
import (
"crypto/rand"
"crypto/rsa"
"testing"
)
func TestSignAndVerify(t *testing.T) {
header := &Header{
Algorithm: "RS256",
Typ: "JWT",
}
payload := &ClaimSet{
Iss: "http://google.com/",
Aud: "",
Exp: 3610,
Iat: 10,
}
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
t.Fatal(err)
}
token, err := Encode(header, payload, privateKey)
if err != nil {
t.Fatal(err)
}
err = Verify(token, &privateKey.PublicKey)
if err != nil {
t.Fatal(err)
}
}
func TestVerifyFailsOnMalformedClaim(t *testing.T) {
err := Verify("abc.def", nil)
if err == nil {
t.Error("got no errors; want improperly formed JWT not to be verified")
}
}
+33
View File
@@ -0,0 +1,33 @@
// Copyright 2014 The Go 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 jwt_test
import (
"context"
"golang.org/x/oauth2/jwt"
)
func ExampleJWTConfig() {
ctx := context.Background()
conf := &jwt.Config{
Email: "xxx@developer.com",
// The contents of your RSA private key or your PEM file
// that contains a private key.
// If you have a p12 file instead, you
// can use `openssl` to export the private key into a pem file.
//
// $ openssl pkcs12 -in key.p12 -out key.pem -nodes
//
// It only supports PEM containers with no passphrase.
PrivateKey: []byte("-----BEGIN RSA PRIVATE KEY-----..."),
Subject: "user@example.com",
TokenURL: "https://provider.com/o/oauth2/token",
}
// Initiate an http.Client, the following GET request will be
// authorized and authenticated on the behalf of user@example.com.
client := conf.Client(ctx)
client.Get("...")
}
+190
View File
@@ -0,0 +1,190 @@
// Copyright 2014 The Go 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 jwt
import (
"context"
"encoding/base64"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
"golang.org/x/oauth2/jws"
)
var dummyPrivateKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAx4fm7dngEmOULNmAs1IGZ9Apfzh+BkaQ1dzkmbUgpcoghucE
DZRnAGd2aPyB6skGMXUytWQvNYav0WTR00wFtX1ohWTfv68HGXJ8QXCpyoSKSSFY
fuP9X36wBSkSX9J5DVgiuzD5VBdzUISSmapjKm+DcbRALjz6OUIPEWi1Tjl6p5RK
1w41qdbmt7E5/kGhKLDuT7+M83g4VWhgIvaAXtnhklDAggilPPa8ZJ1IFe31lNlr
k4DRk38nc6sEutdf3RL7QoH7FBusI7uXV03DC6dwN1kP4GE7bjJhcRb/7jYt7CQ9
/E9Exz3c0yAp0yrTg0Fwh+qxfH9dKwN52S7SBwIDAQABAoIBAQCaCs26K07WY5Jt
3a2Cw3y2gPrIgTCqX6hJs7O5ByEhXZ8nBwsWANBUe4vrGaajQHdLj5OKfsIDrOvn
2NI1MqflqeAbu/kR32q3tq8/Rl+PPiwUsW3E6Pcf1orGMSNCXxeducF2iySySzh3
nSIhCG5uwJDWI7a4+9KiieFgK1pt/Iv30q1SQS8IEntTfXYwANQrfKUVMmVF9aIK
6/WZE2yd5+q3wVVIJ6jsmTzoDCX6QQkkJICIYwCkglmVy5AeTckOVwcXL0jqw5Kf
5/soZJQwLEyBoQq7Kbpa26QHq+CJONetPP8Ssy8MJJXBT+u/bSseMb3Zsr5cr43e
DJOhwsThAoGBAPY6rPKl2NT/K7XfRCGm1sbWjUQyDShscwuWJ5+kD0yudnT/ZEJ1
M3+KS/iOOAoHDdEDi9crRvMl0UfNa8MAcDKHflzxg2jg/QI+fTBjPP5GOX0lkZ9g
z6VePoVoQw2gpPFVNPPTxKfk27tEzbaffvOLGBEih0Kb7HTINkW8rIlzAoGBAM9y
1yr+jvfS1cGFtNU+Gotoihw2eMKtIqR03Yn3n0PK1nVCDKqwdUqCypz4+ml6cxRK
J8+Pfdh7D+ZJd4LEG6Y4QRDLuv5OA700tUoSHxMSNn3q9As4+T3MUyYxWKvTeu3U
f2NWP9ePU0lV8ttk7YlpVRaPQmc1qwooBA/z/8AdAoGAW9x0HWqmRICWTBnpjyxx
QGlW9rQ9mHEtUotIaRSJ6K/F3cxSGUEkX1a3FRnp6kPLcckC6NlqdNgNBd6rb2rA
cPl/uSkZP42Als+9YMoFPU/xrrDPbUhu72EDrj3Bllnyb168jKLa4VBOccUvggxr
Dm08I1hgYgdN5huzs7y6GeUCgYEAj+AZJSOJ6o1aXS6rfV3mMRve9bQ9yt8jcKXw
5HhOCEmMtaSKfnOF1Ziih34Sxsb7O2428DiX0mV/YHtBnPsAJidL0SdLWIapBzeg
KHArByIRkwE6IvJvwpGMdaex1PIGhx5i/3VZL9qiq/ElT05PhIb+UXgoWMabCp84
OgxDK20CgYAeaFo8BdQ7FmVX2+EEejF+8xSge6WVLtkaon8bqcn6P0O8lLypoOhd
mJAYH8WU+UAy9pecUnDZj14LAGNVmYcse8HFX71MoshnvCTFEPVo4rZxIAGwMpeJ
5jgQ3slYLpqrGlcbLgUXBUgzEO684Wk/UV9DFPlHALVqCfXQ9dpJPg==
-----END RSA PRIVATE KEY-----`)
func TestJWTFetch_JSONResponse(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{
"access_token": "90d64460d14870c08c81352a05dedd3465940a7c",
"scope": "user",
"token_type": "bearer",
"expires_in": 3600
}`))
}))
defer ts.Close()
conf := &Config{
Email: "aaa@xxx.com",
PrivateKey: dummyPrivateKey,
TokenURL: ts.URL,
}
tok, err := conf.TokenSource(context.Background()).Token()
if err != nil {
t.Fatal(err)
}
if !tok.Valid() {
t.Errorf("got invalid token: %v", tok)
}
if got, want := tok.AccessToken, "90d64460d14870c08c81352a05dedd3465940a7c"; got != want {
t.Errorf("access token = %q; want %q", got, want)
}
if got, want := tok.TokenType, "bearer"; got != want {
t.Errorf("token type = %q; want %q", got, want)
}
if got := tok.Expiry.IsZero(); got {
t.Errorf("token expiry = %v, want none", got)
}
scope := tok.Extra("scope")
if got, want := scope, "user"; got != want {
t.Errorf("scope = %q; want %q", got, want)
}
}
func TestJWTFetch_BadResponse(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"scope": "user", "token_type": "bearer"}`))
}))
defer ts.Close()
conf := &Config{
Email: "aaa@xxx.com",
PrivateKey: dummyPrivateKey,
TokenURL: ts.URL,
}
tok, err := conf.TokenSource(context.Background()).Token()
if err != nil {
t.Fatal(err)
}
if tok == nil {
t.Fatalf("got nil token; want token")
}
if tok.Valid() {
t.Errorf("got invalid token: %v", tok)
}
if got, want := tok.AccessToken, ""; got != want {
t.Errorf("access token = %q; want %q", got, want)
}
if got, want := tok.TokenType, "bearer"; got != want {
t.Errorf("token type = %q; want %q", got, want)
}
scope := tok.Extra("scope")
if got, want := scope, "user"; got != want {
t.Errorf("token scope = %q; want %q", got, want)
}
}
func TestJWTFetch_BadResponseType(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"access_token":123, "scope": "user", "token_type": "bearer"}`))
}))
defer ts.Close()
conf := &Config{
Email: "aaa@xxx.com",
PrivateKey: dummyPrivateKey,
TokenURL: ts.URL,
}
tok, err := conf.TokenSource(context.Background()).Token()
if err == nil {
t.Error("got a token; expected error")
if got, want := tok.AccessToken, ""; got != want {
t.Errorf("access token = %q; want %q", got, want)
}
}
}
func TestJWTFetch_Assertion(t *testing.T) {
var assertion string
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
assertion = r.Form.Get("assertion")
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{
"access_token": "90d64460d14870c08c81352a05dedd3465940a7c",
"scope": "user",
"token_type": "bearer",
"expires_in": 3600
}`))
}))
defer ts.Close()
conf := &Config{
Email: "aaa@xxx.com",
PrivateKey: dummyPrivateKey,
PrivateKeyID: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
TokenURL: ts.URL,
}
_, err := conf.TokenSource(context.Background()).Token()
if err != nil {
t.Fatalf("Failed to fetch token: %v", err)
}
parts := strings.Split(assertion, ".")
if len(parts) != 3 {
t.Fatalf("assertion = %q; want 3 parts", assertion)
}
gotjson, err := base64.RawURLEncoding.DecodeString(parts[0])
if err != nil {
t.Fatalf("invalid token header; err = %v", err)
}
got := jws.Header{}
if err := json.Unmarshal(gotjson, &got); err != nil {
t.Errorf("failed to unmarshal json token header = %q; err = %v", gotjson, err)
}
want := jws.Header{
Algorithm: "RS256",
Typ: "JWT",
KeyID: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
}
if got != want {
t.Errorf("access token header = %q; want %q", got, want)
}
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2015 The Go 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 linkedin provides constants for using OAuth2 to access LinkedIn.
package linkedin // import "golang.org/x/oauth2/linkedin"
import (
"golang.org/x/oauth2"
)
// Endpoint is LinkedIn's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://www.linkedin.com/uas/oauth2/authorization",
TokenURL: "https://www.linkedin.com/uas/oauth2/accessToken",
}
+22
View File
@@ -0,0 +1,22 @@
// Copyright 2016 The Go 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 mediamath provides constants for using OAuth2 to access MediaMath.
package mediamath // import "golang.org/x/oauth2/mediamath"
import (
"golang.org/x/oauth2"
)
// Endpoint is MediaMath's OAuth 2.0 endpoint for production.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://api.mediamath.com/oauth2/v1.0/authorize",
TokenURL: "https://api.mediamath.com/oauth2/v1.0/token",
}
// SandboxEndpoint is MediaMath's OAuth 2.0 endpoint for sandbox.
var SandboxEndpoint = oauth2.Endpoint{
AuthURL: "https://t1sandbox.mediamath.com/oauth2/v1.0/authorize",
TokenURL: "https://t1sandbox.mediamath.com/oauth2/v1.0/token",
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2016 The Go 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 microsoft provides constants for using OAuth2 to access Windows Live ID.
package microsoft // import "golang.org/x/oauth2/microsoft"
import (
"golang.org/x/oauth2"
)
// LiveConnectEndpoint is Windows's Live ID OAuth 2.0 endpoint.
var LiveConnectEndpoint = oauth2.Endpoint{
AuthURL: "https://login.live.com/oauth20_authorize.srf",
TokenURL: "https://login.live.com/oauth20_token.srf",
}
+1 -1
View File
@@ -5,7 +5,7 @@
// Package oauth2 provides support for making
// OAuth2 authorized and authenticated HTTP requests.
// It can additionally grant authorization with Bearer JWT.
package oauth2
package oauth2 // import "golang.org/x/oauth2"
import (
"bytes"
+448
View File
@@ -0,0 +1,448 @@
// Copyright 2014 The Go 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 oauth2
import (
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"time"
"golang.org/x/net/context"
)
type mockTransport struct {
rt func(req *http.Request) (resp *http.Response, err error)
}
func (t *mockTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
return t.rt(req)
}
func newConf(url string) *Config {
return &Config{
ClientID: "CLIENT_ID",
ClientSecret: "CLIENT_SECRET",
RedirectURL: "REDIRECT_URL",
Scopes: []string{"scope1", "scope2"},
Endpoint: Endpoint{
AuthURL: url + "/auth",
TokenURL: url + "/token",
},
}
}
func TestAuthCodeURL(t *testing.T) {
conf := newConf("server")
url := conf.AuthCodeURL("foo", AccessTypeOffline, ApprovalForce)
const want = "server/auth?access_type=offline&approval_prompt=force&client_id=CLIENT_ID&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=foo"
if got := url; got != want {
t.Errorf("got auth code URL = %q; want %q", got, want)
}
}
func TestAuthCodeURL_CustomParam(t *testing.T) {
conf := newConf("server")
param := SetAuthURLParam("foo", "bar")
url := conf.AuthCodeURL("baz", param)
const want = "server/auth?client_id=CLIENT_ID&foo=bar&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=baz"
if got := url; got != want {
t.Errorf("got auth code = %q; want %q", got, want)
}
}
func TestAuthCodeURL_Optional(t *testing.T) {
conf := &Config{
ClientID: "CLIENT_ID",
Endpoint: Endpoint{
AuthURL: "/auth-url",
TokenURL: "/token-url",
},
}
url := conf.AuthCodeURL("")
const want = "/auth-url?client_id=CLIENT_ID&response_type=code"
if got := url; got != want {
t.Fatalf("got auth code = %q; want %q", got, want)
}
}
func TestExchangeRequest(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.String() != "/token" {
t.Errorf("Unexpected exchange request URL, %v is found.", r.URL)
}
headerAuth := r.Header.Get("Authorization")
if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" {
t.Errorf("Unexpected authorization header, %v is found.", headerAuth)
}
headerContentType := r.Header.Get("Content-Type")
if headerContentType != "application/x-www-form-urlencoded" {
t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
t.Errorf("Failed reading request body: %s.", err)
}
if string(body) != "code=exchange-code&grant_type=authorization_code&redirect_uri=REDIRECT_URL" {
t.Errorf("Unexpected exchange payload, %v is found.", string(body))
}
w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer"))
}))
defer ts.Close()
conf := newConf(ts.URL)
tok, err := conf.Exchange(context.Background(), "exchange-code")
if err != nil {
t.Error(err)
}
if !tok.Valid() {
t.Fatalf("Token invalid. Got: %#v", tok)
}
if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
}
if tok.TokenType != "bearer" {
t.Errorf("Unexpected token type, %#v.", tok.TokenType)
}
scope := tok.Extra("scope")
if scope != "user" {
t.Errorf("Unexpected value for scope: %v", scope)
}
}
func TestExchangeRequest_JSONResponse(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.String() != "/token" {
t.Errorf("Unexpected exchange request URL, %v is found.", r.URL)
}
headerAuth := r.Header.Get("Authorization")
if headerAuth != "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ=" {
t.Errorf("Unexpected authorization header, %v is found.", headerAuth)
}
headerContentType := r.Header.Get("Content-Type")
if headerContentType != "application/x-www-form-urlencoded" {
t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
t.Errorf("Failed reading request body: %s.", err)
}
if string(body) != "code=exchange-code&grant_type=authorization_code&redirect_uri=REDIRECT_URL" {
t.Errorf("Unexpected exchange payload, %v is found.", string(body))
}
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"access_token": "90d64460d14870c08c81352a05dedd3465940a7c", "scope": "user", "token_type": "bearer", "expires_in": 86400}`))
}))
defer ts.Close()
conf := newConf(ts.URL)
tok, err := conf.Exchange(context.Background(), "exchange-code")
if err != nil {
t.Error(err)
}
if !tok.Valid() {
t.Fatalf("Token invalid. Got: %#v", tok)
}
if tok.AccessToken != "90d64460d14870c08c81352a05dedd3465940a7c" {
t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
}
if tok.TokenType != "bearer" {
t.Errorf("Unexpected token type, %#v.", tok.TokenType)
}
scope := tok.Extra("scope")
if scope != "user" {
t.Errorf("Unexpected value for scope: %v", scope)
}
expiresIn := tok.Extra("expires_in")
if expiresIn != float64(86400) {
t.Errorf("Unexpected non-numeric value for expires_in: %v", expiresIn)
}
}
func TestExtraValueRetrieval(t *testing.T) {
values := url.Values{}
kvmap := map[string]string{
"scope": "user", "token_type": "bearer", "expires_in": "86400.92",
"server_time": "1443571905.5606415", "referer_ip": "10.0.0.1",
"etag": "\"afZYj912P4alikMz_P11982\"", "request_id": "86400",
"untrimmed": " untrimmed ",
}
for key, value := range kvmap {
values.Set(key, value)
}
tok := Token{raw: values}
scope := tok.Extra("scope")
if got, want := scope, "user"; got != want {
t.Errorf("got scope = %q; want %q", got, want)
}
serverTime := tok.Extra("server_time")
if got, want := serverTime, 1443571905.5606415; got != want {
t.Errorf("got server_time value = %v; want %v", got, want)
}
refererIP := tok.Extra("referer_ip")
if got, want := refererIP, "10.0.0.1"; got != want {
t.Errorf("got referer_ip value = %v, want %v", got, want)
}
expiresIn := tok.Extra("expires_in")
if got, want := expiresIn, 86400.92; got != want {
t.Errorf("got expires_in value = %v, want %v", got, want)
}
requestID := tok.Extra("request_id")
if got, want := requestID, int64(86400); got != want {
t.Errorf("got request_id value = %v, want %v", got, want)
}
untrimmed := tok.Extra("untrimmed")
if got, want := untrimmed, " untrimmed "; got != want {
t.Errorf("got untrimmed = %q; want %q", got, want)
}
}
const day = 24 * time.Hour
func TestExchangeRequest_JSONResponse_Expiry(t *testing.T) {
seconds := int32(day.Seconds())
for _, c := range []struct {
expires string
want bool
}{
{fmt.Sprintf(`"expires_in": %d`, seconds), true},
{fmt.Sprintf(`"expires_in": "%d"`, seconds), true}, // PayPal case
{fmt.Sprintf(`"expires": %d`, seconds), true}, // Facebook case
{`"expires": false`, false}, // wrong type
{`"expires": {}`, false}, // wrong type
{`"expires": "zzz"`, false}, // wrong value
} {
testExchangeRequest_JSONResponse_expiry(t, c.expires, c.want)
}
}
func testExchangeRequest_JSONResponse_expiry(t *testing.T, exp string, want bool) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(fmt.Sprintf(`{"access_token": "90d", "scope": "user", "token_type": "bearer", %s}`, exp)))
}))
defer ts.Close()
conf := newConf(ts.URL)
t1 := time.Now().Add(day)
tok, err := conf.Exchange(context.Background(), "exchange-code")
t2 := time.Now().Add(day)
if got := (err == nil); got != want {
if want {
t.Errorf("unexpected error: got %v", err)
} else {
t.Errorf("unexpected success")
}
}
if !want {
return
}
if !tok.Valid() {
t.Fatalf("Token invalid. Got: %#v", tok)
}
expiry := tok.Expiry
if expiry.Before(t1) || expiry.After(t2) {
t.Errorf("Unexpected value for Expiry: %v (shold be between %v and %v)", expiry, t1, t2)
}
}
func TestExchangeRequest_BadResponse(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"scope": "user", "token_type": "bearer"}`))
}))
defer ts.Close()
conf := newConf(ts.URL)
tok, err := conf.Exchange(context.Background(), "code")
if err != nil {
t.Fatal(err)
}
if tok.AccessToken != "" {
t.Errorf("Unexpected access token, %#v.", tok.AccessToken)
}
}
func TestExchangeRequest_BadResponseType(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"access_token":123, "scope": "user", "token_type": "bearer"}`))
}))
defer ts.Close()
conf := newConf(ts.URL)
_, err := conf.Exchange(context.Background(), "exchange-code")
if err == nil {
t.Error("expected error from invalid access_token type")
}
}
func TestExchangeRequest_NonBasicAuth(t *testing.T) {
tr := &mockTransport{
rt: func(r *http.Request) (w *http.Response, err error) {
headerAuth := r.Header.Get("Authorization")
if headerAuth != "" {
t.Errorf("Unexpected authorization header, %v is found.", headerAuth)
}
return nil, errors.New("no response")
},
}
c := &http.Client{Transport: tr}
conf := &Config{
ClientID: "CLIENT_ID",
Endpoint: Endpoint{
AuthURL: "https://accounts.google.com/auth",
TokenURL: "https://accounts.google.com/token",
},
}
ctx := context.WithValue(context.Background(), HTTPClient, c)
conf.Exchange(ctx, "code")
}
func TestPasswordCredentialsTokenRequest(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
expected := "/token"
if r.URL.String() != expected {
t.Errorf("URL = %q; want %q", r.URL, expected)
}
headerAuth := r.Header.Get("Authorization")
expected = "Basic Q0xJRU5UX0lEOkNMSUVOVF9TRUNSRVQ="
if headerAuth != expected {
t.Errorf("Authorization header = %q; want %q", headerAuth, expected)
}
headerContentType := r.Header.Get("Content-Type")
expected = "application/x-www-form-urlencoded"
if headerContentType != expected {
t.Errorf("Content-Type header = %q; want %q", headerContentType, expected)
}
body, err := ioutil.ReadAll(r.Body)
if err != nil {
t.Errorf("Failed reading request body: %s.", err)
}
expected = "grant_type=password&password=password1&scope=scope1+scope2&username=user1"
if string(body) != expected {
t.Errorf("res.Body = %q; want %q", string(body), expected)
}
w.Header().Set("Content-Type", "application/x-www-form-urlencoded")
w.Write([]byte("access_token=90d64460d14870c08c81352a05dedd3465940a7c&scope=user&token_type=bearer"))
}))
defer ts.Close()
conf := newConf(ts.URL)
tok, err := conf.PasswordCredentialsToken(context.Background(), "user1", "password1")
if err != nil {
t.Error(err)
}
if !tok.Valid() {
t.Fatalf("Token invalid. Got: %#v", tok)
}
expected := "90d64460d14870c08c81352a05dedd3465940a7c"
if tok.AccessToken != expected {
t.Errorf("AccessToken = %q; want %q", tok.AccessToken, expected)
}
expected = "bearer"
if tok.TokenType != expected {
t.Errorf("TokenType = %q; want %q", tok.TokenType, expected)
}
}
func TestTokenRefreshRequest(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.String() == "/somethingelse" {
return
}
if r.URL.String() != "/token" {
t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL)
}
headerContentType := r.Header.Get("Content-Type")
if headerContentType != "application/x-www-form-urlencoded" {
t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
}
body, _ := ioutil.ReadAll(r.Body)
if string(body) != "grant_type=refresh_token&refresh_token=REFRESH_TOKEN" {
t.Errorf("Unexpected refresh token payload, %v is found.", string(body))
}
}))
defer ts.Close()
conf := newConf(ts.URL)
c := conf.Client(context.Background(), &Token{RefreshToken: "REFRESH_TOKEN"})
c.Get(ts.URL + "/somethingelse")
}
func TestFetchWithNoRefreshToken(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.String() == "/somethingelse" {
return
}
if r.URL.String() != "/token" {
t.Errorf("Unexpected token refresh request URL, %v is found.", r.URL)
}
headerContentType := r.Header.Get("Content-Type")
if headerContentType != "application/x-www-form-urlencoded" {
t.Errorf("Unexpected Content-Type header, %v is found.", headerContentType)
}
body, _ := ioutil.ReadAll(r.Body)
if string(body) != "client_id=CLIENT_ID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN" {
t.Errorf("Unexpected refresh token payload, %v is found.", string(body))
}
}))
defer ts.Close()
conf := newConf(ts.URL)
c := conf.Client(context.Background(), nil)
_, err := c.Get(ts.URL + "/somethingelse")
if err == nil {
t.Errorf("Fetch should return an error if no refresh token is set")
}
}
func TestRefreshToken_RefreshTokenReplacement(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"access_token":"ACCESS TOKEN", "scope": "user", "token_type": "bearer", "refresh_token": "NEW REFRESH TOKEN"}`))
return
}))
defer ts.Close()
conf := newConf(ts.URL)
tkr := tokenRefresher{
conf: conf,
ctx: context.Background(),
refreshToken: "OLD REFRESH TOKEN",
}
tk, err := tkr.Token()
if err != nil {
t.Errorf("got err = %v; want none", err)
return
}
if tk.RefreshToken != tkr.refreshToken {
t.Errorf("tokenRefresher.refresh_token = %q; want %q", tkr.refreshToken, tk.RefreshToken)
}
}
func TestConfigClientWithToken(t *testing.T) {
tok := &Token{
AccessToken: "abc123",
}
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("Authorization"), fmt.Sprintf("Bearer %s", tok.AccessToken); got != want {
t.Errorf("Authorization header = %q; want %q", got, want)
}
return
}))
defer ts.Close()
conf := newConf(ts.URL)
c := conf.Client(context.Background(), tok)
req, err := http.NewRequest("GET", ts.URL, nil)
if err != nil {
t.Error(err)
}
_, err = c.Do(req)
if err != nil {
t.Error(err)
}
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2015 The Go 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 odnoklassniki provides constants for using OAuth2 to access Odnoklassniki.
package odnoklassniki // import "golang.org/x/oauth2/odnoklassniki"
import (
"golang.org/x/oauth2"
)
// Endpoint is Odnoklassniki's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://www.odnoklassniki.ru/oauth/authorize",
TokenURL: "https://api.odnoklassniki.ru/oauth/token.do",
}
+22
View File
@@ -0,0 +1,22 @@
// Copyright 2015 The Go 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 paypal provides constants for using OAuth2 to access PayPal.
package paypal // import "golang.org/x/oauth2/paypal"
import (
"golang.org/x/oauth2"
)
// Endpoint is PayPal's OAuth 2.0 endpoint in live (production) environment.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize",
TokenURL: "https://api.paypal.com/v1/identity/openidconnect/tokenservice",
}
// SandboxEndpoint is PayPal's OAuth 2.0 endpoint in sandbox (testing) environment.
var SandboxEndpoint = oauth2.Endpoint{
AuthURL: "https://www.sandbox.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize",
TokenURL: "https://api.sandbox.paypal.com/v1/identity/openidconnect/tokenservice",
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2016 The Go 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 slack provides constants for using OAuth2 to access Slack.
package slack // import "golang.org/x/oauth2/slack"
import (
"golang.org/x/oauth2"
)
// Endpoint is Slack's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://slack.com/oauth/authorize",
TokenURL: "https://slack.com/api/oauth.access",
}
+72
View File
@@ -0,0 +1,72 @@
// Copyright 2014 The Go 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 oauth2
import (
"testing"
"time"
)
func TestTokenExtra(t *testing.T) {
type testCase struct {
key string
val interface{}
want interface{}
}
const key = "extra-key"
cases := []testCase{
{key: key, val: "abc", want: "abc"},
{key: key, val: 123, want: 123},
{key: key, val: "", want: ""},
{key: "other-key", val: "def", want: nil},
}
for _, tc := range cases {
extra := make(map[string]interface{})
extra[tc.key] = tc.val
tok := &Token{raw: extra}
if got, want := tok.Extra(key), tc.want; got != want {
t.Errorf("Extra(%q) = %q; want %q", key, got, want)
}
}
}
func TestTokenExpiry(t *testing.T) {
now := time.Now()
cases := []struct {
name string
tok *Token
want bool
}{
{name: "12 seconds", tok: &Token{Expiry: now.Add(12 * time.Second)}, want: false},
{name: "10 seconds", tok: &Token{Expiry: now.Add(expiryDelta)}, want: true},
{name: "-1 hour", tok: &Token{Expiry: now.Add(-1 * time.Hour)}, want: true},
}
for _, tc := range cases {
if got, want := tc.tok.expired(), tc.want; got != want {
t.Errorf("expired (%q) = %v; want %v", tc.name, got, want)
}
}
}
func TestTokenTypeMethod(t *testing.T) {
cases := []struct {
name string
tok *Token
want string
}{
{name: "bearer-mixed_case", tok: &Token{TokenType: "beAREr"}, want: "Bearer"},
{name: "default-bearer", tok: &Token{}, want: "Bearer"},
{name: "basic", tok: &Token{TokenType: "basic"}, want: "Basic"},
{name: "basic-capitalized", tok: &Token{TokenType: "Basic"}, want: "Basic"},
{name: "mac", tok: &Token{TokenType: "mac"}, want: "MAC"},
{name: "mac-caps", tok: &Token{TokenType: "MAC"}, want: "MAC"},
{name: "mac-mixed_case", tok: &Token{TokenType: "mAc"}, want: "MAC"},
}
for _, tc := range cases {
if got, want := tc.tok.Type(), tc.want; got != want {
t.Errorf("TokenType(%q) = %v; want %v", tc.name, got, want)
}
}
}
+108
View File
@@ -0,0 +1,108 @@
package oauth2
import (
"net/http"
"net/http/httptest"
"testing"
"time"
)
type tokenSource struct{ token *Token }
func (t *tokenSource) Token() (*Token, error) {
return t.token, nil
}
func TestTransportNilTokenSource(t *testing.T) {
tr := &Transport{}
server := newMockServer(func(w http.ResponseWriter, r *http.Request) {})
defer server.Close()
client := &http.Client{Transport: tr}
resp, err := client.Get(server.URL)
if err == nil {
t.Errorf("got no errors, want an error with nil token source")
}
if resp != nil {
t.Errorf("Response = %v; want nil", resp)
}
}
func TestTransportTokenSource(t *testing.T) {
ts := &tokenSource{
token: &Token{
AccessToken: "abc",
},
}
tr := &Transport{
Source: ts,
}
server := newMockServer(func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("Authorization"), "Bearer abc"; got != want {
t.Errorf("Authorization header = %q; want %q", got, want)
}
})
defer server.Close()
client := &http.Client{Transport: tr}
res, err := client.Get(server.URL)
if err != nil {
t.Fatal(err)
}
res.Body.Close()
}
// Test for case-sensitive token types, per https://github.com/golang/oauth2/issues/113
func TestTransportTokenSourceTypes(t *testing.T) {
const val = "abc"
tests := []struct {
key string
val string
want string
}{
{key: "bearer", val: val, want: "Bearer abc"},
{key: "mac", val: val, want: "MAC abc"},
{key: "basic", val: val, want: "Basic abc"},
}
for _, tc := range tests {
ts := &tokenSource{
token: &Token{
AccessToken: tc.val,
TokenType: tc.key,
},
}
tr := &Transport{
Source: ts,
}
server := newMockServer(func(w http.ResponseWriter, r *http.Request) {
if got, want := r.Header.Get("Authorization"), tc.want; got != want {
t.Errorf("Authorization header (%q) = %q; want %q", val, got, want)
}
})
defer server.Close()
client := &http.Client{Transport: tr}
res, err := client.Get(server.URL)
if err != nil {
t.Fatal(err)
}
res.Body.Close()
}
}
func TestTokenValidNoAccessToken(t *testing.T) {
token := &Token{}
if token.Valid() {
t.Errorf("got valid with no access token; want invalid")
}
}
func TestExpiredWithExpiry(t *testing.T) {
token := &Token{
Expiry: time.Now().Add(-5 * time.Hour),
}
if token.Valid() {
t.Errorf("got valid with expired token; want invalid")
}
}
func newMockServer(handler func(w http.ResponseWriter, r *http.Request)) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(handler))
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2016 The Go 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 uber provides constants for using OAuth2 to access Uber.
package uber // import "golang.org/x/oauth2/uber"
import (
"golang.org/x/oauth2"
)
// Endpoint is Uber's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://login.uber.com/oauth/v2/authorize",
TokenURL: "https://login.uber.com/oauth/v2/token",
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2015 The Go 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 vk provides constants for using OAuth2 to access VK.com.
package vk // import "golang.org/x/oauth2/vk"
import (
"golang.org/x/oauth2"
)
// Endpoint is VK's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://oauth.vk.com/authorize",
TokenURL: "https://oauth.vk.com/access_token",
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright 2017 The Go 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 yandex provides constants for using OAuth2 to access Yandex APIs.
package yandex // import "golang.org/x/oauth2/yandex"
import (
"golang.org/x/oauth2"
)
// Endpoint is the Yandex OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://oauth.yandex.com/authorize",
TokenURL: "https://oauth.yandex.com/token",
}