new packaging

This commit is contained in:
Livio Amstutz 2019-11-19 16:28:51 +01:00
parent 4a91d34f2e
commit 6b4b8f20a5
5 changed files with 60 additions and 45 deletions

View file

@ -1,4 +1,4 @@
package defaults package rp
import ( import (
"context" "context"
@ -12,8 +12,6 @@ import (
"github.com/caos/oidc/pkg/oidc" "github.com/caos/oidc/pkg/oidc"
grants_tx "github.com/caos/oidc/pkg/oidc/grants/tokenexchange" grants_tx "github.com/caos/oidc/pkg/oidc/grants/tokenexchange"
"github.com/caos/oidc/pkg/rp"
"github.com/caos/oidc/pkg/rp/tokenexchange"
"github.com/caos/oidc/pkg/utils" "github.com/caos/oidc/pkg/utils"
) )
@ -23,18 +21,18 @@ const (
) )
type DefaultRP struct { type DefaultRP struct {
endpoints rp.Endpoints endpoints Endpoints
oauthConfig oauth2.Config oauthConfig oauth2.Config
config *rp.Config config *Config
httpClient *http.Client httpClient *http.Client
cookieHandler *utils.CookieHandler cookieHandler *utils.CookieHandler
verifier rp.Verifier verifier Verifier
} }
func NewDefaultRelayingParty(rpConfig *rp.Config, rpOpts ...DefaultReplayingPartyOpts) (tokenexchange.DelegationTokenExchangeRP, error) { func NewDefaultRelayingParty(rpConfig *Config, rpOpts ...DefaultRPOpts) (DelegationTokenExchangeRP, error) {
p := &DefaultRP{ p := &DefaultRP{
config: rpConfig, config: rpConfig,
httpClient: utils.DefaultHTTPClient, httpClient: utils.DefaultHTTPClient,
@ -55,15 +53,15 @@ func NewDefaultRelayingParty(rpConfig *rp.Config, rpOpts ...DefaultReplayingPart
return p, nil return p, nil
} }
type DefaultReplayingPartyOpts func(p *DefaultRP) type DefaultRPOpts func(p *DefaultRP)
func WithCookieHandler(cookieHandler *utils.CookieHandler) DefaultReplayingPartyOpts { func WithCookieHandler(cookieHandler *utils.CookieHandler) DefaultRPOpts {
return func(p *DefaultRP) { return func(p *DefaultRP) {
p.cookieHandler = cookieHandler p.cookieHandler = cookieHandler
} }
} }
func WithHTTPClient(client *http.Client) DefaultReplayingPartyOpts { func WithHTTPClient(client *http.Client) DefaultRPOpts {
return func(p *DefaultRP) { return func(p *DefaultRP) {
p.httpClient = client p.httpClient = client
} }
@ -169,7 +167,7 @@ func (p *DefaultRP) discover() error {
return err return err
} }
p.endpoints = rp.GetEndpoints(discoveryConfig) p.endpoints = GetEndpoints(discoveryConfig)
p.oauthConfig = oauth2.Config{ p.oauthConfig = oauth2.Config{
ClientID: p.config.ClientID, ClientID: p.config.ClientID,
ClientSecret: p.config.ClientSecret, ClientSecret: p.config.ClientSecret,

View file

@ -1,4 +1,4 @@
package defaults package rp
import ( import (
"bytes" "bytes"
@ -15,11 +15,24 @@ import (
"gopkg.in/square/go-jose.v2" "gopkg.in/square/go-jose.v2"
"github.com/caos/oidc/pkg/oidc" "github.com/caos/oidc/pkg/oidc"
"github.com/caos/oidc/pkg/rp"
str_utils "github.com/caos/utils/strings" str_utils "github.com/caos/utils/strings"
) )
func NewVerifier(issuer, clientID string, keySet oidc.KeySet, confOpts ...ConfFunc) rp.Verifier { //DefaultVerifier implements the `Verifier` interface
type DefaultVerifier struct {
config *verifierConfig
keySet oidc.KeySet
}
//ConfFunc is the type for providing dynamic verifierConfig
type ConfFunc func(*verifierConfig)
//ACRVerifier specifies the function to be used by the `DefaultVerifier` for validating the acr claim
type ACRVerifier func(string) error
//NewDefaultVerifier creates `DefaultVerifier` with the given
//issuer, clientID, keyset and possible configOptions
func NewDefaultVerifier(issuer, clientID string, keySet oidc.KeySet, confOpts ...ConfFunc) Verifier {
conf := &verifierConfig{ conf := &verifierConfig{
issuer: issuer, issuer: issuer,
clientID: clientID, clientID: clientID,
@ -33,52 +46,53 @@ func NewVerifier(issuer, clientID string, keySet oidc.KeySet, confOpts ...ConfFu
opt(conf) opt(conf)
} }
} }
return &Verifier{config: conf, keySet: keySet} return &DefaultVerifier{config: conf, keySet: keySet}
} }
type Verifier struct { //WithIgnoreIssuedAt will turn off iat claim verification
config *verifierConfig
keySet oidc.KeySet
}
type ConfFunc func(*verifierConfig)
func WithIgnoreIssuedAt() func(*verifierConfig) { func WithIgnoreIssuedAt() func(*verifierConfig) {
return func(conf *verifierConfig) { return func(conf *verifierConfig) {
conf.iat.ignore = true conf.iat.ignore = true
} }
} }
//WithIssuedAtOffset mitigates the risk of iat to be in the future
//because of clock skews with the ability to add an offset to the current time
func WithIssuedAtOffset(offset time.Duration) func(*verifierConfig) { func WithIssuedAtOffset(offset time.Duration) func(*verifierConfig) {
return func(conf *verifierConfig) { return func(conf *verifierConfig) {
conf.iat.offset = offset conf.iat.offset = offset
} }
} }
//WithIssuedAtMaxAge provides the ability to define the maximum duration between iat and now
func WithIssuedAtMaxAge(maxAge time.Duration) func(*verifierConfig) { func WithIssuedAtMaxAge(maxAge time.Duration) func(*verifierConfig) {
return func(conf *verifierConfig) { return func(conf *verifierConfig) {
conf.iat.maxAge = maxAge conf.iat.maxAge = maxAge
} }
} }
//WithNonce TODO: ?
func WithNonce(nonce string) func(*verifierConfig) { func WithNonce(nonce string) func(*verifierConfig) {
return func(conf *verifierConfig) { return func(conf *verifierConfig) {
conf.nonce = nonce conf.nonce = nonce
} }
} }
//WithACRVerifier sets the verifier for the acr claim
func WithACRVerifier(verifier ACRVerifier) func(*verifierConfig) { func WithACRVerifier(verifier ACRVerifier) func(*verifierConfig) {
return func(conf *verifierConfig) { return func(conf *verifierConfig) {
conf.acr = verifier conf.acr = verifier
} }
} }
//WithAuthTimeMaxAge provides the ability to define the maximum duration between auth_time and now
func WithAuthTimeMaxAge(maxAge time.Duration) func(*verifierConfig) { func WithAuthTimeMaxAge(maxAge time.Duration) func(*verifierConfig) {
return func(conf *verifierConfig) { return func(conf *verifierConfig) {
conf.maxAge = maxAge conf.maxAge = maxAge
} }
} }
//WithSupportedSigningAlgorithms overwrites the default RS256 signing algorithm
func WithSupportedSigningAlgorithms(algs ...string) func(*verifierConfig) { func WithSupportedSigningAlgorithms(algs ...string) func(*verifierConfig) {
return func(conf *verifierConfig) { return func(conf *verifierConfig) {
conf.supportedSignAlgs = algs conf.supportedSignAlgs = algs
@ -111,9 +125,9 @@ type iatConfig struct {
maxAge time.Duration maxAge time.Duration
} }
type ACRVerifier func(string) error //DefaultACRVerifier implements `ACRVerifier` returning an error
//if non of the provided values matches the acr claim
func DefaultACRVerifier(possibleValues []string) func(string) error { func DefaultACRVerifier(possibleValues []string) ACRVerifier {
return func(acr string) error { return func(acr string) error {
if !str_utils.Contains(possibleValues, acr) { if !str_utils.Contains(possibleValues, acr) {
return ErrAcrInvalid(possibleValues, acr) return ErrAcrInvalid(possibleValues, acr)
@ -122,7 +136,10 @@ func DefaultACRVerifier(possibleValues []string) func(string) error {
} }
} }
func (v *Verifier) Verify(ctx context.Context, accessToken, idTokenString string) (*oidc.IDTokenClaims, error) { //Verify implements the `Verify` method of the `Verifier` interface
//according to https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
//and https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowTokenValidation
func (v *DefaultVerifier) Verify(ctx context.Context, accessToken, idTokenString string) (*oidc.IDTokenClaims, error) {
v.config.now = time.Now().UTC() v.config.now = time.Now().UTC()
idToken, err := v.VerifyIDToken(ctx, idTokenString) idToken, err := v.VerifyIDToken(ctx, idTokenString)
if err != nil { if err != nil {
@ -134,14 +151,15 @@ func (v *Verifier) Verify(ctx context.Context, accessToken, idTokenString string
return idToken, nil return idToken, nil
} }
func (v *Verifier) now() time.Time { func (v *DefaultVerifier) now() time.Time {
if v.config.now.IsZero() { if v.config.now.IsZero() {
v.config.now = time.Now().UTC().Round(time.Second) v.config.now = time.Now().UTC().Round(time.Second)
} }
return v.config.now return v.config.now
} }
func (v *Verifier) VerifyIDToken(ctx context.Context, idTokenString string) (*oidc.IDTokenClaims, error) { //VerifyIDToken: https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
func (v *DefaultVerifier) VerifyIDToken(ctx context.Context, idTokenString string) (*oidc.IDTokenClaims, error) {
//1. if encrypted --> decrypt //1. if encrypted --> decrypt
decrypted, err := v.decryptToken(idTokenString) decrypted, err := v.decryptToken(idTokenString)
if err != nil { if err != nil {
@ -206,7 +224,7 @@ func (v *Verifier) VerifyIDToken(ctx context.Context, idTokenString string) (*oi
// return err // return err
} }
func (v *Verifier) parseToken(tokenString string) (*oidc.IDTokenClaims, []byte, error) { func (v *DefaultVerifier) parseToken(tokenString string) (*oidc.IDTokenClaims, []byte, error) {
parts := strings.Split(tokenString, ".") parts := strings.Split(tokenString, ".")
if len(parts) != 3 { if len(parts) != 3 {
return nil, nil, ValidationError("token contains an invalid number of segments") //TODO: err NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed) return nil, nil, ValidationError("token contains an invalid number of segments") //TODO: err NewValidationError("token contains an invalid number of segments", ValidationErrorMalformed)
@ -220,14 +238,14 @@ func (v *Verifier) parseToken(tokenString string) (*oidc.IDTokenClaims, []byte,
return idToken, payload, err return idToken, payload, err
} }
func (v *Verifier) checkIssuer(issuer string) error { func (v *DefaultVerifier) checkIssuer(issuer string) error {
if v.config.issuer != issuer { if v.config.issuer != issuer {
return ErrIssuerInvalid(v.config.issuer, issuer) return ErrIssuerInvalid(v.config.issuer, issuer)
} }
return nil return nil
} }
func (v *Verifier) checkAudience(audiences []string) error { func (v *DefaultVerifier) checkAudience(audiences []string) error {
if !str_utils.Contains(audiences, v.config.clientID) { if !str_utils.Contains(audiences, v.config.clientID) {
return ErrAudienceMissingClientID(v.config.clientID) return ErrAudienceMissingClientID(v.config.clientID)
} }
@ -238,7 +256,7 @@ func (v *Verifier) checkAudience(audiences []string) error {
//4. if multiple aud strings --> check if azp //4. if multiple aud strings --> check if azp
//5. if azp --> check azp == client_id //5. if azp --> check azp == client_id
func (v *Verifier) checkAuthorizedParty(audiences []string, authorizedParty string) error { func (v *DefaultVerifier) checkAuthorizedParty(audiences []string, authorizedParty string) error {
if len(audiences) > 1 { if len(audiences) > 1 {
if authorizedParty == "" { if authorizedParty == "" {
return ErrAzpMissing() return ErrAzpMissing()
@ -250,7 +268,7 @@ func (v *Verifier) checkAuthorizedParty(audiences []string, authorizedParty stri
return nil return nil
} }
func (v *Verifier) checkSignature(ctx context.Context, idTokenString string, payload []byte) (jose.SignatureAlgorithm, error) { func (v *DefaultVerifier) checkSignature(ctx context.Context, idTokenString string, payload []byte) (jose.SignatureAlgorithm, error) {
jws, err := jose.ParseSigned(idTokenString) jws, err := jose.ParseSigned(idTokenString)
if err != nil { if err != nil {
return "", err return "", err
@ -344,7 +362,7 @@ func (v *Verifier) checkSignature(ctx context.Context, idTokenString string, pay
// return nil // return nil
// } // }
func (v *Verifier) checkExpiration(expiration time.Time) error { func (v *DefaultVerifier) checkExpiration(expiration time.Time) error {
expiration = expiration.Round(time.Second) expiration = expiration.Round(time.Second)
if !v.now().Before(expiration) { if !v.now().Before(expiration) {
return ErrExpInvalid(expiration) return ErrExpInvalid(expiration)
@ -352,7 +370,7 @@ func (v *Verifier) checkExpiration(expiration time.Time) error {
return nil return nil
} }
func (v *Verifier) checkIssuedAt(issuedAt time.Time) error { func (v *DefaultVerifier) checkIssuedAt(issuedAt time.Time) error {
if v.config.iat.ignore { if v.config.iat.ignore {
return nil return nil
} }
@ -370,7 +388,7 @@ func (v *Verifier) checkIssuedAt(issuedAt time.Time) error {
} }
return nil return nil
} }
func (v *Verifier) checkNonce(nonce string) error { func (v *DefaultVerifier) checkNonce(nonce string) error {
if v.config.nonce == "" { if v.config.nonce == "" {
return nil return nil
} }
@ -379,13 +397,13 @@ func (v *Verifier) checkNonce(nonce string) error {
} }
return nil return nil
} }
func (v *Verifier) checkAuthorizationContextClassReference(acr string) error { func (v *DefaultVerifier) checkAuthorizationContextClassReference(acr string) error {
if v.config.acr != nil { if v.config.acr != nil {
return v.config.acr(acr) return v.config.acr(acr)
} }
return nil return nil
} }
func (v *Verifier) checkAuthTime(authTime time.Time) error { func (v *DefaultVerifier) checkAuthTime(authTime time.Time) error {
if v.config.maxAge == 0 { if v.config.maxAge == 0 {
return nil return nil
} }
@ -400,7 +418,7 @@ func (v *Verifier) checkAuthTime(authTime time.Time) error {
return nil return nil
} }
func (v *Verifier) decryptToken(tokenString string) (string, error) { func (v *DefaultVerifier) decryptToken(tokenString string) (string, error) {
return tokenString, nil //TODO: impl return tokenString, nil //TODO: impl
} }
@ -423,7 +441,7 @@ func (v *Verifier) decryptToken(tokenString string) (string, error) {
// return token, nil //TODO: impl // return token, nil //TODO: impl
// } // }
func (v *Verifier) verifyAccessToken(accessToken, atHash string, sigAlgorithm jose.SignatureAlgorithm) error { func (v *DefaultVerifier) verifyAccessToken(accessToken, atHash string, sigAlgorithm jose.SignatureAlgorithm) error {
if atHash == "" { if atHash == "" {
return nil //TODO: return error return nil //TODO: return error
} }

View file

@ -1,4 +1,4 @@
package defaults package rp
import ( import (
"github.com/caos/oidc/pkg/oidc/grants/tokenexchange" "github.com/caos/oidc/pkg/oidc/grants/tokenexchange"

View file

@ -1,4 +1,4 @@
package defaults package rp
import ( import (
"fmt" "fmt"

View file

@ -1,4 +1,4 @@
package tokenexchange package rp
import ( import (
"context" "context"
@ -6,12 +6,11 @@ import (
"golang.org/x/oauth2" "golang.org/x/oauth2"
"github.com/caos/oidc/pkg/oidc/grants/tokenexchange" "github.com/caos/oidc/pkg/oidc/grants/tokenexchange"
"github.com/caos/oidc/pkg/rp"
) )
//TokenExchangeRP extends the `RelayingParty` interface for the *draft* oauth2 `Token Exchange` //TokenExchangeRP extends the `RelayingParty` interface for the *draft* oauth2 `Token Exchange`
type TokenExchangeRP interface { type TokenExchangeRP interface {
rp.RelayingParty RelayingParty
//TokenExchange implement the `Token Echange Grant` exchanging some token for an other //TokenExchange implement the `Token Echange Grant` exchanging some token for an other
TokenExchange(context.Context, *tokenexchange.TokenExchangeRequest) (*oauth2.Token, error) TokenExchange(context.Context, *tokenexchange.TokenExchangeRequest) (*oauth2.Token, error)