Skip to content

[Bug] AWS: OIDC security scheme name has wrong suffix in generated OpenAPI spec, causing JWT authorizer not to be attached to routes #919

@alessandrofalc0ne

Description

@alessandrofalc0ne

Description

When using oidc_rule with ApiOptions(security=[...]) and MethodOptions(security=[...]),
the generated OpenAPI spec contains a name mismatch between the security scheme defined in
components/securitySchemes and the name referenced in route security definitions.

This causes API Gateway to silently ignore the JWT authorizer on all routes — the authorizer
is created in API Gateway but never attached to any route.

Environment

  • Nitric CLI: v1.61.1
  • Nitric Python SDK: v1.2.3
  • Provider: AWS (Pulumi)
  • Region: eu-west-1

Steps to Reproduce

from nitric.resources import api, ApiOptions, oidc_rule
from nitric.resources.apis import MethodOptions

cognito_auth = oidc_rule(
    "cognito-auth-rule",
    issuer="https://cognito-idp.eu-west-1.amazonaws.com/POOL_ID/.well-known/openid-configuration",
    audiences=["CLIENT_ID"]
)

auth_api = api("auth", ApiOptions(security=[cognito_auth([])]))

@auth_api.post("/logout")
async def logout_handler(ctx):
    ...

Root Cause

In nitric/resources/apis.py, _attach_oidc constructs the resource name as
f"{options.name}-{api_name}":

# nitric/resources/apis.py
def _attach_oidc(api_name: str, options: OidcOptions) -> OidcSecurityDefinition:
    return Nitric._create_resource(
        OidcSecurityDefinition, f"{options.name}-{api_name}", api_name, options
        #                        ^^^^^^^^^^^^^^^^^^^^^^^^^^
        #                        becomes self.name → sent as req.Id.Name via gRPC
    )

This name (cognito-auth-rule-auth) becomes the req.Id.Name sent via gRPC to the CLI,
which in pkg/collector/service.go uses it as the key for the security scheme:

// pkg/collector/service.go:152
s.apiSecurityDefinition[req.GetApiSecurityDefinition().GetApiName()][req.Id.GetName()] = req.GetApiSecurityDefinition()

And in pkg/collector/spec.go it becomes the key in SecuritySchemes:

// pkg/collector/spec.go:563-588
for schemeName, securityScheme := range serviceRequirements.apiSecurityDefinition[apiName] {
    // schemeName = "cognito-auth-rule-auth"
    api.Components.SecuritySchemes[schemeName] = ...
}

Meanwhile, route security references use the original name (cognito-auth-rule),
producing this mismatch in the final OpenAPI spec:

"securitySchemes": {
  "cognito-auth-rule-auth": { ... }   ← scheme key
},
"paths": {
  "/logout": {
    "post": {
      "security": [{"cognito-auth-rule": []}]   ← references original name
    }
  }
}

API Gateway receives an OpenAPI spec with an unresolvable security reference and silently
ignores the authorizer.

Expected Behavior

The security scheme name in securitySchemes should match the name referenced in route
security definitions. The authorizer should be attached to all routes that declare it.

Actual Behavior

The JWT authorizer is created in API Gateway (visible in the Authorizers tab) but is
not attached to any route. Routes show "Authorization: NONE" in the API Gateway console.

Workaround

Two changes are needed in nitric/resources/apis.py:

1. Change _attach_oidc to use options.name as the resource name instead of
f"{options.name}-{api_name}":

def _attach_oidc(api_name: str, options: OidcOptions) -> OidcSecurityDefinition:
    return Nitric._create_resource(
        OidcSecurityDefinition, options.name, api_name, options  # use options.name directly
    )

2. Add rule_name field to OidcSecurityDefinition and use it in _oidc_to_resource:

class OidcSecurityDefinition(BaseResource):
    api_name: str
    issuer: str
    rule_name: str   # ← add this
    audiences: List[str]

    def __init__(self, name: str, api_name: str, options: OidcOptions):
        super().__init__(name)
        self.api_name = api_name
        self.issuer = options.issuer
        self.audiences = options.audiences
        self.rule_name = options.name   # ← add this

def _oidc_to_resource(b: OidcSecurityDefinition) -> ResourceIdentifier:
    return ResourceIdentifier(name=b.rule_name, type=ResourceType.ApiSecurityDefinition)
    #                                ^^^^^^^^^ was b.name

Since _create_resource deduplicates by name, using options.name directly means that
the same OIDC rule applied to multiple APIs would conflict. A safer fix might be to keep
the composite key internally but ensure the ResourceIdentifier.name always uses
options.name — which is what the _oidc_to_resource fix achieves.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions