From 4d2387c472c59c3ef7fe62d32cc36167ff740a9e Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Thu, 30 Jan 2020 08:48:18 +0100 Subject: [PATCH] fix idtokenvalidity and marshalling --- pkg/oidc/code_challenge.go | 2 +- pkg/oidc/token.go | 196 +++++++++++++++++++++++++++---------- pkg/op/default_op.go | 15 +-- pkg/op/tokenrequest.go | 2 - 4 files changed, 145 insertions(+), 70 deletions(-) diff --git a/pkg/oidc/code_challenge.go b/pkg/oidc/code_challenge.go index b336514..e3035c2 100644 --- a/pkg/oidc/code_challenge.go +++ b/pkg/oidc/code_challenge.go @@ -27,7 +27,7 @@ func VerifyCodeChallenge(c *CodeChallenge, codeVerifier string) bool { return false //TODO: ? } if c.Method == CodeChallengeMethodS256 { - codeVerifier = utils.HashString(sha256.New(), codeVerifier) + codeVerifier = NewSHACodeChallenge(codeVerifier) } return codeVerifier == c.Challenge } diff --git a/pkg/oidc/token.go b/pkg/oidc/token.go index 2a52a23..05357d3 100644 --- a/pkg/oidc/token.go +++ b/pkg/oidc/token.go @@ -2,6 +2,7 @@ package oidc import ( "encoding/json" + "strings" "time" "github.com/caos/oidc/pkg/utils" @@ -9,32 +10,160 @@ import ( "gopkg.in/square/go-jose.v2" ) -type IDTokenClaims struct { +type Tokens struct { + *oauth2.Token + IDTokenClaims *IDTokenClaims + IDToken string +} + +type AccessTokenClaims struct { Issuer string Subject string Audiences []string Expiration time.Time IssuedAt time.Time - AuthTime time.Time + NotBefore time.Time + JWTID string + AuthorizedParty string Nonce string + AuthTime time.Time + CodeHash string AuthenticationContextClassReference string AuthenticationMethodsReferences []string + SessionID string + Scopes []string + ClientID string + AccessTokenUseNumber int +} + +type IDTokenClaims struct { + Issuer string + Subject string + Audiences []string + Expiration time.Time + NotBefore time.Time + IssuedAt time.Time + JWTID string + UpdatedAt time.Time AuthorizedParty string + Nonce string + AuthTime time.Time AccessTokenHash string CodeHash string + AuthenticationContextClassReference string + AuthenticationMethodsReferences []string + ClientID string Signature jose.SignatureAlgorithm //TODO: ??? } +type jsonToken struct { + Issuer string `json:"iss,omitempty"` + Subject string `json:"sub,omitempty"` + Audiences []string `json:"aud,omitempty"` + Expiration int64 `json:"exp,omitempty"` + NotBefore int64 `json:"nbf,omitempty"` + IssuedAt int64 `json:"iat,omitempty"` + JWTID string `json:"jti,omitempty"` + UpdatedAt int64 `json:"updated_at,omitempty"` + AuthorizedParty string `json:"azp,omitempty"` + Nonce string `json:"nonce,omitempty"` + AuthTime int64 `json:"auth_time,omitempty"` + AccessTokenHash string `json:"at_hash,omitempty"` + CodeHash string `json:"c_hash,omitempty"` + AuthenticationContextClassReference string `json:"acr,omitempty"` + AuthenticationMethodsReferences []string `json:"amr,omitempty"` + SessionID string `json:"sid,omitempty"` + Actor interface{} `json:"act,omitempty"` + Scopes string `json:"scope,omitempty"` + ClientID string `json:"client_id,omitempty"` + AuthorizedActor interface{} `json:"may_act,omitempty"` //TODO: impl + AccessTokenUseNumber int `json:"at_use_nbr,omitempty"` +} + +func (t *AccessTokenClaims) MarshalJSON() ([]byte, error) { + j := jsonToken{ + Issuer: t.Issuer, + Subject: t.Subject, + Audiences: t.Audiences, + Expiration: timeToJSON(t.Expiration), + NotBefore: timeToJSON(t.NotBefore), + IssuedAt: timeToJSON(t.IssuedAt), + JWTID: t.JWTID, + AuthorizedParty: t.AuthorizedParty, + Nonce: t.Nonce, + AuthTime: timeToJSON(t.AuthTime), + CodeHash: t.CodeHash, + AuthenticationContextClassReference: t.AuthenticationContextClassReference, + AuthenticationMethodsReferences: t.AuthenticationMethodsReferences, + SessionID: t.SessionID, + Scopes: strings.Join(t.Scopes, " "), + ClientID: t.ClientID, + AccessTokenUseNumber: t.AccessTokenUseNumber, + } + return json.Marshal(j) +} + +func (t *AccessTokenClaims) UnmarshalJSON(b []byte) error { + var j jsonToken + if err := json.Unmarshal(b, &j); err != nil { + return err + } + audience := j.Audiences + if len(audience) == 1 { + audience = strings.Split(audience[0], " ") + } + t.Issuer = j.Issuer + t.Subject = j.Subject + t.Audiences = audience + t.Expiration = time.Unix(j.Expiration, 0).UTC() + t.NotBefore = time.Unix(j.NotBefore, 0).UTC() + t.IssuedAt = time.Unix(j.IssuedAt, 0).UTC() + t.JWTID = j.JWTID + t.AuthorizedParty = j.AuthorizedParty + t.Nonce = j.Nonce + t.AuthTime = time.Unix(j.AuthTime, 0).UTC() + t.CodeHash = j.CodeHash + t.AuthenticationContextClassReference = j.AuthenticationContextClassReference + t.AuthenticationMethodsReferences = j.AuthenticationMethodsReferences + t.SessionID = j.SessionID + t.Scopes = strings.Split(j.Scopes, " ") + t.ClientID = j.ClientID + t.AccessTokenUseNumber = j.AccessTokenUseNumber + return nil +} + +func (t *IDTokenClaims) MarshalJSON() ([]byte, error) { + j := jsonToken{ + Issuer: t.Issuer, + Subject: t.Subject, + Audiences: t.Audiences, + Expiration: timeToJSON(t.Expiration), + NotBefore: timeToJSON(t.NotBefore), + IssuedAt: timeToJSON(t.IssuedAt), + JWTID: t.JWTID, + UpdatedAt: timeToJSON(t.UpdatedAt), + AuthorizedParty: t.AuthorizedParty, + Nonce: t.Nonce, + AuthTime: timeToJSON(t.AuthTime), + AccessTokenHash: t.AccessTokenHash, + CodeHash: t.CodeHash, + AuthenticationContextClassReference: t.AuthenticationContextClassReference, + AuthenticationMethodsReferences: t.AuthenticationMethodsReferences, + ClientID: t.ClientID, + } + return json.Marshal(j) +} + func (t *IDTokenClaims) UnmarshalJSON(b []byte) error { - var i jsonIDToken + var i jsonToken if err := json.Unmarshal(b, &i); err != nil { return err } audience := i.Audiences - // if len(audience) == 1 { - // audience = strings.Split(audience[0], " ") - // } + if len(audience) == 1 { + audience = strings.Split(audience[0], " ") + } t.Issuer = i.Issuer t.Subject = i.Subject t.Audiences = audience @@ -50,54 +179,6 @@ func (t *IDTokenClaims) UnmarshalJSON(b []byte) error { return nil } -func (t *IDTokenClaims) MarshalJSON() ([]byte, error) { - j := jsonIDToken{ - Issuer: t.Issuer, - Subject: t.Subject, - Audiences: t.Audiences, - Expiration: t.Expiration.Unix(), - IssuedAt: t.IssuedAt.Unix(), - AuthTime: t.AuthTime.Unix(), - Nonce: t.Nonce, - AuthenticationContextClassReference: t.AuthenticationContextClassReference, - AuthenticationMethodsReferences: t.AuthenticationMethodsReferences, - AuthorizedParty: t.AuthorizedParty, - AccessTokenHash: t.AccessTokenHash, - CodeHash: t.CodeHash, - } - return json.Marshal(j) -} - -type jsonIDToken struct { - Issuer string `json:"iss,omitempty"` - Subject string `json:"sub,omitempty"` - Audiences []string `json:"aud,omitempty"` - Expiration int64 `json:"exp,omitempty"` - IssuedAt int64 `json:"iat,omitempty"` - AuthTime int64 `json:"auth_time,omitempty"` - Nonce string `json:"nonce,omitempty"` - AuthenticationContextClassReference string `json:"acr,omitempty"` - AuthenticationMethodsReferences []string `json:"amr,omitempty"` - AuthorizedParty string `json:"azp,omitempty"` - AccessTokenHash string `json:"at_hash,omitempty"` - CodeHash string `json:"c_hash,omitempty"` -} - -type Tokens struct { - *oauth2.Token - IDTokenClaims *IDTokenClaims - IDToken string -} - -type AccessTokenClaims struct { - Issuer string - Subject string - Audiences []string - Expiration time.Time - IssuedAt time.Time - NotBefore time.Time -} - func ClaimHash(claim string, sigAlgorithm jose.SignatureAlgorithm) (string, error) { hash, err := utils.GetHashAlgorithm(sigAlgorithm) if err != nil { @@ -106,3 +187,10 @@ func ClaimHash(claim string, sigAlgorithm jose.SignatureAlgorithm) (string, erro return utils.HashString(hash, claim), nil } + +func timeToJSON(t time.Time) int64 { + if t.IsZero() { + return 0 + } + return t.Unix() +} diff --git a/pkg/op/default_op.go b/pkg/op/default_op.go index 5acae08..8d140b5 100644 --- a/pkg/op/default_op.go +++ b/pkg/op/default_op.go @@ -3,7 +3,6 @@ package op import ( "context" "net/http" - "time" "github.com/gorilla/schema" @@ -20,8 +19,6 @@ const ( AuthMethodBasic AuthMethod = "client_secret_basic" AuthMethodPost = "client_secret_post" AuthMethodNone = "none" - - DefaultIDTokenValidity = time.Duration(5 * time.Minute) ) var ( @@ -47,9 +44,8 @@ type DefaultOP struct { } type Config struct { - Issuer string - IDTokenValidity time.Duration - CryptoKey [32]byte + Issuer string + CryptoKey [32]byte // ScopesSupported: oidc.SupportedScopes, // ResponseTypesSupported: responseTypes, // GrantTypesSupported: oidc.SupportedGrantTypes, @@ -198,13 +194,6 @@ func (p *DefaultOP) Crypto() Crypto { return p.crypto } -func (p *DefaultOP) IDTokenValidity() time.Duration { - if p.config.IDTokenValidity == 0 { - p.config.IDTokenValidity = DefaultIDTokenValidity - } - return p.config.IDTokenValidity -} - func (p *DefaultOP) HandleKeys(w http.ResponseWriter, r *http.Request) { Keys(w, r, p) } diff --git a/pkg/op/tokenrequest.go b/pkg/op/tokenrequest.go index cf32432..c8a7fe8 100644 --- a/pkg/op/tokenrequest.go +++ b/pkg/op/tokenrequest.go @@ -4,7 +4,6 @@ import ( "context" "errors" "net/http" - "time" "github.com/gorilla/schema" @@ -14,7 +13,6 @@ import ( type Exchanger interface { Issuer() string - IDTokenValidity() time.Duration Storage() Storage Decoder() *schema.Decoder Signer() Signer