Authorization policies using access token

This topic has 10 replies, 2 voices, and was last updated 4 years, 8 months ago by sharma_udit.

  • Author
    Posts
  • #19259
     sharma_udit
    Participant

    Hi,

    I am using OpenAM 5.1 in an agentless manner in the following way:

    UserAgent -> API GW -> OpenAM

    The app is a SPA that makes api calls to the API gateway. API gateway proxies and orchestrates the requests to OpenAM. I am using the resource owner password grant flow for getting the access token and refresh token. Now, whenever the UserAgent makes a request for a resource, I want to run authorization policies in OpenAM to check whether the user is authorized to access the resource or not. This decision will be based on the users group membership in ldap.

    However, I am noticing that the role based authorization policies do not work when the subject is passed in as an access token. Do the OpenAM authorization policies only work properly with an ssotoken? If yes, then how do I utilize OpenAM’s authorization policies in my scenario?

    #19285
     Scott Heger
    Participant

    I’d first like to point out that for a SPA, the recommended OAuth grant flow is the Implicit Grant flow due to a SPA being a public client.

    The second thing I’d like to point out is that OAuth is an authorization protocol, not an authentication protocol. That said, no, you can’t use an access_token as a means to identify the subject in OpenAM authorization policies. You can, however, use an OpenID Connect/JWT Claim as a means to identify the subject as OpenID Connect is an authentication protocol. So if you are going to continue with this approach, I would suggest you modify your SPA to use the Implicit Grant flow and add OpenID Connect into the mix so that you get an id_token that can be presented as the subject in your authorization policy requests.

    On a final note, why not utilize the scope in the access_token to indicate what the user is authorized to access? Afterall that is the whole point of the scope. Per section 7 or the OAuth 2.0 specification (https://tools.ietf.org/html/rfc6749#section-7) it states the following:

    The client accesses protected resources by presenting the access
    token to the resource server. The resource server MUST validate the
    access token and ensure that it has not expired and that its scope
    covers the requested resource.

    #19287
     sharma_udit
    Participant

    Thanks for the reply scott.

    The client is a trusted first party application and doesn’t need to do the OAuth dance. This is the primary reason why the resource owner password grant has been chosen. The access token and refresh token are jwts and not opaque tokens. They do contain the sub claim that identifies a subject. However, subject/group based authorization policies in openam still don’t work.

    Representing permissions as scopes does make sense but scopes are defined and validated for an oauth client and not validated against the user’s entitlements. I am not sure how to map a user to a set of scopes using OpenAM.

    The way it could work is if I use the authenticate apis to get the ssoToken and check the user authorizations using the ssoToken. After that I mint an access token using only the scopes that the user is entitled for using the authorization response.

    I haven’t checked the passing the id_token as we dont want to store the id_token in the browser. We will be using the user_info endpoint for getting the user claims instead of the id_token.

    #19309
     Scott Heger
    Participant

    Right, it has to be either an SSOToken or an OpenID Connect/JWT Claim to identify the subject in an authorization policy.

    The default scope implementation class that OpenAM ships with treats scopes as profile attributes for the resource owner. What that means is that out of the box the scopes that you can request would map to profile attributes of the user who authenticated during the request for the access_token (resource owner, not the client). If your user profiles could contain some attribute(s) and corresponding value(s) that your client could interpret as entitlements then you could use that approach. Another option is to build your own custom scope implementation class that could provide whatever you needed.

    That is if you wanted to use OAuth as a true authorization protocol. Of course to ensure that the bearer of the access_token is the one who authenticated to obtain the access_token you would implement OpenID Connect on top of OAuth.

    In lieu of using OAuth for authorization properly you could just do as you suggest and utilize the REST API of OpenAM for authentication to obtain an SSOToken for the user and then use that in authorization calls. I don’t see where you would then have a need for OAuth after that.

    #19310
     sharma_udit
    Participant

    Hi Scott,

    Thanks for the reply,

    I did look at the custom scope validator class but found out that implementing it will be too much customization for our needs. Would have been better it was available as a script like the oidc claims script.

    I am using OpenID connect on top of OAuth. I am just not using the id_token. Other features of the openid connect like the user_info endpoint and dynamic registration etc will still be utilized.
    With the advent of a jwt access token. We didn’t really see the need for the id_token. The access token jwt already contains the subject information. Proving that the bearer of the access_token is the one who authenticated can be done using proof of possession.

    We want to use the access token as we dont want to come to OpenAM for session validation in each api call. The plan is that every call to a business API will be made just using the access token. The API gateway will verify the token using the jwks exposed by OpenAM. Apart from signature verification the API gateway will also check whether the AT has the right scopes.

    #19314
     Scott Heger
    Participant

    Ok, so back to your original question…an access_token and, after looking deeper into this, even an OpenID Connect/JWT Claim won’t be able to be used by itself to identify a user in your datastore to then be able to find their group memberships. You would need an SSOToken for that. I understand not wanting to validate the SSOToken for each API call. In fact that is not recommended anyway as it would have a drastically negative impact on your CTS server(s). Edge devices that use SSOTokens typically make use of a caching mechanism to cache both session validity and policy decisions for a given session. If I recall correctly, policy agents cache each for 3 minutes by default. They also take advantage of OpenAM’s notification ability to receive updates on session changes to be able to dirty that cache.

    But, if you want to stick with OAuth/OpenID Connect and you are planning on using the userinfo endpoint anyway, you could create a custom OIDC Claims Script and define a claim called “groups” that is tied to a claim attribute that maps to the user profile attribute “ismemberof”. That is a dynamic attribute that contains the DNs of the groups that the user is a member of. When you call the userinfo endpoint, passing in the access_token it would return something like this:

    {
        "given_name": "Sam",
        "family_name": "Carter",
        "name": "Sam Carter",
        "groups": [
            "cn=slack,ou=groups,dc=example,dc=com",
            "cn=Test,ou=groups,dc=example,dc=com"
        ],
        "sub": "[email protected]"
    }

    This could be an approach if your API can read the groups out of this response and authorize the request based on what the returned groups are.

    You would need to update your OpenID Connect Provider settings to use this custom claims script and you would need to add groups as a supported claim. You would also need to add ismemberof to the LDAP User Attributes list in your Data Store.

    Now, as noted above, the ismemberof attribute is a dynamic attribute and as such any changes to that (i.e. groups added or removed from a user) won’t be immediately picked up by OpenAM unless you disable the idm cache. That could have a negative impact on your LDAP server as after doing so any call to a user’s profile data will result in a call to your LDAP server vs the OpenAM cache.

    #19315
     sharma_udit
    Participant

    Thanks for the detailed response Scott. I really appreciate your time and great advice.

    I also prefer this approach but the only issue is that in this case we are going to have our Authorization policies sitting in the API gateway. We really wanted to utilize OpenAM for that. So we have settled for this:
    API -> OpenAM: authenticate(username, pw)
    OpenAM -> API: SSO Token

    API -> OpenAM: Evaluate Policies Tree(ssoToken)
    OpenAM -> API: Policy Decision

    API -> API: Translate policy decision to permitted scopes for the user
    API -> OpenAM: Get OAuth tokens for the permitted scopes
    OpenAM -> API: Access token, Refresh token

    API -> Client: Return OAuth tokens

    This way the access token will always have permitted scopes for the user.

    However, I still don’t understand the need for having an user SSO token for doing authorization checks. The policy evaluation is already a privileged call. Ideally the PDP should just take the Admin sso token and user id in some form and give the policy decision..

    #19317
     Scott Heger
    Participant

    Yes, my approach would mean that your AuthZ policies sit in your API Gateway. But I wanted to throw it out there as an option.

    I’m curious, what do you mean by “translate policy decision to permitted scopes for the user”? Are you saying that based on the policy decision you will request a different set of scopes in your access_token request?

    #19318
     sharma_udit
    Participant

    Yes, Authorization policy will tell what actions the user is allowed to perform and API gateway will just map those actions to scopes while making the oauth2 authorize request.

    #19320
     Scott Heger
    Participant

    Are you using standard policy actions (i.e. GET, POST, PUT, etc.) or are you creating custom actions? Can you provide an example? I assume that these actions are related to the resource the user is trying to get at via the API. Are you then doing a policy evaluation for each resource requested and then generating an access_token and validating that access_token for each request?

    #19321
     sharma_udit
    Participant

    Standard policy actions are one way to do it but then I will have to define multiple resources that will essentially become the exact api call the user is making. I am doing it a bit differently for performance benefits.

    e.g
    PolicySet(Application) Name – MyApplication
    Resource Type – CustomResource
    Actions – get_profile, update_profile, lock_account, unlock_account. These actions are also defined as valid scopes on the Oauth client for the application.
    Default Action State – Deny
    Resources – spa/l1

    After this just define multiple policies against the same resource(spa/l1) that allow the actions to be performed based on the user group.

    This will probably not work when the same user is part of multiple groups but that is not going to happen in my use case.

    I am just generating the access token when the user logs in. The policy evaluation is done only at the time the user is logging in. Policy evaluation evaluates the whole tree for the application. So it gets all the authorization decisions in a bulk. After that it mints the access token. So the access token will have all the scopes the user is entitled to in an array. Every time the access token expires, the refresh token can be used to get a new access token with the same scopes without doing the authorization checks again.

Viewing 11 posts - 1 through 11 (of 11 total)

You must be logged in to reply to this topic.

©2022 ForgeRock - we provide an identity and access platform to secure every online relationship for the enterprise market, educational sector and even entire countries. Click to view our privacy policy and terms of use.

Log in with your credentials

Forgot your details?