OAuth Grant Type Selection

Hi,

We are trying to define a solution for selectively enabling access to a financial institution’s APIs. The APIs will be accessed by both internal and external users from a UI application.

I wanted to know which OAuth2 Grant Type is best suited for this integration and consumption scenario. Here is some more information on the problem statement:

1. External users can be configured in the institution’s Active Directory and tagged as external users. This will help me authenticating them against the Active Directory.

2.The client (external users) are not resource owners, but will be granted access to the banking user’s data by the APIs for analytics and other use-cases.

3.External users will have selective access to the APIs. There is a definite requirement for role-bases access to the APIs. Users will get allocated to roles which have select access to the APIs.

4.Overall navigation flow is – User Login->Authentication->Authorization->API access & invocation.

Couple of questions –

1.Which Grant Type is best suited for this scenario? Will it be Resource Owner or the more complex 3-leg Authorization Code grant type?

2.Does Apigee allow roles to be associated with scopes in order to implement role based access.

Thanks in advance.

Solved Solved
2 4 1,043
1 ACCEPTED SOLUTION

The more complex 3-legged grant type is used for untrusted clients. Understand client to mean "application", not "customer of the financial institution".

client = Application = program code.

You recognize that an authorization decision requires a knowledge of the identity of the end user sending the requests. For that we need user Authentication. And for that, somehow the user needs to present credentials to a system that verifies the creds - an Identity Provider.

  • A trusted app or client, an app produced by the owners of the API or an app produced by the owners of IDP itself, can be trusted to handle user credentials and not misuse them. It won't cache them unnecessarily. It won't re-authenticate without user consent. And so on.
  • A third-party client is in general not trusted, and therefore must not be permitted to handle user credentials.

If your users employ a third-party app, a phone app, a desktop app, etc developed by people other than the owners of the API, then... you want 3-legged OAuth. With this approach, the user authentication interaction is delegated to a "trusted user agent", which is usually, the standard web browser for a platform. We all are trained to type in usernames and passwords to our web browsers and trust that the browser (Chrome, FF, Edge, Safari, etc) will do the right thing.With the 3-legged flow the trusted user agent gets the Authorization code, which is not usable by the user agent. Instead the user agent sends that back to the originating client app, which exchanges the code for a token. All of this is necessary to avoid allowing the untrusted app to handle sensitive user credentials. There is a foundation of trust required: we must trust the web browser.

If your users employ only a trusted app, then...it's so much simpler. You can use a 2-legged grant type. Eg . password grant. The client can be trusted to handle the user credentials. The user creds get sent to the API endpoint. The API endpoint sends those creds to the IDP, the IDP returns user information, the API endpoint mints a token, and returns it to the client. Simple.

In the case of a website - the same things apply. Either the website is trusted to handle user credentials, or it is not. If the owners of the website also own the API endpoint, then we assume the website is trusted. So the user can just directly key in the credentials on the website, and the website can securely verify these credentials via API calls over HTTPS.

In either case - whether you use a 3-legged or 2-legged grant - the client receives a token. It's usually an opaque token, which refers to some information held at the token issuer (in our case, Apigee Edge). Let's call this data "metadata". It might include:

  • the authenticated user
  • the time of issuance
  • the expiry
  • the scope of the token (if any)
  • user metadata: roles, names, email address, etc
  • the client used to obtain the token

Really the token is just a reference to this metadata. When the app presents the token to the API server, the API endpoint can validate the token (VerifyAccessToken) and implicitly retrieve all of this metadata, and then the API endpoint can make decisions on how to serve the request based on that metadata. Decisions pertaining to Authorization, sure, but also: routing decisions, rate limiting decisions, response shaping, etc.

If you want to get fancy you can use 2-way TLS and also store the client cert fingerprint in the token metadata. Then after VerifyAccessToken you can check that the current client TLS fingerprint is the same as the fingerprint used to acquire the token. We call this "TLS token binding".


Does Apigee allow roles to be associated with scopes in order to implement role based access.

Sure. Apigee Edge ~allows~ this. But Apigee Edge does not include a role-to-scope mapping database. You would have to provide this. Also: Apigee Edge does not provide a system by which you, as API publisher, can centrally stipulate which scopes are required for which specific API calls. You can include a Scope element in VerifyAccessToken, but ... that scope is hardcoded. So that you may need to create N different VerifyAccessToken policies if you want to map N different scopes across your APIs. And then you need to attach the policy with the right policy config for each of those API calls, probably in a conditional flow.

There are better ways to do things, if you externalize both of those data sets from your API proxies. Keep a table of role-to-scope mappings, and keep a table of api-to-required-scope mappings. Then package the lookup of that stuff into a SharedFlow and perform a FlowCallout to resolve the question "can the user who possesses this token perform the action being requested right now?" to a YES or NO answer.

If you are using a trusted app, you may want to simplify the matter and skip the abstraction of the scope completely. The scope is really handy for third-party apps; in this case a user may want to allow one third-party app the ability to do one thing (let's say, read the account totals), and another third party app could do another thing (maybe update the account profile). So the user grants consent with a given scope to each app.

When it's a trusted app, the user often wants the app to be empowered to do .... everything the user is authorized to do. Therefore you could just use the role of the user to make the authorization decision, eliminating the scope concept completely.

Eliminating the scope, you still need to externalize the mapping of api-to-required-role somewhere. The idea is simple; we want a table of {verb, resource, environment, roleset}. A request arrives for {verb, resource}. A combination of the data in the request and ambient data specifies the {environment}; this might include time-of-day, or transaction amount, or transaction frequency. The token presented includes the roleset. Then it's a simple matter of looking up the {verb, resource, environment} tuple and seeing if the roles required there are present on the token. YES or NO.

As I said, Apigee Edge doesn't do this natively today, but it's pretty simple to build it either as an externally-callable (and cacheable) microservice, or ... if your needs are small and simple, you could just store the {verb, resource, environment, roleset} table in a KVM as json and use a JavaScript policy to do the lookup and return the YES/NO answer.

View solution in original post

4 REPLIES 4

The more complex 3-legged grant type is used for untrusted clients. Understand client to mean "application", not "customer of the financial institution".

client = Application = program code.

You recognize that an authorization decision requires a knowledge of the identity of the end user sending the requests. For that we need user Authentication. And for that, somehow the user needs to present credentials to a system that verifies the creds - an Identity Provider.

  • A trusted app or client, an app produced by the owners of the API or an app produced by the owners of IDP itself, can be trusted to handle user credentials and not misuse them. It won't cache them unnecessarily. It won't re-authenticate without user consent. And so on.
  • A third-party client is in general not trusted, and therefore must not be permitted to handle user credentials.

If your users employ a third-party app, a phone app, a desktop app, etc developed by people other than the owners of the API, then... you want 3-legged OAuth. With this approach, the user authentication interaction is delegated to a "trusted user agent", which is usually, the standard web browser for a platform. We all are trained to type in usernames and passwords to our web browsers and trust that the browser (Chrome, FF, Edge, Safari, etc) will do the right thing.With the 3-legged flow the trusted user agent gets the Authorization code, which is not usable by the user agent. Instead the user agent sends that back to the originating client app, which exchanges the code for a token. All of this is necessary to avoid allowing the untrusted app to handle sensitive user credentials. There is a foundation of trust required: we must trust the web browser.

If your users employ only a trusted app, then...it's so much simpler. You can use a 2-legged grant type. Eg . password grant. The client can be trusted to handle the user credentials. The user creds get sent to the API endpoint. The API endpoint sends those creds to the IDP, the IDP returns user information, the API endpoint mints a token, and returns it to the client. Simple.

In the case of a website - the same things apply. Either the website is trusted to handle user credentials, or it is not. If the owners of the website also own the API endpoint, then we assume the website is trusted. So the user can just directly key in the credentials on the website, and the website can securely verify these credentials via API calls over HTTPS.

In either case - whether you use a 3-legged or 2-legged grant - the client receives a token. It's usually an opaque token, which refers to some information held at the token issuer (in our case, Apigee Edge). Let's call this data "metadata". It might include:

  • the authenticated user
  • the time of issuance
  • the expiry
  • the scope of the token (if any)
  • user metadata: roles, names, email address, etc
  • the client used to obtain the token

Really the token is just a reference to this metadata. When the app presents the token to the API server, the API endpoint can validate the token (VerifyAccessToken) and implicitly retrieve all of this metadata, and then the API endpoint can make decisions on how to serve the request based on that metadata. Decisions pertaining to Authorization, sure, but also: routing decisions, rate limiting decisions, response shaping, etc.

If you want to get fancy you can use 2-way TLS and also store the client cert fingerprint in the token metadata. Then after VerifyAccessToken you can check that the current client TLS fingerprint is the same as the fingerprint used to acquire the token. We call this "TLS token binding".


Does Apigee allow roles to be associated with scopes in order to implement role based access.

Sure. Apigee Edge ~allows~ this. But Apigee Edge does not include a role-to-scope mapping database. You would have to provide this. Also: Apigee Edge does not provide a system by which you, as API publisher, can centrally stipulate which scopes are required for which specific API calls. You can include a Scope element in VerifyAccessToken, but ... that scope is hardcoded. So that you may need to create N different VerifyAccessToken policies if you want to map N different scopes across your APIs. And then you need to attach the policy with the right policy config for each of those API calls, probably in a conditional flow.

There are better ways to do things, if you externalize both of those data sets from your API proxies. Keep a table of role-to-scope mappings, and keep a table of api-to-required-scope mappings. Then package the lookup of that stuff into a SharedFlow and perform a FlowCallout to resolve the question "can the user who possesses this token perform the action being requested right now?" to a YES or NO answer.

If you are using a trusted app, you may want to simplify the matter and skip the abstraction of the scope completely. The scope is really handy for third-party apps; in this case a user may want to allow one third-party app the ability to do one thing (let's say, read the account totals), and another third party app could do another thing (maybe update the account profile). So the user grants consent with a given scope to each app.

When it's a trusted app, the user often wants the app to be empowered to do .... everything the user is authorized to do. Therefore you could just use the role of the user to make the authorization decision, eliminating the scope concept completely.

Eliminating the scope, you still need to externalize the mapping of api-to-required-role somewhere. The idea is simple; we want a table of {verb, resource, environment, roleset}. A request arrives for {verb, resource}. A combination of the data in the request and ambient data specifies the {environment}; this might include time-of-day, or transaction amount, or transaction frequency. The token presented includes the roleset. Then it's a simple matter of looking up the {verb, resource, environment} tuple and seeing if the roles required there are present on the token. YES or NO.

As I said, Apigee Edge doesn't do this natively today, but it's pretty simple to build it either as an externally-callable (and cacheable) microservice, or ... if your needs are small and simple, you could just store the {verb, resource, environment, roleset} table in a KVM as json and use a JavaScript policy to do the lookup and return the YES/NO answer.

Hi Dino,

Thanks for your detailed answer and explanation.This is very helpful. A few additional points -

1. The client/app in our case will be developed by the financial institution and hence can be treated as a trusted app.

2. I was thinking of considering using OpenId connect to request authentication and authorization in a single request.

3. The authorization grant type can be resource owner. Whilst the consumers are not the actual resource owners in a trusted app scenario they can be treated as as resource owners (with read access).

4. I was wondering if the token to role mapping should be looked up and verified by an interceptor or pre-processor? The intent behind using an interceptor is to avoid doing this in the actual API. If possible please point me in the direction of some samples or documentation.

Thanks & Regards

Hi Sharad.

Yes - ok

OpenID connect is a wrapper around OAuth2 authorization flow. It's nice if you want to own the IDP and the APIs but not necessarily the App. (In other words, you'd like to promote an ecosystem of untrusted or not a-priori trusted apps). OpenID Connect may be more than you need for a system in which the app and the APIs and the IDP are all provided by the same party (you). In this case, maybe just use password grant. Have the app pass in the user credentials and the API endpoint can authenticate the user by calling out to the IDP.

Here's a working example: https://bit.ly/2RDmO8q

But you might want to use OpenID Connect if you already have an OpenID Connect provider wrapped around your IDP. In that case, half the work is done for you, and you may as well leverage it.

As for token-to-role mapping.... The token itself will refer to metadata. VerifyAccessToken brings that metadata into "context" for the current message. That means you can retrieve roles stored on the token by referring to context variables.

The key thing now is to map those roles to privileges. And for that... yes you could use an Interceptor pattern in Apigee Edge. There's an artifact called a "shared flow" in Apigee Edge which is basically a callable sequence of policies. You can configure a sharedflow as a FlowHook so that the sharedflow gets called for all inbound requests handled by an Apigee Edge environment. That works as an interceptor.

best regards, Dino

also, this video might be helpful too.

Many thanks for your inputs. Most useful.

I will read up the additional information and get back with questions.