Membrane API Gateway returns 401 Unauthorized with a link to this page when the jwtAuth plugin finds a well-formed JWT and a key for its kid, but the token fails verification. The request is rejected in the request flow.
The response contains the status 401 Unauthorized, a detail that names the failed check, the problem type https://membrane-api.io/problems/security and a link to this page. The type security means that a security plugin refused access to the API. The same check is also logged at info level.
| Check | Configuration |
|---|---|
Signature with the key selected by kid | jwks |
exp present and not expired (30 seconds clock skew) | Always |
sub present | Always |
aud | expectedAud |
iss | expectedIss |
tid | expectedTid |
The signature does not verify with the key the kid selects. The token was altered, or it was signed with a different key that uses the same kid.
The exp claim lies in the past, with 30 seconds of allowed clock skew. Also check that the clocks of the gateway and the identity provider are in sync.
The token has no exp or no sub claim. Both are required.
The aud claim does not match expectedAud. A token with an aud claim also fails when expectedAud is not set at all.
The iss claim does not match expectedIss, or the tid claim does not match expectedTid.
aud, iss and tid claims with the configuration.expectedAud, expectedIss and expectedTid to the values the provider puts into its tokens:api:
port: 4000
flow:
- jwtAuth:
expectedAud: order
expectedIss: https://auth.example.com
jwks:
jwksUris: https://auth.example.com/keys
target:
url: http://localhost:3000
api:
port: 4000
flow:
- jwtAuth:
expectedAud: order
expectedIss: https://auth.example.com
jwks:
jwksUris: https://auth.example.com/keys
target:
url: http://localhost:3000
To accept any audience, set expectedAud: any!!. This is strongly discouraged. See the JWT tutorial for more.
By default, Membrane runs in development mode and adds details to error responses that help you find the problem. These details may reveal information about your setup. In production, turn them off:
configuration:
production: trueconfiguration: production: true
Add the configuration document to your apis.yaml and separate it from the APIs with ---.
Configuration reference:
Tutorials with runnable examples:
Still stuck? See the troubleshooting guide or ask on GitHub Issues. For commercial support, contact info@predic8.de.