Browse Source

migrate gplus to google oauth2 provider (#7885)

* migrate gplus to google oauth2 provider. this still provides support for old gplus connections.

* Update models/oauth2.go

Co-Authored-By: Antoine GIRARD <sapk@users.noreply.github.com>

* make vendor
tags/v1.10.0-rc1
techknowlogick Antoine GIRARD 8 months ago
parent
commit
7a8e299c7c
39 changed files with 3992 additions and 63 deletions
  1. +1
    -1
      go.mod
  2. +1
    -1
      models/oauth2.go
  3. +3
    -3
      modules/auth/oauth2/oauth2.go
  4. BIN
      public/img/auth/google.png
  5. BIN
      public/img/auth/google_plus.png
  6. +1
    -1
      templates/admin/auth/new.tmpl
  7. +526
    -0
      vendor/cloud.google.com/go/compute/metadata/metadata.go
  8. +10
    -0
      vendor/github.com/markbates/goth/providers/google/endpoint.go
  9. +13
    -0
      vendor/github.com/markbates/goth/providers/google/endpoint_legacy.go
  10. +37
    -52
      vendor/github.com/markbates/goth/providers/google/google.go
  11. +4
    -4
      vendor/github.com/markbates/goth/providers/google/session.go
  12. +38
    -0
      vendor/golang.org/x/oauth2/google/appengine.go
  13. +77
    -0
      vendor/golang.org/x/oauth2/google/appengine_gen1.go
  14. +27
    -0
      vendor/golang.org/x/oauth2/google/appengine_gen2_flex.go
  15. +154
    -0
      vendor/golang.org/x/oauth2/google/default.go
  16. +40
    -0
      vendor/golang.org/x/oauth2/google/doc.go
  17. +209
    -0
      vendor/golang.org/x/oauth2/google/google.go
  18. +74
    -0
      vendor/golang.org/x/oauth2/google/jwt.go
  19. +201
    -0
      vendor/golang.org/x/oauth2/google/sdk.go
  20. +182
    -0
      vendor/golang.org/x/oauth2/jws/jws.go
  21. +185
    -0
      vendor/golang.org/x/oauth2/jwt/jwt.go
  22. +20
    -0
      vendor/google.golang.org/appengine/.travis.yml
  23. +90
    -0
      vendor/google.golang.org/appengine/CONTRIBUTING.md
  24. +100
    -0
      vendor/google.golang.org/appengine/README.md
  25. +135
    -0
      vendor/google.golang.org/appengine/appengine.go
  26. +20
    -0
      vendor/google.golang.org/appengine/appengine_vm.go
  27. +46
    -0
      vendor/google.golang.org/appengine/errors.go
  28. +10
    -0
      vendor/google.golang.org/appengine/go.mod
  29. +22
    -0
      vendor/google.golang.org/appengine/go.sum
  30. +142
    -0
      vendor/google.golang.org/appengine/identity.go
  31. +611
    -0
      vendor/google.golang.org/appengine/internal/app_identity/app_identity_service.pb.go
  32. +64
    -0
      vendor/google.golang.org/appengine/internal/app_identity/app_identity_service.proto
  33. +786
    -0
      vendor/google.golang.org/appengine/internal/modules/modules_service.pb.go
  34. +80
    -0
      vendor/google.golang.org/appengine/internal/modules/modules_service.proto
  35. +25
    -0
      vendor/google.golang.org/appengine/namespace.go
  36. +20
    -0
      vendor/google.golang.org/appengine/timeout.go
  37. +18
    -0
      vendor/google.golang.org/appengine/travis_install.sh
  38. +12
    -0
      vendor/google.golang.org/appengine/travis_test.sh
  39. +8
    -1
      vendor/modules.txt

+ 1
- 1
go.mod View File

@@ -68,8 +68,8 @@ require (
github.com/lib/pq v1.2.0
github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
github.com/lunny/levelqueue v0.0.0-20190217115915-02b525a4418e
github.com/markbates/goth v1.56.0
github.com/mailru/easyjson v0.7.0 // indirect
github.com/markbates/goth v1.56.0
github.com/mattn/go-isatty v0.0.7
github.com/mattn/go-oci8 v0.0.0-20190320171441-14ba190cf52d // indirect
github.com/mattn/go-sqlite3 v1.11.0


+ 1
- 1
models/oauth2.go View File

@@ -40,7 +40,7 @@ var OAuth2Providers = map[string]OAuth2Provider{
ProfileURL: oauth2.GetDefaultProfileURL("gitlab"),
},
},
"gplus": {Name: "gplus", DisplayName: "Google+", Image: "/img/auth/google_plus.png"},
"gplus": {Name: "gplus", DisplayName: "Google", Image: "/img/auth/google.png"},
"openidConnect": {Name: "openidConnect", DisplayName: "OpenID Connect", Image: "/img/auth/openid_connect.png"},
"twitter": {Name: "twitter", DisplayName: "Twitter", Image: "/img/auth/twitter.png"},
"discord": {Name: "discord", DisplayName: "Discord", Image: "/img/auth/discord.png"},


+ 3
- 3
modules/auth/oauth2/oauth2.go View File

@@ -22,7 +22,7 @@ import (
"github.com/markbates/goth/providers/gitea"
"github.com/markbates/goth/providers/github"
"github.com/markbates/goth/providers/gitlab"
"github.com/markbates/goth/providers/gplus"
"github.com/markbates/goth/providers/google"
"github.com/markbates/goth/providers/openidConnect"
"github.com/markbates/goth/providers/twitter"
"github.com/satori/go.uuid"
@@ -166,8 +166,8 @@ func createProvider(providerName, providerType, clientID, clientSecret, openIDCo
}
}
provider = gitlab.NewCustomisedURL(clientID, clientSecret, callbackURL, authURL, tokenURL, profileURL, "read_user")
case "gplus":
provider = gplus.New(clientID, clientSecret, callbackURL, "email")
case "gplus": // named gplus due to legacy gplus -> google migration (Google killed Google+). This ensures old connections still work
provider = google.New(clientID, clientSecret, callbackURL)
case "openidConnect":
if provider, err = openidConnect.New(clientID, clientSecret, callbackURL, openIDConnectAutoDiscoveryURL); err != nil {
log.Warn("Failed to create OpenID Connect Provider with name '%s' with url '%s': %v", providerName, openIDConnectAutoDiscoveryURL, err)


BIN
public/img/auth/google.png View File

Before After
Width: 64  |  Height: 64  |  Size: 1.6KB

BIN
public/img/auth/google_plus.png View File

Before After
Width: 64  |  Height: 64  |  Size: 1.2KB

+ 1
- 1
templates/admin/auth/new.tmpl View File

@@ -102,7 +102,7 @@
<span>{{.i18n.Tr "admin.auths.tip.github"}}</span>
<li>GitLab</li>
<span>{{.i18n.Tr "admin.auths.tip.gitlab"}}</span>
<li>Google+</li>
<li>Google</li>
<span>{{.i18n.Tr "admin.auths.tip.google_plus"}}</span>
<li>OpenID Connect</li>
<span>{{.i18n.Tr "admin.auths.tip.openid_connect"}}</span>


+ 526
- 0
vendor/cloud.google.com/go/compute/metadata/metadata.go View File

@@ -0,0 +1,526 @@
// Copyright 2014 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package metadata provides access to Google Compute Engine (GCE)
// metadata and API service accounts.
//
// This package is a wrapper around the GCE metadata service,
// as documented at https://developers.google.com/compute/docs/metadata.
package metadata // import "cloud.google.com/go/compute/metadata"

import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"runtime"
"strings"
"sync"
"time"
)

const (
// metadataIP is the documented metadata server IP address.
metadataIP = "169.254.169.254"

// metadataHostEnv is the environment variable specifying the
// GCE metadata hostname. If empty, the default value of
// metadataIP ("169.254.169.254") is used instead.
// This is variable name is not defined by any spec, as far as
// I know; it was made up for the Go package.
metadataHostEnv = "GCE_METADATA_HOST"

userAgent = "gcloud-golang/0.1"
)

type cachedValue struct {
k string
trim bool
mu sync.Mutex
v string
}

var (
projID = &cachedValue{k: "project/project-id", trim: true}
projNum = &cachedValue{k: "project/numeric-project-id", trim: true}
instID = &cachedValue{k: "instance/id", trim: true}
)

var (
defaultClient = &Client{hc: &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
ResponseHeaderTimeout: 2 * time.Second,
},
}}
subscribeClient = &Client{hc: &http.Client{
Transport: &http.Transport{
Dial: (&net.Dialer{
Timeout: 2 * time.Second,
KeepAlive: 30 * time.Second,
}).Dial,
},
}}
)

// NotDefinedError is returned when requested metadata is not defined.
//
// The underlying string is the suffix after "/computeMetadata/v1/".
//
// This error is not returned if the value is defined to be the empty
// string.
type NotDefinedError string

func (suffix NotDefinedError) Error() string {
return fmt.Sprintf("metadata: GCE metadata %q not defined", string(suffix))
}

func (c *cachedValue) get(cl *Client) (v string, err error) {
defer c.mu.Unlock()
c.mu.Lock()
if c.v != "" {
return c.v, nil
}
if c.trim {
v, err = cl.getTrimmed(c.k)
} else {
v, err = cl.Get(c.k)
}
if err == nil {
c.v = v
}
return
}

var (
onGCEOnce sync.Once
onGCE bool
)

// OnGCE reports whether this process is running on Google Compute Engine.
func OnGCE() bool {
onGCEOnce.Do(initOnGCE)
return onGCE
}

func initOnGCE() {
onGCE = testOnGCE()
}

func testOnGCE() bool {
// The user explicitly said they're on GCE, so trust them.
if os.Getenv(metadataHostEnv) != "" {
return true
}

ctx, cancel := context.WithCancel(context.Background())
defer cancel()

resc := make(chan bool, 2)

// Try two strategies in parallel.
// See https://github.com/googleapis/google-cloud-go/issues/194
go func() {
req, _ := http.NewRequest("GET", "http://"+metadataIP, nil)
req.Header.Set("User-Agent", userAgent)
res, err := defaultClient.hc.Do(req.WithContext(ctx))
if err != nil {
resc <- false
return
}
defer res.Body.Close()
resc <- res.Header.Get("Metadata-Flavor") == "Google"
}()

go func() {
addrs, err := net.LookupHost("metadata.google.internal")
if err != nil || len(addrs) == 0 {
resc <- false
return
}
resc <- strsContains(addrs, metadataIP)
}()

tryHarder := systemInfoSuggestsGCE()
if tryHarder {
res := <-resc
if res {
// The first strategy succeeded, so let's use it.
return true
}
// Wait for either the DNS or metadata server probe to
// contradict the other one and say we are running on
// GCE. Give it a lot of time to do so, since the system
// info already suggests we're running on a GCE BIOS.
timer := time.NewTimer(5 * time.Second)
defer timer.Stop()
select {
case res = <-resc:
return res
case <-timer.C:
// Too slow. Who knows what this system is.
return false
}
}

// There's no hint from the system info that we're running on
// GCE, so use the first probe's result as truth, whether it's
// true or false. The goal here is to optimize for speed for
// users who are NOT running on GCE. We can't assume that
// either a DNS lookup or an HTTP request to a blackholed IP
// address is fast. Worst case this should return when the
// metaClient's Transport.ResponseHeaderTimeout or
// Transport.Dial.Timeout fires (in two seconds).
return <-resc
}

// systemInfoSuggestsGCE reports whether the local system (without
// doing network requests) suggests that we're running on GCE. If this
// returns true, testOnGCE tries a bit harder to reach its metadata
// server.
func systemInfoSuggestsGCE() bool {
if runtime.GOOS != "linux" {
// We don't have any non-Linux clues available, at least yet.
return false
}
slurp, _ := ioutil.ReadFile("/sys/class/dmi/id/product_name")
name := strings.TrimSpace(string(slurp))
return name == "Google" || name == "Google Compute Engine"
}

// Subscribe calls Client.Subscribe on a client designed for subscribing (one with no
// ResponseHeaderTimeout).
func Subscribe(suffix string, fn func(v string, ok bool) error) error {
return subscribeClient.Subscribe(suffix, fn)
}

// Get calls Client.Get on the default client.
func Get(suffix string) (string, error) { return defaultClient.Get(suffix) }

// ProjectID returns the current instance's project ID string.
func ProjectID() (string, error) { return defaultClient.ProjectID() }

// NumericProjectID returns the current instance's numeric project ID.
func NumericProjectID() (string, error) { return defaultClient.NumericProjectID() }

// InternalIP returns the instance's primary internal IP address.
func InternalIP() (string, error) { return defaultClient.InternalIP() }

// ExternalIP returns the instance's primary external (public) IP address.
func ExternalIP() (string, error) { return defaultClient.ExternalIP() }

// Email calls Client.Email on the default client.
func Email(serviceAccount string) (string, error) { return defaultClient.Email(serviceAccount) }

// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func Hostname() (string, error) { return defaultClient.Hostname() }

// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func InstanceTags() ([]string, error) { return defaultClient.InstanceTags() }

// InstanceID returns the current VM's numeric instance ID.
func InstanceID() (string, error) { return defaultClient.InstanceID() }

// InstanceName returns the current VM's instance ID string.
func InstanceName() (string, error) { return defaultClient.InstanceName() }

// Zone returns the current VM's zone, such as "us-central1-b".
func Zone() (string, error) { return defaultClient.Zone() }

// InstanceAttributes calls Client.InstanceAttributes on the default client.
func InstanceAttributes() ([]string, error) { return defaultClient.InstanceAttributes() }

// ProjectAttributes calls Client.ProjectAttributes on the default client.
func ProjectAttributes() ([]string, error) { return defaultClient.ProjectAttributes() }

// InstanceAttributeValue calls Client.InstanceAttributeValue on the default client.
func InstanceAttributeValue(attr string) (string, error) {
return defaultClient.InstanceAttributeValue(attr)
}

// ProjectAttributeValue calls Client.ProjectAttributeValue on the default client.
func ProjectAttributeValue(attr string) (string, error) {
return defaultClient.ProjectAttributeValue(attr)
}

// Scopes calls Client.Scopes on the default client.
func Scopes(serviceAccount string) ([]string, error) { return defaultClient.Scopes(serviceAccount) }

func strsContains(ss []string, s string) bool {
for _, v := range ss {
if v == s {
return true
}
}
return false
}

// A Client provides metadata.
type Client struct {
hc *http.Client
}

// NewClient returns a Client that can be used to fetch metadata. All HTTP requests
// will use the given http.Client instead of the default client.
func NewClient(c *http.Client) *Client {
return &Client{hc: c}
}

// getETag returns a value from the metadata service as well as the associated ETag.
// This func is otherwise equivalent to Get.
func (c *Client) getETag(suffix string) (value, etag string, err error) {
// Using a fixed IP makes it very difficult to spoof the metadata service in
// a container, which is an important use-case for local testing of cloud
// deployments. To enable spoofing of the metadata service, the environment
// variable GCE_METADATA_HOST is first inspected to decide where metadata
// requests shall go.
host := os.Getenv(metadataHostEnv)
if host == "" {
// Using 169.254.169.254 instead of "metadata" here because Go
// binaries built with the "netgo" tag and without cgo won't
// know the search suffix for "metadata" is
// ".google.internal", and this IP address is documented as
// being stable anyway.
host = metadataIP
}
u := "http://" + host + "/computeMetadata/v1/" + suffix
req, _ := http.NewRequest("GET", u, nil)
req.Header.Set("Metadata-Flavor", "Google")
req.Header.Set("User-Agent", userAgent)
res, err := c.hc.Do(req)
if err != nil {
return "", "", err
}
defer res.Body.Close()
if res.StatusCode == http.StatusNotFound {
return "", "", NotDefinedError(suffix)
}
all, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", "", err
}
if res.StatusCode != 200 {
return "", "", &Error{Code: res.StatusCode, Message: string(all)}
}
return string(all), res.Header.Get("Etag"), nil
}

// Get returns a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
//
// If the GCE_METADATA_HOST environment variable is not defined, a default of
// 169.254.169.254 will be used instead.
//
// If the requested metadata is not defined, the returned error will
// be of type NotDefinedError.
func (c *Client) Get(suffix string) (string, error) {
val, _, err := c.getETag(suffix)
return val, err
}

func (c *Client) getTrimmed(suffix string) (s string, err error) {
s, err = c.Get(suffix)
s = strings.TrimSpace(s)
return
}

func (c *Client) lines(suffix string) ([]string, error) {
j, err := c.Get(suffix)
if err != nil {
return nil, err
}
s := strings.Split(strings.TrimSpace(j), "\n")
for i := range s {
s[i] = strings.TrimSpace(s[i])
}
return s, nil
}

// ProjectID returns the current instance's project ID string.
func (c *Client) ProjectID() (string, error) { return projID.get(c) }

// NumericProjectID returns the current instance's numeric project ID.
func (c *Client) NumericProjectID() (string, error) { return projNum.get(c) }

// InstanceID returns the current VM's numeric instance ID.
func (c *Client) InstanceID() (string, error) { return instID.get(c) }

// InternalIP returns the instance's primary internal IP address.
func (c *Client) InternalIP() (string, error) {
return c.getTrimmed("instance/network-interfaces/0/ip")
}

// Email returns the email address associated with the service account.
// The account may be empty or the string "default" to use the instance's
// main account.
func (c *Client) Email(serviceAccount string) (string, error) {
if serviceAccount == "" {
serviceAccount = "default"
}
return c.getTrimmed("instance/service-accounts/" + serviceAccount + "/email")
}

// ExternalIP returns the instance's primary external (public) IP address.
func (c *Client) ExternalIP() (string, error) {
return c.getTrimmed("instance/network-interfaces/0/access-configs/0/external-ip")
}

// Hostname returns the instance's hostname. This will be of the form
// "<instanceID>.c.<projID>.internal".
func (c *Client) Hostname() (string, error) {
return c.getTrimmed("instance/hostname")
}

// InstanceTags returns the list of user-defined instance tags,
// assigned when initially creating a GCE instance.
func (c *Client) InstanceTags() ([]string, error) {
var s []string
j, err := c.Get("instance/tags")
if err != nil {
return nil, err
}
if err := json.NewDecoder(strings.NewReader(j)).Decode(&s); err != nil {
return nil, err
}
return s, nil
}

// InstanceName returns the current VM's instance ID string.
func (c *Client) InstanceName() (string, error) {
host, err := c.Hostname()
if err != nil {
return "", err
}
return strings.Split(host, ".")[0], nil
}

// Zone returns the current VM's zone, such as "us-central1-b".
func (c *Client) Zone() (string, error) {
zone, err := c.getTrimmed("instance/zone")
// zone is of the form "projects/<projNum>/zones/<zoneName>".
if err != nil {
return "", err
}
return zone[strings.LastIndex(zone, "/")+1:], nil
}

// InstanceAttributes returns the list of user-defined attributes,
// assigned when initially creating a GCE VM instance. The value of an
// attribute can be obtained with InstanceAttributeValue.
func (c *Client) InstanceAttributes() ([]string, error) { return c.lines("instance/attributes/") }

// ProjectAttributes returns the list of user-defined attributes
// applying to the project as a whole, not just this VM. The value of
// an attribute can be obtained with ProjectAttributeValue.
func (c *Client) ProjectAttributes() ([]string, error) { return c.lines("project/attributes/") }

// InstanceAttributeValue returns the value of the provided VM
// instance attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// InstanceAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func (c *Client) InstanceAttributeValue(attr string) (string, error) {
return c.Get("instance/attributes/" + attr)
}

// ProjectAttributeValue returns the value of the provided
// project attribute.
//
// If the requested attribute is not defined, the returned error will
// be of type NotDefinedError.
//
// ProjectAttributeValue may return ("", nil) if the attribute was
// defined to be the empty string.
func (c *Client) ProjectAttributeValue(attr string) (string, error) {
return c.Get("project/attributes/" + attr)
}

// Scopes returns the service account scopes for the given account.
// The account may be empty or the string "default" to use the instance's
// main account.
func (c *Client) Scopes(serviceAccount string) ([]string, error) {
if serviceAccount == "" {
serviceAccount = "default"
}
return c.lines("instance/service-accounts/" + serviceAccount + "/scopes")
}

// Subscribe subscribes to a value from the metadata service.
// The suffix is appended to "http://${GCE_METADATA_HOST}/computeMetadata/v1/".
// The suffix may contain query parameters.
//
// Subscribe calls fn with the latest metadata value indicated by the provided
// suffix. If the metadata value is deleted, fn is called with the empty string
// and ok false. Subscribe blocks until fn returns a non-nil error or the value
// is deleted. Subscribe returns the error value returned from the last call to
// fn, which may be nil when ok == false.
func (c *Client) Subscribe(suffix string, fn func(v string, ok bool) error) error {
const failedSubscribeSleep = time.Second * 5

// First check to see if the metadata value exists at all.
val, lastETag, err := c.getETag(suffix)
if err != nil {
return err
}

if err := fn(val, true); err != nil {
return err
}

ok := true
if strings.ContainsRune(suffix, '?') {
suffix += "&wait_for_change=true&last_etag="
} else {
suffix += "?wait_for_change=true&last_etag="
}
for {
val, etag, err := c.getETag(suffix + url.QueryEscape(lastETag))
if err != nil {
if _, deleted := err.(NotDefinedError); !deleted {
time.Sleep(failedSubscribeSleep)
continue // Retry on other errors.
}
ok = false
}
lastETag = etag

if err := fn(val, ok); err != nil || !ok {
return err
}
}
}

// Error contains an error response from the server.
type Error struct {
// Code is the HTTP response status code.
Code int
// Message is the server response message.
Message string
}

func (e *Error) Error() string {
return fmt.Sprintf("compute: Received %d `%s`", e.Code, e.Message)
}

+ 10
- 0
vendor/github.com/markbates/goth/providers/google/endpoint.go View File

@@ -0,0 +1,10 @@
// +build go1.9

package google

import (
goog "golang.org/x/oauth2/google"
)

// Endpoint is Google's OAuth 2.0 endpoint.
var Endpoint = goog.Endpoint

+ 13
- 0
vendor/github.com/markbates/goth/providers/google/endpoint_legacy.go View File

@@ -0,0 +1,13 @@
// +build !go1.9

package google

import (
"golang.org/x/oauth2"
)

// Endpoint is Google's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://accounts.google.com/o/oauth2/auth",
TokenURL: "https://accounts.google.com/o/oauth2/token",
}

vendor/github.com/markbates/goth/providers/gplus/gplus.go → vendor/github.com/markbates/goth/providers/google/google.go View File

@@ -1,42 +1,36 @@
// Package gplus implements the OAuth2 protocol for authenticating users through Google+.
// This package can be used as a reference implementation of an OAuth2 provider for Goth.
package gplus
// Package google implements the OAuth2 protocol for authenticating users
// through Google.
package google

import (
"bytes"
"encoding/json"
"io"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"

"fmt"
"github.com/markbates/goth"
"golang.org/x/oauth2"
)

const (
authURL string = "https://accounts.google.com/o/oauth2/auth?access_type=offline"
tokenURL string = "https://accounts.google.com/o/oauth2/token"
endpointProfile string = "https://www.googleapis.com/oauth2/v2/userinfo"
)
const endpointProfile string = "https://www.googleapis.com/oauth2/v2/userinfo"

// New creates a new Google+ provider, and sets up important connection details.
// You should always call `gplus.New` to get a new Provider. Never try to create
// New creates a new Google provider, and sets up important connection details.
// You should always call `google.New` to get a new Provider. Never try to create
// one manually.
func New(clientKey, secret, callbackURL string, scopes ...string) *Provider {
p := &Provider{
ClientKey: clientKey,
Secret: secret,
CallbackURL: callbackURL,
providerName: "gplus",
providerName: "google",
}
p.config = newConfig(p, scopes)
return p
}

// Provider is the implementation of `goth.Provider` for accessing Google+.
// Provider is the implementation of `goth.Provider` for accessing Google.
type Provider struct {
ClientKey string
Secret string
@@ -57,14 +51,15 @@ func (p *Provider) SetName(name string) {
p.providerName = name
}

// Client returns an HTTP client to be used in all fetch operations.
func (p *Provider) Client() *http.Client {
return goth.HTTPClientWithFallBack(p.HTTPClient)
}

// Debug is a no-op for the gplus package.
// Debug is a no-op for the google package.
func (p *Provider) Debug(debug bool) {}

// BeginAuth asks Google+ for an authentication end-point.
// BeginAuth asks Google for an authentication endpoint.
func (p *Provider) BeginAuth(state string) (goth.Session, error) {
var opts []oauth2.AuthCodeOption
if p.prompt != nil {
@@ -77,7 +72,17 @@ func (p *Provider) BeginAuth(state string) (goth.Session, error) {
return session, nil
}

// FetchUser will go to Google+ and access basic information about the user.
type googleUser struct {
ID string `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
FirstName string `json:"given_name"`
LastName string `json:"family_name"`
Link string `json:"link"`
Picture string `json:"picture"`
}

// FetchUser will go to Google and access basic information about the user.
func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
sess := session.(*Session)
user := goth.User{
@@ -88,7 +93,7 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
}

if user.AccessToken == "" {
// data is not yet retrieved since accessToken is still empty
// Data is not yet retrieved, since accessToken is still empty.
return user, fmt.Errorf("%s cannot get user information without accessToken", p.providerName)
}

@@ -102,47 +107,30 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, response.StatusCode)
}

bits, err := ioutil.ReadAll(response.Body)
responseBytes, err := ioutil.ReadAll(response.Body)
if err != nil {
return user, err
}

err = json.NewDecoder(bytes.NewReader(bits)).Decode(&user.RawData)
if err != nil {
var u googleUser
if err := json.Unmarshal(responseBytes, &u); err != nil {
return user, err
}

err = userFromReader(bytes.NewReader(bits), &user)
return user, err
}

func userFromReader(reader io.Reader, user *goth.User) error {
u := struct {
ID string `json:"id"`
Email string `json:"email"`
Name string `json:"name"`
FirstName string `json:"given_name"`
LastName string `json:"family_name"`
Link string `json:"link"`
Picture string `json:"picture"`
}{}

err := json.NewDecoder(reader).Decode(&u)
if err != nil {
return err
}

// Extract the user data we got from Google into our goth.User.
user.Name = u.Name
user.FirstName = u.FirstName
user.LastName = u.LastName
user.NickName = u.Name
user.Email = u.Email
//user.Description = u.Bio
user.AvatarURL = u.Picture
user.UserID = u.ID
//user.Location = u.Location.Name
// Google provides other useful fields such as 'hd'; get them from RawData
if err := json.Unmarshal(responseBytes, &user.RawData); err != nil {
return user, err
}

return err
return user, nil
}

func newConfig(provider *Provider, scopes []string) *oauth2.Config {
@@ -150,11 +138,8 @@ func newConfig(provider *Provider, scopes []string) *oauth2.Config {
ClientID: provider.ClientKey,
ClientSecret: provider.Secret,
RedirectURL: provider.CallbackURL,
Endpoint: oauth2.Endpoint{
AuthURL: authURL,
TokenURL: tokenURL,
},
Scopes: []string{},
Endpoint: Endpoint,
Scopes: []string{},
}

if len(scopes) > 0 {
@@ -162,7 +147,7 @@ func newConfig(provider *Provider, scopes []string) *oauth2.Config {
c.Scopes = append(c.Scopes, scope)
}
} else {
c.Scopes = []string{"profile", "email", "openid"}
c.Scopes = []string{"email"}
}
return c
}
@@ -183,7 +168,7 @@ func (p *Provider) RefreshToken(refreshToken string) (*oauth2.Token, error) {
return newToken, err
}

// SetPrompt sets the prompt values for the GPlus OAuth call. Use this to
// SetPrompt sets the prompt values for the google OAuth call. Use this to
// force users to choose and account every time by passing "select_account",
// for example.
// See https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters

vendor/github.com/markbates/goth/providers/gplus/session.go → vendor/github.com/markbates/goth/providers/google/session.go View File

@@ -1,4 +1,4 @@
package gplus
package google

import (
"encoding/json"
@@ -9,7 +9,7 @@ import (
"github.com/markbates/goth"
)

// Session stores data during the auth process with Google+.
// Session stores data during the auth process with Google.
type Session struct {
AuthURL string
AccessToken string
@@ -17,7 +17,7 @@ type Session struct {
ExpiresAt time.Time
}

// GetAuthURL will return the URL set by calling the `BeginAuth` function on the Google+ provider.
// GetAuthURL will return the URL set by calling the `BeginAuth` function on the Google provider.
func (s Session) GetAuthURL() (string, error) {
if s.AuthURL == "" {
return "", errors.New(goth.NoAuthUrlErrorMessage)
@@ -25,7 +25,7 @@ func (s Session) GetAuthURL() (string, error) {
return s.AuthURL, nil
}

// Authorize the session with Google+ and return the access token to be stored for future use.
// Authorize the session with Google and return the access token to be stored for future use.
func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string, error) {
p := provider.(*Provider)
token, err := p.config.Exchange(goth.ContextForClient(p.Client()), params.Get("code"))

+ 38
- 0
vendor/golang.org/x/oauth2/google/appengine.go View File

@@ -0,0 +1,38 @@
// 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 google

import (
"context"
"time"

"golang.org/x/oauth2"
)

// Set at init time by appengine_gen1.go. If nil, we're not on App Engine standard first generation (<= Go 1.9) or App Engine flexible.
var appengineTokenFunc func(c context.Context, scopes ...string) (token string, expiry time.Time, err error)

// Set at init time by appengine_gen1.go. If nil, we're not on App Engine standard first generation (<= Go 1.9) or App Engine flexible.
var appengineAppIDFunc func(c context.Context) string

// AppEngineTokenSource returns a token source that fetches tokens from either
// the current application's service account or from the metadata server,
// depending on the App Engine environment. See below for environment-specific
// details. If you are implementing a 3-legged OAuth 2.0 flow on App Engine that
// involves user accounts, see oauth2.Config instead.
//
// First generation App Engine runtimes (<= Go 1.9):
// AppEngineTokenSource returns a token source that fetches tokens issued to the
// current App Engine application's service account. The provided context must have
// come from appengine.NewContext.
//
// Second generation App Engine runtimes (>= Go 1.11) and App Engine flexible:
// AppEngineTokenSource is DEPRECATED on second generation runtimes and on the
// flexible environment. It delegates to ComputeTokenSource, and the provided
// context and scopes are not used. Please use DefaultTokenSource (or ComputeTokenSource,
// which DefaultTokenSource will use in this case) instead.
func AppEngineTokenSource(ctx context.Context, scope ...string) oauth2.TokenSource {
return appEngineTokenSource(ctx, scope...)
}

+ 77
- 0
vendor/golang.org/x/oauth2/google/appengine_gen1.go View File

@@ -0,0 +1,77 @@
// Copyright 2018 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 appengine

// This file applies to App Engine first generation runtimes (<= Go 1.9).

package google

import (
"context"
"sort"
"strings"
"sync"

"golang.org/x/oauth2"
"google.golang.org/appengine"
)

func init() {
appengineTokenFunc = appengine.AccessToken
appengineAppIDFunc = appengine.AppID
}

// See comment on AppEngineTokenSource in appengine.go.
func appEngineTokenSource(ctx context.Context, scope ...string) oauth2.TokenSource {
scopes := append([]string{}, scope...)
sort.Strings(scopes)
return &gaeTokenSource{
ctx: ctx,
scopes: scopes,
key: strings.Join(scopes, " "),
}
}

// aeTokens helps the fetched tokens to be reused until their expiration.
var (
aeTokensMu sync.Mutex
aeTokens = make(map[string]*tokenLock) // key is space-separated scopes
)

type tokenLock struct {
mu sync.Mutex // guards t; held while fetching or updating t
t *oauth2.Token
}

type gaeTokenSource struct {
ctx context.Context
scopes []string
key string // to aeTokens map; space-separated scopes
}

func (ts *gaeTokenSource) Token() (*oauth2.Token, error) {
aeTokensMu.Lock()
tok, ok := aeTokens[ts.key]
if !ok {
tok = &tokenLock{}
aeTokens[ts.key] = tok
}
aeTokensMu.Unlock()

tok.mu.Lock()
defer tok.mu.Unlock()
if tok.t.Valid() {
return tok.t, nil
}
access, exp, err := appengineTokenFunc(ts.ctx, ts.scopes...)
if err != nil {
return nil, err
}
tok.t = &oauth2.Token{
AccessToken: access,
Expiry: exp,
}
return tok.t, nil
}

+ 27
- 0
vendor/golang.org/x/oauth2/google/appengine_gen2_flex.go View File

@@ -0,0 +1,27 @@
// Copyright 2018 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 !appengine

// This file applies to App Engine second generation runtimes (>= Go 1.11) and App Engine flexible.

package google

import (
"context"
"log"
"sync"

"golang.org/x/oauth2"
)

var logOnce sync.Once // only spam about deprecation once

// See comment on AppEngineTokenSource in appengine.go.
func appEngineTokenSource(ctx context.Context, scope ...string) oauth2.TokenSource {
logOnce.Do(func() {
log.Print("google: AppEngineTokenSource is deprecated on App Engine standard second generation runtimes (>= Go 1.11) and App Engine flexible. Please use DefaultTokenSource or ComputeTokenSource.")
})
return ComputeTokenSource("")
}

+ 154
- 0
vendor/golang.org/x/oauth2/google/default.go View File

@@ -0,0 +1,154 @@
// 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 (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"os"
"path/filepath"
"runtime"

"cloud.google.com/go/compute/metadata"
"golang.org/x/oauth2"
)

// Credentials holds Google credentials, including "Application Default Credentials".
// For more details, see:
// https://developers.google.com/accounts/docs/application-default-credentials
type Credentials struct {
ProjectID string // may be empty
TokenSource oauth2.TokenSource

// JSON contains the raw bytes from a JSON credentials file.
// This field may be nil if authentication is provided by the
// environment and not with a credentials file, e.g. when code is
// running on Google Cloud Platform.
JSON []byte
}

// DefaultCredentials is the old name of Credentials.
//
// Deprecated: use Credentials instead.
type DefaultCredentials = Credentials

// DefaultClient returns an HTTP Client that uses the
// DefaultTokenSource to obtain authentication credentials.
func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) {
ts, err := DefaultTokenSource(ctx, scope...)
if err != nil {
return nil, err
}
return oauth2.NewClient(ctx, ts), nil
}

// DefaultTokenSource returns the token source for
// "Application Default Credentials".
// It is a shortcut for FindDefaultCredentials(ctx, scope).TokenSource.
func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) {
creds, err := FindDefaultCredentials(ctx, scope...)
if err != nil {
return nil, err
}
return creds.TokenSource, nil
}

// FindDefaultCredentials searches for "Application Default Credentials".
//
// It looks for credentials in the following places,
// preferring the first location found:
//
// 1. A JSON file whose path is specified by the
// GOOGLE_APPLICATION_CREDENTIALS environment variable.
// 2. A JSON file in a location known to the gcloud command-line tool.
// On Windows, this is %APPDATA%/gcloud/application_default_credentials.json.
// On other systems, $HOME/.config/gcloud/application_default_credentials.json.
// 3. On Google App Engine standard first generation runtimes (<= Go 1.9) it uses
// the appengine.AccessToken function.
// 4. On Google Compute Engine, Google App Engine standard second generation runtimes
// (>= Go 1.11), and Google App Engine flexible environment, it fetches
// credentials from the metadata server.
func FindDefaultCredentials(ctx context.Context, scopes ...string) (*Credentials, error) {
// First, try the environment variable.
const envVar = "GOOGLE_APPLICATION_CREDENTIALS"
if filename := os.Getenv(envVar); filename != "" {
creds, err := readCredentialsFile(ctx, filename, scopes)
if err != nil {
return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err)
}
return creds, nil
}

// Second, try a well-known file.
filename := wellKnownFile()
if creds, err := readCredentialsFile(ctx, filename, scopes); err == nil {
return creds, nil
} else if !os.IsNotExist(err) {
return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err)
}

// Third, if we're on a Google App Engine standard first generation runtime (<= Go 1.9)
// use those credentials. App Engine standard second generation runtimes (>= Go 1.11)
// and App Engine flexible use ComputeTokenSource and the metadata server.
if appengineTokenFunc != nil {
return &DefaultCredentials{
ProjectID: appengineAppIDFunc(ctx),
TokenSource: AppEngineTokenSource(ctx, scopes...),
}, nil
}

// Fourth, if we're on Google Compute Engine, an App Engine standard second generation runtime,
// or App Engine flexible, use the metadata server.
if metadata.OnGCE() {
id, _ := metadata.ProjectID()
return &DefaultCredentials{
ProjectID: id,
TokenSource: ComputeTokenSource("", scopes...),
}, nil
}

// None are found; return helpful error.
const url = "https://developers.google.com/accounts/docs/application-default-credentials"
return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url)
}

// CredentialsFromJSON obtains Google credentials from a JSON value. The JSON can
// represent either a Google Developers Console client_credentials.json file (as in
// ConfigFromJSON) or a Google Developers service account key file (as in
// JWTConfigFromJSON).
func CredentialsFromJSON(ctx context.Context, jsonData []byte, scopes ...string) (*Credentials, error) {
var f credentialsFile
if err := json.Unmarshal(jsonData, &f); err != nil {
return nil, err
}
ts, err := f.tokenSource(ctx, append([]string(nil), scopes...))
if err != nil {
return nil, err
}
return &DefaultCredentials{
ProjectID: f.ProjectID,
TokenSource: ts,
JSON: jsonData,
}, nil
}

func wellKnownFile() string {
const f = "application_default_credentials.json"
if runtime.GOOS == "windows" {
return filepath.Join(os.Getenv("APPDATA"), "gcloud", f)
}
return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f)
}

func readCredentialsFile(ctx context.Context, filename string, scopes []string) (*DefaultCredentials, error) {
b, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return CredentialsFromJSON(ctx, b, scopes...)
}

+ 40
- 0
vendor/golang.org/x/oauth2/google/doc.go View File

@@ -0,0 +1,40 @@
// Copyright 2018 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 provides support for making OAuth2 authorized and authenticated
// HTTP requests to Google APIs. It supports the Web server flow, client-side
// credentials, service accounts, Google Compute Engine service accounts, and Google
// App Engine service accounts.
//
// A brief overview of the package follows. For more information, please read
// https://developers.google.com/accounts/docs/OAuth2
// and
// https://developers.google.com/accounts/docs/application-default-credentials.
//
// OAuth2 Configs
//
// Two functions in this package return golang.org/x/oauth2.Config values from Google credential
// data. Google supports two JSON formats for OAuth2 credentials: one is handled by ConfigFromJSON,
// the other by JWTConfigFromJSON. The returned Config can be used to obtain a TokenSource or
// create an http.Client.
//
//
// Credentials
//
// The Credentials type represents Google credentials, including Application Default
// Credentials.
//
// Use FindDefaultCredentials to obtain Application Default Credentials.
// FindDefaultCredentials looks in some well-known places for a credentials file, and
// will call AppEngineTokenSource or ComputeTokenSource as needed.
//
// DefaultClient and DefaultTokenSource are convenience methods. They first call FindDefaultCredentials,
// then use the credentials to construct an http.Client or an oauth2.TokenSource.
//
// Use CredentialsFromJSON to obtain credentials from either of the two JSON formats
// described in OAuth2 Configs, above. The TokenSource in the returned value is the
// same as the one obtained from the oauth2.Config returned from ConfigFromJSON or
// JWTConfigFromJSON, but the Credentials may contain additional information
// that is useful is some circumstances.
package google // import "golang.org/x/oauth2/google"

+ 209
- 0
vendor/golang.org/x/oauth2/google/google.go View File

@@ -0,0 +1,209 @@
// 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 google

import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
"strings"
"time"

"cloud.google.com/go/compute/metadata"
"golang.org/x/oauth2"
"golang.org/x/oauth2/jwt"
)

// Endpoint is Google's OAuth 2.0 endpoint.
var Endpoint = oauth2.Endpoint{
AuthURL: "https://accounts.google.com/o/oauth2/auth",
TokenURL: "https://oauth2.googleapis.com/token",
AuthStyle: oauth2.AuthStyleInParams,
}

// JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow.
const JWTTokenURL = "https://oauth2.googleapis.com/token"

// ConfigFromJSON uses a Google Developers Console client_credentials.json
// file to construct a config.
// client_credentials.json can be downloaded from
// https://console.developers.google.com, under "Credentials". Download the Web
// application credentials in the JSON format and provide the contents of the
// file as jsonKey.
func ConfigFromJSON(jsonKey []byte, scope ...string) (*oauth2.Config, error) {
type cred struct {
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
RedirectURIs []string `json:"redirect_uris"`
AuthURI string `json:"auth_uri"`
TokenURI string `json:"token_uri"`
}
var j struct {
Web *cred `json:"web"`
Installed *cred `json:"installed"`
}
if err := json.Unmarshal(jsonKey, &j); err != nil {
return nil, err
}
var c *cred
switch {
case j.Web != nil:
c = j.Web
case j.Installed != nil:
c = j.Installed
default:
return nil, fmt.Errorf("oauth2/google: no credentials found")
}
if len(c.RedirectURIs) < 1 {
return nil, errors.New("oauth2/google: missing redirect URL in the client_credentials.json")
}
return &oauth2.Config{
ClientID: c.ClientID,
ClientSecret: c.ClientSecret,
RedirectURL: c.RedirectURIs[0],
Scopes: scope,
Endpoint: oauth2.Endpoint{
AuthURL: c.AuthURI,
TokenURL: c.TokenURI,
},
}, nil
}

// JWTConfigFromJSON uses a Google Developers service account JSON key file to read
// the credentials that authorize and authenticate the requests.
// Create a service account on "Credentials" for your project at
// https://console.developers.google.com to download a JSON key file.
func JWTConfigFromJSON(jsonKey []byte, scope ...string) (*jwt.Config, error) {
var f credentialsFile
if err := json.Unmarshal(jsonKey, &f); err != nil {
return nil, err
}
if f.Type != serviceAccountKey {
return nil, fmt.Errorf("google: read JWT from JSON credentials: 'type' field is %q (expected %q)", f.Type, serviceAccountKey)
}
scope = append([]string(nil), scope...) // copy
return f.jwtConfig(scope), nil
}

// JSON key file types.
const (
serviceAccountKey = "service_account"
userCredentialsKey = "authorized_user"
)

// credentialsFile is the unmarshalled representation of a credentials file.
type credentialsFile struct {
Type string `json:"type"` // serviceAccountKey or userCredentialsKey

// Service Account fields
ClientEmail string `json:"client_email"`
PrivateKeyID string `json:"private_key_id"`
PrivateKey string `json:"private_key"`
TokenURL string `json:"token_uri"`
ProjectID string `json:"project_id"`

// User Credential fields
// (These typically come from gcloud auth.)
ClientSecret string `json:"client_secret"`
ClientID string `json:"client_id"`
RefreshToken string `json:"refresh_token"`
}

func (f *credentialsFile) jwtConfig(scopes []string) *jwt.Config {
cfg := &jwt.Config{
Email: f.ClientEmail,
PrivateKey: []byte(f.PrivateKey),
PrivateKeyID: f.PrivateKeyID,
Scopes: scopes,
TokenURL: f.TokenURL,
}
if cfg.TokenURL == "" {
cfg.TokenURL = JWTTokenURL
}
return cfg
}

func (f *credentialsFile) tokenSource(ctx context.Context, scopes []string) (oauth2.TokenSource, error) {
switch f.Type {
case serviceAccountKey:
cfg := f.jwtConfig(scopes)
return cfg.TokenSource(ctx), nil
case userCredentialsKey:
cfg := &oauth2.Config{
ClientID: f.ClientID,
ClientSecret: f.ClientSecret,
Scopes: scopes,
Endpoint: Endpoint,
}
tok := &oauth2.Token{RefreshToken: f.RefreshToken}
return cfg.TokenSource(ctx, tok), nil
case "":
return nil, errors.New("missing 'type' field in credentials")
default:
return nil, fmt.Errorf("unknown credential type: %q", f.Type)
}
}

// ComputeTokenSource returns a token source that fetches access tokens
// from Google Compute Engine (GCE)'s metadata server. It's only valid to use
// this token source if your program is running on a GCE instance.
// If no account is specified, "default" is used.
// If no scopes are specified, a set of default scopes are automatically granted.
// Further information about retrieving access tokens from the GCE metadata
// server can be found at https://cloud.google.com/compute/docs/authentication.
func ComputeTokenSource(account string, scope ...string) oauth2.TokenSource {
return oauth2.ReuseTokenSource(nil, computeSource{account: account, scopes: scope})
}

type computeSource struct {
account string
scopes []string
}

func (cs computeSource) Token() (*oauth2.Token, error) {
if !metadata.OnGCE() {
return nil, errors.New("oauth2/google: can't get a token from the metadata service; not running on GCE")
}
acct := cs.account
if acct == "" {
acct = "default"
}
tokenURI := "instance/service-accounts/" + acct + "/token"
if len(cs.scopes) > 0 {
v := url.Values{}
v.Set("scopes", strings.Join(cs.scopes, ","))
tokenURI = tokenURI + "?" + v.Encode()
}
tokenJSON, err := metadata.Get(tokenURI)
if err != nil {
return nil, err
}
var res struct {
AccessToken string `json:"access_token"`
ExpiresInSec int `json:"expires_in"`
TokenType string `json:"token_type"`
}
err = json.NewDecoder(strings.NewReader(tokenJSON)).Decode(&res)
if err != nil {
return nil, fmt.Errorf("oauth2/google: invalid token JSON from metadata: %v", err)
}
if res.ExpiresInSec == 0 || res.AccessToken == "" {
return nil, fmt.Errorf("oauth2/google: incomplete token received from metadata")
}
tok := &oauth2.Token{
AccessToken: res.AccessToken,
TokenType: res.TokenType,
Expiry: time.Now().Add(time.Duration(res.ExpiresInSec) * time.Second),
}
// NOTE(cbro): add hidden metadata about where the token is from.
// This is needed for detection by client libraries to know that credentials come from the metadata server.
// This may be removed in a future version of this library.
return tok.WithExtra(map[string]interface{}{
"oauth2.google.tokenSource": "compute-metadata",
"oauth2.google.serviceAccount": acct,
}), nil
}

+ 74
- 0
vendor/golang.org/x/oauth2/google/jwt.go View File

@@ -0,0 +1,74 @@
// 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 (
"crypto/rsa"
"fmt"
"time"

"golang.org/x/oauth2"
"golang.org/x/oauth2/internal"
"golang.org/x/oauth2/jws"
)

// JWTAccessTokenSourceFromJSON uses a Google Developers service account JSON
// key file to read the credentials that authorize and authenticate the
// requests, and returns a TokenSource that does not use any OAuth2 flow but
// instead creates a JWT and sends that as the access token.
// The audience is typically a URL that specifies the scope of the credentials.
//
// Note that this is not a standard OAuth flow, but rather an
// optimization supported by a few Google services.
// Unless you know otherwise, you should use JWTConfigFromJSON instead.
func JWTAccessTokenSourceFromJSON(jsonKey []byte, audience string) (oauth2.TokenSource, error) {
cfg, err := JWTConfigFromJSON(jsonKey)
if err != nil {
return nil, fmt.Errorf("google: could not parse JSON key: %v", err)
}
pk, err := internal.ParseKey(cfg.PrivateKey)
if err != nil {
return nil, fmt.Errorf("google: could not parse key: %v", err)
}
ts := &jwtAccessTokenSource{
email: cfg.Email,
audience: audience,
pk: pk,
pkID: cfg.PrivateKeyID,
}
tok, err := ts.Token()
if err != nil {
return nil, err
}
return oauth2.ReuseTokenSource(tok, ts), nil
}

type jwtAccessTokenSource struct {
email, audience string
pk *rsa.PrivateKey
pkID string
}

func (ts *jwtAccessTokenSource) Token() (*oauth2.Token, error) {
iat := time.Now()
exp := iat.Add(time.Hour)
cs := &jws.ClaimSet{
Iss: ts.email,
Sub: ts.email,
Aud: ts.audience,
Iat: iat.Unix(),
Exp: exp.Unix(),
}
hdr := &jws.Header{
Algorithm: "RS256",
Typ: "JWT",
KeyID: string(ts.pkID),
}
msg, err := jws.Encode(hdr, cs, ts.pk)
if err != nil {
return nil, fmt.Errorf("google: could not encode JWT: %v", err)
}
return &oauth2.Token{AccessToken: msg, TokenType: "Bearer", Expiry: exp}, nil
}

+ 201
- 0
vendor/golang.org/x/oauth2/google/sdk.go View File

@@ -0,0 +1,201 @@
// 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 (
"bufio"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"os"
"os/user"
"path/filepath"
"runtime"
"strings"
"time"

"golang.org/x/oauth2"
)

type sdkCredentials struct {
Data []struct {
Credential struct {
ClientID string `json:"client_id"`
ClientSecret string `json:"client_secret"`
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
TokenExpiry *time.Time `json:"token_expiry"`
} `json:"credential"`
Key struct {
Account string `json:"account"`
Scope string `json:"scope"`
} `json:"key"`
}
}

// An SDKConfig provides access to tokens from an account already
// authorized via the Google Cloud SDK.
type SDKConfig struct {
conf oauth2.Config
initialToken *oauth2.Token
}

// NewSDKConfig creates an SDKConfig for the given Google Cloud SDK
// account. If account is empty, the account currently active in
// Google Cloud SDK properties is used.
// Google Cloud SDK credentials must be created by running `gcloud auth`
// before using this function.
// The Google Cloud SDK is available at https://cloud.google.com/sdk/.
func NewSDKConfig(account string) (*SDKConfig, error) {
configPath, err := sdkConfigPath()
if err != nil {
return nil, fmt.Errorf("oauth2/google: error getting SDK config path: %v", err)
}
credentialsPath := filepath.Join(configPath, "credentials")
f, err := os.Open(credentialsPath)
if err != nil {
return nil, fmt.Errorf("oauth2/google: failed to load SDK credentials: %v", err)
}
defer f.Close()

var c sdkCredentials
if err := json.NewDecoder(f).Decode(&c); err != nil {
return nil, fmt.Errorf("oauth2/google: failed to decode SDK credentials from %q: %v", credentialsPath, err)
}
if len(c.Data) == 0 {
return nil, fmt.Errorf("oauth2/google: no credentials found in %q, run `gcloud auth login` to create one", credentialsPath)
}
if account == "" {
propertiesPath := filepath.Join(configPath, "properties")
f, err := os.Open(propertiesPath)
if err != nil {
return nil, fmt.Errorf("oauth2/google: failed to load SDK properties: %v", err)
}
defer f.Close()
ini, err := parseINI(f)
if err != nil {
return nil, fmt.Errorf("oauth2/google: failed to parse SDK properties %q: %v", propertiesPath, err)
}
core, ok := ini["core"]
if !ok {
return nil, fmt.Errorf("oauth2/google: failed to find [core] section in %v", ini)
}
active, ok := core["account"]
if !ok {
return nil, fmt.Errorf("oauth2/google: failed to find %q attribute in %v", "account", core)
}
account = active
}

for _, d := range c.Data {
if account == "" || d.Key.Account == account {
if d.Credential.AccessToken == "" && d.Credential.RefreshToken == "" {
return nil, fmt.Errorf("oauth2/google: no token available for account %q", account)
}
var expiry time.Time
if d.Credential.TokenExpiry != nil {
expiry = *d.Credential.TokenExpiry
}
return &SDKConfig{
conf: oauth2.Config{
ClientID: d.Credential.ClientID,
ClientSecret: d.Credential.ClientSecret,
Scopes: strings.Split(d.Key.Scope, " "),
Endpoint: Endpoint,
RedirectURL: "oob",
},
initialToken: &oauth2.Token{
AccessToken: d.Credential.AccessToken,
RefreshToken: d.Credential.RefreshToken,
Expiry: expiry,
},
}, nil
}
}
return nil, fmt.Errorf("oauth2/google: no such credentials for account %q", account)
}

// Client returns an HTTP client using Google Cloud SDK credentials to
// authorize requests. The token will auto-refresh as necessary. The
// underlying http.RoundTripper will be obtained using the provided
// context. The returned client and its Transport should not be
// modified.
func (c *SDKConfig) Client(ctx context.Context) *http.Client {
return &http.Client{
Transport: &oauth2.Transport{
Source: c.TokenSource(ctx),
},
}
}

// TokenSource returns an oauth2.TokenSource that retrieve tokens from
// Google Cloud SDK credentials using the provided context.
// It will returns the current access token stored in the credentials,
// and refresh it when it expires, but it won't update the credentials
// with the new access token.
func (c *SDKConfig) TokenSource(ctx context.Context) oauth2.TokenSource {
return c.conf.TokenSource(ctx, c.initialToken)
}

// Scopes are the OAuth 2.0 scopes the current account is authorized for.
func (c *SDKConfig) Scopes() []string {
return c.conf.Scopes
}

func parseINI(ini io.Reader) (map[string]map[string]string, error) {
result := map[string]map[string]string{
"": {}, // root section
}
scanner := bufio.NewScanner(ini)
currentSection := ""
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if strings.HasPrefix(line, ";") {
// comment.
continue
}
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
currentSection = strings.TrimSpace(line[1 : len(line)-1])
result[currentSection] = map[string]string{}
continue
}
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 && parts[0] != "" {
result[currentSection][strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
}
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("error scanning ini: %v", err)
}
return result, nil
}

// sdkConfigPath tries to guess where the gcloud config is located.
// It can be overridden during tests.
var sdkConfigPath = func() (string, error) {
if runtime.GOOS == "windows" {
return filepath.Join(os.Getenv("APPDATA"), "gcloud"), nil
}
homeDir := guessUnixHomeDir()
if homeDir == "" {
return "", errors.New("unable to get current user home directory: os/user lookup failed; $HOME is empty")
}
return filepath.Join(homeDir, ".config", "gcloud"), nil
}

func guessUnixHomeDir() string {
// Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470
if v := os.Getenv("HOME"); v != "" {
return v
}
// Else, fall back to user.Current:
if u, err := user.Current(); err == nil {
return u.HomeDir
}
return ""
}

+ 182
- 0
vendor/golang.org/x/oauth2/jws/jws.go View File

@@ -0,0 +1,182 @@
// 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 jws provides a partial implementation
// of JSON Web Signature encoding and decoding.
// It exists to support the golang.org/x/oauth2 package.
//
// See RFC 7515.
//
// Deprecated: this package is not intended for public use and might be
// 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 // import "golang.org/x/oauth2/jws"

import (
"bytes"
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"strings"
"time"
)

// ClaimSet contains information about the JWT signature including the
// permissions being requested (scopes), the target of the token, the issuer,
// the time the token was issued, and the lifetime of the token.
type ClaimSet struct {
Iss string `json:"iss"` // email address of the client_id of the application making the access token request
Scope string `json:"scope,omitempty"` // space-delimited list of the permissions the application requests
Aud string `json:"aud"` // descriptor of the intended target of the assertion (Optional).
Exp int64 `json:"exp"` // the expiration time of the assertion (seconds since Unix epoch)
Iat int64 `json:"iat"` // the time the assertion was issued (seconds since Unix epoch)
Typ string `json:"typ,omitempty"` // token type (Optional).

// Email for which the application is requesting delegated access (Optional).
Sub string `json:"sub,omitempty"`

// The old name of Sub. Client keeps setting Prn to be
// complaint with legacy OAuth 2.0 providers. (Optional)
Prn string `json:"prn,omitempty"`

// See http://tools.ietf.org/html/draft-jones-json-web-token-10#section-4.3
// This array is marshalled using custom code (see (c *ClaimSet) encode()).
PrivateClaims map[string]interface{} `json:"-"`
}

func (c *ClaimSet) encode() (string, error) {
// Reverting time back for machines whose time is not perfectly in sync.
// If client machine's time is in the future according
// to Google servers, an access token will not be issued.
now := time.Now().Add(-10 * time.Second)
if c.Iat == 0 {
c.Iat = now.Unix()
}
if c.Exp == 0 {
c.Exp = now.Add(time.Hour).Unix()
}
if c.Exp < c.Iat {
return "", fmt.Errorf("jws: invalid Exp = %v; must be later than Iat = %v", c.Exp, c.Iat)
}

b, err := json.Marshal(c)
if err != nil {
return "", err
}

if len(c.PrivateClaims) == 0 {
return base64.RawURLEncoding.EncodeToString(b), nil
}

// Marshal private claim set and then append it to b.
prv, err := json.Marshal(c.PrivateClaims)
if err != nil {
return "", fmt.Errorf("jws: invalid map of private claims %v", c.PrivateClaims)
}

// Concatenate public and private claim JSON objects.
if !bytes.HasSuffix(b, []byte{'}'}) {
return "", fmt.Errorf("jws: invalid JSON %s", b)
}
if !bytes.HasPrefix(prv, []byte{'{'}) {
return "", fmt.Errorf("jws: invalid JSON %s", prv)
}
b[len(b)-1] = ',' // Replace closing curly brace with a comma.
b = append(b, prv[1:]...) // Append private claims.
return base64.RawURLEncoding.EncodeToString(b), nil
}

// Header represents the header for the signed JWS payloads.
type Header struct {
// The algorithm used for signature.
Algorithm string `json:"alg"`

// Represents the token type.
Typ string `json:"typ"`

// The optional hint of which key is being used.
KeyID string `json:"kid,omitempty"`
}

func (h *Header) encode() (string, error) {
b, err := json.Marshal(h)
if err != nil {
return "", err
}
return base64.RawURLEncoding.EncodeToString(b), nil
}

// Decode decodes a claim set from a JWS payload.
func Decode(payload string) (*ClaimSet, error) {
// decode returned id token to get expiry
s := strings.Split(payload, ".")
if len(s) < 2 {
// TODO(jbd): Provide more context about the error.
return nil, errors.New("jws: invalid token received")
}
decoded, err := base64.RawURLEncoding.DecodeString(s[1])
if err != nil {
return nil, err
}
c := &ClaimSet{}
err = json.NewDecoder(bytes.NewBuffer(decoded)).Decode(c)
return c, err
}

// Signer returns a signature for the given data.
type Signer func(data []byte) (sig []byte, err error)

// EncodeWithSigner encodes a header and claim set with the provided signer.
func EncodeWithSigner(header *Header, c *ClaimSet, sg Signer) (string, error) {
head, err := header.encode()
if err != nil {
return "", err
}
cs, err := c.encode()
if err != nil {
return "", err
}
ss := fmt.Sprintf("%s.%s", head, cs)
sig, err := sg([]byte(ss))
if err != nil {
return "", err
}
return fmt.Sprintf("%s.%s", ss, base64.RawURLEncoding.EncodeToString(sig)), nil
}

// Encode encodes a signed JWS with provided header and claim set.
// This invokes EncodeWithSigner using crypto/rsa.SignPKCS1v15 with the given RSA private key.
func Encode(header *Header, c *ClaimSet, key *rsa.PrivateKey) (string, error) {
sg := func(data []byte) (sig []byte, err error) {
h := sha256.New()
h.Write(data)
return rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA256, h.Sum(nil))
}
return EncodeWithSigner(header, c, sg)
}

// Verify tests whether the provided JWT token's signature was produced by the private key
// associated with the supplied public key.
func Verify(token string, key *rsa.PublicKey) error {
parts := strings.Split(token, ".")
if len(parts) != 3 {
return errors.New("jws: invalid token received, token must have 3 parts")
}

signedContent := parts[0] + "." + parts[1]
signatureString, err := base64.RawURLEncoding.DecodeString(parts[2])
if err != nil {
return err
}

h := sha256.New()
h.Write([]byte(signedContent))
return rsa.VerifyPKCS1v15(key, crypto.SHA256, h.Sum(nil), []byte(signatureString))
}

+ 185
- 0
vendor/golang.org/x/oauth2/jwt/jwt.go View File

@@ -0,0 +1,185 @@
// 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 implements the OAuth 2.0 JSON Web Token flow, commonly
// known as "two-legged OAuth 2.0".
//
// See: https://tools.ietf.org/html/draft-ietf-oauth-jwt-bearer-12
package jwt

import (
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
"time"

"golang.org/x/oauth2"
"golang.org/x/oauth2/internal"
"golang.org/x/oauth2/jws"
)

var (
defaultGrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer"
defaultHeader = &jws.Header{Algorithm: "RS256", Typ: "JWT"}
)

// Config is the configuration for using JWT to fetch tokens,
// commonly known as "two-legged OAuth 2.0".
type Config struct {
// Email is the OAuth client identifier used when communicating with
// the configured OAuth provider.
Email string

// PrivateKey contains the contents of an RSA private key or the
// contents of a PEM file that contains a private key. The provided
// private key is used to sign JWT payloads.
// PEM containers with a passphrase are not supported.
// Use the following command to convert a PKCS 12 file into a PEM.
//
// $ openssl pkcs12 -in key.p12 -out key.pem -nodes
//
PrivateKey []byte

// PrivateKeyID contains an optional hint indicating which key is being
// used.
PrivateKeyID string

// Subject is the optional user to impersonate.
Subject string

// Scopes optionally specifies a list of requested permission scopes.
Scopes []string

// TokenURL is the endpoint required to complete the 2-legged JWT flow.
TokenURL string

// Expires optionally specifies how long the token is valid for.
Expires time.Duration

// Audience optionally specifies the intended audience of the
// request. If empty, the value of TokenURL is used as the
// intended audience.
Audience string

// PrivateClaims optionally specifies custom private claims in the JWT.
// See http://tools.ietf.org/html/draft-jones-json-web-token-10#section-4.3
PrivateClaims map[string]interface{}

// UseIDToken optionally specifies whether ID token should be used instead
// of access token when the server returns both.
UseIDToken bool
}

// TokenSource returns a JWT TokenSource using the configuration
// in c and the HTTP client from the provided context.
func (c *Config) TokenSource(ctx context.Context) oauth2.TokenSource {
return oauth2.ReuseTokenSource(nil, jwtSource{ctx, c})
}

// Client returns an HTTP client wrapping the context's
// HTTP transport and adding Authorization headers with tokens
// obtained from c.
//
// 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))
}

// jwtSource is a source that always does a signed JWT request for a token.
// It should typically be wrapped with a reuseTokenSource.
type jwtSource struct {
ctx context.Context
conf *Config
}

func (js jwtSource) Token() (*oauth2.Token, error) {
pk, err := internal.ParseKey(js.conf.PrivateKey)
if err != nil {
return nil, err
}
hc := oauth2.NewClient(js.ctx, nil)
claimSet := &jws.ClaimSet{
Iss: js.conf.Email,
Scope: strings.Join(js.conf.Scopes, " "),
Aud: js.conf.TokenURL,
PrivateClaims: js.conf.PrivateClaims,
}
if subject := js.conf.Subject; subject != "" {
claimSet.Sub = subject
// prn is the old name of sub. Keep setting it
// to be compatible with legacy OAuth 2.0 providers.
claimSet.Prn = subject
}
if t := js.conf.Expires; t > 0 {
claimSet.Exp = time.Now().Add(t).Unix()
}
if aud := js.conf.Audience; aud != "" {
claimSet.Aud = aud
}
h := *defaultHeader
h.KeyID = js.conf.PrivateKeyID
payload, err := jws.Encode(&h, claimSet, pk)
if err != nil {
return nil, err
}
v := url.Values{}
v.Set("grant_type", defaultGrantType)
v.Set("assertion", payload)
resp, err := hc.PostForm(js.conf.TokenURL, v)
if err != nil {
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20))
if err != nil {
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
}
if c := resp.StatusCode; c < 200 || c > 299 {
return nil, &oauth2.RetrieveError{
Response: resp,
Body: body,
}
}
// tokenRes is the JSON response body.
var tokenRes struct {
AccessToken string `json:"access_token"`
TokenType string `json:"token_type"`
IDToken string `json:"id_token"`
ExpiresIn int64 `json:"expires_in"` // relative seconds from now
}
if err := json.Unmarshal(body, &tokenRes); err != nil {
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
}
token := &oauth2.Token{
AccessToken: tokenRes.AccessToken,
TokenType: tokenRes.TokenType,
}
raw := make(map[string]interface{})
json.Unmarshal(body, &raw) // no error checks for optional fields
token = token.WithExtra(raw)

if secs := tokenRes.ExpiresIn; secs > 0 {
token.Expiry = time.Now().Add(time.Duration(secs) * time.Second)
}
if v := tokenRes.IDToken; v != "" {
// decode returned id token to get expiry
claimSet, err := jws.Decode(v)
if err != nil {
return nil, fmt.Errorf("oauth2: error decoding JWT token: %v", err)
}
token.Expiry = time.Unix(claimSet.Exp, 0)
}
if js.conf.UseIDToken {
if tokenRes.IDToken == "" {
return nil, fmt.Errorf("oauth2: response doesn't have JWT token")
}
token.AccessToken = tokenRes.IDToken
}
return token, nil
}

+ 20
- 0
vendor/google.golang.org/appengine/.travis.yml View File

@@ -0,0 +1,20 @@
language: go

go_import_path: google.golang.org/appengine

install:
- ./travis_install.sh

script:
- ./travis_test.sh

matrix:
include:
- go: 1.8.x
env: GOAPP=true
- go: 1.9.x
env: GOAPP=true
- go: 1.10.x
env: GOAPP=false
- go: 1.11.x
env: GO111MODULE=on

+ 90
- 0
vendor/google.golang.org/appengine/CONTRIBUTING.md View File

@@ -0,0 +1,90 @@
# Contributing

1. Sign one of the contributor license agreements below.
1. Get the package:

`go get -d google.golang.org/appengine`
1. Change into the checked out source:

`cd $GOPATH/src/google.golang.org/appengine`
1. Fork the repo.
1. Set your fork as a remote:

`git remote add fork git@github.com:GITHUB_USERNAME/appengine.git`
1. Make changes, commit to your fork.
1. Send a pull request with your changes.
The first line of your commit message is conventionally a one-line summary of the change, prefixed by the primary affected package, and is used as the title of your pull request.

# Testing

## Running system tests

Download and install the [Go App Engine SDK](https://cloud.google.com/appengine/docs/go/download). Make sure the `go_appengine` dir is in your `PATH`.

Set the `APPENGINE_DEV_APPSERVER` environment variable to `/path/to/go_appengine/dev_appserver.py`.

Run tests with `goapp test`:

```
goapp test -v google.golang.org/appengine/...
```