Tiered OAuth: Working Examples?

145 views
Skip to first unread message

Joseph Shook

unread,
Jun 16, 2023, 11:56:26 AM6/16/23
to UDAP

I want to get some work done on implementing Tiered OAuth as a server.  But first I wanted to see it in action as a client.  I am looking for an example.  I understand that EMR Direct (healthtogo) and Okta (udap.zimt.work) have this working.  So, I set off to try it out.   

 

DataHolder’s Auth Server ->  https://stage.healthtogo.me:8181/oauth/stage/authz

Identity Provider’s Auth Server ->  https://udap.zimt.work/oauth2/aus5wvee13EWm169M1d7

 

Below is a authorize request to healthtogo including the udap scope and idp=https://udap.zimt.work/oauth2/aus5wvee13EWm169M1d7

 

 

https://stage.healthtogo.me:8181/oauth/stage/authz?response_type=code&state=YvMZsqQKlkfO-8HhqVItcc0FNylBhA5zrMWSploBwg0&client_id=e6d94a0a-7435-4c2d-8122-117a4cafcef9&scope=openid udap user/Patient.r&idp=https://udap.zimt.work/oauth2/aus5wvee13EWm169M1d7&redirect_uri=https://udaped.fhirlabs.net/udapBusinessToBusiness&aud=https://stage.healthtogo.me:8181/fhir/r4/stage

I would expect the IdP to interact with the user but instead I am interacting with the healthtogo’ ABC Hospital System login screen as shown in the attachment.  

 

UdapTieredQuestion.png

Julie Maas

unread,
Jun 16, 2023, 12:07:26 PM6/16/23
to udap-d...@googlegroups.com
Hi Joe,
The link underneath the text boxes takes you to the "bring my own credentials" workflow you want. The page in the screenshot ensures the right authorization is collected, to allow data flow from the data holder to the client app, and also provides a way to proceed in case the user sticks with credentials native to the data holder instead. 
Try clicking that link and let us know of any issues.
You can see a detailed video of that process here: https://www.youtube.com/watch?v=oOqMgW_esBc 

--
You received this message because you are subscribed to the Google Groups "UDAP" group.
To unsubscribe from this group and stop receiving emails from it, send an email to udap-discuss...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/udap-discuss/247f4dc9-c90b-4ef0-8575-e10f6e693418n%40googlegroups.com.

Joseph Shook

unread,
Jun 16, 2023, 1:43:29 PM6/16/23
to UDAP
OK, that video helped visualize the workflow.  Thank you.  

But I am getting an error_description back during redirect to my client:

https://udaped.fhirlabs.net/udapBusinessToBusiness?error_description=upstream+identity+information+was+not+accepted+by+data+holder&state=s9ncmUTwjPvT7HIbLUkgi7iCl8xbQiuUZ_uwj1nBlkc&error=access_denied

This is URL I used to start the process:


I think the interesting thing I noticed is when examining the ABC login page's link: Authorize by signing in using credentials issued by your organization


Notice scope=openid+fhirUser in link.
I would have expected that to be openid+udap according to Security IG and udap.org.

Luis Maas

unread,
Jun 16, 2023, 2:39:30 PM6/16/23
to udap-d...@googlegroups.com, Joseph Shook

Hi Joe,

The access_denied error here means that the this data holder did not recognize the user information provided by the upstream IdP, i.e. it could not map the IdP user information to a user in the data holder's system.

Regarding the scopes in the second authz request, the data holder will not include 'udap' in the scopes sent to the upstream IdP because the data holder is not invoking Tiered OAuth a second time with the IdP. That is also why the data holder is not including idp=[upstream IdP URL] in its request.

Luis

Joseph Shook

unread,
Jun 16, 2023, 3:22:40 PM6/16/23
to UDAP
Hey Luis.  

I used the credentials for https://securefhir.zimt.work/udap-public, as included in the spreadsheet on line 27.  I am not sure if there is another set of credentials to use.  The indication in the UI at the IdP looks successful.  I end up with a cached login so I can't repeat the process to capture a video.  

So the goal is still to experience Tiered OAuth as a client...


Regarding scope.   Reading the UDAP spec (extracted below) I am struggling to understand.  Isn't HealthToGo the Resource Holder (Auth Server)?  And https://udap.zimt.work/oauth2/aus5wvee13EWm169M1d7 is the IdP?
If I was automatically redirected the example and text below indicates the udap scope should be included.  HealthToGo uses the UI and the the Get request.  I am not undertanding why this would not require the udap scope.  

3.4 If the Resource Holder trusts the IdP and has successfully obtained a client_id from the IdP, then the Resource Holder requests authentication of the user and authorization to access the user’s identity data from the IdP by redirecting the user to the IdP’s authorization endpoint. The Resource Holder’s Authorization Server MAY interact with the user before initiating the redirection, e.g. to inform the user that they will be redirected to the IdP for the purpose of authentication.

Note that the Resource Holder is acting as a client application when interacting with the IdP. In this tiered relationship, the original Client Application does not interact directly with the IdP. The Resource Holder includes the scopes “openid” and “udap” in its request to signal to the IdP that UDAP Tiered OAuth for User Authentication  is being requested. If this scope is omitted, the behavior of the IdP is entirely unspecified and the IdP SHOULD NOT proceed with the UDAP Tiered OAuth for User Authentication  workflow. The Resource Holder MUST use the authorization code flow when redirecting the user to the IdP’s authorization endpoint (even if the Client App requested a different grant type when connecting to the Resource Holder’s authorization endpoint):

HTTP/1.1 302 Found
Location: https://idp.example.com/optionalpath/authorize?
  response_type=code&
  state=resource_holder_random_state&
  client_id=resourceHolderClientIDforIdP&
  scope=openid+udap&
  nonce=resource_holder_nonce&
  redirect_uri=https://resourceholder.example.net/redirect HTTP/1.1

The use of the nonce parameter is RECOMMENDED. The Resource Holder MUST generate its own random value for the state parameter and MUST NOT reuse the value provided by the Client App in Step 2. Note: if the Resource Holder interacts with the user prior to the redirection to the IdP, then the Resource Holder MAY alternatively cause the user’s browser to be directed to the IdP’s authorization endpoint as the result of clicking on a hyperlink or other user action that results in the browser generating a new GET request rather than using the 302 redirection method described above.



Dan Cinnamon

unread,
Jun 16, 2023, 4:09:21 PM6/16/23
to UDAP
Hi Joe-

I took a quick look on my end, and you are indeed getting an ID/Access token minted from the IDP (or at least I"m seeing successful log entries from not long ago for the EMRDirect data holder). So I concur with Luis that this is more of a data mapping issue (meaning the value I'm passing back doesn't necessarily match what their ID is).

Luis- if memory serves (and this is going back to January now)- I think when you and I were testing in this exact configuration (with EMR direct as data holder, and me as IDP) we stopped here, realizing that I likely wouldn't have a proper ID that your system could match on.  Do you remember? I can certainly make adjustments on the IDP end to pass a different ID if it'd help match on something within your system.

Joe- we do know that Tom's server and mine have matching IDs.  Have you attempted pointing your client at the udap.poolnook.me server with the IDP being the same (udap.zimt.work)?
-Dan

Joseph Shook

unread,
Jun 16, 2023, 4:24:09 PM6/16/23
to UDAP
Hi Dan.  Good to see you in the mix.

udap.poolnook.me does not appear to be running.  I can't find it in DNS at the moment.

Matching IDs?  client_id?  Not following that.  
Dan- back in Harrison Nevada you said, "once you get authorization code flow working, tiered OAuth will be easy".  Well after a few days of thinking this through I have not found the "easy" part. :)  

- Joe

Dan Cinnamon

unread,
Jun 16, 2023, 4:35:57 PM6/16/23
to UDAP
Ahh- sorry about that: the latest address for Tom's server is actually: https://udap.fhir.poolnook.me/evernorth (I spoke with him quick here before posting).
As we've been refactoring our implementation we've been updating a few things.  

What I mean by Matching IDs is that right now i'm (the udap.zimt.work IDP) is passing back a valid fhirUser claim in the ID token, but that's not necessarily what EMR direct is performing it's match on. So EMRdirect is simply saying "great, I have an ID token but I have no idea which one of my users you are".  That's where I'm not sure we actually went any further than that in January when Luis and I were testing together.

Tom Loomis

unread,
Jun 16, 2023, 4:46:30 PM6/16/23
to UDAP
Thanks Dan!.  Updated the spreadsheet.   udap.fast.poolnook.me is the correct url.  Apologies.

Dan Cinnamon

unread,
Jun 16, 2023, 4:52:35 PM6/16/23
to UDAP
Sure Tom- 

I did want to clarify one thing though...

https://udap.fast.poolnook.me is the authorization server that secures the https://udap.fhir.poolnook.me/evernorth FHIR test server.

In other words, if you go to https://udap.fhir.poolnook.me/evernorth/.well-known/udap, you'll find references to https://udap.fast.poolnook.me.

So if an end client wishes to begin a tiered oauth against your server, where you're the dataholder, they'll actually begin the flow at https://udap.fhir.poolnook.me/evernorth.

Just looking to clarify the roles/responsibilities a little bit.

Cheers!

-Dan

Joseph Shook

unread,
Jun 16, 2023, 5:41:58 PM6/16/23
to UDAP
And now we have Tom.  It is late for you on a Friday, but I appreciate the attention.

Anyway this is what I tried next. 

Data Holder AuthServer -> udap.fast.poolnook.me
IdP AuthServer ->  udap.zimt.work

  &state=1M_ZYrHNRZ7olveIeFKxW3QZDV4p-e3x846MlZdgDMA
  &client_id=0oaa00hrkjopl2TT75d7
  &scope=openid udap system/Patient.r

That gives my client back an authorization code

HTTP/1.1 302 Found
?code=WQCmDz9bXW0EKvoPdirdphCrhS8F4g3A26R1jmNQnUA& state=1M_ZYrHNRZ7olveIeFKxW3QZDV4p-e3x846MlZdgDMA

This is not what I expect according to the IG and udap.org. I expect the Data Holder's Authorization Server to do this work on the clien'ts behalf.

4.3 If the Resource Holder receives an authorization code from the IdP and the state value is valid, the Resource Holder then connects to the IDP’s token endpoint on the back-end to retrieve an ID token and access token for use with the IdP’s UserInfo endpoint. The Resource Holder MUST authenticate to the IdP’s token endpoint as detailed in Section 5 of UDAP JWT-based Client Authentication:

POST /optionalpath/token HTTP/1.1
Host: idp.example.com
Content-type: application/x-www-form-urlencoded

grant_type=authorization_code&
  code=authz_code_from_idp &
  client_assertion_type=urn:ietf:params:oauth:grant-type:jwt-bearer&
  client_assertion=eyJh[…remainder of AnT omitted for brevity…]&
  udap=1


 - Joe

Dan Cinnamon

unread,
Jun 16, 2023, 5:56:30 PM6/16/23
to udap-d...@googlegroups.com
The urls you posted look good- the main question here is- what server sent you the 302? If everything was successful, the expectation is that the data holder server would return that (after doing the full oauth dance-authorize/token/callback/etc) with the idp. 
In short- are you seeing /authorize calls on both the data holder and the idp?

On Fri, Jun 16, 2023 at 4:42 PM Joseph Shook <joes...@gmail.com> wrote:

This message originated outside your organization.




You received this message because you are subscribed to a topic in the Google Groups "UDAP" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/udap-discuss/5q9m4vAlJHk/unsubscribe.
To unsubscribe from this group and all its topics, send an email to udap-discuss...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/udap-discuss/1f68f76a-bf05-4535-8e69-966fef9c0e76n%40googlegroups.com.
--

Dan Cinnamon (he/him/his)

Principal Solutions Architect

612-747-6324

dan.ci...@okta.com


Joseph Shook

unread,
Jun 16, 2023, 7:06:38 PM6/16/23
to UDAP
Dan- I guess if I watch the network traffic in the web browser dev tools I do see an authcode come back from the IdP as a redirect to udap.fast.poolnook.me.  Here is a series of redirects for a cached IdP login.

So as a client I still have to "cash in" my authorization code for an access_token after the last step in this sequence diagram.  At least I think that is the case as I digest all of this.  Correct me if I am wrong.

The IG in section 6.3  has this sentence:
If the data holder successfully maps the authenticated user to a user or role in its own system, as appropriate for the resources requested, it SHALL also obtain authorization from the user for the scopes requested by the client app, if such authorization is required, as per Section 4.5 of UDAP Tiered OAuth, returning to the workflow defined in Section 4.1 or Section 5.1 of this guide, for consumer-facing or B2B apps, respectively.

I am not sure why it indicates returning to 4.1 or 5.1 rather than 4.2 or 5.2.  After all I have an authorization code now and would be in the "Obtaining an access token" part of the workflow.TieredOAuthSequenceDiagram.png

- JoE

Dan Cinnamon

unread,
Jun 19, 2023, 9:39:40 AM6/19/23
to UDAP
Hi Joe-
Yes- you're tracking properly. At this point Tom's server is providing you with an authorization code, which you then still have to turn in for a token from Tom's server.

Given you had a logged in session already it appears- you wouldn't have seen a login screen... however if you weren't already logged in, you would've seen a login page from my sever (udap.zimt.work)- not Tom's (udap.fast.poolnook.me).  So even though your client is ultimately getting a token from Tom's server- the user never actually had to provide credentials there- instead that duty was delegated out via tiered-oauth.  If you were to redo your test in a private/incognito window, you'd see this more explicitly.

Questions:
I assume in here poolnook is sending a POST to get a Identity Token?  Yes it is. You don't see it in the browser because that is happening in the backchannel (meaning- poolnook is directly calling zimt.work behind the scenes to get that token).

How this matches to clients registration, I am not sure yet. 
During that very first request to poolnook/authorize, the /authorize endpoint will grab the "idp" value and it will look to see if it is registered with that IDP yet.  
Meaning- poolnook at that point itself becomes a client AND a server. Behind the scenes (still, during that first authorize request) poolnook does a UDAP DCR with udap.zimt.work, and gets it's own client_id for it's use in getting tokens from udap.zimt.work (if it's not already registered).  You should see that the client ids in the 2 authorize calls are different. One is your client, and one is poolnook acting as a client to get an identity from udap.zimt.work. One final comment here is that udap.zimt.work (the IDP) doesn't really ever "see" your end client- it thinks it's dealing exclusively with poolnook and that's it. Conversely, your client doesn't really deal with udap.zimt.work directly either- other than slapping the url in the /authorize endpoint.

I am not sure why it indicates returning to 4.1 or 5.1 rather than 4.2 or 5.2. 
Reading the comment in it's intended context- I can see why it's phrased this way, however I see where you're coming from.  Given how much is going on, this could probably use some additional language to help clarify that you're essentially "done" with 4.1, and the immediate next step is 4.2.  Also, B2B flow doesn't apply at all for tiered-oauth- only B2C.  So the "or 5.1" piece should probably just be stripped out.

Tom Loomis

unread,
Jun 19, 2023, 10:40:37 AM6/19/23
to UDAP
Joe:

I'm around this week and now watching this thread again if you need me to check logs etc.

Tom

Joe Shook

unread,
Jun 19, 2023, 12:23:30 PM6/19/23
to udap-d...@googlegroups.com
Thank you everyone for commenting.  This will be a good thread for others working through Tiered Oauth.

Thanks Tom, for the offer.  I think I will be in construction mode for a while before I come back to integrate.  I have all the tools to do the job now.  Queue the construction noise...

Stay tuned.

- JoE

Joseph Shook

unread,
Jul 3, 2023, 1:12:43 PM7/3/23
to UDAP
FYI, I have a successful pass of Tiered OAuth using Okta as an Identity Server and one of my lab based Identity Servers.  I am using my client and my auth server as the dataholder role.

I just wanted to share this with someone who cares.  My wife is not interested in Tiered OAuth no matter how much I tell her about it.  :) 

There is a lot more engineering time before I am happy with it, but I may try to push code to the cloud before the engineering is done.  

Happy 4th of July weekend!

- JoE

Brett Stringham

unread,
Jul 3, 2023, 1:25:53 PM7/3/23
to udap-d...@googlegroups.com
Awesome Joe! A moment worthy of some fireworks... great work!!

UDAP

unread,
Jul 3, 2023, 1:30:22 PM7/3/23
to UDAP
Thanks for sharing, Joe! 

You're helping the industry tremendously with this excellent progress on the path to reusable identities. We'll try to cross test with you soon, once you have endpoints posted on the implementers spreadsheet.

Sending you cheers from our team here in San Diego. Happy 4th to you too.

--The UDAP.org Team

Dan Cinnamon

unread,
Jul 5, 2023, 9:33:46 AM7/5/23
to udap-d...@googlegroups.com
Awesome- Congratulations Joe!


Dan Cinnamon (he/him/his)

Principal Solutions Architect

612-747-6324

dan.ci...@okta.com



Joseph Shook

unread,
Jul 17, 2023, 9:45:09 AM7/17/23
to UDAP
I started a discussion in Zulip.   Under the FAST :: 2023 CMS Connectathon  topic.  It is relevant to everyone in this thread in preperation for the Connectathon and presentation.

Joseph Shook

unread,
Jul 17, 2023, 10:51:16 AM7/17/23
to UDAP
Does EMR Direct have a UDAP enabled IdP for testing that I can use?  I do not see it published in the spreadsheet.

Dan Cinnamon

unread,
Jul 17, 2023, 11:52:06 AM7/17/23
to UDAP
Hey Joe-
I'm in zulip- I don't see the chat that you started. Is there perhaps a link to it that you could help me out with?

I'm planning on bringing my services down for a bit for some updates prior to tomorrow's calls.

Joseph Shook

unread,
Jul 17, 2023, 1:22:26 PM7/17/23
to UDAP

Dan Cinnamon

unread,
Jul 17, 2023, 4:00:53 PM7/17/23
to UDAP
Thanks Joe- got it!

Joseph Shook

unread,
Jul 17, 2023, 9:08:09 PM7/17/23
to UDAP
Dan not sure if you are monitoring or getting notified by Zulip, but I posted this over there.

@Dan Cinnamon after your last deploy the jwt iss and sub claims no longer match your baseurl. It should be https://udap.zimt.work/oauth2/aus5wvee13EWm169M1d7. The URI subject alt name is good with one matching value.

I see healthtogo catching this error when I try Tiered OAuth through them.

Dan Cinnamon

unread,
Jul 17, 2023, 10:21:00 PM7/17/23
to UDAP
Hey @Brett Stringham-

Just a quick FYI that while I was "under the hood" getting things ready for the connectathon tomorrow... i went ahead and fixed that "kid" thing that you raised a while back. As a reminder- when you were including the "kid" value in your JWT header during registration-- I was not properly saving that, but instead throwing it away and generating my own kid.  Now, I've taken care to, if provided in the JWT header, save the kid value you provide.

-Dan

Reply all
Reply to author
Forward
0 new messages