diff --git a/pkg/oidc/authorization.go b/pkg/oidc/authorization.go index a78d9dc..cfcacdc 100644 --- a/pkg/oidc/authorization.go +++ b/pkg/oidc/authorization.go @@ -7,6 +7,7 @@ import ( "time" "golang.org/x/text/language" + "gopkg.in/square/go-jose.v2" ) const ( @@ -149,12 +150,12 @@ type AccessTokenResponse struct { } type JWTTokenRequest struct { - Issuer string `json:"iss"` - Subject string `json:"sub"` - Scopes Scopes `json:"scope"` - Audience string `json:"aud"` - IssuedAt Time `json:"iat"` - ExpiresAt Time `json:"exp"` + Issuer string `json:"iss"` + Subject string `json:"sub"` + Scopes Scopes `json:"scope"` + Audience []string `json:"aud"` + IssuedAt Time `json:"iat"` + ExpiresAt Time `json:"exp"` } func (j *JWTTokenRequest) GetClientID() string { @@ -185,7 +186,7 @@ func (j *JWTTokenRequest) GetIssuer() string { } func (j *JWTTokenRequest) GetAudience() []string { - return []string{j.Audience} + return j.Audience } func (j *JWTTokenRequest) GetExpiration() time.Time { @@ -212,6 +213,8 @@ func (j *JWTTokenRequest) GetAuthorizedParty() string { return "" } +func (j *JWTTokenRequest) SetSignature(algorithm jose.SignatureAlgorithm) {} + type TokenExchangeRequest struct { subjectToken string `schema:"subject_token"` subjectTokenType string `schema:"subject_token_type"` diff --git a/pkg/op/default_op.go b/pkg/op/default_op.go index 9cbc03c..cd21b03 100644 --- a/pkg/op/default_op.go +++ b/pkg/op/default_op.go @@ -238,6 +238,10 @@ func (p *DefaultOP) HandleDiscovery(w http.ResponseWriter, r *http.Request) { Discover(w, CreateDiscoveryConfig(p, p.Signer())) } +func (p *DefaultOP) Probes() []ProbesFn { + return nil +} + func (p *DefaultOP) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) ([]byte, error) { keyID := "" for _, sig := range jws.Signatures { diff --git a/pkg/op/storage.go b/pkg/op/storage.go index 0eee936..cb1a2fc 100644 --- a/pkg/op/storage.go +++ b/pkg/op/storage.go @@ -30,6 +30,7 @@ type OPStorage interface { AuthorizeClientIDSecret(context.Context, string, string) error GetUserinfoFromScopes(context.Context, string, []string) (*oidc.Userinfo, error) GetUserinfoFromToken(context.Context, string, string) (*oidc.Userinfo, error) + GetKeysByServiceAccount(ctx context.Context, id string) (*jose.JSONWebKeySet, error) } type Storage interface { diff --git a/pkg/op/tokenrequest.go b/pkg/op/tokenrequest.go index 09ffd67..093b906 100644 --- a/pkg/op/tokenrequest.go +++ b/pkg/op/tokenrequest.go @@ -144,20 +144,20 @@ func AuthorizeCodeChallenge(ctx context.Context, tokenReq *oidc.AccessTokenReque } type ClientJWTVerifier struct { - claims *oidc.JWTTokenRequest - Storage + claims *oidc.JWTTokenRequest + storage Storage } func (c ClientJWTVerifier) Storage() Storage { - panic("implement me") + return c.storage } func (c ClientJWTVerifier) Issuer() string { - panic("implement me") + return c.claims.Issuer } func (c ClientJWTVerifier) ClientID() string { - panic("implement me") + return c.claims.Issuer } func (c ClientJWTVerifier) SupportedSignAlgs() []string { @@ -165,7 +165,8 @@ func (c ClientJWTVerifier) SupportedSignAlgs() []string { } func (c ClientJWTVerifier) KeySet() oidc.KeySet { - return c.claims + // return c.claims + return nil } func (c ClientJWTVerifier) ACR() oidc.ACRVerifier { @@ -177,11 +178,13 @@ func (c ClientJWTVerifier) MaxAge() time.Duration { } func (c ClientJWTVerifier) MaxAgeIAT() time.Duration { - panic("implement me") + //TODO: define in conf/opts + return 1 * time.Hour } func (c ClientJWTVerifier) Offset() time.Duration { - panic("implement me") + //TODO: define in conf/opts + return time.Second } func JWTExchange(w http.ResponseWriter, r *http.Request, exchanger VerifyExchanger) { @@ -189,12 +192,11 @@ func JWTExchange(w http.ResponseWriter, r *http.Request, exchanger VerifyExchang if err != nil { RequestError(w, r, err) } - claims := new(oidc.JWTTokenRequest) - //var keyset oidc.KeySet - verifier := new(ClientJWTVerifier) - req, err := VerifyJWTAssertion(r.Context(), assertion, verifier) + + claims, err := VerifyJWTAssertion(r.Context(), assertion, exchanger.Storage()) if err != nil { RequestError(w, r, err) + return } resp, err := CreateJWTTokenResponse(r.Context(), claims, exchanger) @@ -210,24 +212,39 @@ type JWTAssertionVerifier interface { oidc.Verifier } -func VerifyJWTAssertion(ctx context.Context, assertion string, v JWTAssertionVerifier) (*oidc.JWTTokenRequest, error) { - claims := new(oidc.JWTTokenRequest) - payload, err := oidc.ParseToken(assertion, claims) - - oidc.CheckAudience(claims.Audience, v) - - oidc.CheckExpiration(claims.ExpiresAt, v) - - oidc.CheckIssuedAt(claims.IssuedAt, v) - - if claims.Issuer != claims.Subject { - +func VerifyJWTAssertion(ctx context.Context, assertion string, storage Storage) (*oidc.JWTTokenRequest, error) { + verifier := &ClientJWTVerifier{ + storage: storage, + claims: new(oidc.JWTTokenRequest), + } + payload, err := oidc.ParseToken(assertion, verifier.claims) + if err != nil { + return nil, err } - v.Storage().GetClientByClientID(ctx, claims.Issuer) - keySet := &ClientAssertionKeySet{v.Storage(), claims.Issuer} + if err = oidc.CheckAudience(verifier.claims.GetAudience(), verifier); err != nil { + return nil, err + } - oidc.CheckSignature(ctx, assertion, payload, claims, nil, keySet) + if err = oidc.CheckExpiration(verifier.claims.GetExpiration(), verifier); err != nil { + return nil, err + } + + if err = oidc.CheckIssuedAt(verifier.claims.GetIssuedAt(), verifier); err != nil { + return nil, err + } + + if verifier.claims.Issuer != verifier.claims.Subject { + //TODO: implement delegation (openid core / oauth rfc) + } + verifier.Storage().GetClientByClientID(ctx, verifier.claims.Subject) + + keySet := &ClientAssertionKeySet{storage, verifier.claims.Subject} + + if err = oidc.CheckSignature(ctx, assertion, payload, verifier.claims, nil, keySet); err != nil { + return nil, err + } + return verifier.claims, nil } type ClientAssertionKeySet struct { @@ -241,7 +258,7 @@ func (c *ClientAssertionKeySet) VerifySignature(ctx context.Context, jws *jose.J keyID = sig.Header.KeyID break } - keySet, err := c.Storage.GetKeysByServiceAccount(id) + keySet, err := c.Storage.GetKeysByServiceAccount(ctx, c.id) if err != nil { return nil, errors.New("error fetching keys") }