Currently in v6.0.0 master: Authorization Server by sjpadgett · Pull Request #4013 · openemr/openemr · GitHub
Here is a summary from post on issue thread: Implement OA2 Authorization server to support: · Issue #3956 · openemr/openemr · GitHub to catch up.
As soon as I can get some documentation done I plan to merge.
- If you are currently or plan to use our apis, I strongly encourage you to pay attention to this PR as our api base url has to change to support multi site.
- Also changing is one will have to register their client regardless if using the resource(password) or authorization grant as explained above. Once registered then always registered.
- Mostly OIDC compliant authorization with some, unnecessary for openemr, items left to do.
- Password grant can receive a token via hitting the oauth2/default/token endpoint with the password grant type.(we’ve removed the custom oauth2/default/password endpoint 11-18-2020)
- Refresh token is as normal with new access token issued by hitting oauth2/default/token endpoint.
I plan to do a complete api session for final testing before merge by documenting vis several posts in sequence here, starting with registration.
For clarity, all example urls with ‘default’ part is the targeted site id. I’m also not going to explain what every attribute means as there is more than enough information on the web. Plus, I hate to type.
Here’s registration Request(note: post_logout_redirect_uris is optional):
curl -X POST -k -H 'Content-Type: application/json' -i https://localhost/openemrv6/oauth2/default/registration --data '{
"application_type": "private",
"redirect_uris":
["https://client.example.org/callback"],
"post_logout_redirect_uris":
["https://client.example.org/logout/callback"],
"client_name": "A Private App",
"token_endpoint_auth_method": "client_secret_post",
"contacts": ["me@example.org", "them@example.org"]
}'
Response:
{
"client_id": "LnjqojEEjFYe5j2Jp9m9UnmuxOnMg4VodEJj3yE8_OA",
"client_secret": "j21ecvLmFi9HPc_Hv0t7Ptmf1pVcZQLtHjIdU7U9tkS9WAjFJwVMav0G8ogTJ62q4BATovC7BQ19Qagc4x9BBg",
"registration_access_token": "uiDSXx2GNSvYy5n8eW50aGrJz0HjaGpUdrGf07Agv_Q",
"registration_client_uri": "https:\/\/localhost\/openemrv6\/oauth2\/default\/client\/6eUVG0-qK2dYiwfYdECKIw",
"client_id_issued_at": 1604767861,
"client_secret_expires_at": 0,
"contacts": ["me@example.org", "them@example.org"],
"application_type": "private",
"client_name": "A Private App",
"redirect_uris": ["https:\/\/client.example.org\/callback"],
"token_endpoint_auth_method": "client_secret_post"
}
Next will be an authorization code or code grant type request:
curl -X GET -k -i 'https://localhost/openemrv6/oauth2/default/authorize?
response_type=code
&client_id=LnjqojEEjFYe5j2Jp9m9UnmuxOnMg4VodEJj3yE8_OA
&state=a85b870548dd8880ddb7c3192439f468fe63396f
&scope=openid email phone address api:pofh api:fhir
&redirect_uri=https://client.example.org/callback'
Response is a redirect to get user consent.
- Status : 301 Moved Permanently
- Location : https://localhost/openemrv6/oauth2/default/provider/login
Ignore Register button as forgot to remove. Then
Where an authorization code is returned and client sends a request for access_token:
curl -X POST -k -H 'Content-Type: application/x-www-form-urlencoded'
-i https://localhost/openemrv6/oauth2/default/token
--data 'grant_type=authorization_code
&client_id=LnjqojEEjFYe5j2Jp9m9UnmuxOnMg4VodEJj3yE8_OA
&client_secret=j21ecvLmFi9HPc_Hv0t7Ptmf1pVcZQLtHjIdU7U9tkS9WAjFJwV....
&redirect_uri=https://client.example.org/callback
&code=def50200102853212bca0b139843....'
with a json response:
{
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJ...",
"token_type": "Bearer",
"expires_in": 3600,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQi...",
"refresh_token": "def502001d015749fa13456a9d39faeccbf659..."
}
I’ll maybe come back to this to continue with refresh however, the web can describe this flow better than me. Next will do an api call using our issued token.
Here is a create patent FHIR request just to show it:
curl -X POST -k -H 'Authorization: Bearer eyJ0eXAiOiJ...' -i https://localhost/openemrv6/apis/default/fhir/Patient
--data '{"id":"46","meta":{"versionId":"1","lastUpdated":"2020-03-24T19:49:24"},"resourceType":"Patient","active":true,"name":[{"use":"official","family":"Aaaa Jr.","given":["Jim","Adam"]}],"gender":"male","birthDate":"1969-04-11","address":[{"line":["16129 Barry Rd"],"city":"Brandon","state":"FL","postalCode":"33511"}]}'
Response is a 201 created status and body:
{
"validationErrors": [],
"internalErrors": [],
"data": {
"pid": 511,
"uuid": "91f45c9b-36bc-4c8c-a646-030a758b771e"
}
}
So a couple other points real quick:
Hitting the https://localhost/openemrv6/oauth2/default/.well-known/openid-configuration endpoint will return any needed discovery of supported claims and scopes and supported auth endpoints as a json.
example:
{
"issuer": "https://localhost/openemrv6/oauth2/default",
"authorization_endpoint": "https://localhost/openemrv6/oauth2/default/authorize",
"token_endpoint": "https://localhost/openemrv6/oauth2/default/token",
"jwks_uri": "https://localhost/openemrv6/oauth2/default/jwk",
"userinfo_endpoint": "https://localhost/openemrv6/oauth2/default/userinfo",
"registration_endpoint": "https://localhost/openemrv6/oauth2/default/registration",
"scopes_supported": [
"openid",
"profile",
"name",
"given_name",
"family_name",
"nickname",
"phone",
"address",
"email",
"email_verified",
"api:oemr",
"api:fhir",
"api:port",
"api:pofh"
],
"response_types_supported": [
"code",
"token",
"id_token",
"code token",
"code id_token",
"token id_token",
"code token id_token"
],
"code_challenge_methods_supported": [
"S256",
"plain"
],
"grant_types_supported": [
"authorization_code",
"password",
"refresh_token"
],
"response_modes_supported": [
"query",
"fragment",
"form_post"
],
"subject_types_supported": [
"public"
],
"claims_supported": [
"aud",
"email",
"email_verified",
"exp",
"family_name",
"given_name",
"iat",
"iss",
"locale",
"name",
"sub"
],
"require_request_uri_registration": ["false"],
"id_token_signing_alg_values_supported": [
"RS256"
],
"token_endpoint_auth_methods_supported": [
"client_secret_post"
],
"token_endpoint_auth_signing_alg_values_supported": [
"RS256"
]
}
Forgot second point.
So once in codebase this phase 1 and 2(OAuth2 and OIDC) towards our ONC Api requirement. Besides Api testing requirements, the last phase of Api access will be the Smart on FHIR requirement.
Good luck.