You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Replace the custom Lambda API Gateway authorizer for the CAPE common API with a
native API Gateway Cognito User Pools authorizer, so AWS handles JWT
signature verification, expiry, and JWKS rotation instead of homegrown code.
Motivation
The current default authorizer (assets/api/authz/default_apigw_authorizer.py)
base64url-decodes the JWT payload and maps claims, but it does not verify the
token signature and effectively allows all callers. That is a security gap and
ongoing maintenance burden. A native Cognito User Pools authorizer:
verifies the token against the user pool (signature, expiry, token_use),
removes the need to maintain JWT verification / JWKS handling in Lambda,
reduces the risk that login breaks due to homegrown verification bugs.
The frontend already sends Authorization: Bearer <cognito access_token>, and in
this deployment the Cognito username == email, so a human-readable triggering_user_name is available from token claims without switching to the id
token or adding scopes.
Current state (context for whoever picks this up)
The shared ApiGateway construct assumes every authorizer is a Lambda:
capeinfra/resources/api.py_create_api_authorizer_lambdas() iterates the authorizers config and, for each, builds a Lambda from authorizer_def["file"], a lambda role, and an API-gateway invoke role. There
is no branch for a non-Lambda authorizer.
_render_spec() unconditionally sets role and uri
(handler.function.invoke_arn) into the template kwargs per authorizer.
The OpenAPI template assets/api/capi/capi-openapi-301.yaml.j2 renders the securitySchemes block with x-amazon-apigateway-authtype: "custom" and authorizerUri / authorizerCredentials (lines ~2116-2152), plus a top-level security: list generated from the authorizer names.
Config: Pulumi.cape-cod-dev.yaml / Pulumi.cape-cod-public.yaml define the default authorizer with type: request and file: pointing at the custom
authorizer source.
Handlers read identity from the custom authorizer context:
post_workflow_run.py reads triggering_user_id / triggering_user_name
from event.requestContext.authorizer and writes conf.cape.
get_workflow_runs.py reads triggering_user_id from event.requestContext.authorizer.
The Cognito user pool already exists at capeinfra.meta.principals.user_pool
(see capeinfra/meta/capemeta.py), and api.py already imports the capeinfra.meta singleton (it uses capeinfra.meta.policies), so the user pool
ARN (capeinfra.meta.principals.user_pool.arn) is reachable from the construct.
Proposed change (four sites)
capeinfra/resources/api.py
Branch the authorizer loop on authorizer_def["type"]. For cognito_user_pools, skip Lambda + role creation and instead record the
provider ARN(s) (from capeinfra.meta.principals.user_pool.arn).
Branch _render_spec() so a cognito authorizer emits provider_arns
instead of role/uri.
Keep the existing Lambda path fully intact for any non-cognito authorizer
(backward compatible; the construct is shared by all CAPE APIs).
assets/api/capi/capi-openapi-301.yaml.j2
Add a securitySchemes variant for cognito_user_pools: x-amazon-apigateway-authtype: cognito_user_pools with x-amazon-apigateway-authorizer: { type: cognito_user_pools, providerARNs: [...] },
and name: Authorization, in: header.
Pulumi.cape-cod-dev.yaml and Pulumi.cape-cod-public.yaml
Change the default authorizer type to cognito_user_pools and drop file (no Lambda source needed).
Handlers assets/api/capi/handlers/post_workflow_run.py and get_workflow_runs.py
Resolve identity from event.requestContext.authorizer.claims.* instead of
the custom context:
triggering_user_id = claims["sub"]
triggering_user_name = claims.get("email") or claims.get("cognito:username") or claims.get("username")
capeinfra/resources/api.py is a shared construct used by all CAPE APIs;
the change must stay backward compatible for the Lambda authorizer path.
Changing the authorizer type on an existing API Gateway may force
redeployment of the authorizer/API resources.
Must be validated with pulumi preview / a dev deploy: confirm the API accepts
a Cognito access token, that event.requestContext.authorizer.claims contains sub and username/email, and that conf.cape is still populated correctly
on trigger.
Depends-on-nothing, but pairs with the frontend work already merged (shared
axios client sending the bearer token; per-user run listing via conf.cape).
Summary
Replace the custom Lambda API Gateway authorizer for the CAPE common API with a
native API Gateway Cognito User Pools authorizer, so AWS handles JWT
signature verification, expiry, and JWKS rotation instead of homegrown code.
Motivation
The current default authorizer (
assets/api/authz/default_apigw_authorizer.py)base64url-decodes the JWT payload and maps claims, but it does not verify the
token signature and effectively allows all callers. That is a security gap and
ongoing maintenance burden. A native Cognito User Pools authorizer:
token_use),The frontend already sends
Authorization: Bearer <cognito access_token>, and inthis deployment the Cognito
username == email, so a human-readabletriggering_user_nameis available from token claims without switching to the idtoken or adding scopes.
Current state (context for whoever picks this up)
The shared
ApiGatewayconstruct assumes every authorizer is a Lambda:capeinfra/resources/api.py_create_api_authorizer_lambdas()iterates theauthorizersconfig and, for each, builds a Lambda fromauthorizer_def["file"], a lambda role, and an API-gateway invoke role. Thereis no branch for a non-Lambda authorizer.
_render_spec()unconditionally setsroleanduri(
handler.function.invoke_arn) into the template kwargs per authorizer.assets/api/capi/capi-openapi-301.yaml.j2renders thesecuritySchemesblock withx-amazon-apigateway-authtype: "custom"andauthorizerUri/authorizerCredentials(lines ~2116-2152), plus a top-levelsecurity:list generated from the authorizer names.Pulumi.cape-cod-dev.yaml/Pulumi.cape-cod-public.yamldefine thedefaultauthorizer withtype: requestandfile:pointing at the customauthorizer source.
post_workflow_run.pyreadstriggering_user_id/triggering_user_namefrom
event.requestContext.authorizerand writesconf.cape.get_workflow_runs.pyreadstriggering_user_idfromevent.requestContext.authorizer.The Cognito user pool already exists at
capeinfra.meta.principals.user_pool(see
capeinfra/meta/capemeta.py), andapi.pyalready imports thecapeinfra.metasingleton (it usescapeinfra.meta.policies), so the user poolARN (
capeinfra.meta.principals.user_pool.arn) is reachable from the construct.Proposed change (four sites)
capeinfra/resources/api.pyauthorizer_def["type"]. Forcognito_user_pools, skip Lambda + role creation and instead record theprovider ARN(s) (from
capeinfra.meta.principals.user_pool.arn)._render_spec()so a cognito authorizer emitsprovider_arnsinstead of
role/uri.(backward compatible; the construct is shared by all CAPE APIs).
assets/api/capi/capi-openapi-301.yaml.j2securitySchemesvariant forcognito_user_pools:x-amazon-apigateway-authtype: cognito_user_poolswithx-amazon-apigateway-authorizer: { type: cognito_user_pools, providerARNs: [...] },and
name: Authorization,in: header.Pulumi.cape-cod-dev.yamlandPulumi.cape-cod-public.yamldefaultauthorizertypetocognito_user_poolsand dropfile(no Lambda source needed).Handlers
assets/api/capi/handlers/post_workflow_run.pyandget_workflow_runs.pyevent.requestContext.authorizer.claims.*instead ofthe custom context:
triggering_user_id = claims["sub"]triggering_user_name = claims.get("email") or claims.get("cognito:username") or claims.get("username")tests/test_workflow_user_attribution.pyaccordingly.default_apigw_authorizer.pybecomes dead code; remove it (or keep only ifwe want a method-level fallback, see allow authorizer override at api method level #218).
Risks / validation
capeinfra/resources/api.pyis a shared construct used by all CAPE APIs;the change must stay backward compatible for the Lambda authorizer path.
redeployment of the authorizer/API resources.
pulumi preview/ a dev deploy: confirm the API acceptsa Cognito access token, that
event.requestContext.authorizer.claimscontainssubandusername/email, and thatconf.capeis still populated correctlyon trigger.
Related
axios client sending the bearer token; per-user run listing via
conf.cape).