This page describes how to use FHIR Consent resources to determine data access of FHIR stores in the Cloud Healthcare API.
Configure a FHIR access control enabled store
To configure a FHIR store with consent enforcement, complete the following steps:
Create a FHIR store if you don't already have one.
Set the following FHIR store's
ConsentConfig
parameters to enable consent enforcement:version
: Specifies which consent enforcement version is being used for the FHIR store. This value can only be set once by eitherCreateFhirStore
orUpdateFhirStore
. After it's set, you must callApplyConsents
orApplyAdminConsents
to change the version.access_enforced
: If set totrue
, when accessing FHIR resources, the consent headers provided will be verified against consent directives given by consumers.consent_header_handling
: If set toPERMIT_EMPTY_SCOPE
(default), the server allows requests without (or empty)X-Consent-Scope
header. If set toREQUIRED_ON_READ
andaccess_enforced
=true
, the server rejects all requests without (or empty)X-Consent-Scope
header.
Set up a new FHIR store with ConsentConfig
curl -X POST \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "Content-Type: application/json" \ --data "{ 'version': 'R4', 'enableUpdateCreate': true, 'consentConfig': { 'version': 'V1', 'accessEnforced': true } }" "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores?fhirStoreId=FHIR_STORE_ID"
You should receive a JSON response similar to the following:
{ "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID", "version": "R4", "enableUpdateCreate": true, "consentConfig": { "version": "V1" } }
If you have an existing store already, use UpdateFhirStore
to set the
ConsentConfig
with the consent enforcement version
as
V1
and set accessEnforced
to true
.
curl -X PATCH \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "Content-Type: application/json" \ --data "{ 'consentConfig': { 'version': 'V1', 'accessEnforced': true } }" "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID?update_mask=consentConfig"
Define Policies Using Consent Resource
Policies are represented through Consent resource. The resource fields' purpose and use are described in the Data Model Docs.
Here is an example of all resources that can be created for this particular example.
Create FHIR resources
The following sample shows how to execute a [FHIR bundle](/healthcare-api/docs/how-tos/fhir-bundles) to populate the following resources:
- A Practitioner resource with name Jeffrey Brown
- A Patient resource with name Darcy Smith
- An Observation resource showing hemoglobin measurement of Darcy
(LOINC
718-7
) that was collected by the Happy Hospital - An Observation resource showing glucose measurement of Darcy
(LOINC
15074-8
). - A Consent from Darcy to permit Jeffrey Brown using application
App/123
to access her data collected by the Happy Hospital - A Consent from Darcy to permit Jeffrey Brown to access any of her data for
emergency treatment
(
ETREAT
) - A Consent from the Happy Hospital to permit Jeffrey Brown to access all data
when doing biomedical research (
BIORCH
) with applicationApp/golden
cat > bundle.json << 'EOF' { "resourceType": "Bundle", "type": "transaction", "entry": [ { "request": {"method": "PUT", "url": "Practitioner/12942879-f89f-41ae-aa80-0b911b649833"}, "resource": { "active": true, "birthDate": "1970-05-23", "gender": "male", "id": "12942879-f89f-41ae-aa80-0b911b649833", "name": [{ "family": "Brown", "given": ["Jeffrey"], "use": "official" }], "resourceType": "Practitioner" } }, { "request": {"method": "PUT", "url": "Patient/3c6aa096-c054-4c22-b2b4-1e4a4d203de2"}, "resource": { "active": true, "birthDate": "1990-01-01", "gender": "female", "id": "3c6aa096-c054-4c22-b2b4-1e4a4d203de2", "name": [{ "family": "Smith", "given": ["Darcy"], "use": "official" }], "meta": { "tag": [{ "system": "http://terminology.hl7.org/CodeSystem/common-tags", "code": "employee" }] }, "resourceType": "Patient" } }, { "request": {"method": "PUT", "url": "Observation/7473784b-46a8-470c-b9a6-fe38a01025aa"}, "resource": { "id": "7473784b-46a8-470c-b9a6-fe38a01025aa", "meta": {"source": "http://example.com/HappyHospital"}, "code": { "coding": [{ "code": "718-7", "system": "http://loinc.org", "display": "Hemoglobin [Mass/volume] in Blood" }] }, "effectivePeriod": {"start": "2021-12-10T05:30:10+01:00"}, "issued": "2021-12-10T13:30:10+01:00", "resourceType": "Observation", "status": "final", "subject": {"reference": "Patient/3c6aa096-c054-4c22-b2b4-1e4a4d203de2"}, "valueQuantity": { "code": "g/dL", "system": "http://unitsofmeasure.org", "unit": "g/dl", "value": 7.2 } } }, { "request": {"method": "PUT", "url": "Observation/68583624-9921-4158-8754-2a306c689abd"}, "resource": { "id": "68583624-9921-4158-8754-2a306c689abd", "code": { "coding": [{ "code": "15074-8", "system": "http://loinc.org", "display": "Glucose [Moles/volume] in Blood" }] }, "effectivePeriod": {"start": "2021-12-01T05:30:10+01:00"}, "issued": "2021-12-01T13:30:10+01:00", "resourceType": "Observation", "status": "final", "subject": {"reference": "Patient/3c6aa096-c054-4c22-b2b4-1e4a4d203de2"}, "valueQuantity": { "code": "mmol/L", "system": "http://unitsofmeasure.org", "unit": "mmol/l", "value": 6.3 } } }, { "request": {"method": "PUT", "url": "Consent/10998b60-a252-405f-aa47-0702554ddc8e"}, "resource": { "category": [{ "coding": [{ "code": "59284-0", "system": "http://terminology.hl7.org/CodeSystem/consentcategorycodes" }] }], "id": "10998b60-a252-405f-aa47-0702554ddc8e", "patient": {"reference": "Patient/3c6aa096-c054-4c22-b2b4-1e4a4d203de2"}, "policyRule": { "coding": [{ "code": "OPTIN", "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode" }] }, "provision": { "actor": [ { "reference": {"reference": "Practitioner/12942879-f89f-41ae-aa80-0b911b649833"}, "role": { "coding": [{ "code": "GRANTEE", "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode" }] } } ], "extension": [ { "url": "https://g.co/fhir/medicalrecords/Environment", "valueCodeableConcept": { "coding": [{ "code": "123", "system": "App" }] } }, { "url": "https://g.co/fhir/medicalrecords/DataSource", "valueUri": "http://example.com/HappyHospital" } ], "type": "permit" }, "resourceType": "Consent", "scope": { "coding": [{ "code": "patient-privacy", "system": "http://terminology.hl7.org/CodeSystem/consentscope" }] }, "status": "active" } }, { "request": {"method": "PUT", "url": "Consent/73c54e8d-2789-403b-9dee-13085c5d5e34"}, "resource": { "category": [{ "coding": [{ "code": "59284-0", "system": "http://terminology.hl7.org/CodeSystem/consentcategorycodes" }] }], "id": "73c54e8d-2789-403b-9dee-13085c5d5e34", "patient": {"reference": "Patient/3c6aa096-c054-4c22-b2b4-1e4a4d203de2"}, "policyRule": { "coding": [{ "code": "OPTIN", "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode" }] }, "provision": { "actor": [ { "reference": {"reference": "Practitioner/12942879-f89f-41ae-aa80-0b911b649833"}, "role": { "coding": [{ "code": "GRANTEE", "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode" }] } } ], "purpose": [{ "code": "ETREAT", "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason" }], "type": "permit" }, "resourceType": "Consent", "scope": { "coding": [{ "code": "patient-privacy", "system": "http://terminology.hl7.org/CodeSystem/consentscope" }] }, "status": "active" } }, { "request": {"method": "PUT", "url": "Consent/5c8e3f8a-9fd5-480d-a08e-f29b89feccde"}, "resource": { "category": [{ "coding": [{ "code": "57017-6", "system": "http://loinc.org" }] }], "id": "5c8e3f8a-9fd5-480d-a08e-f29b89feccde", "patient": {}, "extension": [{ "url": "https://g.co/fhir/medicalrecords/ConsentAdminPolicy" }], "policyRule": { "coding": [{ "code": "OPTIN", "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode" }] }, "provision": { "actor": [ { "reference": {"reference": "Practitioner/12942879-f89f-41ae-aa80-0b911b649833"}, "role": { "coding": [{ "code": "GRANTEE", "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode" }] } } ], "purpose": [{ "code": "BIORCH", "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason" }], "extension": [ { "url": "https://g.co/fhir/medicalrecords/Environment", "valueCodeableConcept": { "coding": [{ "code": "golden", "system": "App" }] } } ], "type": "permit" }, "resourceType": "Consent", "scope": {}, "status": "active" } } ] } EOF curl -X POST \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "Content-Type: application/fhir+json; charset=utf-8" \ --data @bundle.json \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir"
You should receive a JSON response similar to the following:
{ "entry": [ { "response": { "etag": "W/\"VERSION_ID\"", "lastModified": "2022-09-01T17:31:40.423469+00:00", "location": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Practitioner/12942879-f89f-41ae-aa80-0b911b649833/_history/VERSION_ID", "status": "201 Created" } }, { "response": { "etag": "W/\"VERSION_ID\"", "lastModified": "2022-09-01T17:31:40.423469+00:00", "location": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Patient/3c6aa096-c054-4c22-b2b4-1e4a4d203de2/_history/VERSION_ID", "status": "201 Created" } }, { "response": { "etag": "W/\"VERSION_ID\"", "lastModified": "2022-09-01T17:31:40.423469+00:00", "location": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/7473784b-46a8-470c-b9a6-fe38a01025aa/_history/VERSION_ID", "status": "201 Created" } }, { "response": { "etag": "W/\"VERSION_ID\"", "lastModified": "2022-09-01T17:31:40.423469+00:00", "location": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/68583624-9921-4158-8754-2a306c689abd/_history/VERSION_ID", "status": "201 Created" } }, { "response": { "etag": "W/\"VERSION_ID\"", "lastModified": "2022-09-01T17:31:40.423469+00:00", "location": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Consent/10998b60-a252-405f-aa47-0702554ddc8e/_history/VERSION_ID", "status": "201 Created" } }, { "response": { "etag": "W/\"VERSION_ID\"", "lastModified": "2022-09-01T17:31:40.423469+00:00", "location": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Consent/73c54e8d-2789-403b-9dee-13085c5d5e34/_history/VERSION_ID", "status": "201 Created" } }, { "response": { "etag": "W/\"VERSION_ID\"", "lastModified": "2022-09-01T17:31:40.423469+00:00", "location": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Consent/5c8e3f8a-9fd5-480d-a08e-f29b89feccde/_history/VERSION_ID", "status": "201 Created" } } ], "resourceType": "Bundle", "type": "transaction-response" }
Following are more examples of R4 Consent resource that demonstrates how complex policies can be represented.
Sample patient consent directive
{ "resourceType": "Consent", "id": "patient-consent-example", "patient": { "reference": "Patient/f001" }, "category": [ { "coding": [ { "system": "http://loinc.org", "code": "59284-0" } ] } ], "scope": { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/consentscope", "code": "patient-privacy" } ] }, "policyRule": { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "OPTIN" } ] }, "status": "active", "provision": { "type": "permit", "actor": [ { "reference": { "reference": "Practitioner/f002" }, "role": { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode", "code": "GRANTEE" } ] } } ], "purpose": [ { "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", "code": "TREAT" } ], "class": [ { "system": "http://hl7.org/fhir/resource-types", "code": "Encounter" } ], "data": [ { "meaning": "instance", "reference": { "reference": "Encounter/e001" } } ], "extension": [ { "url": "https://g.co/fhir/medicalrecords/Environment", "valueCodeableConcept": { "coding": [ { "system": "iso3166-1", "code": "CA" } ] } }, { "url": "https://g.co/fhir/medicalrecords/DataTag", "valueCoding": { "system": "http://terminology.hl7.org/CodeSystem/common-tags", "code": "actionable" } }, { "url": "https://g.co/fhir/medicalrecords/DataTag", "extension": [ { "url": "https://g.co/fhir/medicalrecords/DataTag", "valueCoding": { "system": "http://example.com/custom-tags", "code": "archived" } }, { "url": "https://g.co/fhir/medicalrecords/DataTag", "valueCoding": { "system": "http://example.com/custom-tags", "code": "insensitive" } } ] }, { "url": "https://g.co/fhir/medicalrecords/DataSource", "valueUri": "http://somesystem.example.org/foo" } ], "securityLabel": [ { "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", "code": "R" }, { "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "PSY" } ] } }
The preceding example represents a patient Consent resource in which a patient
f001
gives permission to a practitioner f002
with
the purpose of providing regular treatment represented by TREAT
.
The practitioner is from the geolocation iso3166-1/CA
. This
Consent resource permits the practitioner to access the patient data if the
data satisfies all of the following conditions.
- It is an
Encounter
type with the IDEncounter/e001
. - It comes from the source
http://somesystem.example.org/foo
. - It satisfies at least one of the following conditions on tag (resources can be tagged by setting Meta.tags'
system
andcode
field): - Has the tag (
system
=http://terminology.hl7.org/CodeSystem/common-tags
andcode
=actionable
) - Has both tags (
system
=http://example.com/custom-tags
andcode
=archived
) and (system
=http://example.com/custom-tags
andcode
=insensitive
) - It has at least one of the following security labels
system
=http://terminology.hl7.org/CodeSystem/v3-Confidentiality
andcode
is one ofR
,N
,M
,L
,U
.system
=http://terminology.hl7.org/CodeSystem/v3-ActCode
andcode
=PSY
.
Sample admin policy directive
{ "resourceType": "Consent", "id": "admin-policy-example", "patient": {}, "extension": [{ "url": "https://g.co/fhir/medicalrecords/ConsentAdminPolicy" }], "category": [ { "coding": [ { "system": "http://loinc.org", "code": "57017-6" } ] } ], "scope": {}, "policyRule": { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "OPTIN" } ] }, "status": "active", "provision": { "type": "permit", "actor": [ { "reference": { "reference": "Practitioner/f002" }, "role": { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode", "code": "GRANTEE" } ] } } ], "purpose": [ { "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", "code": "TREAT" } ], "class": [ { "system": "http://hl7.org/fhir/resource-types", "code": "Encounter" } ], "data": [ { "meaning": "instance", "reference": { "reference": "Encounter/e001" } } ], "extension": [ { "url": "https://g.co/fhir/medicalrecords/Environment", "valueCodeableConcept": { "coding": [ { "system": "iso3166-1", "code": "CA" } ] } }, { "url": "https://g.co/fhir/medicalrecords/DataTag", "valueCoding": { "system": "http://terminology.hl7.org/CodeSystem/common-tags", "code": "actionable" } }, { "url": "https://g.co/fhir/medicalrecords/DataTag", "extension": [ { "url": "https://g.co/fhir/medicalrecords/DataTag", "valueCoding": { "system": "http://example.com/custom-tags", "code": "archived" } }, { "url": "https://g.co/fhir/medicalrecords/DataTag", "valueCoding": { "system": "http://example.com/custom-tags", "code": "insensitive" } } ] }, { "url": "https://g.co/fhir/medicalrecords/DataSource", "valueUri": "http://somesystem.example.org/foo" } ], "securityLabel": [ { "system": "http://terminology.hl7.org/CodeSystem/v3-Confidentiality", "code": "R" }, { "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "PSY" } ] } }
The preceding example represents an admin policy Consent resource gives permission
to a practitioner f002
with the purpose of providing regular
treatment represented by TREAT
. The practitioner is from the
geolocation iso3166-1/CA
. This Consent resource permits the
practitioner to access the patient data if the data satisfies all of
the following conditions:
- It is an
Encounter
type with the IDEncounter/e001
. - It comes from the source
http://somesystem.example.org/foo
. - It satisfies at least one of the following conditions on tag:
- Has the tag (
system
=http://terminology.hl7.org/CodeSystem/common-tags
andcode
=actionable
) - Has both tags (
system
=http://example.com/custom-tags
andcode
=archived
) and (system
=http://example.com/custom-tags
andcode
=insensitive
) - It has at least one of the following security labels
system
=http://terminology.hl7.org/CodeSystem/v3-Confidentiality
andcode
is one ofR
,N
,M
,L
,U
.system
=http://terminology.hl7.org/CodeSystem/v3-ActCode
andcode
=PSY
.
Sample admin cascading policy directive
{ "resourceType": "Consent", "id": "admin-cascading-policy-example", "patient": {}, "extension": [ { "url": "https://g.co/fhir/medicalrecords/ConsentAdminPolicy" }, { "url": "https://g.co/fhir/medicalrecords/CascadingPolicy" } ], "category": [ { "coding": [ { "system": "http://loinc.org", "code": "57017-6" } ] } ], "scope": {}, "policyRule": { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/v3-ActCode", "code": "OPTIN" } ] }, "status": "active", "provision": { "type": "permit", "actor": [ { "reference": { "reference": "Practitioner/f002" }, "role": { "coding": [ { "system": "http://terminology.hl7.org/CodeSystem/v3-RoleCode", "code": "GRANTEE" } ] } } ], "purpose": [ { "system": "http://terminology.hl7.org/CodeSystem/v3-ActReason", "code": "TREAT" } ], "class": [ { "system": "http://hl7.org/fhir/resource-types", "code": "Patient" } ], "extension": [ { "url": "https://g.co/fhir/medicalrecords/Environment", "valueCodeableConcept": { "coding": [ { "system": "iso3166-1", "code": "CA" } ] } }, { "url": "https://g.co/fhir/medicalrecords/DataTag", "valueCoding": { "system": "http://terminology.hl7.org/CodeSystem/common-tags", "code": "employee" } } ] } }
The preceding example represents an admin cascading policy Consent resource giving
permission to a practitioner f002
with the purpose of providing
regular treatment represented by TREAT
. The practitioner is from
the geolocation iso3166-1/CA
. This Consent resource permits the
practitioner to access the compartment data of patients with tag
employee
. All resource criteria
only applies to the compartment base resources, meaning the Patient resource, as it
controls which resources to cascade from.
Enforce patient consents or admin policies
Enforce the patient's consents by ApplyConsents
curl -X POST \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "Content-Type: application/json" \ --data "{'validateOnly': false}" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID:applyConsents"
You should receive a JSON response similar to the following:
{ "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID" }
The response contains an operation name. To track the status of the operation, you can use the [Operation `get` method](/healthcare-api/docs/reference/rest/v1/projects.locations.datasets.operations/get):
curl -X GET \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID"
When the operation finishes, the server returns a response with the status of the operation in JSON format:
{ "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID"", "metadata": { "@type": "type.googleapis.com/google.cloud.healthcare.v1beta1.OperationMetadata", "apiMethodName": "google.cloud.healthcare.v1beta1.fhir.FhirStoreService.ApplyConsents", "createTime": "CREATE_TIME", "endTime": "END_TIME", "logsUrl": "https://console.cloud.google.com/logs/query/CLOUD_LOGGING_URL", "counter": { "success": "2", "secondarySuccess": "5" } }, "done": true, "response": { "@type": "type.googleapis.com/google.cloud.healthcare.v1beta1.fhir.ApplyConsentsResponse", "consentApplySuccess": "2", "affectedResources": "5" } }
This response indicated that the server successfully processed 2 consents and updated the consensual access of 5 resources (1 Patient, 2 Consents, 2 Observations).
Enforce the admin policy by ApplyAdminConsents
curl -X POST \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "Content-Type: application/json" \ --data "{ 'validateOnly': false, 'newConsentsList': { 'names': ['projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Consent/5c8e3f8a-9fd5-480d-a08e-f29b89feccde/_history/VERSION_ID'] } }" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID:applyAdminConsents"
You should receive a JSON response similar to the following:
{ "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID" }
The response contains an operation name. To track the status of the operation, you can use the [Operation `get` method](/healthcare-api/docs/reference/rest/v1/projects.locations.datasets.operations/get):
curl -X GET \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID"
When the operation finishes, the server returns a response with the status of the operation in JSON format:
{ "name": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/operations/OPERATION_ID"", "metadata": { "@type": "type.googleapis.com/google.cloud.healthcare.v1beta1.OperationMetadata", "apiMethodName": "google.cloud.healthcare.v1beta1.fhir.FhirStoreService.ApplyAdminConsents", "createTime": "CREATE_TIME", "endTime": "END_TIME", "logsUrl": "https://console.cloud.google.com/logs/query/CLOUD_LOGGING_URL", "counter": { "success": "1", "secondarySuccess": "7" } }, "done": true, "response": { "@type": "type.googleapis.com/google.cloud.healthcare.v1beta1.fhir.ApplyAdminConsentsResponse", "consentApplySuccess": "1", "affectedResources": "7" } }
This response indicated that the server successfully processed 1 admin policy and updated the consensual access of 7 resources (1 Practitioner, 1 Patient, 2 Observations, 2 patient Consents and 1 admin policy).
Enforcement on consents stored within a FHIR store won't come into effect
until ApplyConsents
(for patient consents) or ApplyAdminConsents
(for admin
policies and admin cascading policies) is called and completes successfully. If
you add, modify, or remove consents after you have run ApplyConsents
or
ApplyAdminConsents
, you must run it again for those consents to be included in
the enforcement model.
FHIR resources are indexed asynchronously, so there might be a slight delay
between the time when ApplyConsents
or ApplyAdminConsents
completes and when
the enforcement model is reflected in search results. This delay is only
expected for search requests.
If this is the first time you're setting up consent enforcement on the FHIR
store, wait for the ApplyConsents
or ApplyAdminConsents
long-running
operation to complete before making consent-aware requests.
To call ApplyConsents
on a subset of patients, you can use the following
filters:
PatientScope
: to runApplyConsents
on a list of patient IDs with up to 10,000 patientsTimeRange
: to runApplyConsent
on a list of Patient resource IDs whose Consent resources are updated during a certain time range
To call ApplyAdminConsents
: you need to provide the full list of all
policies that you want to apply (not an incremental list). As a result, an empty
list will void enforcement from all admin policies from the store. Each policy
must be a resource version name if the
FHIR store
is versioning, and a resource name otherwise.
You can use operations.get
to retrieve the ProgressCounter
of the operation. Upon completion there is an ApplyConsentsResponse which is included in the
Operation.response
.
The counters in the ProgressCounter
and ApplyConsentsResponse
or
ApplyAdminConsentsResponse
are described in the following table.
ProgressCounter |
ApplyConsentsResponse or ApplyAdminConsentsResponse |
Description |
---|---|---|
success |
consentApplySuccess |
The number of Consent resources that the operation successfully processed. |
failure |
consentApplyFailure |
The number of Consent resources that are unsupported or invalid. You can either view error logs in Cloud Logging or when validateOnly is false , check consent enforcement status using CheckConsentEnforcementStatus or CheckPatientConsentEnforcementStatus to retrieve error details. |
secondarySuccess |
affectedResources |
When validateOnly is false , it represents the number of FHIR resources that were successfully re-indexed because of the effect of the consent change. |
secondaryFailure |
failedResources |
When validateOnly is false , it represents the number of FHIR resources that may have consent change but failed to reindex. This may affect search with consent context but not other methods. To see the error details, you can view error logs in Cloud Logging. |
When FHIR Consent resources are processed, you can use the following APIs to check the status of enforcement for a single consent or all consents from a patient:
CheckConsentEnforcementStatus
: returns aParameters
(STU3, R4) resource that lists the following parameters:id
: represents the resource ID of the Consent resourcelastUpdated
: represents the time when the consent was last enforcedversionId
: represents the version ID used for consent enforcementconsent-enforcement-status
: represents the status of consent enforcement
CheckPatientConsentEnforcementStatus
: returns aBundle
(STU3, R4) of theParameters
(STU3, R4) resource consisting of enforcement status of all the consents from a single patient
For admin policy, CheckConsentEnforcementStatus
can only be used for checking
the enforcement status of a single Consent admin policy. Alternatively, you
can use fhirStores.get
to see all the active admin policies applied to the store.
Consent Enforcement Status
The consent-enforcement-status
can have any of the following values:
OFF
: represents the default enforcement status of a new Consent resource in which the Consent resource has never been processed.ENFORCEABLE
: the state in which the Consent resource has been processed successfully.INACTIVE
: an inactive state in which the Consent resource is ignored.UNSUPPORTED
: the state of a Consent resource that may conform to FHIR specifications, but is unenforceable. This is due to limited implementation of the FHIR consent enforcement with the current level of feature support.ENFORCEMENT_LIMIT_EXCEEDED
: the state when the FHIR consent resource format and the level of support for the resource are error-free, however one or more of the following conditions are true:The Patient has a large set of Consent resources.
The size of the consent directives across all active consents is greater than the maximum allowed size of the consent directives for a FHIR server to enforce them.
Search with Consent context
The Cloud Healthcare API supports searches for FHIR resources in a given
FHIR store with actor
, purpose
, and environment
as query parameters. The
response contains only those resources that are consented.
Search FHIR resources with consent scope
- Practitioner Jeffrey Brown (identified by
Practitioner/12942879-f89f-41ae-aa80-0b911b649833
) using a trusted applicationApp/123
searches all Observations withstatus=final
. - Practitioner Jeffrey Brown (identified by
Practitioner/12942879-f89f-41ae-aa80-0b911b649833
) using applicationApp/123
searches all Observations from Patient Darcy. - Practitioner Jeffrey Brown (identified by
Practitioner/12942879-f89f-41ae-aa80-0b911b649833
) using applicationApp/123
searches all Observations from Patient Darcy for emergency treatment purposes. - Practitioner Jeffrey Brown (identified by
Practitioner/12942879-f89f-41ae-aa80-0b911b649833
) searches Observations withstatus=final
for two purposes, treatment and research - A hospital IT administrator uses
bypass
to search for all Practitioners at the hospital.
curl -X GET \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "X-Consent-Scope: actor/Practitioner/12942879-f89f-41ae-aa80-0b911b649833 env/App/123" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation?status=final"
You should receive a JSON response similar to the following:
{ "entry": [ { "fullUrl": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION_ID/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/7473784b-46a8-470c-b9a6-fe38a01025aa", "resource": { "code": { "coding": [ { "code": "718-7", "display": "Hemoglobin [Mass/volume] in Blood", "system": "http://loinc.org" } ] }, "effectivePeriod": { "start": "2021-12-10T05:30:10+01:00" }, "id": "7473784b-46a8-470c-b9a6-fe38a01025aa", "issued": "2021-12-10T13:30:10+01:00", "meta": { "lastUpdated": "2022-09-01T17:31:40.423469+00:00", "source": "http://example.com/HappyHospital", "versionId": "VERSION_ID" }, "resourceType": "Observation", "status": "final", "subject": { "reference": "Patient/3c6aa096-c054-4c22-b2b4-1e4a4d203de2" }, "valueQuantity": { "code": "g/dL", "system": "http://unitsofmeasure.org", "unit": "g/dl", "value": 7.2 } }, "search": { "mode": "match" } } ], "link": [ { "relation": "search", "url": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/?status=final" }, { "relation": "first", "url": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/?status=final" }, { "relation": "self", "url": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/?status=final" } ], "resourceType": "Bundle", "total": 1, "type": "searchset" }
curl -X GET \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "X-Consent-Scope: actor/Practitioner/12942879-f89f-41ae-aa80-0b911b649833 env/App/123" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation?subject:Patient.name=Darcy"
You should receive a JSON response similar to the following:
{ "link": [ { "relation": "search", "url": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/?subject%3APatient.name=Darcy" }, { "relation": "first", "url": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/?subject%3APatient.name=Darcy" }, { "relation": "self", "url": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/?subject%3APatient.name=Darcy" } ], "resourceType": "Bundle", "total": 0, "type": "searchset" }
The preceding query is a chained search. Because the consent scenario
actor/Practitioner/12942879-f89f-41ae-aa80-0b911b649833 env/App/123
is denied access for the Patient Darcy resource (identified by
Patient/3c6aa096-c054-4c22-b2b4-1e4a4d203de2
), the FHIR server
returns no Observation from the Patient as if the Patient does not exist.
curl -X GET \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "X-Consent-Scope: actor/Practitioner/12942879-f89f-41ae-aa80-0b911b649833 purp/v3/ETREAT env/App/123" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation?subject:Patient.name=Darcy"
You should receive a JSON response similar to the following:
{ "entry": [ { "fullUrl": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/68583624-9921-4158-8754-2a306c689abd", "resource": { "code": { "coding": [ { "code": "15074-8", "display": "Glucose [Moles/volume] in Blood", "system": "http://loinc.org" } ] }, "effectivePeriod": { "start": "2021-12-01T05:30:10+01:00" }, "id": "68583624-9921-4158-8754-2a306c689abd", "issued": "2021-12-01T13:30:10+01:00", "meta": { "lastUpdated": "2022-09-01T17:31:40.423469+00:00", "versionId": "VERSION_ID" }, "resourceType": "Observation", "status": "final", "subject": { "reference": "Patient/3c6aa096-c054-4c22-b2b4-1e4a4d203de2" }, "valueQuantity": { "code": "mmol/L", "system": "http://unitsofmeasure.org", "unit": "mmol/l", "value": 6.3 } }, "search": { "mode": "match" } }, { "fullUrl": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/7473784b-46a8-470c-b9a6-fe38a01025aa", "resource": { "code": { "coding": [ { "code": "718-7", "display": "Hemoglobin [Mass/volume] in Blood", "system": "http://loinc.org" } ] }, "effectivePeriod": { "start": "2021-12-10T05:30:10+01:00" }, "id": "7473784b-46a8-470c-b9a6-fe38a01025aa", "issued": "2021-12-10T13:30:10+01:00", "meta": { "lastUpdated": "2022-09-01T17:31:40.423469+00:00", "source": "http://example.com/HappyHospital", "versionId": "VERSION_ID" }, "resourceType": "Observation", "status": "final", "subject": { "reference": "Patient/3c6aa096-c054-4c22-b2b4-1e4a4d203de2" }, "valueQuantity": { "code": "g/dL", "system": "http://unitsofmeasure.org", "unit": "g/dl", "value": 7.2 } }, "search": { "mode": "match" } } ], "link": [ { "relation": "search", "url": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/?subject:Patient.name=Darcy" }, { "relation": "first", "url": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/?subject:Patient.name=Darcy" }, { "relation": "self", "url": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/?subject:Patient.name=Darcy" } ], "resourceType": "Bundle", "total": 2, "type": "searchset" }
curl -X GET \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "X-Consent-Scope: actor/Practitioner/12942879-f89f-41ae-aa80-0b911b649833 purp/v3/TREAT purp/v3/HRESCH" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation?status=final"
You should receive a JSON response similar to the following:
{ "issue": [ { "code": "security", "details": { "text": "permission_denied" }, "diagnostics": "the maximum number of allowed consent purpose scopes is 1, got 2", "severity": "error" } ], "resourceType": "OperationOutcome" }
In this case, Practitioner Jeffrey Brown should remove an unnecessary purpose from the `X-Consent-Scope` in the request.
curl -X GET \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "X-Consent-Scope: bypass actor/Admin/ef0592c9-6724-467e-878d-f879e537cd15 env/net/HappyNet" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Practitioner?"
Because bypass
is provided, the consent checks were skipped. You
should receive a JSON response similar to the following:
{ "entry": [ { "fullUrl": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Practitioner/12942879-f89f-41ae-aa80-0b911b649833", "resource": { "active": true, "birthDate": "1970-05-23", "gender": "male", "id": "12942879-f89f-41ae-aa80-0b911b649833", "meta": { "lastUpdated": "2022-09-01T17:31:40.423469+00:00", "versionId": "VERSION_ID" }, "name": [ { "family": "Brown", "given": [ "Jeffrey" ], "use": "official" } ], "resourceType": "Practitioner" }, "search": { "mode": "match" } } ], "link": [ { "relation": "search", "url": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Practitioner/?" }, { "relation": "first", "url": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Practitioner/?" }, { "relation": "self", "url": "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Practitioner/?" } ], "resourceType": "Bundle", "total": 1, "type": "searchset" }
Get resource with Consent context
The Cloud Healthcare API supports get FHIR resource in a given
FHIR store with actor
, purpose
, and environment
as query parameters. The
response contains only those resources that are consented.
Get FHIR resources with consent scope
- Practitioner Jeffrey Brown (identified by
Practitioner/12942879-f89f-41ae-aa80-0b911b649833
) using applicationApp/123
reads the hemoglobin measurement of the Patient (in this example,Observation/7473784b-46a8-470c-b9a6-fe38a01025aa
). - Practitioner Jeffrey Brown (identified by
Practitioner/12942879-f89f-41ae-aa80-0b911b649833
) using unknown applicationApp/unknown
reads the hemoglobin measurement of the Patient (in this example,Observation/7473784b-46a8-470c-b9a6-fe38a01025aa
). - Practitioner Jeffrey Brown (identified by
Practitioner/12942879-f89f-41ae-aa80-0b911b649833
) doing biomedical research using applicationApp/golden
reads the Darcy's birthDate (in this example,Patient/3c6aa096-c054-4c22-b2b4-1e4a4d203de2
). - Practitioner Jeffrey Brown (identified by
Practitioner/12942879-f89f-41ae-aa80-0b911b649833
) requests emergency unauthorized access to a patient's record, using the "break-the-glass" protocol. (in this example,Observation/7473784b-46a8-470c-b9a6-fe38a01025aa
).
curl -X GET \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "X-Consent-Scope: actor/Practitioner/12942879-f89f-41ae-aa80-0b911b649833 env/App/123" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/7473784b-46a8-470c-b9a6-fe38a01025aa"
Because the requestor was given consent, the response is the content of the Observation resource.
{ "code": { "coding": [ { "code": "718-7", "display": "Hemoglobin [Mass/volume] in Blood", "system": "http://loinc.org" } ] }, "effectivePeriod": { "start": "2021-12-10T05:30:10+01:00" }, "id": "7473784b-46a8-470c-b9a6-fe38a01025aa", "issued": "2021-12-10T13:30:10+01:00", "meta": { "lastUpdated": "2022-09-01T17:31:40.423469+00:00", "source": "http://example.com/HappyHospital", "versionId": "VERSION_ID" }, "resourceType": "Observation", "status": "final", "subject": { "reference": "Patient/3c6aa096-c054-4c22-b2b4-1e4a4d203de2" }, "valueQuantity": { "code": "g/dL", "system": "http://unitsofmeasure.org", "unit": "g/dl", "value": 7.2 } }
curl -X GET \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "X-Consent-Scope: actor/Practitioner/12942879-f89f-41ae-aa80-0b911b649833 env/App/unknown" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/7473784b-46a8-470c-b9a6-fe38a01025aa"
Because the boundary access of the requestor (`App/unknown`) is not permitted by the Patient's consent, the request is denied.
{ "issue": [ { "code": "security", "details": { "text": "permission_denied" }, "diagnostics": "Consent access denied or the resource being accessed does not exist", "severity": "error" } ], "resourceType": "OperationOutcome" }
curl -X GET \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "X-Consent-Scope: actor/Practitioner/12942879-f89f-41ae-aa80-0b911b649833 purp/v3/BIORCH env/App/golden" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Patient/3c6aa096-c054-4c22-b2b4-1e4a4d203de2"
Because the requestor was given consent, the response is the content of the Patient resource.
{ "active": true, "birthDate": "1990-01-01", "gender": "female", "id": "3c6aa096-c054-4c22-b2b4-1e4a4d203de2", "meta": { "lastUpdated": "2022-09-01T17:31:40.423469+00:00", "versionId": "VERSION_ID", "tag": [{ "system": "http://terminology.hl7.org/CodeSystem/common-tags", "code": "employee" }] }, "name": [ { "family": "Smith", "given": [ "Darcy" ], "use": "official" } ], "resourceType": "Patient" }
curl -X GET \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "X-Consent-Scope: btg actor/Practitioner/12942879-f89f-41ae-aa80-0b911b649833" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Observation/7473784b-46a8-470c-b9a6-fe38a01025aa"
Because the consent's authorization method is btg
, the server skips consent
checks. The response is the content of the Observation resource.
{ "code": { "coding": [ { "code": "718-7", "display": "Hemoglobin [Mass/volume] in Blood", "system": "http://loinc.org" } ] }, "effectivePeriod": { "start": "2021-12-10T05:30:10+01:00" }, "id": "7473784b-46a8-470c-b9a6-fe38a01025aa", "issued": "2021-12-10T13:30:10+01:00", "meta": { "lastUpdated": "2022-09-01T17:31:40.423469+00:00", "source": "http://example.com/HappyHospital", "versionId": "VERSION_ID" }, "resourceType": "Observation", "status": "final", "subject": { "reference": "Patient/3c6aa096-c054-4c22-b2b4-1e4a4d203de2" }, "valueQuantity": { "code": "g/dL", "system": "http://unitsofmeasure.org", "unit": "g/dl", "value": 7.2 } }
Configure consent header
The following sections describe the supported consent enforcement methods in the Cloud Healthcare API and how resource access is enforced when you make a consent-aware request.
When making a request, your authorization server is responsible for generating access tokens with the relevant consent scope.
Set HTTP header
Consent scopes are passed to the Cloud Healthcare API using the
X-Consent-Scope
HTTP header. The Cloud Healthcare API uses this header to
enforce consent-based access control on data in FHIR stores.
A FHIR request can support limited number of consent entry scopes. Up to three
entries of actor
, one of purp
, and one of env
can be included in a given
FHIR request.
For special scopes, a FHIR request can support one of btg
, or
bypass
.
Set HTTP headers for trusted applications
This section is only required if you're using a customer-controlled authorization server. In this instance, you must also use a SMARTproxy or similar proxy.
Some trusted applications may make calls directly to the Cloud Healthcare API with the consent scopes in the specified HTTP header. This enables direct consent enforcement without the need for a SMARTproxy or another proxy to convert between external authorization servers and Google Cloud.
For example, your application might be registered for a subset of scopes, such
as an application environment
scope, or the application might present a
selection widget to set some scope entries such as the accessor's purpose
.
A trusted user or a trusted application could also use
the btg
or the bypass
scope entries, which are subject to
post-audit reviews.
Configure your authorization server for consent scopes
The Cloud Healthcare API provides built-in support for FHIR Consent enforcement based on the input consent scopes. FHIR store administrators are responsible for creating and configuring an authorization server outside of the Cloud Healthcare API that grants consent scopes.
Sample access token
The following sample shows an access token encoded in base64:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzM4NCJ9.eyJpc3MiOiJjb25zZW50LnRva2VuLm9yZyIsImlhdCI6MTYxMjg4NDA4NSwiZXhwIjoxNjQ0NDIwMDg1LCJhdWQiOiJ3d3cuZXhhbXBsZS5jb20iLCJzdWIiOiJkb2N0b3IuZ2FicmllbGFAZXhhbXBsZS5jb20iLCJzY29wZSI6Im9pZGMgYWN0b3IvUHJhY3RpdGlvbmVyLzEyMyBhY3Rvci9Hcm91cC85OTkgcHVycC92My9UUkVBVCBlbnYvQXBwL2FiYyJ9.fC7ljkVUUx8fwUOrJuONcrqA-WKC-k_Bclzlgds0Cq6H_gEe3nUjPlSOCTQsIdYB
After decoding the access token, you can see that it contains the following payload:
{
"iss": "consent.token.org",
"iat": 1612884085,
"exp": 1644420085,
"aud": "www.example.com",
"sub": "doctor.gabriela@example.com",
"scope": "oidc actor/Practitioner/123 actor/Group/999 purp/v3/TREAT env/App/abc"
}
Configure SMARTProxy
SMARTProxy is an open source proxy from Google that provides the following features:
Allows the Cloud Healthcare API FHIR server to accept and validate consent-aware access tokens.
Allows the FHIR implementation in the Cloud Healthcare API to include consent-aware access tokens as part of the Cloud Healthcare API management and permission model.
Supports token features for SMART on FHIR support as well.
When you make a request to retrieve data from the Cloud Healthcare API through SMARTProxy, the following occurs:
SMARTProxy accepts a request from a client containing a consent-aware token.
SMARTProxy validates the consent-aware token through a JWT authorization server that you own.
SMARTProxy reads the scopes from the consent-aware token and passes them to the Cloud Healthcare API through the HTTP header.
The Cloud Healthcare API receives the headers and validates them to enforce consent directives on the request. The Cloud Healthcare API then returns a response through the SMARTProxy to the client.
Configure a Google Cloud service account
A proxy can only have a single Google Cloud service account. If multiple clients use the same proxy, then the clients will use the same service account. Use caution when sharing a service account with multiple clients for the following reasons:
To read the FHIR data in the Cloud Healthcare API, the service account can be configured to have broad read and write permissions. For more information on permissions, see Controlling access to Cloud Healthcare API resources. See General best practices for setting up proxy.
The Cloud Audit Logs
principal email address is tied to the service account.
For example, if you call the Cloud Healthcare API directly using your Google Account for authentication, then Cloud Audit Logs logs your email address as the principal email address. When you use a proxy to call the Cloud Healthcare API, the proxy uses its own service account and the principal email address is the service account's email address and the original account is not defined.
Audit logs
Audit logs are generated when there is an access request or when the resources' access enforcement changes.
Access audit logs
If audit logs are
enabled
on the FHIR store, then a consentMode
metadata field is included in the audit
logs available in Cloud Logging. The consentMode
may have one of the following
values:
off
: the FHIR store configuration hasconsentConfig.accessEnforced
set tofalse
and does not allow consent-aware requests.emptyScope
: the FHIR store hasconsentConfig.accessEnforced
set totrue
but a consent scope header was not included. As a result, consents were not enforced.enforced
: the FHIR store hasconsentConfig.accessEnforced
set totrue
and the consent scope header was present. As a result, consents were evaluated and enforced on the request.btg
: the FHIR request hadbtg
provided in the consent scope header. As a result, consent checks were skipped. This request is intended to be used for emergencies and subject to post-audit review only.bypass
: the FHIR request had onlybypass
provided in the consent scope header. As a result, consent checks were skipped. This request is intended to be used by a trusted workflow (such as an administrator or a trusted application instead of end-users) so that this audit log is different from thebtg
, which is used for data governance checks.
Optionally, you may set access_determination_log_config
to VERBOSE
to record more information
about why a request is granted or denied.
Access enforcement change audit logs
When the compartment base resource changes (for example, removing a patient's
employee
tag): The access control on the changed resource and its compartment
may change due to Admin Cascading Policy. It will trigger reindexing on all of
its compartment resources. The reindex progress for each compartment base
resource update can be tracked in Cloud Logging with the filter
jsonPayload.@type="type.googleapis.com/google.cloud.healthcare.logging.FhirConsentCascadeLogEntry"
.
Sample cascade reindex progress log
{ "insertId": "tz2gtza8", "jsonPayload": { "@type": "type.googleapis.com/google.cloud.healthcare.logging.FhirConsentCascadeLogEntry", "state": "STATE_FINISHED", "affectedResources": "2", "lastUpdated": "YYYY-MM-DDTHH:MM:SS+ZZ:ZZ", "compartmentBaseResourceName": "projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Patient/PATIENT_RESOURCE_ID/_history/PATIENT_RESOURCE_VERSION" }, "resource": { "type": "healthcare_fhir_store", "labels": { "location": "LOCATION", "dataset_id": "DATASET_ID", "fhir_store_id": "FHIR_STORE_ID", "project_id": "PROJECT_ID" } }, "timestamp": "YYYY-MM-DDTHH:MM:SS+ZZ:ZZ", "severity": "INFO", "logName": "projects/PROJECT_ID/logs/healthcare.googleapis.com%2Fconsent_cascading_fhir", "receiveTimestamp": "YYYY-MM-DDTHH:MM:SS+ZZ:ZZ" }
jsonPayload.state
is the state of the reindex operation, jsonPayload.affectedResources
is the number of reindexed compartment resources, and jsonPayload.lastUpdated
is the timestamp of patient resource update. If the operation just started, then jsonPayload.state="STATE_STARTED"
, and jsonPayload.affectedResources
won't be present.
Constraints and limitations
This section shows the constraints and limits for FHIR R4, but the same constraints and limits apply to FHIR STU3.
Type | Constraints and limits |
---|---|
Single Consent resource |
|
Enforcement model |
|
X-Consent-Scope |
|
Supported methods |
|
Performance |
|
Best practices
The following sections describe best practices when using FHIR Access Control.
General best practices
Don't import FHIR resources and call
ApplyConsents
orApplyAdminConsents
in parallel. We recommend you to first import FHIR resources and then callApplyConsents
orApplyAdminConsents
. However, if the resources to be imported don't include any Patient or Consent resources, then the enforcement model won't be affected and processing consents or admin policies is not necessary.Don't create custom searches and call
ApplyConsents
in parallel. We recommend you to do them one after the other.If your workflows require calling multiple
ApplyConsents
on disjointPatientScope
, they can be called in parallel.ApplyAdminConsents
can run in parallel with any number ofApplyConsents
, but not with anotherApplyAdminConsents
.When setting up the proxy, restrict the IAM service account with read-only permissions to avoid writing one patient's data to another patient's records.
Don't use the consent proxy when creating or updating records.
Validate all write requests to prevent unexpected modification of cross-patient data.
When cascading consents are enforced, compartment base resources must be imported first, followed by the remaining compartment resources. Alternatively, all compartment resources can be wrapped within a single bundle and ingested using
fhir.executeBundle
.
Delete Patient resource
When deleting a Patient resource, if you also want to remove the consent
enforcement for that patient (especially when
FhirStore.disableReferentialIntegrity
is true), we recommend you to follow
this order of operations:
Delete all Consent resources belonging to the Patient resource.
Call
ApplyConsents
with thePatientScope
filter.
Set up an existing store for consent access
To set up an existing store for consent access, complete these steps:
Use
UpdateFhirStore
to set theConsentConfig
with the consent enforcementversion
asV1
and setaccessEnforced
totrue
.curl -X PATCH \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "Content-Type: application/json" \ --data "{ 'consentConfig': { 'version': 'V1', 'accessEnforced': true } }" "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID?update_mask=consentConfig"
Process patient consents or admin policies
ApplyConsents
for patient consents
curl -X POST \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "Content-Type: application/json" \ --data "{'validateOnly': false}" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID:applyConsents"
ApplyAdminConsents
for admin policies and admin cascading policies.
curl -X POST \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "Content-Type: application/json" \ --data "{ 'newConsentsList': { 'names': [ 'projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Consent/RESOURCE_ID_1/_history/VERSION_ID_1', ... 'projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID/fhir/Consent/RESOURCE_ID_N/_history/VERSION_ID_N' ] }, 'validateOnly': false }" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID:applyAdminConsents"
How often to run ApplyConsents or ApplyAdminConsents
When the
ConsentConfig
field is unset: TheConsentConfig
field is unset both when a FHIR store is first created and when theConsentConfig
field is cleared. Once theConsentConfig
field is unset, you must repeat the set up the store for consent access before making consent-aware requests to avoid evaluating out of date consent enforcement policies.When the enforcement model changes: When a Consent resource is created, updated, or deleted, the enforcement model changes. In such cases, you must call
ApplyConsents
orApplyAdminConsents
for these changes to take effect.If you can keep track of the Patients with Consent changes, we recommend you to use
PatientScope
filter to avoid re-processing the entire store. This filter is useful to immediately refresh enforcement of a small set of Patients.You can also run
ApplyConsents
periodically using theTimeRange
filter. This filter is useful when immediate refreshment is not critical. For example, the following request refreshes enforcement for consent changes between UTC 0AM 2022-09-20 and UTC 0AM 2022-09-21.curl -X POST \ -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \ -H "Content-Type: application/json" \ --data "{ 'validateOnly': false, 'timeRange': { 'start': '2022-09-20T00:00:00Z', 'end': '2022-09-21T00:00:00Z', } }" \ "https://healthcare.googleapis.com/v1beta1/projects/PROJECT_ID/locations/LOCATION/datasets/DATASET_ID/fhirStores/FHIR_STORE_ID:applyConsents"