V6 Authorization and API changes afoot

Finally was successful in getting through the entire flow on this. Something I noted in addition to the above tutorial was needed to place the state parameter when making a request for an access token which was also returned along with the authorization code (also needed to do it pretty quickly before it timed out :slight_smile: ). really neat stuff now that i am beginning to understand how it all works.

1 Like

Hi @sjpadgett
Thanks a lot for your clarification.

Just a few additional issues I’ll happy to understand better.

  1. “Password grant” - In which step of the process I can use it? what are the parameters need to send for that? when is better to use a password instead of a token?

  2. Do you think to integrate MFA in the authentication process (for users that registered to MFA in the user table)? is it can be fit together?

1 Like

Hi @Amiel,

  1. The password grant doesn’t require an authorization server sign in, only registration. The endpoint for password is the token endpoint with a password grant type and client_id. A secret currently is not required.
curl -X POST -k -H 'Content-Type: application/x-www-form-urlencoded' 
-i 'https://localhost/oauth2/default/token' 
--data 'grant_type=password
&scope=openid api:fhir'


  "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9....",
  "token_type": "Bearer",
  "expires_in": 3600,
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSU...",
  "refresh_token": "def50200ded9989134976302793b9cea72895d5..."
  1. I don’t plan to do MFA as is kind of out of scope for what we currently need for ONC. However, one could add a different third party provider to the flow and hook out of server sign in dialog. Or somewhere depending on what our current MFA requires. I’ve never looked at it and don’t know if only requires a dongle or also sends text/email etc…

edit: changed request password grant endpoint example to default/token as the most correct. Using default/password works also and is unique to openemr.

Here is a password refresh ex:

curl -X POST -k -H 'Content-Type: application/x-www-form-urlencoded'
-i 'https://localhost/oauth2/default/token' 
--data 'grant_type=refresh_token
1 Like

Just updated Authentication documentation for this feature:
openemr/API_README.md at master · openemr/openemr · GitHub

@sjpadgett , just really awesome stuff!!!

And of course the fans are happy


1 Like

Thanks for the coding/review help and especially keeping up with the darn official documenting @brady.miller.

@Amiel You folks are probably the furthest along with implementing this feature. Just wanted you to be aware that Brady and I have tightened security and updated the feature and is in master now.

I am very curious how you plan to use the feature. Are you primarily going to use with password grant or how do you plan to use the password grant?

Are you still looking into hooking in a third party provider or MFA?
Do you plan to use the public application type for your users and thus the code challenge flow?
Besides the implemented custom scopes of api:fhir, api:oemr, api:port and api:pofh are there others i’ve missed?

Feedback by anyone is appreciated before I move on to adding a SMART layer which i’ll probably start a new thread to track. SMART will not be included in our upcoming initial release of v6.0.0 but will soon follow.

Hi @sjpadgett
Thank you for being interested in us.
We are actually at the end of the development of a client-side application based on React for Emergency medical centers, the application needs to start a pilot in the next weeks therefore for now we implement Grant password authentication that matches to existing code, I hope we continue to Code grant when the project will grow up.
For MFA (we must for security requirements) we plan to expand the existing MFA found in the login of openemr to grant password in the API (for users that turn the MFA on), hopefully to create pull request soon.

Our application use a openemr API and FHIR api but most of the calls address to our zend modules that expand the api.

On this occasion I invite you to take a look on our projects, all the source is open, and from the last weekend integrated with the latest Openemr code.

backend modules -

React client -

devops tools -

Always happy about collaboration.

Best regards

1 Like

@sjpadgett @brady.miller
Just the last issue is missing in the new authorization server - logout action. would Do you plan to write it soon?
Thanks a lot…

Thanks @Amiel for sharing what your team has been up to. Very exciting work of which exposure on this thread may help some other community members. I know I learn a couple things.

Concerning logout action, yes I hope to have a PR up tonight or tomorrow that will include logout, revoke and maybe finalize the userinfo endpoint.

I’m still a little concerned with registration and may add a few items to track.

As for MFA, I assume our current core solution meets your need although, it sure would be handy to have in authorization server for apis.

Edit: Looking at a logout, i’m somewhat undecided how best to handle in our implementation.
I don’t issue cookies to user agent/user to maintain a persistent session state. I persist in a table.
As far as server is concerned, you are signed into the server unless a refresh token has expired at which point client must sign back into server to get a new token.
So a logout for us would really just be a revoke of refresh token.

How do you see this working?

For client logout here is pattern i’m thinking. Please, anyone with further input, please chime in.

  • basic
GET https://{baseUrl}/logout?id_token_hint=${id_token}

The trusted user session associated/identified by the id token will be deleted and a confirmation displayed.

  • registered redirects request initiates a logout and redirects to the post_logout_redirect_uri. The registered post_logout_redirect_uri and the request post_logout_redirect_uri must match.
GET https://{baseUrl}/logout?

Both initiate a redirect to either the OP confirm dialog or the redirected endpoint depending on request.

HTTP 302 Found
Location: https://post_logout_redirect_uri/redirect&state=${state}

If post_logout_redirect_uris is provided during registration then after session is destroyed, server will redirect to that endpoint otherwise, a logged out message is sent to user logging out.

We maintain user sessions as trusted users where a log out is essentially removing the trusted user resulting in resource server dispatch denying any tokens, valid or not, from advancing api request until user logs back into identity server.

I don’t see any reason to revoke tokens because that would essentially just be invalidating current session the tokens were issue against. If disagreement, please comment.

@Amiel this probably is of interest you.

1 Like

Looks excellent. thank you!

Announcing the token introspection endpoint and if I may say, at one point I just wanted to have a response of yep or nope. I mean, why be so formal!:slight_smile:

  • Only access_token and refresh_tokens will be validated. I may add id_token later but, it is really not needed IMO.

  • All Http status responses will be 200 with response showing whether active and the token status. Exceptions are in the case token fails signature verification or a mangled request then you’ll see appropriate 400/401/500.

  • To fetch url from discovery json use 'introspection_endpoint' that yields something like https://localhost/oauth2/default/introspect

  • Request attributes:

Field Description Type Required
client_id Application client Id String Yes
client_secret Application Client Secret. If client has private(confidential) registration status, then a client secret is mandatory otherwise, public apps only require client_id. String Yes
token_type_hint The appropriate token hint of access_token or refresh_token String Yes
token The string value of the token returned from auth token endpoints. String Yes
curl -X POST -k -H 'Content-Type: application/x-www-form-urlencoded'
 -i  'https://localhost:port/oauth2/default/introspect' 
--data 'client_id=kbyuFDidLLm280LIwVFiazOqj...
  • Responses:

Active Token

Response Body Field Value Returned
active true
status ‘active’
exp Expiry Epoch Time
sub The subject of the token. Mostly user_id UUID
scope Token scopes
client_id Application Client ID Value

Expired Token

Response Body Field Value Returned
active false
status ‘expired’
exp Expiry Epoch Time
sub The subject of the token. Mostly user_id UUID
scope Token scopes
client_id Application Client ID Value

Revoked/Logged out User Token

Response Body Field Value Returned
active false
status ‘revoked’
exp Expiry Epoch Time
sub The subject of the token. Mostly user_id UUID
scope Token scopes
client_id Application Client ID Value

Invalid Client Id or Client Secret
Also case of token client info doesn’t match trusted user. In both regards let’s not return anything useful

Response Body Field Value Returned
active false
status ‘invalid’
  • A refresh_token example response
    "active": true,
    "status": "active",
    "scope": "openid email phone api:fhir api:pofh site:default",
    "client_id": "kbyuFDidLLm280LIwVFiazOqjO3ty8KH",
    "exp": 1614792378,
    "sub": "91e65743-aa8c-4a7e-a183-706912c92436"
  • A token invalid client example response

So I think this gives a good overview of this endpoint. Comments welcomed.

Temporary note: I got a jump start on documenting this for comments with the PR going up within a day and in master shortly thereafter. I’ll post back once in master.

1 Like

Hi I’m trying to test out the new API, I see that it requires SSL.

I looked on the instructions and it said to set the baseurl at Administration->Globals->Connectors->'Site Address

Did the Site Address field move? I don’t see it in connectors, there’s an eRX site address but I don’t think that’s it.

Hi @RachelEllison
Hope you’re using v6.0.0-dev because this is not a v5.0.2 feature. However,

also I don’t check for a SSL certificate, yet, but, soon. Still, hit endpoints as https://…

Hi Jerry,

I am on version 6.0.0-dev

The site address textbox is missing in my connectors settings.

I tried pasting the the example on the readme without any changes and got the below error.

When is the last time u updated your dev version with master as this was added just recently?

I believe about 3 weeks ago. The curl queries I had used previously no longer work so I assume it’s due to the API changes.

That’s because we removed the old auth method and add new which is different endpoints.
Your request look okay.
Pull in current master and rebuild. Then try to register after setting up globals.

I’ll throw out there for other’s benefits who are trying to follow along with the CURL requests on the api documentation that I had to urlencode my curl request to get things to work. I’m on ubuntu w/ bash on the command line so not sure if its different on other environments as any spaces, colons, or slashes were creating errors.

Note you use the client_id you get from the registration request Jerry mentions. The state parameter is a randomly generated string you will use as a CSRF token. It will get returned to you at the callback URL you provided in the redirect_uris as part of your application client registration call and you can use it to make sure the request hasn’t been tampered with.

curl -X GET -k -i ‘https://localhost:9300/oauth2/default/authorize?response_type=code&client_id=9qAEZoCDYOdDlCeU1oJbNo-uItuzKxEqBCLz9hq6McA&state=a85b870548dd8880ddb7c3192439f468fe63396f&scope=openid%20email%20phone%20address%20api%3Afhir

(Note I’m using the default OpenEMR easy development docker install here).

1 Like

So another thing I’ve been running into problems and figured out that outside of the initial registration request any use of the redirect_uri parameter that @sjpadgett uses in his examples would return invalid responses. I haven’t dug into the code yet as to figure out why that is, but if you only use the redirect_uri parameter in the initial registration request, I was able to get things to work from start to finish.

Note the code parameter returned from the authorization request https://localhost:9300/oauth2/default/authorize (after you provide username/password and authorize the scopes) is VERY shortlived (I think its set to 60 seconds) so if you are manually testing this on the CLI you’ll want to make sure to handle that final request to https://localhost:9300/oauth2/default/token to get your access token is done quickly.