new packaging
This commit is contained in:
parent
4a91d34f2e
commit
6b4b8f20a5
5 changed files with 60 additions and 45 deletions
|
@ -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,
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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"
|
|
@ -1,4 +1,4 @@
|
||||||
package defaults
|
package rp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
|
@ -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)
|
Loading…
Add table
Add a link
Reference in a new issue