Your IdP is the source of truth for User ↔ Role mapping. Apigee is the source of truth for API resource ↔ Scope mapping.
An user grants scopes to an app and hence scopes are closer to the operations of an app.
A role gives an user permission to perform certain operations in the system and hence is closer to the user.
Translating a role to a set of scopes may not be the best way. Apigee inherently manages the app and scope relationship. A backend tier could process role information (in a JWT) and enforce authorization.
I think a best practice is something similar to the (A) option you described, but with an extra element.
Configure Apigee (OAuthV2/GenerateAccessToken) to attach roles or groups to the tokenvia custom attributes, as asserted by the backend IdP
Configure the mapping of roles → permissions in an external authorization service. This could also be a static table that is stored in KVM if the data is small enough.
What is this 2nd thing?
When Apigee Edge receive a valid OAuth token, it will automatically retrieve the user scopes associated to the token. The Apigee Edge proxy can then call to the Authorization service with a request including {role, route, verb} . The Authz service simply response “true” or “false” indicating that the request ought to be allowed or not.
The Authz service is really just a data table using all possible combinations of the tuple of {role, route, verb} as the key, and true/false as the value.
This is very similar to a XACML service, if you’ve heard of that. But there’s no need to buy a complicated commercial service. It’s really just a database lookup. The table looks like this: