Protect your APIs using WebSEAL and Cloud Identity
Today I’m going to show how we can integrate OAuth bearer tokens issued by Cloud Identity with a WebSEAL instance, making use of the new OAuth Introspection capability added to WebSEAL in ISAM 9.0.7.0.
ISAM has supported OAuth authentication for some time, however this is usually only used with access tokens issue by the API Protection feature as it makes use of the STS in order to validate access tokens. OAuth has had a standardised API for token validation for some time now, in the form of RFC 7662, OAuth introspection.
I’ve spoken about OAuth introspection before, but to give a very brief overview, it allows a OAuth client to present a give token to the endpoint and find out whether or not the token is valid, and potentially more information beyond that depending on the Authorization Server.
The primary focus of this article is going to be on WebSEAL. These steps can be applied to any scenario that you have a cloud identity issued access token, and you want to allow an OAuth client to access resources protected by WebSEAL. A primary example of this would be if you’ve got an entirely in browser implicit flow configured, but want to let that client application call some backend APIs with the token its received.
Its also of note that the provider of the access token isn't limited to Cloud Identity, any compliant OpenID Connect or OAuth provider can be used.
Performing Introspection Manually
Before we can introspect we need to get an access token. I’ve got a very simple in browser implicit flow configured to do this. We initiate the flow with:
https://lmf.ice.ibmcloud.com/v1.0/endpoint/default/authorize?client_id=c4097f88-e788-4017-91da-5c9d56dc91e6&state=someState&nonce=someNonce&scope=scope1+openid&redirect_uri=https://www.lmfapp.com:18443&response_type=id_token+token
Some parts of this URL will need to change based on usage, particularly the client_id and redirect_uri.
After doing the implicit flow, my simple application shows:
So now that we have a token, lets go ahead and manually introspect it with cURL to see what we get:
[lmf@lemon 03:57:33 ~]$ curl https://lmf.ice.ibmcloud.com/oidc/endpoint/default/introspect -d "token=bmljZSB0cnkK&client_id=c4097f88-e788-4017-91da-5c9d56dc91e6&client_secret=hunter2" | jq .
{
"ext": { "tenantId": "lmf.ice.ibmcloud.com" },
"sub": "50Q4QK9UTM",
"realmName": "cloudIdentityRealm",
"uniqueSecurityName": "50Q4QK9UTM",
"iss": "https://lmf.ice.ibmcloud.com/oidc/endpoint/default",
"active": true,
"preferred_username": "lfarrell@au1.ibm.com",
"token_type": "Bearer",
"client_id": "c4097f88-e788-4017-91da-5c9d56dc91e6",
"aud": "c4097f88-e788-4017-91da-5c9d56dc91e6",
"acr": "urn:ibm:security:policy:id:1",
"grant_type": "implicit",
"restrictEntitlements": false,
"scope": "openid",
"grant_id": "1f11f040-dd9e-4c2c-bf70-e959b4eb6470",
"tenantId": "lmf.ice.ibmcloud.com",
"userType": "regular",
"exp": 1566934179,
"iat": 1566926979
}
We can refer back to this now when it comes to configuring some values later on. We’ve also confirmed the details we need to use in order to introspect tokens on Cloud Identity.
Configuring WebSEAL
WebSEAL configuration for introspection occurs within the [oauth-introspection] stanza. See the stanza reference for details on every entry.
To integrate with Cloud Identity, I’m setting the following entries:
[oauth-introspection]
oauth-introspection-auth = https
introspection-endpoint = https://lmf.ice.ibmcloud.com/oidc/endpoint/default/introspect
client-id = c4097f88-e788-4017-91da-5c9d56dc91e6
client-secret = **obfuscated**
mapped-identity = {preferred_username}
external-user = true
auth-method = client_secret_post
introspection-response-attributes = +*
We’ll go over each of these individually, there are a few entries like proxy and continue-on-auth-failure which I’ve not changed from their default value.
-
oauth-introspection-auth
We’ve set this to https to enable introspection for https traffic
-
introspection-endpoint
We’ve set this to a cloud identity tenants introspection URL
-
client-id / client-secret
The credentials of the client we want to use to introspect the token
-
mapped-identity
This is the entry from the introspect response which we want to use as the ISAM username. After inspecting the above introspection payload I’ve decided to use preferred_username. This field supports both static and introspection provided values. For example if you wanted a static prefix of "cloud-identity-" on the username, you would set the entry to cloud-identity-{preferred_username}
-
external-user
Depending on the identity sources you have configured in cloud identiy, its quite likely that this will need to be set to true. If your user logs into cloud identity using a username which doesn't exists in the ISAM directory, the authentication will fail
-
auth-method
We want WebSEAL to present the client-id and client-secret as post parameters to the introspection endpoint. Currently WebSEAL supports both POST parameters and BA.
-
introspection-response-attributes
This entry dictates which attributes to add to the credential. We’ve opted to add them all using +*
Trying it out
Now lets try invoking WebSEAL with that access token, and see if we can access any resources:
$ curl -k https://www.lmfapp.com:20443 -H "Authorization: Bearer bmljZSB0cnkK"
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2//EN">
...
<center><img src="/pics/iv30.gif" alt=""></center>
...
This simple cURL test shows we are able to access the WebSEAL landing page. But lets inspect the credential to see what is offered to the backend applications. I extracted the iv-cred:
BAKs3DCCBvgMADCCBvIwggbuAgIJBzA1MDEwGQIBAQIBAAICEAACAgCAAgECBAYDBAUGBwgMFGxmYXJyZWxsQGF1MS5pYm0uY29tMAACAQEwggasMIIGqDAsDANhY3IwJTAjAgEEDBx1cm46aWJtOnNlY3VyaXR5OnBvbGljeTppZDoxBAAwFwwGYWN0aXZlMA0wCwIBBAwEdHJ1ZQQAMDQMA2F1ZDAtMCsCAQQMJGM0MDk3Zjg4LWU3ODgtNDAxNy05MWRhLTVjOWQ1NmRjOTFlNgQAMCIMFEFVVEhFTlRJQ0FUSU9OX0xFVkVMMAowCAIBBAwBMQQAMEMMF0FaTl9DUkVEX0FVVEhOTUVDSF9JTkZPMCgwJgIBBAwfT0F1dGggSW50cm9zcGVjdCBBdXRoZW50aWNhdGlvbgQAMDMMEkFaTl9DUkVEX0FVVEhaTl9JRDAdMBsCAQQMFGxmYXJyZWxsQGF1MS5pYm0uY29tBAAwLwwYQVpOX0NSRURfQVVUSF9FUE9DSF9USU1FMBMwEQIBBAwKMTU2NjkzNjIzOAQAMDEMFEFaTl9DUkVEX0FVVEhfTUVUSE9EMBkwFwIBBAwQb2F1dGgtaW50cm9zcGVjdAQAMC0MFUFaTl9DUkVEX0JST1dTRVJfSU5GTzAUMBICAQQMC2N1cmwvNy41OS4wBAAwJgwSQVpOX0NSRURfSVBfRkFNSUxZMBAwDgIBBAwHQUZfSU5FVAQAMCkMEEFaTl9DUkVEX01FQ0hfSUQwFTATAgEEDAxJVl9MREFQX1YzLjAEADAzDBxBWk5fQ1JFRF9ORVRXT1JLX0FERFJFU1NfQklOMBMwEQIBBAwKMHhhYzE1MDAwMQQAMDMMHEFaTl9DUkVEX05FVFdPUktfQUREUkVTU19TVFIwEzARAgEEDAoxNzIuMjEuMC4xBAAwLQwZQVpOX0NSRURfUFJJTkNJUEFMX0RPTUFJTjAQMA4CAQQMB0RlZmF1bHQEADA4DBdBWk5fQ1JFRF9QUklOQ0lQQUxfTkFNRTAdMBsCAQQMFGxmYXJyZWxsQGF1MS5pYm0uY29tBAAwSAwXQVpOX0NSRURfUFJJTkNJUEFMX1VVSUQwLTArAgEEDCQwMDAwMDAwMS0wMDAwLTEwMDAtODAwMi0wMzA0MDUwNjA3MDgEADAtDBFBWk5fQ1JFRF9RT1BfSU5GTzAYMBYCAQQMD1NTSzogVExTVjEyOiA5QwQAMEgMFEFaTl9DUkVEX1JFR0lTVFJZX0lEMDAwLgIBBAwnY249bGZhcnJlbGxAYXUxLmlibS5jb20sY249RXh0ZXJuYWxVc2VyBAAwJwwQQVpOX0NSRURfVkVSU0lPTjATMBECAQQMCjB4MDAwMDA5MDcEADA6DAljbGllbnRfaWQwLTArAgEEDCRjNDA5N2Y4OC1lNzg4LTQwMTctOTFkYS01YzlkNTZkYzkxZTYEADAaDANleHAwEzARAgEEDAoxNTY2OTQxNjY2BAAwOQwIZ3JhbnRfaWQwLTArAgEEDCRkODRmNmRmZi00ODYyLTRmMjUtYmE0Zi1hMGVhMDJjOTk2YzMEADAfDApncmFudF90eXBlMBEwDwIBBAwIaW1wbGljaXQEADAaDANpYXQwEzARAgEEDAoxNTY2OTM0NDY2BAAwQgwDaXNzMDswOQIBBAwyaHR0cHM6Ly9sbWYuaWNlLmlibWNsb3VkLmNvbS9vaWRjL2VuZHBvaW50L2RlZmF1bHQEADAzDBJwcmVmZXJyZWRfdXNlcm5hbWUwHTAbAgEEDBRsZmFycmVsbEBhdTEuaWJtLmNvbQQAMCgMCXJlYWxtTmFtZTAbMBkCAQQMEmNsb3VkSWRlbnRpdHlSZWFsbQQAMCYMFHJlc3RyaWN0RW50aXRsZW1lbnRzMA4wDAIBBAwFZmFsc2UEADAYDAVzY29wZTAPMA0CAQQMBm9wZW5pZAQAMBoMA3N1YjATMBECAQQMCjUwUTRRSzlVVE0EADA5DBh0YWd2YWx1ZV9sb2dpbl91c2VyX25hbWUwHTAbAgEEDBRsZmFycmVsbEBhdTEuaWJtLmNvbQQAMEcMFnRhZ3ZhbHVlX3Nlc3Npb25faW5kZXgwLTArAgEEDCRjZTExMGIyZS1jOTA1LTExZTktYmFlYy0wMjQyYWMxNTAwMDMEADApDAh0ZW5hbnRJZDAdMBsCAQQMFGxtZi5pY2UuaWJtY2xvdWQuY29tBAAwHQwKdG9rZW5fdHlwZTAPMA0CAQQMBkJlYXJlcgQAMCkMEnVuaXF1ZVNlY3VyaXR5TmFtZTATMBECAQQMCjUwUTRRSzlVVE0EADAcDAh1c2VyVHlwZTAQMA4CAQQMB3JlZ3VsYXIEAA==
Which when decoded produced:
Name
|
Value(s)
|
AUTHENTICATION_LEVEL
|
1
|
AZN_CRED_AUTHNMECH_INFO
|
OAuth Introspect Authentication
|
AZN_CRED_AUTHZN_ID
|
lfarrell@au1.ibm.com
|
AZN_CRED_AUTH_EPOCH_TIME
|
1566936238
|
AZN_CRED_AUTH_METHOD
|
oauth-introspect
|
AZN_CRED_BROWSER_INFO
|
curl/7.59.0
|
AZN_CRED_IP_FAMILY
|
AF_INET
|
AZN_CRED_MECH_ID
|
IV_LDAP_V3.0
|
AZN_CRED_NETWORK_ADDRESS_BIN
|
0xac150001
|
AZN_CRED_NETWORK_ADDRESS_STR
|
172.21.0.1
|
AZN_CRED_PRINCIPAL_DOMAIN
|
Default
|
AZN_CRED_PRINCIPAL_NAME
|
lfarrell@au1.ibm.com |
AZN_CRED_PRINCIPAL_UUID
|
00000001-0000-1000-8002-030405060708
|
AZN_CRED_QOP_INFO
|
SSK: TLSV12: 9C
|
AZN_CRED_REGISTRY_ID
|
cn=lfarrell@au1.ibm.com,cn=ExternalUser
|
AZN_CRED_VERSION
|
0x00000907
|
acr
|
urn:ibm:security:policy:id:1
|
active
|
true
|
aud
|
c4097f88-e788-4017-91da-5c9d56dc91e6
|
client_id
|
c4097f88-e788-4017-91da-5c9d56dc91e6
|
exp
|
1566941666
|
grant_id
|
d84f6dff-4862-4f25-ba4f-a0ea02c996c3
|
grant_type
|
implicit
|
iat
|
1566934466
|
iss
|
https://lmf.ice.ibmcloud.com/oidc/endpoint/default
|
preferred_username
|
lfarrell@au1.ibm.com
|
realmName
|
cloudIdentityRealm
|
restrictEntitlements
|
false
|
scope
|
openid
|
sub
|
50Q4QK9UTM
|
tagvalue_login_user_name
|
lfarrell@au1.ibm.com
|
tagvalue_session_index
|
ce110b2e-c905-11e9-baec-0242ac150003
|
tenantId
|
lmf.ice.ibmcloud.com
|
token_type
|
Bearer
|
uniqueSecurityName
|
50Q4QK9UTM
|
userType
|
regular
|
We can see that aside from the usual WebSEAL credential elements, we’ve got all of the introspection values included too.
Note: If you’re seeing introspection fail with an error like
2019-08-28-05:40:00.415+10:00I----- 0x38AD54CC webseald WARNING wiv ssl SSLConnection.cpp 2401 0x7f584c046700
DPWIV1228W WebSEAL could not establish a secure connection to the server, lmf.ice.ibmcloud.com, for the junction (Function call: gsk_secure_soc_init; failed error: 0x1a4 GSK_ERROR_SOCKET_CLOSED).
2019-08-28-05:40:00.415+10:00I----- 0x38983425 webseald ERROR wad general AMWJsonClient.cpp 673 0x7f584c046700
DPWAD1061E Failed to connect to the server: lmf.ice.ibmcloud.com:443.
Be sure to take a look at this technote https://www-01.ibm.com/support/docview.wss?uid=ibm10960908
This has shown how Cloud Identity issued access tokens can be used as a way of authenticating and accessing APIs protected by WebSEAL. A key scenario when protecting on-premise or legacy deployments, which now use users and identities that are managed out of Cloud Identity.