Skip to main content

Standalone Launch

Step 1: Your Application Requests an Authorization Code

  • Your application would like to authenticate the user using the OAuth 2.0 workflow.
  • To initiate this process, your app needs to link (using HTTP GET) to the authorize endpoint and append the following querystring parameters:

response_type:This parameter must contain the value "code".

client_id: This parameter contains your web application's client ID issued by Epic.

redirect_uri: This parameter contains your application's redirect URI. After the request completes on the Epic server, this URI will be called as a callback.

state: This optional parameter is generated by your app and is opaque to the EHR. The EHR's authorization server will append it to each subsequent exchange in the workflow for you to validate session integrity.

scope: This parameter describes the information for which the web application is requesting access.

aud: Health care organizations can optionally configure their system to require the aud parameter for Standalone and EHR launch workflows. The value to use is the base URL of the resource server the application intends to access, which is typically the FHIR server. Additional parameters for native mobile apps (available starting in the August 2019 version of Epic):

code_challenge: This optional parameter is generated by your app and used for PKCE. This is the S256 hashed version of the code_verifier parameter, which will be used in the token request.

code_challenge_method: This optional parameter indicates the method used for the code_challenge parameter and is required if using that parameter. Currently, only the S256 method is supported.

Here's an example of an authorization request using HTTP GET. You will replace the [redirect_uri], [client_id], [state], and [audience] placeholders with your own values.

https://fhir.epic.com/interconnect-fhir-oauth/oauth2/authorize?response_type=code&redirect_uri=[redirect_uri]&client_id=[client_id]&state=[state]&aud=[audience]

This is an example link:

https://fhir.epic.com/interconnect-fhir-oauth/oauth2/authorize?response_type=code&redirect_uri=https%3A%2F%2Ffhir.epic.com%2Ftest%2Fsmart&client_id=d45049c3-3441-40ef-ab4d-b9cd86a17225&state=abc123&aud=https%3A%2F%2Ffhir.epic.com%2Finterconnect-fhir-oauth%2Fapi%2Ffhir%2Fdstu2

Step 2: EHR's Authorization Server Authenticates the User and Authorizes Access.

The EHR's authorization server reviews the request from your application, authenticates the user (sample credentials found here), and authorizes access If approved, the authorization server redirects the browser to the redirect URL supplied in the initial request and appends the following querystring parameter.

code: This parameter contains the authorization code generated by Epic, which will be exchanged for the access token in the next step.

state: This parameter will have the same value as the earlier state parameter. For more information, refer to Step 1. Here's an example of what the redirect will look like if Epic's authorization server accepts the request:

https://fhir.epic.com/test/smart?code=yfNg-rSc1t5O2p6jVAZLyY00uOOte5KM1y3YUxqsJQnBKEMNsYqOPTyVqcCH3YXaPkLztO9Rvf7bhLqQTwALHcHN6raxpTbR1eVgV2QyLA_4K0HrJO92et3qRXiXPkj7&state=abc123

Step 3: Your Application Exchanges the Authorization Code for an Access Token

After receiving the authorization code, your application trades the code for a JSON object containing an access token and contextual information by sending an HTTP POST to the token endpoint using a Content-Type header with value of "application/x-www-form-urlencoded".

Access Token Request: If You Are Not Using a Client Secret

The following parameters are required in the POST body:

grant_type: For the Standalone launch flow, this should contain the value "authorization_code".

code: This parameter contains the authorization code sent from Epic's authorization server to your application as a querystring parameter on the redirect URI as described above.

redirect_uri: This parameter must contain the same redirect URI that you provided in the initial access request. The value of this parameter needs to be URL encoded.

client_id: This parameter must contain the application's client ID issued by Epic that you provided in the initial request. Here's an example of what an HTTP POST request for an access token might look like:

POST https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token HTTP/1.1

Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=yfNg-rSc1t5O2p6jVAZLyY00uOOte5KM1y3YUxqsJQnBKEMNsYqOPTyVqcCH3YXaPkLztO9Rvf7bhLqQTwALHcHN6raxpTbR1eVgV2QyLA_4K0HrJO92et3qRXiXPkj7&redirect_uri=https%3A%2F%2Ffhir.epic.com%2Ftest%2Fsmart&client_id=d45049c3-3441-40ef-ab4d-b9cd86a17225

The authorization server responds to the HTTP POST request with a JSON object that includes an access token. The response contains the following fields:

access_token: This parameter contains the access token issued by Epic to your application and is used in future requests.

token_type: In Epic's OAuth 2.0 implementation, this parameter always includes the value bearer.

expires_in: This parameter contains the number of seconds for which the access token is valid.

scope: This parameter describes the access your application is authorized for.

id_token: Returned only for applications that have requested an openid scope. See above for more info on OpenID Connect id_tokens.

This parameter follows the guidelines described earlier in this document.

patient: For patient-facing workflows, this parameter identifies the FHIR ID for the patient on whose behalf authorization to the system was granted.The patient's FHIR ID is not returned for provider-facing standalone launch workflows.

epic.dstu2.patient: For patient-facing workflows, this parameter identifies the DSTU2 FHIR ID for the patient on whose behalf authorization to the system was granted.The patient's FHIR ID is not returned for provider-facing standalone launch workflows.

Note that you can pass additional parameters if needed based on the integration configuration.

Here's an example of what a JSON object including an access token might look like:

{
"access_token": "Nxfve4q3H9TKs5F5vf6kRYAZqzK7j9LHvrg1Bw7fU_07_FdV9aRzLCI1GxOn20LuO2Ahl5RkRnz-p8u1MeYWqA85T8s4Ce3LcgQqIwsTkI7wezBsMduPw_xkVtLzLU2O",
"token_type": "bearer",
"expires_in": 3240,
"scope": "Patient.read Patient.search ",
"patient": "T1wI5bk8n1YVgvWk9D05BmRV0Pi3ECImNSK8DKyKltsMB"
}

At this point, authorization is complete and the web application can access the protected patient data it requested using FHIR APIs.

Access Token Request: If You Are Using a Client Secret:

After receiving the authorization code, your application trades the code for a JSON object containing an access token and contextual information by sending an HTTP POST to the token endpoint using a Content-Type header with value of "application/x-www-form-urlencoded".

The Epic on FHIR website can generate a client secret (effectively a password) for your app to use to obtain refresh tokens, and store the hashed secret for you, or Epic community members can upload a client secret hash that you provide them when they activate your app for their system. If you provide community members a client secret hash to upload, you should use a unique client secret per Epic community member and per environment type (non-production and production) for each Epic community member.

The following parameters are required in the POST body:

grant_type: This should contain the value authorization_code. code: This parameter contains the authorization code sent from Epic's authorization server to your application as a querystring parameter on the redirect URI as described above. redirect_uri: This parameter must contain the same redirect URI that you provided in the initial access request. The value of this parameter needs to be URL encoded.

  • Note: The client_id parameter is not passed in the the POST body if you use client secret authentication, which is different from the access token request for apps that do not use refresh tokens.

  • You will instead pass an Authorization HTTP header with client_id and client_secret URL encoded and passed as a username and password. Conceptually the Authorization HTTP header will have this value: base64(client_id:client_secret).

For example, using the following client_id and client_secret:

  • client_id: d45049c3-3441-40ef-ab4d-b9cd86a17225

  • URL encoded client_id: d45049c3-3441-40ef-ab4d-b9cd86a17225 Note: base64 encoding Epic's client IDs will have no effect

  • client_secret: this-is-the-secret-2/7

  • URL encoded client_secret: this-is-the-secret-2%2F7

Would result in this Authorization header:

  • Authorization: Basic base64Encode{d45049c3-3441-40ef-ab4d-b9cd86a17225:this-is-the-secret-2%2F7}

or

  • Authorization: Basic ZDQ1MDQ5YzMtMzQ0MS00MGVmLWFiNGQtYjljZDg2YTE3MjI1OnRoaXMtaXMtdGhlLXNlY3JldC0yJTJGNw==

Here's an example of what a valid HTTP POST might look like:

POST https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Authorization: Basic ZDQ1MDQ5YzMtMzQ0MS00MGVmLWFiNGQtYjljZDg2YTE3MjI1OnRoaXMtaXMtdGhlLXNlY3JldC0yJTJGNw==
grant_type=authorization_code&code=yfNg-rSc1t5O2p6jVAZLyY00uOOte5KM1y3YUxqsJQnBKEMNsYqOPTyVqcCH3YXaPkLztO9Rvf7bhLqQTwALHcHN6raxpTbR1eVgV2QyLA_4K0HrJO92et3qRXiXPkj7&redirect_uri=https%3A%2F%2Ffhir.epic.com%2Ftest%2Fsmart

The authorization server responds to the HTTP POST request with a JSON object that includes an access token and a refresh token. The response contains the following fields:

refresh_token: This parameter contains the refresh token issued by Epic to your application and can be used to obtain a new access token. For more information on how this works, see Step 5. See the Epic-Issued OAuth 2.0 Tokens appendix section for details on handling refresh tokens.

access_token: This parameter contains the access token issued by Epic to your application and is used in future requests. See the Epic-Issued OAuth 2.0 Tokens appendix section for details on handling access tokens.

token_type: In Epic's OAuth 2.0 implementation, this parameter always includes the value bearer.

expires_in: This parameter contains the number of seconds for which the access token is valid.

scope: This parameter describes the access your application is authorized for.

id_token: Returned only for applications that have requested an openid scope. See above for more info on OpenID Connect id_tokens.

This parameter follows the guidelines of the OpenID Connect (OIDC) Core 1.0 specification.It is signed but not encrypted.

patient: For patient-facing workflows, this parameter identifies the FHIR ID for the patient on whose behalf authorization to the system was granted.The patient's FHIR ID is not returned for provider-facing standalone launch workflows.

epic.dstu2.patient: For patient-facing workflows, this parameter identifies the DSTU2 FHIR ID for the patient on whose behalf authorization to the system was granted.The patient's FHIR ID is not returned for provider-facing standalone launch workflows.

encounter: This parameter identifies the FHIR ID for the patient’s encounter, if in context at time of launch. The encounter token corresponds to the FHIR Encounter resource.The encounter FHIR ID is not returned for standalone launch workflows.

location: This parameter identifies the FHIR ID for the ecounter department, if in context at time of launch. The location token corresponds to the FHIR Location resource.The location FHIR ID is not returned for standalone launch workflows.

appointment: This parameter identifies the FHIR ID for the patient’s appointment, if appointment context is available at time of launch. The appointment token corresponds to the FHIR Appointment resource.The appointment FHIR ID is not returned for standalone launch workflows.

loginDepartment: This parameter identifies the FHIR ID of the user's login department for launches from Hyperspace. The loginDepartment token corresponds to the FHIR Location resource.The loginDepartment FHIR ID is not returned for standalone launch workflows.

Note that you can pass additional parameters if needed based on the integration configuration.

Here's an example of what a JSON object including an access token and refres token might look like:

{
"access_token": "Nxfve4q3H9TKs5F5vf6kRYAZqzK7j9LHvrg1Bw7fU_07_FdV9aRzLCI1GxOn20LuO2Ahl5RkRnz-p8u1MeYWqA85T8s4Ce3LcgQqIwsTkI7wezBsMduPw_xkVtLzLU2O",
"refresh_token": "H9TKs5F5vf6kRYAZqzK7j9L_07_FdV9aRzLCI1GxOn20LuO2Ahl5R1MeYWqA85T8s4sTkI7wezBsMduPw_xkLzLU2O",
"token_type": "bearer",
"expires_in": 3240,
"scope": "Patient.read Patient.search ",
"patient": "T1wI5bk8n1YVgvWk9D05BmRV0Pi3ECImNSK8DKyKltsMB"
}

At this point, authorization is complete and the web application can access the protected patient data it requested using FHIR APIs.

Step 4: Your Application Uses FHIR APIs to Access Patient Data

With a valid access token, your application can now access protected patient data from the EHR database using FHIR APIs. Queries must contain an Authorization header that includes the access token presented as a Bearer token.

Here's an example of what a valid query looks like:

GET https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/DSTU2/Patient/T1wI5bk8n1YVgvWk9D05BmRV0Pi3ECImNSK8DKyKltsMB HTTP/1.1
Authorization: Bearer Nxfve4q3H9TKs5F5vf6kRYAZqzK7j9LHvrg1Bw7fU_07_FdV9aRzLCI1GxOn20LuO2Ahl5RkRnz-p8u1MeYWqA85T8s4Ce3LcgQqIwsTkI7wezBsMduPw_xkVtLzLU2O

Step 5: Use a Refresh Token to Obtain a New Access Token

  • If your app uses refresh tokens (i.e. it can securely store credentials), then you can use a refresh token to request a new access token when the current access token expires (determined by the expires_in field from the authorization response from step 3).

  • Your application trades the refresh_token for a JSON object containing a new access token and contextual information by sending an HTTP POST to the token endpoint using a Content-Type HTTP header with value of "application/x-www-form-urlencoded".

  • The Epic on FHIR website can generate a client secret (effectively a password) for your app when using refresh tokens, and store the hashed secret for you, or Epic community members can upload a client secret hash that you provide them when they activate your app for their system. If you provide community members a client secret hash to upload, you should use a unique client secret per Epic community member and per environment type (non-production and production) for each Epic community member.

The following parameters are required in the POST body:

grant_type: This parameter always contains the value refresh_token.

refresh_token: The refresh token received from a prior authorization request. An Authorization header using HTTP Basic Authentication is required, where the username is the URL encoded client_id and the password is the URL encoded client_secret.

For example, using the following client_id and client_secret:

  • client_id: d45049c3-3441-40ef-ab4d-b9cd86a17225

  • URL encoded client_id: d45049c3-3441-40ef-ab4d-b9cd86a17225 Note: base64 encoding Epic's client IDs will have no effect

  • client_secret: this-is-the-secret-2/7

  • URL encoded client_secret: this-is-the-secret-2%2F7

Would result in this Authorization header:

  • Authorization: Basic base64Encode{d45049c3-3441-40ef-ab4d-b9cd86a17225:this-is-the-secret-2%2F7} or

  • Authorization: Basic ZDQ1MDQ5YzMtMzQ0MS00MGVmLWFiNGQtYjljZDg2YTE3MjI1OnRoaXMtaXMtdGhlLXNlY3JldC0yJTJGNw==

Here's an example of what a valid HTTP POST might look like:

POST https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token HTTP/1.1
Authorization: Basic ZDQ1MDQ5YzMtMzQ0MS00MGVmLWFiNGQtYjljZDg2YTE3MjI1OnRoaXMtaXMtdGhlLXNlY3JldC0yJTJGNw==
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=j12xcniournlsdf234bgsd

The authorization server responds to the HTTP POST request with a JSON object that includes the new access token. The response contains the following fields:

access_token: This parameter contains the new access token issued.

token_type: In Epic's OAuth 2.0 implementation, this parameter always includes the value bearer.

expires_in: This parameter contains the number of seconds for which the access token is valid.

scope: This parameter describes the access your application is authorized for. An example response to the previous request may look like the following:

{
"access_token": "57CjhZEdiTcCh1nqIwQUw5rODOLP3bSTnMEGNYbBerSeNn8hIUm6Mlc5ruCTfQawjRAoR8sYr8S_7vNdnJfgRKfD6s8mOqPnvJ8vZOHjEvy7l3Ra9frDaEAUBbN-j86k",
"token_type": "bearer",
"expires_in": 3240,
"scope": "Patient.read Patient.search ",
"refresh_token": "b72foiua9asdhnkjanvm"
}

Offline Access for Native and Browser-Based Applications

For a native client app (for example, an iOS mobile app or a Windows desktop app) or a browser-based app (for example, a single-page application) to use the confidential app profile in the SMART App Launch framework, that app needs to use "additional technology (such as dynamic client registration and universal redirect_uris) to protect the secret." If you have a native client or browser-based app, you can register a dynamic client to integrate with Epic using the steps below.

Step 1: Get the initial access token you'll use to register a dynamic client

  • Your app uses a flow like the one described above for a Standalone Launch. Note your application will specifically not be using a client secret, and will use its initial client ID issued by Epic.

  • The end-result of this flow is your app obtaining an initial access token (defined in RFC 7591) used for registering a client instance (step 2). This token is intentionally one-time-use and should be used for registration immediately.

Step 2: Register the dynamic client

  • To register the dynamic client, your application needs to:

  • Generate a public-private key pair on the user’s phone or computer.

  • Native apps can use the same strategies as backend apps to generate key sets

  • Browser-based apps can use the WebCrypto API

  • Securely store that device-specific key pair on the user’s device.

  • Browser-based apps using WebCrypto should follow key storage recommendations

  • Use the access token from step 1 to register a dynamic client using the OAuth 2.0 Dynamic Client Registration Protocol.

This request must have two elements:

software_id: This parameter must contain the application’s client ID issued by Epic.

jwks: This parameter contains a JSON Web Key Set containing the public key from the key pair your application generated in step 2A.

Here’s an example of what an HTTP POST request to register a dynamic client might look like:

POST https://fhir.epic.com/interconnect-fhir-oauth/oauth2/register HTTP/1.1
Content-Type: application/json
Authorization: Bearer Nxfve4q3H9TKs5F5vf6kRYAZqzK7j9LHvrg1Bw7fU_07_FdV9aRzLCI1GxOn20LuO2Ahl5RkRnz-p8u1MeYWqA85T8s4Ce3LcgQqIwsTkI7wezBsMduPw_xkVtLzLU2O

{
"software_id": "d45049c3-3441-40ef-ab4d-b9cd86a17225",
"jwks": {
"keys": [{
"e": "AQAB",
"kty": "RSA",
"n": "vGASMnWdI-ManPgJi5XeT15Uf1tgpaNBmxfa-_bKG6G1DDTsYBy2K1uubppWMcl8Ff_2oWe6wKDMx2-bvrQQkR1zcV96yOgNmfDXuSSR1y7xk1Kd-uUhvmIKk81UvKbKOnPetnO1IftpEBm5Llzy-1dN3kkJqFabFSd3ujqi2ZGuvxfouZ-S3lpTU3O6zxNR6oZEbP2BwECoBORL5cOWOu_pYJvALf0njmamRQ2FKKCC-pf0LBtACU9tbPgHorD3iDdis1_cvk16i9a3HE2h4Hei4-nDQRXfVgXLzgr7GdJf1ArR1y65LVWvtuwNf7BaxVkEae1qKVLa2RUeg8imuw",
}
]
}
}

The EHR stores your app’s public key, issues it a dynamic client ID, and returns a response that might look like:

HTTP/1.1 201 Created
Content-Type: application/json

{
"redirect_uris": [
" https://fhir.epic.com/test/smart"
],
"token_endpoint_auth_method": "none",
"grant_types": [
"urn:ietf:params:oauth:grant-type:jwt-bearer"
],
"software_id": " d45049c3-3441-40ef-ab4d-b9cd86a17225",
"client_id": "G65DA2AF4-1C91-11EC-9280-0050568B7514",
"client_id_issued_at": 1632417134,
"jwks": {
"keys": [{
"kty": "RSA",
"n": "vGASMnWdI-ManPgJi5XeT15Uf1tgpaNBmxfa-_bKG6G1DDTsYBy2K1uubppWMcl8Ff_2oWe6wKDMx2-bvrQQkR1zcV96yOgNmfDXuSSR1y7xk1Kd-uUhvmIKk81UvKbKOnPetnO1IftpEBm5Llzy-1dN3kkJqFabFSd3ujqi2ZGuvxfouZ-S3lpTU3O6zxNR6oZEbP2BwECoBORL5cOWOu_pYJvALf0njmamRQ2FKKCC-pf0LBtACU9tbPgHorD3iDdis1_cvk16i9a3HE2h4Hei4-nDQRXfVgXLzgr7GdJf1ArR1y65LVWvtuwNf7BaxVkEae1qKVLa2RUeg8imuw",
"e": "AQAB"
}
]
}
}

Step 3: Get an access token you can use to retrieve FHIR resources

  • Now that you have registered a dynamic client with its own credentials, there are two ways you can get an access token to make FHIR calls.
  • We recommend that you use the JWT bearer flow.

*In the JWT bearer flow, the client issues a JWT with its client ID as the "sub" (subject) claim, indicating the access token should be issued to the users who registered the client.

  • It is also possible to use the SMART standalone launch sequence again to get a refresh token and then use the refresh token flow.

Note that both flows involve your app making a JSON Web Token and signing it with its private key.

Generate a JSON Web Token

  • Your app follows the guidelines below to create a JWT assertion using it's new private key

  • To get an access token, your confidential app needs to authenticate itself.

  • Your dynamic client uses the private key it generated in step 2 to sign a JWT.

Here’s an example of the JWT body:

{
"sub": "G00000000-0129-A6FA-7EDC-55B3FB6E85F8",
"aud": "https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token",
"jti": "3fca7b08-e5a1-4476-9e4b-4e17f0ad7d1d",
"nbf": 1639694983,
"exp": 1639695283,
"iat": 1639694983,
"iss": "G00000000-0129-A6FA-7EDC-55B3FB6E85F8"
}

And here’s the final encoded JWT:

eyJhbGciOiJSUzI1NiIsImtpZCI6ImJXbGphR0ZsYkNBOE15QnNZWFZ5WVE9PSIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJHMDAwMDAwMDAtMDEyOS1BNkZBLTdFREMtNTVCM0ZCNkU4NUY4IiwiYXVkIjpbImh0dHBzOi8vdnMtaWN4LmVwaWMuY29tL0ludGVyY29ubmVjdC1DdXJyZW50LUZpbmFsLVByaW1hcnkvIl0sImp0aSI6IjNmY2E3YjA4LWU1YTEtNDQ3Ni05ZTRiLTRlMTdmMGFkN2QxZCIsIm5iZiI6MTYzOTY5NDk4MywiZXhwIjoxNjM5Njk1MjgzLCJpYXQiOjE2Mzk2OTQ5ODMsImlzcyI6IkcwMDAwMDAwMC0wMTI5LUE2RkEtN0VEQy01NUIzRkI2RTg1RjgifQ.UyW_--xpDdfB3EbxQ1KwRRuNDGIb034Y9mpExaaYyoVVkfz3ophzueIFMRrcdqTWmH96Ivx7O-fnjvHkb9iv1ELGVS_UYZy-JNb7r5VHDhdEYoRJzomYQzAyZutK9CJuJQZSvJeQLOXFoFN-fuCHIUBmSoTY5FDGy8gmq_a5fD_L-PnrXCD6SyP663s8kP-cFWh1iuXP0pjg8EMuFFEwgSo-chvv6RO4LIBebqkkv5qkgO_nrcXVJpxu42FDNrP2618q2Rw7sdIaewDw_I5026T4jNSoJXT12dHurqzedvC20exOO2MA6Vh1o2iVyzIfPE1EnEf-hk4WRFtQTUlqCQ

Step 3a: Get an access token using the JWT bearer flow

  • For the duration the user selected when approving your app to get an authorization code in step 1, your app can get an access token whenever it needs using the JWT bearer authorization grant described in RFC 7523 Section 2.1.

  • Recall that in the JWT bearer flow, the client issues a JWT to itself with a "sub" (subject) claim that indicates the user on whose behalf the client acts.

  • Because the dynamic client your app registered in step 2 is bound to the user who logged in during the SMART standalone launch sequence in step 1, the server only accepts a JWT where the subject is the user bound to that client.

  • Your app posts the JWT it issued itself in the Generate a JSON Web Token step to the server to get an access token.

The following parameters are required in the POST body:

grant_type: This parameter contains the static value urn:ietf:params:oauth:grant-type:jwt-bearer.

assertion: This parameter contains the JWT your app generated above.

client_id: This parameter contains the dynamic client ID assigned in step 2.

Here’s an example of an HTTP POST request for an access token. You will replace the [assertion] and [client_id] placeholders with your own values.

POST https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token HTTP/1.1 Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=[assertion]&client_id=[client_id]

The authorization server responds to the HTTP POST request with a JSON object that includes an access token. The response contains the following fields:

access_token: This parameter contains the access token issued by Epic to your application and is used in future requests. See the Epic-Issued OAuth 2.0 Tokens appendix section for details on handling access tokens.

token_type: In Epic's OAuth 2.0 implementation, this parameter always includes the value bearer.

expires_in: This parameter contains the number of seconds for which the access token is valid.

epic.dstu2.patient: This parameter identifies the DSTU2 FHIR ID for the patient, if a patient is in context at time of launch.

scope: This parameter describes the access your application is authorized for.

patient: This parameter identifies provides the FHIR ID for the patient.

Note that you can include additional fields in the response if needed based on the integration configuration. For more information, refer to Launching your apptopic. Here's an example of what a JSON object including an access token might look like:

{
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ1cm46R1RNUUFDVVI6Y2UuZ3RtcWFjdXIiLCJjbGllbnRfaWQiOiJHMDAwMDAwMDAtMDEyOS1BNkZBLTdFREMtNTVCM0ZCNkU4NUY4IiwiZXBpYy5lY2kiOiJ1cm46ZXBpYzpzMmN1cmd0IiwiZXBpYy5tZXRhZGF0YSI6ImVTMUF1dng4NlhRanRmVExub2loeks4QVRjdU5OUDFnRHhCRklvR0EyVUhHdlpDRE5PYjVZQ3NFZVhlUjExa1UyTjBHaHIwZjIwcE5yQnJ0V1JUa0l4d3VoVnlYeEVYUjQwVGlkdXNKdTk5ZmZRLWsybjFJWEY4X2I0SS11a054IiwiZXBpYy50b2tlbnR5cGUiOiJhY2Nlc3MiLCJleHAiOjE2Mzk2OTg1ODgsImlhdCI6MTYzOTY5NDk4OCwiaXNzIjoidXJuOkdUTVFBQ1VSOmNlLmd0bXFhY3VyIiwianRpIjoiYzQ1YzU4NjUtY2E3Ny00YWVlLTk3NDEtY2YzNzFiNDlhY2FkIiwibmJmIjoxNjM5Njk0OTg4LCJzdWIiOiJlRmdDY3E4cW1PeXY1b3FEcUFyUS5MQTMifQ.G0Z3PBv4ldpTqkjBjNUnvRYeVnNzT8qvLsGAVN8S9YibJGGCH_Txd6Ph1c9yB2hlQW3dw9IkaAvxxlUuclGMzmtyPXeo8wcWC07t_0vVasS-Ya9VjeDtR1hO8rcqEgV1DhKZ1jsEbzlRvKuZvONew0gL25ug6dOolNXcPcluzK6sxEyf2UoosX-W3nsU0iYZPJI-mf7lMEbsMUOSY8CR-77uBap3suxxHy03BwtkAXP0GwW0KSjOVe7_bsxX9k4DEhWyZuEOgDjEhONQFe2TeuWgUcI2KQeK5HjzmxN3dp56rCZ8zlhlukgw-C0F2IDbkZ5on7g7rl8lm29I7_kq9g",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "patient/Immunization.Read patient/Immunization.read patient/Patient.read patient/Practitioner.read patient/PractitionerRole.read launch/patient offline_access",
"state": "oYK9nyFdUgTb1n_hMHZESDea",
"patient": "eUYSU3eH0lceii-4SYNvpPw3",
"__epic.dstu2.patient": "TJR7HfYCw58VnihLrp.axehccg-4IcjmZd3lw7Spm1F8B"
}

Step 3b: Alternate flow with refresh tokens

  • If you completed Step 3A, you do not need to complete this step.
  • This is an alternate workflow to the JWT bearer flow. We strongly recommend that you use the JWT bearer flow above so you can provide a smoother user experience in which the user only needs to authenticate once.
  • To get a refresh token, your app would need to go through the Standalone Launch sequence a second time.
  • The first time (see Step 1: Get the access token you'll use to register a dynamic client) you were using the public app profile and weren't issued a refresh token.
  • Now that you have registered a dynamic client with its own credentials, you can use the confidential-asymmetric app profile.

Get an authorization code,This is the same as the Get an authorization code step from Step 1 except:

  • The client_id should be that of your dynamic client

Your token request will resemble a backend services grant, using the following parameters:

  • client_assertion – set to a JWT signed with your dynamic client’s private key

  • client_assertion_type – set to urn:ietf:params:oauth:client-assertion-type:jwt-bearer

  • An additional parameter per grant_type (code or refresh_token) described below

Note that the user will once again need to log in.

Exchange the authorization code for an access token

  • When your app makes this request to the token, it needs to authenticate itself to be issued a refresh token.

  • It uses the JWT it generated to authenticate itself per RFC 7523 section 2.2. This request will look like the JWT bearer request except the grant type is authorization_code instead of urn:ietf:params:oauth:grant-type:jwt-bearer and it includes the PKCE code verifier like the access token request you made before registering the dynamic client.

  • In response to this request, you’ll get an access token and a refresh token.

Use the refresh token to get subsequent access tokens

  • When the access token expires, your app can generate a new JWT and use the refresh token flow to get a new access token.

  • This request will look like the JWT bearer request except the grant type is refresh_token instead of

urn:ietf:params:oauth:grant-type:jwt-bearer