vendor: golang.org/x/oauth2@latest
go get golang.org/x/oauth2@latest go mod tidy go mod vendor
This commit is contained in:
parent
df089ee2fb
commit
876d548bc1
2
go.mod
2
go.mod
|
@ -125,7 +125,7 @@ require (
|
||||||
go.uber.org/zap v1.9.1 // indirect
|
go.uber.org/zap v1.9.1 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85
|
golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890
|
golang.org/x/oauth2 v0.0.0-20190220154721-9b3c75971fc9
|
||||||
google.golang.org/api v0.1.0
|
google.golang.org/api v0.1.0
|
||||||
google.golang.org/grpc v1.18.0
|
google.golang.org/grpc v1.18.0
|
||||||
gopkg.in/vmihailenco/msgpack.v2 v2.9.1 // indirect
|
gopkg.in/vmihailenco/msgpack.v2 v2.9.1 // indirect
|
||||||
|
|
6
go.sum
6
go.sum
|
@ -1,6 +1,7 @@
|
||||||
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
|
cloud.google.com/go v0.26.0 h1:e0WKqKTd5BnrG8aKH3J3h+QvEIQtSUcf2n5UZ5ZgLtQ=
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
cloud.google.com/go v0.36.0 h1:+aCSj7tOo2LODWVEuZDZeGCckdt6MlSF+X/rB3wUiS8=
|
cloud.google.com/go v0.36.0 h1:+aCSj7tOo2LODWVEuZDZeGCckdt6MlSF+X/rB3wUiS8=
|
||||||
cloud.google.com/go v0.36.0/go.mod h1:RUoy9p/M4ge0HzT8L+SDZ8jg+Q6fth0CiBuhFJpSV40=
|
cloud.google.com/go v0.36.0/go.mod h1:RUoy9p/M4ge0HzT8L+SDZ8jg+Q6fth0CiBuhFJpSV40=
|
||||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
||||||
|
@ -403,12 +404,15 @@ golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73r
|
||||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76 h1:xx5MUFyRQRbPk6VjWjIE1epE/K5AoDD8QUN116NCy8k=
|
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76 h1:xx5MUFyRQRbPk6VjWjIE1epE/K5AoDD8QUN116NCy8k=
|
||||||
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181129055619-fae4c4e3ad76/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd h1:HuTn7WObtcDo9uEEU7rEqL0jYthdXAmZ6PP+meazmaU=
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 h1:uESlIz09WIHT2I+pasSXcpLYqYK8wHcdCetU3VuMBJE=
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890 h1:uESlIz09WIHT2I+pasSXcpLYqYK8wHcdCetU3VuMBJE=
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190220154721-9b3c75971fc9 h1:pfyU+l9dEu0vZzDDMsdAKa1gZbJYEn6urYXj/+Xkz7s=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190220154721-9b3c75971fc9/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
@ -437,6 +441,8 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
|
||||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
|
google.golang.org/appengine v1.3.0 h1:FBSsiFRMz3LBeXIomRnVzrQwSDj4ibvcRexLG0LZGQk=
|
||||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
|
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
|
||||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
|
|
@ -19,57 +19,6 @@ See godoc for further documentation and examples.
|
||||||
* [godoc.org/golang.org/x/oauth2](http://godoc.org/golang.org/x/oauth2)
|
* [godoc.org/golang.org/x/oauth2](http://godoc.org/golang.org/x/oauth2)
|
||||||
* [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google)
|
* [godoc.org/golang.org/x/oauth2/google](http://godoc.org/golang.org/x/oauth2/google)
|
||||||
|
|
||||||
|
|
||||||
## App Engine
|
|
||||||
|
|
||||||
In change 96e89be (March 2015), we removed the `oauth2.Context2` type in favor
|
|
||||||
of the [`context.Context`](https://golang.org/x/net/context#Context) type from
|
|
||||||
the `golang.org/x/net/context` package. Later replaced by the standard `context` package
|
|
||||||
of the [`context.Context`](https://golang.org/pkg/context#Context) type.
|
|
||||||
|
|
||||||
|
|
||||||
This means it's no longer possible to use the "Classic App Engine"
|
|
||||||
`appengine.Context` type with the `oauth2` package. (You're using
|
|
||||||
Classic App Engine if you import the package `"appengine"`.)
|
|
||||||
|
|
||||||
To work around this, you may use the new `"google.golang.org/appengine"`
|
|
||||||
package. This package has almost the same API as the `"appengine"` package,
|
|
||||||
but it can be fetched with `go get` and used on "Managed VMs" and well as
|
|
||||||
Classic App Engine.
|
|
||||||
|
|
||||||
See the [new `appengine` package's readme](https://github.com/golang/appengine#updating-a-go-app-engine-app)
|
|
||||||
for information on updating your app.
|
|
||||||
|
|
||||||
If you don't want to update your entire app to use the new App Engine packages,
|
|
||||||
you may use both sets of packages in parallel, using only the new packages
|
|
||||||
with the `oauth2` package.
|
|
||||||
|
|
||||||
```go
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"golang.org/x/oauth2"
|
|
||||||
"golang.org/x/oauth2/google"
|
|
||||||
newappengine "google.golang.org/appengine"
|
|
||||||
newurlfetch "google.golang.org/appengine/urlfetch"
|
|
||||||
|
|
||||||
"appengine"
|
|
||||||
)
|
|
||||||
|
|
||||||
func handler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
var c appengine.Context = appengine.NewContext(r)
|
|
||||||
c.Infof("Logging a message with the old package")
|
|
||||||
|
|
||||||
var ctx context.Context = newappengine.NewContext(r)
|
|
||||||
client := &http.Client{
|
|
||||||
Transport: &oauth2.Transport{
|
|
||||||
Source: google.AppEngineTokenSource(ctx, "scope"),
|
|
||||||
Base: &newurlfetch.Transport{Context: ctx},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
client.Get("...")
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Policy for new packages
|
## Policy for new packages
|
||||||
|
|
||||||
We no longer accept new provider-specific packages in this repo. For
|
We no longer accept new provider-specific packages in this repo. For
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
module golang.org/x/oauth2
|
||||||
|
|
||||||
|
go 1.11
|
||||||
|
|
||||||
|
require (
|
||||||
|
cloud.google.com/go v0.34.0
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 // indirect
|
||||||
|
google.golang.org/appengine v1.4.0
|
||||||
|
)
|
|
@ -0,0 +1,12 @@
|
||||||
|
cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg=
|
||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||||
|
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
@ -19,8 +19,9 @@ import (
|
||||||
|
|
||||||
// Endpoint is Google's OAuth 2.0 endpoint.
|
// Endpoint is Google's OAuth 2.0 endpoint.
|
||||||
var Endpoint = oauth2.Endpoint{
|
var Endpoint = oauth2.Endpoint{
|
||||||
AuthURL: "https://accounts.google.com/o/oauth2/auth",
|
AuthURL: "https://accounts.google.com/o/oauth2/auth",
|
||||||
TokenURL: "https://accounts.google.com/o/oauth2/token",
|
TokenURL: "https://accounts.google.com/o/oauth2/token",
|
||||||
|
AuthStyle: oauth2.AuthStyleInParams,
|
||||||
}
|
}
|
||||||
|
|
||||||
// JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow.
|
// JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow.
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/net/context/ctxhttp"
|
"golang.org/x/net/context/ctxhttp"
|
||||||
|
@ -77,6 +78,9 @@ func (e *tokenJSON) expiry() (t time.Time) {
|
||||||
type expirationTime int32
|
type expirationTime int32
|
||||||
|
|
||||||
func (e *expirationTime) UnmarshalJSON(b []byte) error {
|
func (e *expirationTime) UnmarshalJSON(b []byte) error {
|
||||||
|
if len(b) == 0 || string(b) == "null" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
var n json.Number
|
var n json.Number
|
||||||
err := json.Unmarshal(b, &n)
|
err := json.Unmarshal(b, &n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -90,102 +94,71 @@ func (e *expirationTime) UnmarshalJSON(b []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var brokenAuthHeaderProviders = []string{
|
// RegisterBrokenAuthHeaderProvider previously did something. It is now a no-op.
|
||||||
"https://accounts.google.com/",
|
//
|
||||||
"https://api.codeswholesale.com/oauth/token",
|
// Deprecated: this function no longer does anything. Caller code that
|
||||||
"https://api.dropbox.com/",
|
// wants to avoid potential extra HTTP requests made during
|
||||||
"https://api.dropboxapi.com/",
|
// auto-probing of the provider's auth style should set
|
||||||
"https://api.instagram.com/",
|
// Endpoint.AuthStyle.
|
||||||
"https://api.netatmo.net/",
|
func RegisterBrokenAuthHeaderProvider(tokenURL string) {}
|
||||||
"https://api.odnoklassniki.ru/",
|
|
||||||
"https://api.pushbullet.com/",
|
// AuthStyle is a copy of the golang.org/x/oauth2 package's AuthStyle type.
|
||||||
"https://api.soundcloud.com/",
|
type AuthStyle int
|
||||||
"https://api.twitch.tv/",
|
|
||||||
"https://id.twitch.tv/",
|
const (
|
||||||
"https://app.box.com/",
|
AuthStyleUnknown AuthStyle = 0
|
||||||
"https://api.box.com/",
|
AuthStyleInParams AuthStyle = 1
|
||||||
"https://connect.stripe.com/",
|
AuthStyleInHeader AuthStyle = 2
|
||||||
"https://login.mailchimp.com/",
|
)
|
||||||
"https://login.microsoftonline.com/",
|
|
||||||
"https://login.salesforce.com/",
|
// authStyleCache is the set of tokenURLs we've successfully used via
|
||||||
"https://login.windows.net",
|
// RetrieveToken and which style auth we ended up using.
|
||||||
"https://login.live.com/",
|
// It's called a cache, but it doesn't (yet?) shrink. It's expected that
|
||||||
"https://login.live-int.com/",
|
// the set of OAuth2 servers a program contacts over time is fixed and
|
||||||
"https://oauth.sandbox.trainingpeaks.com/",
|
// small.
|
||||||
"https://oauth.trainingpeaks.com/",
|
var authStyleCache struct {
|
||||||
"https://oauth.vk.com/",
|
sync.Mutex
|
||||||
"https://openapi.baidu.com/",
|
m map[string]AuthStyle // keyed by tokenURL
|
||||||
"https://slack.com/",
|
|
||||||
"https://test-sandbox.auth.corp.google.com",
|
|
||||||
"https://test.salesforce.com/",
|
|
||||||
"https://user.gini.net/",
|
|
||||||
"https://www.douban.com/",
|
|
||||||
"https://www.googleapis.com/",
|
|
||||||
"https://www.linkedin.com/",
|
|
||||||
"https://www.strava.com/oauth/",
|
|
||||||
"https://www.wunderlist.com/oauth/",
|
|
||||||
"https://api.patreon.com/",
|
|
||||||
"https://sandbox.codeswholesale.com/oauth/token",
|
|
||||||
"https://api.sipgate.com/v1/authorization/oauth",
|
|
||||||
"https://api.medium.com/v1/tokens",
|
|
||||||
"https://log.finalsurge.com/oauth/token",
|
|
||||||
"https://multisport.todaysplan.com.au/rest/oauth/access_token",
|
|
||||||
"https://whats.todaysplan.com.au/rest/oauth/access_token",
|
|
||||||
"https://stackoverflow.com/oauth/access_token",
|
|
||||||
"https://account.health.nokia.com",
|
|
||||||
"https://accounts.zoho.com",
|
|
||||||
"https://gitter.im/login/oauth/token",
|
|
||||||
"https://openid-connect.onelogin.com/oidc",
|
|
||||||
"https://api.dailymotion.com/oauth/token",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// brokenAuthHeaderDomains lists broken providers that issue dynamic endpoints.
|
// ResetAuthCache resets the global authentication style cache used
|
||||||
var brokenAuthHeaderDomains = []string{
|
// for AuthStyleUnknown token requests.
|
||||||
".auth0.com",
|
func ResetAuthCache() {
|
||||||
".force.com",
|
authStyleCache.Lock()
|
||||||
".myshopify.com",
|
defer authStyleCache.Unlock()
|
||||||
".okta.com",
|
authStyleCache.m = nil
|
||||||
".oktapreview.com",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterBrokenAuthHeaderProvider(tokenURL string) {
|
// lookupAuthStyle reports which auth style we last used with tokenURL
|
||||||
brokenAuthHeaderProviders = append(brokenAuthHeaderProviders, tokenURL)
|
// when calling RetrieveToken and whether we have ever done so.
|
||||||
|
func lookupAuthStyle(tokenURL string) (style AuthStyle, ok bool) {
|
||||||
|
authStyleCache.Lock()
|
||||||
|
defer authStyleCache.Unlock()
|
||||||
|
style, ok = authStyleCache.m[tokenURL]
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// providerAuthHeaderWorks reports whether the OAuth2 server identified by the tokenURL
|
// setAuthStyle adds an entry to authStyleCache, documented above.
|
||||||
// implements the OAuth2 spec correctly
|
func setAuthStyle(tokenURL string, v AuthStyle) {
|
||||||
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
|
authStyleCache.Lock()
|
||||||
// In summary:
|
defer authStyleCache.Unlock()
|
||||||
// - Reddit only accepts client secret in the Authorization header
|
if authStyleCache.m == nil {
|
||||||
// - Dropbox accepts either it in URL param or Auth header, but not both.
|
authStyleCache.m = make(map[string]AuthStyle)
|
||||||
// - Google only accepts URL param (not spec compliant?), not Auth header
|
|
||||||
// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
|
|
||||||
func providerAuthHeaderWorks(tokenURL string) bool {
|
|
||||||
for _, s := range brokenAuthHeaderProviders {
|
|
||||||
if strings.HasPrefix(tokenURL, s) {
|
|
||||||
// Some sites fail to implement the OAuth2 spec fully.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
authStyleCache.m[tokenURL] = v
|
||||||
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
|
|
||||||
// to this package to let users select server bugs.
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values) (*Token, error) {
|
// newTokenRequest returns a new *http.Request to retrieve a new token
|
||||||
bustedAuth := !providerAuthHeaderWorks(tokenURL)
|
// from tokenURL using the provided clientID, clientSecret, and POST
|
||||||
if bustedAuth {
|
// body parameters.
|
||||||
|
//
|
||||||
|
// inParams is whether the clientID & clientSecret should be encoded
|
||||||
|
// as the POST body. An 'inParams' value of true means to send it in
|
||||||
|
// the POST body (along with any values in v); false means to send it
|
||||||
|
// in the Authorization header.
|
||||||
|
func newTokenRequest(tokenURL, clientID, clientSecret string, v url.Values, authStyle AuthStyle) (*http.Request, error) {
|
||||||
|
if authStyle == AuthStyleInParams {
|
||||||
|
v = cloneURLValues(v)
|
||||||
if clientID != "" {
|
if clientID != "" {
|
||||||
v.Set("client_id", clientID)
|
v.Set("client_id", clientID)
|
||||||
}
|
}
|
||||||
|
@ -198,15 +171,70 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||||
if !bustedAuth {
|
if authStyle == AuthStyleInHeader {
|
||||||
req.SetBasicAuth(url.QueryEscape(clientID), url.QueryEscape(clientSecret))
|
req.SetBasicAuth(url.QueryEscape(clientID), url.QueryEscape(clientSecret))
|
||||||
}
|
}
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func cloneURLValues(v url.Values) url.Values {
|
||||||
|
v2 := make(url.Values, len(v))
|
||||||
|
for k, vv := range v {
|
||||||
|
v2[k] = append([]string(nil), vv...)
|
||||||
|
}
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
|
||||||
|
func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values, authStyle AuthStyle) (*Token, error) {
|
||||||
|
needsAuthStyleProbe := authStyle == 0
|
||||||
|
if needsAuthStyleProbe {
|
||||||
|
if style, ok := lookupAuthStyle(tokenURL); ok {
|
||||||
|
authStyle = style
|
||||||
|
needsAuthStyleProbe = false
|
||||||
|
} else {
|
||||||
|
authStyle = AuthStyleInHeader // the first way we'll try
|
||||||
|
}
|
||||||
|
}
|
||||||
|
req, err := newTokenRequest(tokenURL, clientID, clientSecret, v, authStyle)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
token, err := doTokenRoundTrip(ctx, req)
|
||||||
|
if err != nil && needsAuthStyleProbe {
|
||||||
|
// If we get an error, assume the server wants the
|
||||||
|
// clientID & clientSecret in a different form.
|
||||||
|
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
|
||||||
|
// In summary:
|
||||||
|
// - Reddit only accepts client secret in the Authorization header
|
||||||
|
// - Dropbox accepts either it in URL param or Auth header, but not both.
|
||||||
|
// - Google only accepts URL param (not spec compliant?), not Auth header
|
||||||
|
// - Stripe only accepts client secret in Auth header with Bearer method, not Basic
|
||||||
|
//
|
||||||
|
// We used to maintain a big table in this code of all the sites and which way
|
||||||
|
// they went, but maintaining it didn't scale & got annoying.
|
||||||
|
// So just try both ways.
|
||||||
|
authStyle = AuthStyleInParams // the second way we'll try
|
||||||
|
req, _ = newTokenRequest(tokenURL, clientID, clientSecret, v, authStyle)
|
||||||
|
token, err = doTokenRoundTrip(ctx, req)
|
||||||
|
}
|
||||||
|
if needsAuthStyleProbe && err == nil {
|
||||||
|
setAuthStyle(tokenURL, authStyle)
|
||||||
|
}
|
||||||
|
// Don't overwrite `RefreshToken` with an empty value
|
||||||
|
// if this was a token refreshing request.
|
||||||
|
if token != nil && token.RefreshToken == "" {
|
||||||
|
token.RefreshToken = v.Get("refresh_token")
|
||||||
|
}
|
||||||
|
return token, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func doTokenRoundTrip(ctx context.Context, req *http.Request) (*Token, error) {
|
||||||
r, err := ctxhttp.Do(ctx, ContextClient(ctx), req)
|
r, err := ctxhttp.Do(ctx, ContextClient(ctx), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer r.Body.Close()
|
|
||||||
body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
|
body, err := ioutil.ReadAll(io.LimitReader(r.Body, 1<<20))
|
||||||
|
r.Body.Close()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
|
return nil, fmt.Errorf("oauth2: cannot fetch token: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -232,7 +260,7 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
|
||||||
Raw: vals,
|
Raw: vals,
|
||||||
}
|
}
|
||||||
e := vals.Get("expires_in")
|
e := vals.Get("expires_in")
|
||||||
if e == "" {
|
if e == "" || e == "null" {
|
||||||
// TODO(jbd): Facebook's OAuth2 implementation is broken and
|
// TODO(jbd): Facebook's OAuth2 implementation is broken and
|
||||||
// returns expires_in field in expires. Remove the fallback to expires,
|
// returns expires_in field in expires. Remove the fallback to expires,
|
||||||
// when Facebook fixes their implementation.
|
// when Facebook fixes their implementation.
|
||||||
|
@ -256,13 +284,8 @@ func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string,
|
||||||
}
|
}
|
||||||
json.Unmarshal(body, &token.Raw) // no error checks for optional fields
|
json.Unmarshal(body, &token.Raw) // no error checks for optional fields
|
||||||
}
|
}
|
||||||
// Don't overwrite `RefreshToken` with an empty value
|
|
||||||
// if this was a token refreshing request.
|
|
||||||
if token.RefreshToken == "" {
|
|
||||||
token.RefreshToken = v.Get("refresh_token")
|
|
||||||
}
|
|
||||||
if token.AccessToken == "" {
|
if token.AccessToken == "" {
|
||||||
return token, errors.New("oauth2: server response missing access_token")
|
return nil, errors.New("oauth2: server response missing access_token")
|
||||||
}
|
}
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,11 @@ type Config struct {
|
||||||
|
|
||||||
// Expires optionally specifies how long the token is valid for.
|
// Expires optionally specifies how long the token is valid for.
|
||||||
Expires time.Duration
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
// TokenSource returns a JWT TokenSource using the configuration
|
// TokenSource returns a JWT TokenSource using the configuration
|
||||||
|
@ -105,6 +110,9 @@ func (js jwtSource) Token() (*oauth2.Token, error) {
|
||||||
if t := js.conf.Expires; t > 0 {
|
if t := js.conf.Expires; t > 0 {
|
||||||
claimSet.Exp = time.Now().Add(t).Unix()
|
claimSet.Exp = time.Now().Add(t).Unix()
|
||||||
}
|
}
|
||||||
|
if aud := js.conf.Audience; aud != "" {
|
||||||
|
claimSet.Aud = aud
|
||||||
|
}
|
||||||
h := *defaultHeader
|
h := *defaultHeader
|
||||||
h.KeyID = js.conf.PrivateKeyID
|
h.KeyID = js.conf.PrivateKeyID
|
||||||
payload, err := jws.Encode(&h, claimSet, pk)
|
payload, err := jws.Encode(&h, claimSet, pk)
|
||||||
|
|
|
@ -26,17 +26,13 @@ import (
|
||||||
// Deprecated: Use context.Background() or context.TODO() instead.
|
// Deprecated: Use context.Background() or context.TODO() instead.
|
||||||
var NoContext = context.TODO()
|
var NoContext = context.TODO()
|
||||||
|
|
||||||
// RegisterBrokenAuthHeaderProvider registers an OAuth2 server
|
// RegisterBrokenAuthHeaderProvider previously did something. It is now a no-op.
|
||||||
// identified by the tokenURL prefix as an OAuth2 implementation
|
//
|
||||||
// which doesn't support the HTTP Basic authentication
|
// Deprecated: this function no longer does anything. Caller code that
|
||||||
// scheme to authenticate with the authorization server.
|
// wants to avoid potential extra HTTP requests made during
|
||||||
// Once a server is registered, credentials (client_id and client_secret)
|
// auto-probing of the provider's auth style should set
|
||||||
// will be passed as query parameters rather than being present
|
// Endpoint.AuthStyle.
|
||||||
// in the Authorization header.
|
func RegisterBrokenAuthHeaderProvider(tokenURL string) {}
|
||||||
// See https://code.google.com/p/goauth2/issues/detail?id=31 for background.
|
|
||||||
func RegisterBrokenAuthHeaderProvider(tokenURL string) {
|
|
||||||
internal.RegisterBrokenAuthHeaderProvider(tokenURL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config describes a typical 3-legged OAuth2 flow, with both the
|
// Config describes a typical 3-legged OAuth2 flow, with both the
|
||||||
// client application information and the server's endpoint URLs.
|
// client application information and the server's endpoint URLs.
|
||||||
|
@ -71,13 +67,38 @@ type TokenSource interface {
|
||||||
Token() (*Token, error)
|
Token() (*Token, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Endpoint contains the OAuth 2.0 provider's authorization and token
|
// Endpoint represents an OAuth 2.0 provider's authorization and token
|
||||||
// endpoint URLs.
|
// endpoint URLs.
|
||||||
type Endpoint struct {
|
type Endpoint struct {
|
||||||
AuthURL string
|
AuthURL string
|
||||||
TokenURL string
|
TokenURL string
|
||||||
|
|
||||||
|
// AuthStyle optionally specifies how the endpoint wants the
|
||||||
|
// client ID & client secret sent. The zero value means to
|
||||||
|
// auto-detect.
|
||||||
|
AuthStyle AuthStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AuthStyle represents how requests for tokens are authenticated
|
||||||
|
// to the server.
|
||||||
|
type AuthStyle int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AuthStyleAutoDetect means to auto-detect which authentication
|
||||||
|
// style the provider wants by trying both ways and caching
|
||||||
|
// the successful way for the future.
|
||||||
|
AuthStyleAutoDetect AuthStyle = 0
|
||||||
|
|
||||||
|
// AuthStyleInParams sends the "client_id" and "client_secret"
|
||||||
|
// in the POST body as application/x-www-form-urlencoded parameters.
|
||||||
|
AuthStyleInParams AuthStyle = 1
|
||||||
|
|
||||||
|
// AuthStyleInHeader sends the client_id and client_password
|
||||||
|
// using HTTP Basic Authorization. This is an optional style
|
||||||
|
// described in the OAuth2 RFC 6749 section 2.3.1.
|
||||||
|
AuthStyleInHeader AuthStyle = 2
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// AccessTypeOnline and AccessTypeOffline are options passed
|
// AccessTypeOnline and AccessTypeOffline are options passed
|
||||||
// to the Options.AuthCodeURL method. They modify the
|
// to the Options.AuthCodeURL method. They modify the
|
||||||
|
@ -124,7 +145,7 @@ func SetAuthURLParam(key, value string) AuthCodeOption {
|
||||||
//
|
//
|
||||||
// Opts may include AccessTypeOnline or AccessTypeOffline, as well
|
// Opts may include AccessTypeOnline or AccessTypeOffline, as well
|
||||||
// as ApprovalForce.
|
// as ApprovalForce.
|
||||||
// It can also be used to pass the PKCE challange.
|
// It can also be used to pass the PKCE challenge.
|
||||||
// See https://www.oauth.com/oauth2-servers/pkce/ for more info.
|
// See https://www.oauth.com/oauth2-servers/pkce/ for more info.
|
||||||
func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
|
func (c *Config) AuthCodeURL(state string, opts ...AuthCodeOption) string {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
|
|
|
@ -118,13 +118,16 @@ func (t *Token) Extra(key string) interface{} {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// timeNow is time.Now but pulled out as a variable for tests.
|
||||||
|
var timeNow = time.Now
|
||||||
|
|
||||||
// expired reports whether the token is expired.
|
// expired reports whether the token is expired.
|
||||||
// t must be non-nil.
|
// t must be non-nil.
|
||||||
func (t *Token) expired() bool {
|
func (t *Token) expired() bool {
|
||||||
if t.Expiry.IsZero() {
|
if t.Expiry.IsZero() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return t.Expiry.Round(0).Add(-expiryDelta).Before(time.Now())
|
return t.Expiry.Round(0).Add(-expiryDelta).Before(timeNow())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid reports whether t is non-nil, has an AccessToken, and is not expired.
|
// Valid reports whether t is non-nil, has an AccessToken, and is not expired.
|
||||||
|
@ -151,7 +154,7 @@ func tokenFromInternal(t *internal.Token) *Token {
|
||||||
// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along
|
// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along
|
||||||
// with an error..
|
// with an error..
|
||||||
func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
|
func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
|
||||||
tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v)
|
tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v, internal.AuthStyle(c.Endpoint.AuthStyle))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if rErr, ok := err.(*internal.RetrieveError); ok {
|
if rErr, ok := err.(*internal.RetrieveError); ok {
|
||||||
return nil, (*RetrieveError)(rErr)
|
return nil, (*RetrieveError)(rErr)
|
||||||
|
|
|
@ -78,6 +78,12 @@ func IsAppEngine() bool {
|
||||||
return internal.IsAppEngine()
|
return internal.IsAppEngine()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsSecondGen reports whether the App Engine app is running on the second generation
|
||||||
|
// runtimes (>= Go 1.11).
|
||||||
|
func IsSecondGen() bool {
|
||||||
|
return internal.IsSecondGen()
|
||||||
|
}
|
||||||
|
|
||||||
// NewContext returns a context for an in-flight HTTP request.
|
// NewContext returns a context for an in-flight HTTP request.
|
||||||
// This function is cheap.
|
// This function is cheap.
|
||||||
func NewContext(req *http.Request) context.Context {
|
func NewContext(req *http.Request) context.Context {
|
||||||
|
|
|
@ -579,7 +579,10 @@ func logf(c *context, level int64, format string, args ...interface{}) {
|
||||||
Level: &level,
|
Level: &level,
|
||||||
Message: &s,
|
Message: &s,
|
||||||
})
|
})
|
||||||
log.Print(logLevelName[level] + ": " + s)
|
// Only duplicate log to stderr if not running on App Engine second generation
|
||||||
|
if !IsSecondGen() {
|
||||||
|
log.Print(logLevelName[level] + ": " + s)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// flushLog attempts to flush any pending logs to the appserver.
|
// flushLog attempts to flush any pending logs to the appserver.
|
||||||
|
|
|
@ -31,9 +31,15 @@ func AppID(c netcontext.Context) string {
|
||||||
// ../appengine.go. See that file for commentary.
|
// ../appengine.go. See that file for commentary.
|
||||||
func IsStandard() bool {
|
func IsStandard() bool {
|
||||||
// appengineStandard will be true for first-gen runtimes (<= Go 1.9) but not
|
// appengineStandard will be true for first-gen runtimes (<= Go 1.9) but not
|
||||||
// second-gen (>= Go 1.11). Second-gen runtimes set $GAE_ENV so we use that
|
// second-gen (>= Go 1.11).
|
||||||
// to check if we're on a second-gen runtime.
|
return appengineStandard || IsSecondGen()
|
||||||
return appengineStandard || os.Getenv("GAE_ENV") == "standard"
|
}
|
||||||
|
|
||||||
|
// IsStandard is the implementation of the wrapper function of the same name in
|
||||||
|
// ../appengine.go. See that file for commentary.
|
||||||
|
func IsSecondGen() bool {
|
||||||
|
// Second-gen runtimes set $GAE_ENV so we use that to check if we're on a second-gen runtime.
|
||||||
|
return os.Getenv("GAE_ENV") == "standard"
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsFlex is the implementation of the wrapper function of the same name in
|
// IsFlex is the implementation of the wrapper function of the same name in
|
||||||
|
|
|
@ -11,5 +11,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func Main() {
|
func Main() {
|
||||||
|
MainPath = ""
|
||||||
appengine_internal.Main()
|
appengine_internal.Main()
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package internal
|
||||||
|
|
||||||
|
// MainPath stores the file path of the main package. On App Engine Standard
|
||||||
|
// using Go version 1.9 and below, this will be unset. On App Engine Flex and
|
||||||
|
// App Engine Standard second-gen (Go 1.11 and above), this will be the
|
||||||
|
// filepath to package main.
|
||||||
|
var MainPath string
|
|
@ -12,9 +12,12 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Main() {
|
func Main() {
|
||||||
|
MainPath = filepath.Dir(findMainPath())
|
||||||
installHealthChecker(http.DefaultServeMux)
|
installHealthChecker(http.DefaultServeMux)
|
||||||
|
|
||||||
port := "8080"
|
port := "8080"
|
||||||
|
@ -31,6 +34,24 @@ func Main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find the path to package main by looking at the root Caller.
|
||||||
|
func findMainPath() string {
|
||||||
|
pc := make([]uintptr, 100)
|
||||||
|
n := runtime.Callers(2, pc)
|
||||||
|
frames := runtime.CallersFrames(pc[:n])
|
||||||
|
for {
|
||||||
|
frame, more := frames.Next()
|
||||||
|
// Tests won't have package main, instead they have testing.tRunner
|
||||||
|
if frame.Function == "main.main" || frame.Function == "testing.tRunner" {
|
||||||
|
return frame.File
|
||||||
|
}
|
||||||
|
if !more {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func installHealthChecker(mux *http.ServeMux) {
|
func installHealthChecker(mux *http.ServeMux) {
|
||||||
// If no health check handler has been installed by this point, add a trivial one.
|
// If no health check handler has been installed by this point, add a trivial one.
|
||||||
const healthPath = "/_ah/health"
|
const healthPath = "/_ah/health"
|
||||||
|
|
|
@ -490,7 +490,7 @@ golang.org/x/net/http2/hpack
|
||||||
golang.org/x/net/html
|
golang.org/x/net/html
|
||||||
golang.org/x/net/http/httpguts
|
golang.org/x/net/http/httpguts
|
||||||
golang.org/x/net/html/atom
|
golang.org/x/net/html/atom
|
||||||
# golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890
|
# golang.org/x/oauth2 v0.0.0-20190220154721-9b3c75971fc9
|
||||||
golang.org/x/oauth2/jwt
|
golang.org/x/oauth2/jwt
|
||||||
golang.org/x/oauth2
|
golang.org/x/oauth2
|
||||||
golang.org/x/oauth2/internal
|
golang.org/x/oauth2/internal
|
||||||
|
@ -532,7 +532,7 @@ google.golang.org/api/googleapi/internal/uritemplates
|
||||||
google.golang.org/api/gensupport
|
google.golang.org/api/gensupport
|
||||||
google.golang.org/api/googleapi/transport
|
google.golang.org/api/googleapi/transport
|
||||||
google.golang.org/api/transport/http/internal/propagation
|
google.golang.org/api/transport/http/internal/propagation
|
||||||
# google.golang.org/appengine v1.3.0
|
# google.golang.org/appengine v1.4.0
|
||||||
google.golang.org/appengine/urlfetch
|
google.golang.org/appengine/urlfetch
|
||||||
google.golang.org/appengine
|
google.golang.org/appengine
|
||||||
google.golang.org/appengine/datastore
|
google.golang.org/appengine/datastore
|
||||||
|
|
Loading…
Reference in New Issue