Co Authored By Tushar Prasad
Challenge
Authentication and Authorization are one of the pillars in Identity fabric. It becomes important for organizations to strengthen each and every thread. OpenID connect is one of the important protocols which provide Single Sign On and one of the integration points which helps organizations running a multi cloud or hybrid cloud environment to seamlessly consume identities.
In this scenario of openID connect, this article discusses a solution on strengthening on how an OpenID relying party connects to an OpenID Provider and implementing an interception attack resistant solution.
INTERACTION Diagram:
Topic:
Implementing Proof Key For Code Exchange for IBM Security Verify Access Relying Party Flow
Demonstration: IBM Security Verify SaaS is chosen as an OIDC OpenID Provider
Relying Party: IBM Security Verify Access
OIDC OP: Verify SaaS
Introduction of PKCE:
Proof Key for Code Exchange (PKCE) support is a capability (defined in RFC 7636) that adds security when performing the authorization code flow.
It addresses following security concerns for clients:
· Clients cannot store client_secret (Single Page Applications, native apps).
· Prevents Authorization Code Interception Attack where a malicious client (e.g native app) intercepts the authorization code returned from the authorization endpoint and uses it to obtain the access token.
How PKCE Works:
Clients generate random string and perform (BASE64URL (SHA256 (random string))) on this string.
The Hash(code_challenge) is sent in Authorize request along with Hash method used (code_challenge_method).
The initial random string(code_verifier) is sent in the token request.
The Authorization server validates the hash of the code_verifier against the code_challenge using the code_challenge_method.
On successful validation, token is returned to client.
How PKCE addresses security concerns:
Clients will have to present code_verifier in the token request. The hash of the code_verifier is validated against the code_challenge sent by the client in the initial authorize request.
Pre-requisites
IBM Security Verify Relying Party Federation is already required to be set. The link will help to achieve how to set that.
https://www.ibm.com/support/pages/ibm-security-access-manager-federation-cookbook
· IBM Verify Access Java doc needs to be referred as all methods to generate code_challenge is used
Flow:
The processing flow is as follows:
1. Client generates a code_verifier
, and computes code_challenge
using code_challenge_method
.
2. Client makes request to /authorize.
3. Authorization server performs standard OAuth request validation for /authorize.
4. Authorization server checks for presence of code_challenge
and code_challenge_method
.
5. Authorization server stores code_challenge
and code_challenge_method
against authorization code.
6. Authorization server returns authorization code response.
7. Client presents authorization code to /token including the additional code_verifier
.
8. Authorization server performs standard OAuth request validation for /token.
9. Authorization server generates its own code_challenge
, using the presented code_verifier
, and the stored code_challenge_method
.
10. Authorization server compares its generated code_challenge
, to the value which was presented in the initial request to /authorize(and stored against the authorization code).
11. If the two match, then an access_token is issued. If the two do not, the request is rejected.
High Level STEPS:
Import mapping rules:
OIDC_addPKCEAttributes.js
OIDC_oidc_adv_pkce.js
- Make sure advanced mapping rule is used in Federation
ISVA Relying Party Configuration:
Import Mapping Rules
Federations-> Mapping Rules
Mapping Rule Description:
In this OIDC integration between Verify Access (Relying Party) and IBM Verify SaaS(OP), following mapping rules are used.
i. OIDCRP (oidc_rp.js) -> generic , given here as an example
ii. oidc_adv_pkce -> Generic that comes with product with tweak to include pkce
iii. AddPKCEAttributes -> logic that contains generating PKCE
Mapping Rule addPKCEAttributes
- This mapping rule is the core of Generating Proof Key for code exchange , code verifier and code challenge.Main parts of the rule.It checks whether its a call to be made to /authorize endpoint Generates code verifier and code challenge and stores code verifier into ext cache to be used during token endpoint call.(check notes later for alternate way to store it).
function getCodeVerifierandChallenge() {
var hashval="";
codeverifier=OAuthMappingExtUtils.generateRandomString(randomstringlength);
hashval = OAuthMappingExtUtils.SHA256Sum(codeverifier);
hashofverifier = BASE64Utility.encode(hashval, false);
codechallenge=hashofverifier.toString().replace('+','-').replace('/','_').replace('=','');
return [codeverifier,codechallenge]
}
if(operation == "authorize"){
var state = stsuu.getContextAttributes().getAttributeValueByNameAndType("state","urn:ibm:SAM:oidc:rp:authorize:req:param");
var lookupkey = state+"_codeverifier";
IDMappingExtUtils.traceString("oidc_rp_adv pkce generation:\n " + "\n");
verifychallenge = getCodeVerifierandChallenge()
codeverifier = verifychallenge[0];
codechallenge = verifychallenge[1]
extcache.put(lookupkey,codeverifier, ttl);
stsuu.addContextAttribute(new Attribute("code_challenge", "urn:ibm:SAM:oidc:rp:authorize:req:param", codechallenge));
stsuu.addContextAttribute(new Attribute("code_challenge_method", "urn:ibm:SAM:oidc:rp:authorize:req:param", code_challenge_method));
}
· It stores generated code verifier into external cache wherein code_challenge_method is S256
· Lookupkey is chosen here to be state concatenation with a fixed string which is available at /authorize endpoint
· ISVA relying party checks and does the following call during /token endpoint call
if(operation == "token"){
var state = stsuu.getContextAttributes().getAttributeValueByNameAndType("state","urn:ibm:SAM:oidc:rp:authorize:rsp:param");
var lookupkey = state+"_codeverifier";
try{
var tokenchallenge = extcache.getAndRemove(lookupkey);
}
catch(error)
{
IDMappingExtUtils.traceString("Error During GetandRemove " +error);
}
var codechallengeintoken = new com.tivoli.am.fim.trustserver.sts.uuser.Attribute("code_verifier", "urn:ibm:SAM:oidc:rp:token:req:param" , tokenchallenge);
stsuu.addContextAttribute(codechallengeintoken);
}
· Extcache.getAndRemove() is to get the stored code verifier which is passed to the /token endpoint call.
· All the above are part of addPKCEAttributes
Mapping Rule: oidc_adv_pkce
· Any existing advanced ISVA relying party mapping rule can be augmented with PCKE capabilities by putting these
importMappingRule("addPKCEAttributes");
//invoke pkce rule
const use_pkce=true;
if(use_pkce)
{
if(operation == "authorize" || operation== "token")
{
addPKCEAttributes();
}
}
Mapping Rule: OIDCRP
- It’s an identity mapping rule
- In case you already not using one then you can use a sample from
- System-> File Downloads->federation->mapping_rules-> oidc_rp.js with below changes
Before:
var sub = stsuu.getAttributeContainer().getAttributeValueByName("sub"); After: var sub = stsuu.getAttributeContainer().getAttributeValueByName("email");
|
Alternative approach to store key lookup instead of extcache.put(lookupkey,codeverifier, ttl);
IDMappingExtUtils -> into user sessions
setSPSSessionData(java.lang.String key, java.lang.String value)
removeSPSSessionData(java.lang.String key)
|
Configuration Steps:
The configuration steps defined in this blog can be used to integrate with IBM Verify ,IBM Verify Access and other OP Providers as well.
Prerequisites:
Note below items from IBM Verify tenant:
1. Client ID
Applications->Applications->Select the OIDC Application Settings -> API access
e.g
2. Metadata endpoint:
Applications->Applications->Select the OIDC Application Settings -> Sign-on
e.g.
Create Federation:
Federation -> Federations
Partner Configuration:
Configure Reverse Proxy for Federation:
Flow in Action:
Below is the Kickoff URL used to initiate the OIDC flow
Kickoff URL:
https://oidcrp106:444/isam/sps/oidc/rp/oidcrp/kickoff/verifyop?Target=https://oidcrp106:444/
Authorize call:
GET
https://xx.verify.ibm.com/v1.0/endpoint/default/authorize?redirect_uri=https://oidcrp106:444/isam/sps/oidc/rp/oidcrp/redirect/verifyop&code_challenge=HvQiJkzKLXbLROLL1FXfQfspx5VRwTRRPNWTnliGmJY&response_type=code&state=y5HQPVTgFB&scope=openid&code_challenge_method=S256&client_id=bf871bc2-0782-46f0-87ad-31d900d572e3
|
Following can be observed in the trace log.
Authorization Request:
[11/8/23, 3:36:56:870 EST] 0000018f id=00000000 om.tivoli.am.fim.trustserver.sts.utilities.IDMappingExtUtils > traceString ENTRY ****** Code Verifier: slF6NlnA2zVx4ss1j5AV
[11/8/23, 3:36:56:870 EST] 0000018f id=00000000 om.tivoli.am.fim.trustserver.sts.utilities.IDMappingExtUtils < traceString RETURN
[11/8/23, 3:36:56:870 EST] 0000018f id=00000000 i.am.fim.trustserver.sts.utilities.IDMappingExtCacheDMAPImpl > put ENTRY y5HQPVTgFB_codeverifier slF6NlnA2zVx4ss1j5AV 300
[11/8/23, 3:36:56:873 EST] 0000018f id=00000000 i.am.fim.trustserver.sts.utilities.IDMappingExtCacheDMAPImpl < put RETURN
[11/8/23, 3:36:56:873 EST] 0000018f id=00000000 om.tivoli.am.fim.trustserver.sts.utilities.IDMappingExtUtils > traceString ENTRY ****** Setting inside codeverifier: slF6NlnA2zVx4ss1j5AV challenge method: S256
[11/8/23, 3:36:56:873 EST] 0000018f id=00000000 om.tivoli.am.fim.trustserver.sts.utilities.IDMappingExtUtils < traceString RETURN
[11/8/23, 3:36:56:874 EST] 0000018f id=00000000 om.tivoli.am.fim.trustserver.sts.utilities.IDMappingExtUtils > traceString ENTRY
OIDC kickoff request parameters are:
kickoff attribute name: method
kickoff attribute value: GET
kickoff attribute name: Target
kickoff attribute value: https://oidcrp106:444/
/authorize url is: https://axxverify.ibm.com/v1.0/endpoint/default/authorize
OIDC authorize request parameters are:
request attribute: redirect_uri=https://oidcrp106:444/isam/sps/oidc/rp/oidcrp/redirect/verifyop
request attribute: code_challenge=HvQiJkzKLXbLROLL1FXfQfspx5VRwTRRPNWTnliGmJY
request attribute: response_type=code
request attribute: state=y5HQPVTgFB
request attribute: scope=openid
request attribute: code_challenge_method=S256
request attribute: client_id=bf871bc2-0782-46f0-87ad-31d900d572e3
|
The Token request and response are Backchannel calls.
Token Request:
[11/8/23, 3:39:13:873 EST] 0000018e id=00000000 .oidc10.protocol.delegate.rp.OidcRelyingPartyLandingDelegate 1 getInitialParameters Adding token request value: code_verifier=[slF6NlnA2zVx4ss1j5AV]
[11/8/23, 3:39:13:873 EST] 0000018e id=00000000 com.tivoli.am.fim.oidc10.rp.OidcRelyingParty > doTokenExchange ENTRY PJ0i0GhFMQMUi_D12lVczH1BF8xtZPFdflHPitnnILA.8V1Z0uUFs0gl69z_dTzGDm_UqEyTZrp1NUAZc5FMASdTY2Wt4xGQbJNVXy14MqUrjniFjB50d8TGjfZeAML5Tg https://oidcrp106:444/isam/sps/oidc/rp/oidcrp/redirect/verifyop y5HQPVTgFB MAP{code_verifier=[slF6NlnA2zVx4ss1j5AV],}
[11/8/23, 3:39:13:873 EST] 0000018e id=00000000 com.tivoli.am.fim.soap.client.HttpClientImpl 1 HttpClientImpl URLEndPoint = https://xx.verify.ibm.com/v1.0/endpoint/default/token Timeout = 0
[11/8/23, 3:39:13:873 EST] 0000018e id=00000000 com.tivoli.am.fim.soap.client.HttpClientImpl 1 HttpClientImpl Using Basic Authentication with User Id = bf871bc2-0782-46f0-87ad-31d900d572e3
|
Token Response:
[11/8/23, 3:39:14:274 EST] 0000018e id=00000000 com.tivoli.am.fim.oidc10.rp.OidcRelyingParty < doTokenExchange RETURN {"access_token":"Exk9WfO9f0K2lEsUZR8P8sT32aiubpL3Ka0E6NUv","scope":"openid","grant_id":"9f70f38b-0b5c-4ede-8306-83a63b1e08ae","id_token":"eyJhbGciOiJSUzI1NiIsImtpZCI6InNlcnZlciIsInR5cCI6IkpXVCJ9.<<SNIPPED OUT>>.hJVSMTnbAQNyA2ajV02UGRYYZMcSxJVw9S1nG79UQuae9rXy6tnMmkBmvujj0UCyOnOI7Y8xmSsTS2XCz9h48g-vD_1S4E-AGuYudAc_f4GTWhqAibLLUk7JNe35lDOZ2ICV-owZaEe4tb8GeLqmAEr4_t0hmDfxvxsrbjuM5-Op21NuMpwnxwLIyd8kk3B6E8HBrYlsbNoVDVwDrqoAwI-pHfm3qZ6yGX79rGd5UALzuHnEgV2kUVrVeUqWOEI3W_LZSPtlOHX2Wiiklfs1ytv43uJaT17seNcosAMGG1N0Q_Gl9vi0O610p5rYFfrtFWeJ2VxvIKb5i-d90gMd6g","token_type":"Bearer","expires_in":7199}
|
PFA :
1. OIDC_addPKCEAttributes.js
https://github.com/IBM-Security/isam-support/blob/master/src/oauth/RelyingPartyPKCE/Implement_PKCE_Verify_Access_addPKCEAttributes.js
2. OIDC_oidc_adv_pkce.js
https://github.com/IBM-Security/isam-support/blob/master/src/oauth/RelyingPartyPKCE/Implement_PKCE_Verify_Access_oidc_adv_pkce.js
Summary
The steps will help to enable IBM Security Verify Access Relying Party to be able to use PKCE against any OpenID provider whether it’s a mutli cloud or a hybrid cloud environment and thus enhancing the security by making Authorization code flow secure and resistant to interception attack.The steps are plug and play into existing Relying Party flow in Verify Access.