coupergateway / couper

Couper is a lightweight API gateway designed to support developers in building and operating API-driven Web projects
https://couper.io
MIT License
84 stars 15 forks source link

couper-examples test configuration edge failing #807

Open johakoch opened 6 months ago

johakoch commented 6 months ago

See https://github.com/coupergateway/couper-examples/actions/workflows/test.yaml

This results from oidcConfig.GetIssuer() (creating a sync request for the OIDC configuration) now (after https://github.com/coupergateway/couper/pull/796) being in the path for couper verify:

Another minor issue:

  issuer, err := oidcConfig.GetIssuer()
  if err != nil {
    return nil, errors.Oauth2.With(err)
                ^^^^^^^^^^^^^^^^^^^^^^^

If we keep the JWT parser being created in NewOidcClient(), err should not be wrapped here, as errors.Oauth2 is for runtime errors.

johakoch commented 6 months ago

@malud Would it be safe to ignore backend errors with couper verify?

johakoch commented 4 months ago

We could skip the "oidc" example directory for the usual verification and do a

docker-compose up --abort-on-container-exit

instead.

That would require creating different docker-compose.yaml files for the two image-tags to be checked.

johakoch commented 4 months ago

A way to solve this in couper itself: lazy-initialize the JWT parser for OIDC:

func NewOidcClient(evalCtx *hcl.EvalContext, oidcConfig *oidc.Config) (*OidcClient, error) {
    backends := oidcConfig.Backends()
    acClient, err := NewAuthCodeClient(evalCtx, oidcConfig, oidcConfig, backends["token_backend"])
    if err != nil {
        return nil, err
    }

    o := &OidcClient{
        AuthCodeClient: acClient,
        backends:       backends,
        config:         oidcConfig,
        // don't create JWT parser here
    }

// ...

func (o *OidcClient) getJwtParser() (*jwt.Parser, error) {
    if o.jwtParser == nil {
        issuer, err := o.config.GetIssuer()
        if err != nil {
            return nil, err
        }
        options := []jwt.ParserOption{
// ...
        }
        o.jwtParser = jwt.NewParser(options...)
    }
    return o.jwtParser, nil
}

// ...

func (o *OidcClient) validateTokenResponseData(ctx context.Context, tokenResponseData map[string]interface{}, hashedVerifierValue, verifierValue, accessToken string) error {
    idTokenString, ok := tokenResponseData["id_token"].(string)
    if !ok {
        return errors.Oauth2.Message("missing id_token in token response")
    }

    jwtParser, err := o.getJwtParser()
    if err != nil {
        return err
    }

    idTokenClaims := jwt.MapClaims{}
    _, err = jwtParser.ParseWithClaims(idTokenString, idTokenClaims, o.keyfunc)
// ...