Continuing on the theme of authorization from recent blogs, I’ve seen several emerging requirements for what you could describe as federated authorization using an offline assertion. The offline component pertaining to the fact that the policy decision point (PDP), has no prior or post knowledge of the calling user. All of the subject information and context are self contained in the PDP evaluation request. Eg a request that is using a JSON Web Token for example.
A common illustration could be where you have distinct domains or operational boundaries that exist between the assertion issuer and the protected resources. An example could be being able to post a tweet on Twitter with only your Facebook account, with no Twitter profile at all.
A neat feature of OpenAM, is the ability to perform policy decision actions without having prior knowledge of the subject, or in fact having the subject have a profile in the AM user store. To do this requires a few neat steps.
Firstly let me create a resource type – for interest I’ll make a non-URL based resource based on gaining access to a meeting room.
Next step is to add in a policy set for my Meeting Room #1 and a policy to allow my External Users access to it.
My subjects tab for my policy is the first slight difference to a normal OpenAM policy. Firstly my users who are accessing the meeting are external, so will not have a session or entry in the OpenAM profile store. So instead of looking for authenticated users, I switch to check for presented claims. I add in 3 claims – one to check the issuer (obviously only trusted issuers are important to me….but at this step we’re not verifying the issuer, that comes later..), the audience and a claim called Role. Note the claims checks here are simply string comparators not wild cards and no signature checks have been done.
I next add in some actions that my external users can perform against my meeting room. As managers, I add in the ability to order food, but they can’t use the white board!
So far pretty simple. However, there is one big thing we haven’t done. That is to verify the presented JWT. The JWT should be signed by the 3rd party IDP in order to provide authenticity of the initial authentication. For further info on JWT structure see RFE7519 – but basically there are 3 components, a header, payload and signature. The header contains algorithm and data structure information, the payload the user claims and the signature a crypto element. This creates a base64 encoded dot-delimited payload. However…we need to verify the JWT is from the issuer we trust. To do this I create a scripted policy condition that verifies the signature.
The script basically does a check to make sure a JWT is present in the REST PDP call, strips out the various components and creates a corresponding signature based on a shared secret. If the reconstructed signature matches the submitted JWT signature we’re in business.
The script calls in the ForgeRock JSON, JSE, JWS and JWT libraries that are already being used throughout the product, so we’re not having to recreate anything new here.
To test the entire flow, you need to create a JWT with the appropriate claims from a 3rd party IDP. There are lots of online generators that can do this. I used this one to build my JWT.
Note the selection of the algorithm and key. The key is needed in the script on the AM side.
I can now take my newly minted JWT and make the appropriate REST call into OpenAM.
The call sends a request into ../json/policies?_action=evaluate with my payload of the resource I’m trying to access and my JWT (note this is currently submitted both within the subject.jwt attribute and also the environment map due to OPENAM-8893). In order to make the call – remember my subject doesn’t have a session within OpenAM – I create a service account called policyEvaluator that I use to call the REST endpoint with the appropriate privileges.
A successful call results in access to the meeting room, once my JWT has been verified correctly:
If the signature verification fails I am given an advice message:
NB – the appropriate classes and also the primitive byte, need to be added the the Java white list for the policy engine, within the global configuration,