Normally, when you communicate with an API endpoint, you request an JWT / token with corresponding scopes for it (at your identity provider / secure token service), that you then send along with the request (typically in the Authorization
header as a Bearer
token).
Now think of an API that manages customers. If you want to access that API to read a customer object, your application would - after authenticating against the identity server (e.g. Auth0, Azure Active Directory etc.) - request an access token on behalf of the current user with the scope "Customer.Read". If the identity server is allowing to request such a scope for that particular application, it is then included in the returned access token (JWT) - in a special claim called "scope" (for Auth0) or "scp" (for AAD). That claim is normally validated by the API/backend that receives the request...and based on the scopes provided, allows the operation to be fulfilled or the access is denied returning a 403 http status code.
It would be great to being able to inspect the "scope" claim directly at the API gateway level and block or allow all requests based on required scopes to be present in the JWT. Configuration could look something like that:
"github.com/devopsfaith/krakend-jose/validator": {
"alg": "RS256",
[...]
[...]
"scopes": ["Customer.Read"],
"scopes_key": "scope"
[...]
[...]
}
Because the claim can be different depending on the identity solution you are using, you would need to define a scopes_key
(claim to use from the token - like roles_key
- maybe it also makes sense to allow a "nested claim"). The scopes
array would then the define the required scopes to be present in the token. If you add multiple values, e.g. "scopes": ["Customer.Read", "Customer.Create"]
all values must be present. Or you could also introduce a "matcher" property like "scopes_matcher": "any"
(at least one must match) or "scopes_matcher": "all"
(all scopes must be present).
Example:
Sample JWT
{
"aud": "https://myapi.com",
"iss": "https://myissuer.com/",
[...],
[...],
"family_name": "Doe",
"given_name": "John",
"scope": "Customer.Read Customer.Create",
"sub": "myuserid",
[...],
[...],
}
Configuration Endpoint1
"github.com/devopsfaith/krakend-jose/validator": {
"alg": "RS256",
[...]
[...]
"scopes": ["Customer.Read", "Customer.Create"],
"scopes_key": "scope",
"scopes_matcher": "all"
[...]
[...]
}
Result: Request is allowed.
Configuration Endpoint2
"github.com/devopsfaith/krakend-jose/validator": {
"alg": "RS256",
[...]
[...]
"scopes": ["Customer.Read", "Customer.Update"],
"scopes_key": "scope",
"scopes_matcher": "all"
[...]
[...]
}
Result: Request is NOT allowed, because scope "Customer.Update" is not included (and matcher is set to "all").
Configuration Endpoint3
"github.com/devopsfaith/krakend-jose/validator": {
"alg": "RS256",
[...]
[...]
"scopes": ["Customer.Read", "Customer.Update"],
"scopes_key": "scope",
"scopes_matcher": "any"
[...]
[...]
}
Result: Request is allowed, because scope matcher is set to "any" and therefore, only one of the scopes needs to be included.
Being able to block requests directly at the API gateway based on scopes would be a great benefit, because you could "lock out traffic at the frontdoor" of your application and don't need to deal with communication that isn't even supposed to work at the backend.
Would love to contribute that one...but let's discuss what you think of such a feature.
Cheers,
Christian
CC: @alombarte as discussed.