WebSEAL: Using OAuth scope to limit access to APIs
When protecting APIs or any resource using WebSEAL, group based ACLs are an established and simple way of managing access to a given resource. However there are cases that group membership is not the most appropriate way of protecting a resource. This is often the case when access to these APIs is achieved using OAuth. A given OAuth token should not necessarily inherit all access which a user account has and instead access should be governed based on the scopes which the end user has granted to the OAuth client.
When using OAuth to authenticate to WebSEAL, the scope of the token is added to the users WebSEAL session, also known as their credential. So what we’re going to look at in this piece, is how we can limit access to resources based on the contents of a users credential, which will let us fulfil this scenario of limiting access based on OAuth token scope.
The capability to this has been possible using an XSLT authorization rule or by writing a context based access policy for quite some time. However in 9.0.7.0 a new capability was added to the WebSEAL policy engine to make this easier than ever using just a POP.
This article goes into all of the details around manually managing object space policy and attachments, to build understanding from first principals. Be sure to check out the new API Access control capabilities for a more API-centric way of configuring access to APIs
Understanding the policy
The policy takes the form of a POP, with some two(or more) extended attributes applied. These attributes are:
-
eas-trigger – indicates that this pop should active the attribute EAS and perform attribute checks
-
requires – this is the actual rule for attribute requirements, multiple requires statements can be added to ensure that several attributes are present.
This policy requires that in order to satisfy this policy, the users credential must contain the attribute ‘scope’, with the value ‘scope1’. Lets treat this as a rule:
(scope == “scope1”)
The value also supports an ‘OR’ clause:
This policy now allows access if either of the two scope values are present. To follow the previous notation:
(scope == “scope1” OR scope == “scope2”)
Now, if we add an additional requires clause, to mandate an additional attribute:
We now assert that the credential contains the oauth_token_client_id attribute with value “mytestClient”, so our notation is now:
((scope == “scope1” or scope == “scope2”) AND oauth_token_client_id == “mytestClient”)
This allows for a rich set of rules, as we can create both explicit requirements – through individual requires statements, as well as more flexible rules which can be satisfied by one or more attributes by using the OR keyword.
Creating a policy
Now that we understand the sort of policy we can author, lets create a POP from scratch using pdadmin (from the CLI run > isam admin) .
First we’ll create and view our POP:
[lmf@lemon 01:57:59 oauth]$ ssh admin@isam
Welcome to the IBM Security Access Manager appliance
Enter "help" for a list of available commands
isam> isam admin
pdadmin> login -a sec_master
Enter Password: nice try
pdadmin sec_master> pop create checkCredPop
pdadmin sec_master> pop show checkCredPop
Protected object policy: checkCredPop
Description:
Warning: No
Audit level: none
Quality of protection: none
Time of day access: sun, mon, tue, wed, thu, fri, sat, :anytime:local
IP Endpoint Authentication Method Policy
Auth Level: 0 Network: Any Other Network
Now we’ll add the eas-trigger attribute, as we always need that:
pdadmin sec_master> pop modify checkCredPop set attribute eas-trigger trigger_attr_eas
Now we’ll add our requires statements. I’m going to require the scope example and that the token be issued by the client_id test_client
pdadmin sec_master> pop modify checkCredPop set attribute requires scope=example
pdadmin sec_master> pop modify checkCredPop set attribute requires oauth_token_client_id=test_client
Now we can re-inspect our pop, this time listing and viewing the attributes. These reflect the changes we just made:
pdadmin sec_master> pop list checkCredPop attribute
eas-trigger
requires
pdadmin sec_master> pop show checkCredPop attribute eas-trigger
eas-trigger
trigger_attr_eas
pdadmin sec_master> pop show checkCredPop attribute requires
requires
scope=example
oauth_token_client_id=test_client
Now to attach it to our resource, in this instance the ISAM ‘mobile-demo’ which contains a credential viewer:
pdadmin sec_master> pop attach /WebSEAL/isam/mobile-demo checkCredPop
Note: I’ve created a transparent path junction to /mobile-demo on my AAC runtime to access the demo application
Trying it out
Now that we’ve configured and attached our policy lets get a token and invoke our API to see it in action. First lets obtain and use a token without the scope example:
curl -u test_client:hunter2 https://isam.ibm.com/sps/oauth/oauth20/token -d 'grant_type=password&username=testuser&password=alsoHunter2'
{
"access_token": "cbacacceedcbdfeadab",
"token_type": "bearer",
"expires_in": 3589
}
AT: cbacacceedcbdfeadab
curl -k https://isam.ibm.com/mobile-demo/diag/rest.jsp -H 'Authorization: Bearer cbacacceedcbdfeadab' -H 'Accept: application/json'
{
"error_code" : "0x38cf0427",
"error_message" : "Forbidden"
}
Note: This makes use of WebSEAL now being content type aware using the accept header
Denied, as expected. Now to get a new token and try again this time with the scope example:
curl -u test_client:hunter2 https://isam.ibm.com/sps/oauth/oauth20/token -d 'grant_type=password&username=testuser&password=alsoHunter2&scope=example'
{
"access_token": "daacbgdeaeaabfeceaf",
"scope": "example",
"token_type": "bearer",
"expires_in": 3589
}
AT: daacbgdeaeaabfeceaf
curl https://isam.ibm.com/mobile-demo/diag/rest.jsp -H 'Authorization: Bearer daacbgdeaeaabfeceaf' -H 'Accept: application/json'
HTTP/1.1 200 OK
content-language: en-US
content-type: text/html;charset=ISO-8859-1
Username: testuser
Access Manager Credential:
expires[0]: 2019-08-28T17:17:09Z
AZN_CRED_AUTH_EPOCH_TIME[0]: 1567012614
AZN_CRED_NETWORK_ADDRESS_BIN[0]: 0xc0a82a01
AUTHENTICATION_LEVEL[0]: 1
AZN_CRED_AUTH_METHOD[0]: oauth
tagvalue_user_session_id[0]: aXNhbWF1ZzIyYWdhaW4tZGVmYXVsdAA=_XWa3BgAAAIAAAAA4BrdmXQi8MJiefwAAUVhWMGFHOXlhWHBoZEdsdmJqMUNaV0Z5WlhJZ1pHRmhZMkpuWkdWaFpXRmhZbVpsWTJWaFpnQT0=:default
AZN_CRED_AUTHNMECH_INFO[0]: OAuth Authentication
client_type[0]: confidential
AZN_CRED_MECH_ID[0]: IV_LDAP_V3.0
tagvalue_session_index[0]: a1d26884-c9b7-11e9-853b-000c29959bde
AZN_CRED_IP_FAMILY[0]: AF_INET
authorized[0]: TRUE
scope[0]: example
AZN_CRED_PRINCIPAL_UUID[0]: 00000001-0000-1000-8002-030405060708
AZN_CRED_QOP_INFO[0]: SSK: TLSV12: 9C
oauth_token_client_id[0]: test_client
AZN_CRED_AUTHZN_ID[0]: testuser
AZN_CRED_PRINCIPAL_DOMAIN[0]: Default
AZN_CRED_REGISTRY_ID[0]: cn=testuser,cn=ExternalUser
AZN_CRED_PRINCIPAL_NAME[0]: testuser
tagvalue_login_user_name[0]: testuser
AZN_CRED_NETWORK_ADDRESS_STR[0]: 192.168.42.1
access_token[0]: daacbgdeaeaabfeceaf
AZN_CRED_VERSION[0]: 0x00000908
AZN_CRED_BROWSER_INFO[0]: curl/7.59.0
username[0]: testuser
The demo application shows our credential attributes, where we can inspect the credential and see that yes our claims are in fact present.
OAuth scope and multi-valued credential attributes
When the scope attribute is added to the users credential, it can be added in one of two ways. Either as a single attribute value which is comma separated or WebSEAL can be configured to separate the scope values out into individual attribute values in a multivalued credential attribute.
[oauth]
…
# By default the OAuth scope attribute is provided as a single comma separated
# string. By enabling this configuration option the scope attribute will instead
# be provided as a multivalue attribute.
multivalue-scope = true
When using credential attribute POPs to enforce some requirements it is necessary to configure scopes to be a multi valued credential attribute, as the order of requested scopes may change or additional scopes will cause the policy to be ineffective.
The multivalue-scope configuration is also available when using [oauth-introspection]
Apply the same concept to other authentication types
This is a hugely useful capability that has been added in 9.0.7.0 and should be considered not only in the context of OAuth, because this policy is applied to the users credential any WebSEAL session can be evaluated and policy enforced. When using an EAI which adds custom credentials into the users credential exactly the same steps can be used to enforce requirements on credential attributes populated by an EAI. A perfect example of this is the ISAM AAC Authentication service, which adds any completed mechanisms and policies to the users credential.
It is also worth noting that POP evaluation is more performant than authorization rules or a callout to the RTSS for a CBA evaluation. However this evaluation is not as rich as what CBA can provide, so both should be considered and used when appropriate.