<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 OAuth Workflow Walkthrough
An important part of building a research application is the OAuth 2.0 workflow outlined by SMART on FHIR. To demonstrate how to use the OAuth workflow, let's walk through the process and manually perform the steps that the research application needs to implement. For the demonstration, we'll use the FHIR server with the SMART Reference Server provided by the S4S Docker reference stack which is available at http
Step1:  Launch the OAuth workflow
The workflow is lauched by the participant by accessing the authorize endpoint exposed by the SMART Reference Server, which can be determined by looking at the server's conformance statement. For the S4S SMART Reference Server, this endpoint is https
Step2:  Collect the authorization code
By following that link, you may be asked to login with the default credentials (or you may already be logged in), and then asked to complete the authorization process. At the end of the process, you'll be redirected to a URL like https
Step3:  Exchange for access token
With code in hand, we can now get an access token from the SMART Reference Server. This would be completed by your application without any input from the user. Note that since this is a confidential client, we need to use basic authentication when interacting with the SMART Reference Server, where the username and password are the client ID and client secret, respectively.
Step4:  Note
Step5:  Now let's try the same request with the access token
Step6:  Success!
Note
Step7:  SMART Reference Server Developer Resources
Let's experiment with some additional examples showing some debug features available with the SMART Reference Server. For these examples, the SMART Reference Server is accessible at https
Step8:  Inspect a token
Now that we have a token granting us access to a user, let's inspect it using a GET request to https
Step9:  Here we can see information associated with this token. By default when creating a token with the reference stack's debug endpoint, the access token is valid for 1 hour (access_expires), but the approval is valid for 1 year (approval_expires). This means that after 1 hour, attempts to use the access_token when fetching data will fail; however, the approval is still valid for 1 year, so the refresh_token may be used to generate a new access token within this time frame. A real research application will have no control over these time periods - the SMART Reference Server decides how for long the access token should be valid, and the approval expiration time is generally specified by the participant during the authorization process.
For debugging purposes, these parameters can be specified in the request to the debug token endpoint
<ASSISTANT_TASK:>
Python Code:
import requests
from pprint import pprint
redirect_uri = 'https://not-a-real-site/authorized'
data = {
    'client_name': 'Fake Research Application',
    'redirect_uris': [redirect_uri],
    'scope': 'launch/patient patient/*.read offline_access'
}
response = requests.post('https://portal.demo.syncfor.science/oauth/register', json=data)
response_data = response.json()
print(f'Response status code: {response.status_code}')
pprint(response_data)
client_id = response_data['client_id']
client_secret = response_data['client_secret']
Explanation: OAuth Workflow Walkthrough
An important part of building a research application is the OAuth 2.0 workflow outlined by SMART on FHIR. To demonstrate how to use the OAuth workflow, let's walk through the process and manually perform the steps that the research application needs to implement. For the demonstration, we'll use the FHIR server with the SMART Reference Server provided by the S4S Docker reference stack which is available at http://portal.demo.syncfor.science/, but you can follow these same steps using the sandbox environments from Cerner, Epic, and others (see http://dev.smarthealthit.org/ for details). The steps are:
1. Register our application as a client
2. Launch the workflow by asking the SMART Reference Server for authorization
3. Collect the authorization code from the SMART Reference Server
4. Exchange the code for an access token
5. Use the access token to access patient data
Note: You can also run the walkthrough with a local instance of the SMART Reference Server running on Docker - just replace https://portal.demo.syncfor.science/ with http://localhost:9000/ throughout the examples.
Registering the client
A research application must register itself once with the SMART Reference Server before it can access any data. This registration workflow is implemented as described in RFC7591. Client registration is performed by sending a POST request to the registration endpoint at https://portal.demo.syncfor.science/oauth/register. The body of the request is a JSON payload with the application's name, an array of redirect URIs, and the desired scopes. Because we'll be building our example client right inside of this Jupyter Notebook, we won't be hosting it on a regular web server, so the redirect URI that we'll use is really just a placeholder. We can register the fake endpoint https://not-a-real-site/authorized as our placeholder and copy the code from the browser into the notebook at the end of the participant's authorization process.
End of explanation
from urllib.parse import urlencode
params = {
    'response_type': 'code',
    'client_id': client_id,
    'redirect_uri': redirect_uri,
    'scope': 'launch/patient patient/*.read offline_access',
    'state': 'my-obscured-state',
    'aud': 'https://portal.demo.syncfor.science/api/fhir'
}
print(f'https://portal.demo.syncfor.science/oauth/authorize?{urlencode(params)}')
Explanation: Launch the OAuth workflow
The workflow is lauched by the participant by accessing the authorize endpoint exposed by the SMART Reference Server, which can be determined by looking at the server's conformance statement. For the S4S SMART Reference Server, this endpoint is https://portal.demo.syncfor.science/oauth/authorize.
End of explanation
code = '5tQu3bV8XDTYgf8t3VOjzsbVZ5Fuqn'  # replace with your code
Explanation: Collect the authorization code
By following that link, you may be asked to login with the default credentials (or you may already be logged in), and then asked to complete the authorization process. At the end of the process, you'll be redirected to a URL like https://not-a-real-site/authorized?code=5tQu3bV8XDTYgf8t3VOjzsbVZ5Fuqn&state=my-obscured-state, containing the authorization code. Your browser will display an error since there is no real web server here, but that's okay since you are manually playing the role of the web browser by extracting the code URL parameter and pasting it back into this notebook. Copy this code into the snippet below, but act fast since this code expires in 100 seconds!
End of explanation
auth = (client_id, client_secret)  # for basic authentication
data = {
    'grant_type': 'authorization_code',
    'code': code,
    'redirect_uri': redirect_uri
}
response = requests.post('https://portal.demo.syncfor.science/oauth/token', auth=auth, data=data)
response_data = response.json()
print(f'Response status code: {response.status_code}')
pprint(response_data)
access_token = response_data['access_token']
refresh_token = response_data['refresh_token']
patient_id = response_data['patient']
Explanation: Exchange for access token
With code in hand, we can now get an access token from the SMART Reference Server. This would be completed by your application without any input from the user. Note that since this is a confidential client, we need to use basic authentication when interacting with the SMART Reference Server, where the username and password are the client ID and client secret, respectively.
End of explanation
response = requests.get(f'https://portal.demo.syncfor.science/api/fhir/Patient/{patient_id}')  # oops, no header
print(f'Response status code: {response.status_code}')
Explanation: Note: If you receive a 401 response, you may have taken more than 100 seconds at which time the authorization code expires.
You can see that the server gave us the patient ID whose data we now have access to, and the access token we can use to get the data.
Using the tokens to retreive data
Now that we have a client ID, client secret, and access token, we can use these values to get some FHIR data, which our application can store. The access token is simply used as a bearer token in the header of the request to the SMART Reference Server. In the reference stack implementation, the SMART Reference Server then proxies the authenticated request to the HAPI-FHIR server. Normally, the request will fail if you try to access the patient data without the access token:
End of explanation
headers = {
    'Authorization': f'Bearer {access_token}'
}
response = requests.get(f'https://portal.demo.syncfor.science/api/fhir/Patient/{patient_id}', headers=headers)
print(f'Response status code: {response.status_code}')
pprint(response.json())
Explanation: Now let's try the same request with the access token:
End of explanation
auth = (client_id, client_secret)
data = {
    'grant_type': 'refresh_token',
    'refresh_token': refresh_token
}
response = requests.post('https://portal.demo.syncfor.science/oauth/token', auth=auth, data=data)
pprint(response.json())
Explanation: Success!
Note: If you get a 500 server error, you may need to load the patient data into the FHIR server. This can be done by running docker-compose run tasks load-sample-data-stu2. See the GitHub repository for more details.
If the access token has expired, the refresh token can be used to generate a new one with a POST request to the token endpoint at https://portal.demo.syncfor.science/oauth/token (using basic authentication):
End of explanation
username = 'daniel-adams'
patient_id = 'smart-1288992'
data = {
    'client_id': client_id,
    'username': username,
    'patient_id': patient_id
}
response = requests.post('https://portal.demo.syncfor.science/oauth/debug/token', json=data)
response_data = response.json()
print(f'Response status code: {response.status_code}')
pprint(response_data)
access_token = response_data['access_token']
refresh_token = response_data['refresh_token']
Explanation: SMART Reference Server Developer Resources
Let's experiment with some additional examples showing some debug features available with the SMART Reference Server. For these examples, the SMART Reference Server is accessible at https://portal.demo.syncfor.science/. However, if you're running the S4S Docker reference stack locally with Docker, you can access your local SMART Reference Server at http://localhost:9000/.
Token debug endpoints
Once a client has been registered, let's try the token debug endpoints that are included with the SMART Reference Server (for development purposes only) - real servers will not have endpoints available to generate or introspect tokens like this.
Create a token
We can create an access token for a registered client with a POST request to https://portal.demo.syncfor.science/oauth/debug/token. The request should include the client ID obtained from registration, the username of a user of the SMART Reference Server, and the patient ID which is associated with the user. The S4S SMART Reference Server ships with a user named daniel-adams which has access to a patient with ID smart-1288992, so we'll use these.
End of explanation
params = {'token': access_token}  # can be access or refresh token
response = requests.get('https://portal.demo.syncfor.science/oauth/debug/introspect', params=params)
print(f'Response status code: {response.status_code}')
pprint(response.json())
Explanation: Inspect a token
Now that we have a token granting us access to a user, let's inspect it using a GET request to https://portal.demo.syncfor.science/oauth/debug/introspect:
End of explanation
from time import time
data = {
    'client_id': client_id,
    'username': username,
    'patient_id': patient_id,
    'access_lifetime': 3*60*60,  # duration in seconds
    'approval_expires': time() + 180*24*60*60  # UNIX timestamp
}
token_response = requests.post('https://portal.demo.syncfor.science/oauth/debug/token', json=data)
access_token = token_response.json()['access_token']
introspect_response = requests.get('https://portal.demo.syncfor.science/oauth/debug/introspect',
                                   params={'token': access_token})
pprint(introspect_response.json())
Explanation: Here we can see information associated with this token. By default when creating a token with the reference stack's debug endpoint, the access token is valid for 1 hour (access_expires), but the approval is valid for 1 year (approval_expires). This means that after 1 hour, attempts to use the access_token when fetching data will fail; however, the approval is still valid for 1 year, so the refresh_token may be used to generate a new access token within this time frame. A real research application will have no control over these time periods - the SMART Reference Server decides how for long the access token should be valid, and the approval expiration time is generally specified by the participant during the authorization process.
For debugging purposes, these parameters can be specified in the request to the debug token endpoint:
End of explanation
<END_TASK>