I realized how to extend OAuth protocol, logically, when one’s implementation wraps the OAuth endpoints of such as ACSv2 or ADFS v3 in Windows Server 20012 R2. Unfortunately, it doesn’t work (as specified below), since the ACS OAUTH endpoint only issues JWTs and SWTs. However, there are workarounds, as detailed in the coda.
In fact, Microsoft already showed how to do this earlier – without expressing it as a formal “extensibility model” when considering Azure Data Services.
It’s always fun to go to practical (how to make a given OAuth protocol run work for both exchange API in office365 land and restful APIs ) via the intellectual – does the probability density interpretation of quantum mechanics means physics requires perception via acts of cognition (dog-style or human-style) and thus the action effected by a dog’s nose – inducing a more fundamental type of non-rationalistic cognition than that we tend to use – is the key to understanding physics?
When we launch our iphone/windows app seeking an authorization code from the authorization endpoint of the OAuth server, recall that we can extend the endpoint with additional query string parameters. Call these extensions – specific to the custom consent semantics we want. Such semantics are presumably specific to the API port in question – being, for example, custom read-semantics, for directory (read only) ports). In the Microsoft model, these are represented as querystring parameters such as x_permissions=X. Alongside these one can ALSO send “state” information, per the oauth2 standard. This is the key! For “state” allows us to tie together the authorization processing phase of OAuth (to get the one time code), and the token issuing phase of OAuth (that turns the code into a first security token… AND additional “extended” fields). Moreover, it enables us to extend the authorization server to provide for “custom issuing semantics”, suited to the target API port.
Let’s say that our custom iphone/windows app starts up and launches its embedded browser. The latter will shortly navigate to the authorization endpoint configured at app-provisioning time. To the navigation URI, however, the iphone app first adds querystring parameter state=”87 75” (say) and perhaps “x_Permission=directory-compare” – where “compare” is a directory-port specific semantic that is more specialized than mere “read”, requiring directory-specific notions of identity, information models and identity matching (based on DNs, perhaps). Of course, this now goes to our IDP’s OAuthprotocol implementation which, having checked that one cited a registered vendorid and one knows the redirect ‘secret’, transfers to the “one of many” consent.aspx handlers – guarded by websso. Since no websso session will be present, generally, the user must complete websso with some IDP (e.g. ACS and google). The resulting websso assertion received back from ACS then authorizes issuance of a local token that unlocks the websso guard on consentX.aspx.
In our world, that ACS-issued SAML1.1 token is carried forward to consent – for “use in the extensibility model”. As the consentX URI bearing the x-permission and state has just been on a long trip to the IDP and back via ws-fedp (and openid to google, and who knows where within google multi-tenant implementation of openid), consent handling can now use all of the state’s opaque value, the permission request value AND the websso token during its processing for minting a delegation record (and generating a one-time authorization code). But more on that later. For now simply note that the ACS v2 OAuth implementation cannot be extended EXCEPT by containment – which means we must wrap the ACS OAuth endpoint with our own – that implement the desired extensions.
Typically, the generated one-time authorization code is attached to the originally-requested redirect URI parameter, and off the browser goes to that vendor-specific website. Of course, the typical browser/app model integration captures that redirect, or otherwise reacts to its capture by the website – with the result that the browser closes down and control is passed back to the iphone/windows app’s main thread; that now “knows the code”. In the case of windows apps, where the app’s main thread was monitoring each of the redirecting events of the subordinate browser thread (and can thus intervene, stop the browser process, and capture the values on any particular interesting ‘navigation URI’) it can ALSO capture the state information of interest. Of course, it supplied that value in the first place and can confirm that the opaque value matches a stored value (or otherwise locate an outstanding background thread, perhaps keyed on the state information).
The process now calls the token issuing endpoint of the OAUTH service, typically. But, and here is the clue, it can cite the original state information here too (which will itself round trip during token issuing, accompanying the desired security token in the JSON response.) This is the second clue to effective extensibility. For that state has now linked authorization process (in the IDP) with token processing (in the IDP). Though opaque, the IDP can store information associated with the opaque value calculation during authorization for delivery during token processing. Of course the latter process has to be security controlled (so not just “anyone” can go pick up the token). But, that process is secured using one-time, time-limited codes…anyways.
Our idea is that authorization processing caches the websso token (issued by ACS, or otherwise) that unlocked the consent guard using the state information as a key. When the token issuing endpoint is contacted (citing the state and the security code), this is a signal. It’s a signal to extend the JSON response to be minted by the token issuing process with an additional field… bearing the websso token. In other fields with standard names are such as the JWT, minted by a particular relying party configuration of ACS (supporting our OAUTH implementation).
This is all viable for a world in which the oauth-consuming webapp want to now talk to Exchange Online API – using its ws-security and ws-trust enabled soap endpoints. Using the Managed API for Exchange, one can supply the websso token as TokenCredentials. Pushed to the STS of Mirosoft Online, assuming these have the right fields this token can serve double duty, serving now as as the authentication token that enables this STS to issue the access token that the client librarye needs when formulating requests to Exchange API .. to do such as read a mailbox.
This all works in our particular implementation. That is because the websso token our IDP mints is only ‘incidental” to the local process of the IDP minting a local-token unlocking the consent guard. That is websso from the built in IP/FP really creates two tokens, one local and one in SAML blob format …that can be “formatted for Office 365 conventions”.
Of course, the JWT can also be used for those restful API ports that want JWT blobs in HTTP authorization… vs SAML blobs in SOAP header!
AS we hinted above, the design concept doesn’t work (though it works to issue an SWT alongside a JWT). To issue a saml token, we amended the initia idea given above. We ensured that the consent.aspx authorization endpoint is guarded by a websso token. It was THIS SAML token that we eventually use to augment the oauth response, bearing now three tokens: JWT, SWT, and SAML (from websso). We can augment the response because our token issuing endpoint wraps that of ACS (tied to the delegation record created during authorization). The websso token learned during authorization gets tied to the oauth state parameter (that is normally common to both authorization and token issuing calls). Thus what is writtne (keyed to state) during authorization can be retrieved (keyed to state) during token issuing. Thus our token issuer first gets delegated tokens from ACS’s own token issuing endpoint, and then augments that result with the SAML token associated with state (if any). Once given to the thick client (via oauth handshake), the client can use standard ws-trust service calls to exchange it for a microsoft online issuing encrypted token (and an internal nameid suitable for hosted office) that suits use of the Exchange managed API calls targeting an Office 365 Exchange server.