*: revendor
This commit is contained in:
192
vendor/github.com/ericchiang/oidc/oidc.go
generated
vendored
192
vendor/github.com/ericchiang/oidc/oidc.go
generated
vendored
@@ -15,9 +15,9 @@ import (
|
||||
|
||||
var (
|
||||
// ErrTokenExpired indicates that a token parsed by a verifier has expired.
|
||||
ErrTokenExpired = errors.New("ID Token expired")
|
||||
ErrTokenExpired = errors.New("oidc: ID Token expired")
|
||||
// ErrNotSupported indicates that the requested optional OpenID Connect endpoint is not supported by the provider.
|
||||
ErrNotSupported = errors.New("endpoint not supported")
|
||||
ErrNotSupported = errors.New("oidc: endpoint not supported")
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -44,8 +44,8 @@ type Provider struct {
|
||||
JWKSURL string `json:"jwks_uri"`
|
||||
UserInfoURL string `json:"userinfo_endpoint"`
|
||||
|
||||
// Optionally contains extra claims.
|
||||
raw map[string]interface{}
|
||||
// Raw claims returned by the server.
|
||||
rawClaims []byte
|
||||
}
|
||||
|
||||
// NewProvider uses the OpenID Connect disovery mechanism to construct a Provider.
|
||||
@@ -67,20 +67,19 @@ func NewProvider(ctx context.Context, issuer string) (*Provider, error) {
|
||||
if err := json.Unmarshal(body, &p); err != nil {
|
||||
return nil, fmt.Errorf("oidc: failed to decode provider discovery object: %v", err)
|
||||
}
|
||||
// raw claims do not get error checks
|
||||
json.Unmarshal(body, &p.raw)
|
||||
p.rawClaims = body
|
||||
if p.Issuer != issuer {
|
||||
return nil, fmt.Errorf("oidc: issuer did not match the issuer returned by provider, expected %q got %q", issuer, p.Issuer)
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
// Extra returns additional fields returned by the server during discovery.
|
||||
func (p *Provider) Extra(key string) interface{} {
|
||||
if p.raw != nil {
|
||||
return p.raw[key]
|
||||
// Claims returns additional fields returned by the server during discovery.
|
||||
func (p *Provider) Claims(v interface{}) error {
|
||||
if p.rawClaims == nil {
|
||||
return errors.New("oidc: claims not set")
|
||||
}
|
||||
return nil
|
||||
return json.Unmarshal(p.rawClaims, v)
|
||||
}
|
||||
|
||||
// Endpoint returns the OAuth2 auth and token endpoints for the given provider.
|
||||
@@ -95,16 +94,15 @@ type UserInfo struct {
|
||||
Email string `json:"email"`
|
||||
EmailVerified bool `json:"email_verified"`
|
||||
|
||||
// Optionally contains extra claims.
|
||||
raw map[string]interface{}
|
||||
claims []byte
|
||||
}
|
||||
|
||||
// Extra returns additional claims returned by the server.
|
||||
func (u *UserInfo) Extra(key string) interface{} {
|
||||
if u.raw != nil {
|
||||
return u.raw[key]
|
||||
// Claims unmarshals the raw JSON object claims into the provided object.
|
||||
func (u *UserInfo) Claims(v interface{}) error {
|
||||
if u.claims == nil {
|
||||
return errors.New("oidc: claims not set")
|
||||
}
|
||||
return nil
|
||||
return json.Unmarshal(u.claims, v)
|
||||
}
|
||||
|
||||
// UserInfo uses the token source to query the provider's user info endpoint.
|
||||
@@ -130,11 +128,101 @@ func (p *Provider) UserInfo(ctx context.Context, tokenSource oauth2.TokenSource)
|
||||
if err := json.Unmarshal(body, &userInfo); err != nil {
|
||||
return nil, fmt.Errorf("oidc: failed to decode userinfo: %v", err)
|
||||
}
|
||||
// raw claims do not get error checks
|
||||
json.Unmarshal(body, &userInfo.raw)
|
||||
userInfo.claims = body
|
||||
return &userInfo, nil
|
||||
}
|
||||
|
||||
// IDToken is an OpenID Connect extension that provides a predictable representation
|
||||
// of an authorization event.
|
||||
//
|
||||
// The ID Token only holds fields OpenID Connect requires. To access additional
|
||||
// claims returned by the server, use the Claims method.
|
||||
//
|
||||
// idToken, err := idTokenVerifier.Verify(rawIDToken)
|
||||
// if err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
// var claims struct {
|
||||
// Email string `json:"email"`
|
||||
// EmailVerified bool `json:"email_verified"`
|
||||
// }
|
||||
// if err := idToken.Claims(&claims); err != nil {
|
||||
// // handle error
|
||||
// }
|
||||
//
|
||||
type IDToken struct {
|
||||
// The URL of the server which issued this token. This will always be the same
|
||||
// as the URL used for initial discovery.
|
||||
Issuer string
|
||||
|
||||
// The client, or set of clients, that this token is issued for.
|
||||
Audience []string
|
||||
|
||||
// A unique string which identifies the end user.
|
||||
Subject string
|
||||
|
||||
IssuedAt time.Time
|
||||
Expiry time.Time
|
||||
Nonce string
|
||||
|
||||
claims []byte
|
||||
}
|
||||
|
||||
// Claims unmarshals the raw JSON payload of the ID Token into a provided struct.
|
||||
func (i *IDToken) Claims(v interface{}) error {
|
||||
if i.claims == nil {
|
||||
return errors.New("oidc: claims not set")
|
||||
}
|
||||
return json.Unmarshal(i.claims, v)
|
||||
}
|
||||
|
||||
type audience []string
|
||||
|
||||
func (a *audience) UnmarshalJSON(b []byte) error {
|
||||
var s string
|
||||
if json.Unmarshal(b, &s) == nil {
|
||||
*a = audience{s}
|
||||
return nil
|
||||
}
|
||||
var auds []string
|
||||
if err := json.Unmarshal(b, &auds); err != nil {
|
||||
return err
|
||||
}
|
||||
*a = audience(auds)
|
||||
return nil
|
||||
}
|
||||
|
||||
type jsonTime time.Time
|
||||
|
||||
func (j *jsonTime) UnmarshalJSON(b []byte) error {
|
||||
var n json.Number
|
||||
if err := json.Unmarshal(b, &n); err != nil {
|
||||
return err
|
||||
}
|
||||
var unix int64
|
||||
|
||||
if t, err := n.Int64(); err == nil {
|
||||
unix = t
|
||||
} else {
|
||||
f, err := n.Float64()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
unix = int64(f)
|
||||
}
|
||||
*j = jsonTime(time.Unix(unix, 0))
|
||||
return nil
|
||||
}
|
||||
|
||||
type idToken struct {
|
||||
Issuer string `json:"iss"`
|
||||
Subject string `json:"sub"`
|
||||
Audience audience `json:"aud"`
|
||||
Expiry jsonTime `json:"exp"`
|
||||
IssuedAt jsonTime `json:"iat"`
|
||||
Nonce string `json:"nonce"`
|
||||
}
|
||||
|
||||
// IDTokenVerifier provides verification for ID Tokens.
|
||||
type IDTokenVerifier struct {
|
||||
issuer string
|
||||
@@ -143,31 +231,34 @@ type IDTokenVerifier struct {
|
||||
}
|
||||
|
||||
// Verify parse the raw ID Token, verifies it's been signed by the provider, preforms
|
||||
// additional verification, such as checking the expiration, and returns the claims.
|
||||
func (v *IDTokenVerifier) Verify(rawIDToken string) (payload []byte, err error) {
|
||||
payload, err = v.keySet.verifyJWT(rawIDToken)
|
||||
// additional verification, and returns the claims.
|
||||
func (v *IDTokenVerifier) Verify(rawIDToken string) (*IDToken, error) {
|
||||
payload, err := v.keySet.verifyJWT(rawIDToken)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var token struct {
|
||||
Exp float64 `json:"exp"` // JSON numbers are always float64s.
|
||||
Issuer string `json:"iss"`
|
||||
}
|
||||
var token idToken
|
||||
if err := json.Unmarshal(payload, &token); err != nil {
|
||||
return nil, fmt.Errorf("oidc: failed to unmarshal claims: %v", err)
|
||||
}
|
||||
if v.issuer != token.Issuer {
|
||||
return nil, fmt.Errorf("oidc: iss field did not match provider issuer")
|
||||
}
|
||||
if time.Unix(int64(token.Exp), 0).Before(time.Now().Round(time.Second)) {
|
||||
return nil, ErrTokenExpired
|
||||
t := &IDToken{
|
||||
Issuer: token.Issuer,
|
||||
Subject: token.Subject,
|
||||
Audience: []string(token.Audience),
|
||||
Expiry: time.Time(token.Expiry),
|
||||
IssuedAt: time.Time(token.Expiry),
|
||||
Nonce: token.Nonce,
|
||||
claims: payload,
|
||||
}
|
||||
for _, option := range v.options {
|
||||
if err := option.verifyIDTokenPayload(payload); err != nil {
|
||||
if err := option.verifyIDToken(t); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return payload, nil
|
||||
return t, nil
|
||||
}
|
||||
|
||||
// NewVerifier returns an IDTokenVerifier that uses the provider's key set to verify JWTs.
|
||||
@@ -184,7 +275,7 @@ func (p *Provider) NewVerifier(ctx context.Context, options ...VerificationOptio
|
||||
|
||||
// VerificationOption is an option provided to Provider.NewVerifier.
|
||||
type VerificationOption interface {
|
||||
verifyIDTokenPayload(raw []byte) error
|
||||
verifyIDToken(token *IDToken) error
|
||||
}
|
||||
|
||||
// VerifyAudience ensures that an ID Token was issued for the specific client.
|
||||
@@ -199,25 +290,8 @@ type clientVerifier struct {
|
||||
clientID string
|
||||
}
|
||||
|
||||
func (c clientVerifier) verifyIDTokenPayload(payload []byte) error {
|
||||
var token struct {
|
||||
Aud string `json:"aud"`
|
||||
}
|
||||
if err := json.Unmarshal(payload, &token); err == nil {
|
||||
if token.Aud != c.clientID {
|
||||
return errors.New("oidc: id token aud field did not match client_id")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Aud can optionally be an array of strings
|
||||
var token2 struct {
|
||||
Aud []string `json:"aud"`
|
||||
}
|
||||
if err := json.Unmarshal(payload, &token2); err != nil {
|
||||
return fmt.Errorf("oidc: failed to unmarshal aud claim: %v", err)
|
||||
}
|
||||
for _, aud := range token2.Aud {
|
||||
func (c clientVerifier) verifyIDToken(token *IDToken) error {
|
||||
for _, aud := range token.Audience {
|
||||
if aud == c.clientID {
|
||||
return nil
|
||||
}
|
||||
@@ -225,6 +299,22 @@ func (c clientVerifier) verifyIDTokenPayload(payload []byte) error {
|
||||
return errors.New("oidc: id token aud field did not match client_id")
|
||||
}
|
||||
|
||||
// VerifyExpiry ensures that an ID Token has not expired.
|
||||
func VerifyExpiry() VerificationOption {
|
||||
return expiryVerifier{time.Now}
|
||||
}
|
||||
|
||||
type expiryVerifier struct {
|
||||
now func() time.Time
|
||||
}
|
||||
|
||||
func (e expiryVerifier) verifyIDToken(token *IDToken) error {
|
||||
if e.now().After(token.Expiry) {
|
||||
return ErrTokenExpired
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// This method is internal to golang.org/x/oauth2. Just copy it.
|
||||
func contextClient(ctx context.Context) *http.Client {
|
||||
if ctx != nil {
|
||||
|
Reference in New Issue
Block a user