Find out how you can securely request access to someone's verifiable health records and incorporate that data safely into your app. The Health app helps people download, view, and share their health records, including their COVID-19 immunization and test results — and iOS 15 brings support for the SMART Health Card, a verifiable health record that incorporates the FHIR health data standard. We'll show you how your app can go about requesting access to this record and how you can verify the signature of the file using CryptoKit and the issuer's public key.
My name is Annie, and I'm a software engineer on the Health Records team.
I'm joined today by my colleague Cary to show you how you can empower people to share their verifiable health records using HealthKit. Before we dive into verifiable health records, I want to briefly reintroduce the Health Records feature that we launched back in 2018 for iOS 11.3. Health Records allows users to connect to their providers and securely download their clinical records to the Health app for easy storage and visualization.
This is all built on top of industry standards like HL7's Fast Healthcare Interoperability Resources, or FHIR. With user permission, your app can access these health records that users have downloaded with HealthKit. For more details about the existing Health Records API, I encourage you to check out our WWDC 2018 video, "Accessing Health Records with HealthKit" to learn more. For iOS 15, we're leveraging our Health Records work to allow for the download, storage, and sharing of verifiable records in the Health app implemented based on the SMART Health Cards specification.
Let's explore what verifiable health records are and how they're different from the health records data you can currently query for with HealthKit. First, a verifiable health record contains multiple FHIR resources, including a patient resource and one or more clinical resources. These resources are bundled in what we call a SMART Health Card. Issuers, or institutions that create verifiable health records, such as a lab or healthcare provider, cryptographically sign this SMART Health Card as a JSON Web Signature, or JWS. Finally, issuers generate verifiable health records such that they include only necessary information about the patient and their clinical data.
To get a clearer picture of these points, let's look at an example. For this example, the record contains two FHIR immunization resources, one for each dose of a vaccine. It also includes a FHIR patient resource to identify who received the vaccine. These resources are then combined into a SMART Health Card, which, in its raw form, looks like this. The SMART Health Card payload includes information such as the type, which contains URLs that declare what types of data to expect in the health card, and the credential subject, which includes the FHIR version and a Bundle resource that contains the immunization and patient resources. The SMART Health Card is then signed as a JWS, which has two components: A header and a payload.
The JWS header houses the algorithm used to sign the JWS; the public key thumbprint, which specifies which of the issuer's public keys was used to sign; and the algorithm used to compress the payload. The compressed JWS payload, along with the credential subject, contains other claims data, including the issuer URL, where the issuer's public keys are located, the issued date, and an optional expiration date.
In its raw form, this is what a Verifiable Health Record looks like.
For more information, we encourage you to visit the SMART Health Card specification via the link associated with this session.
Now that we've reviewed what a verifiable health record looks like, let me show you how you can import these records into the Health app.
One option is to use the existing Health Records feature. Users can download verifiable health records to the Health app by connecting to a Health Records-enabled provider.
Note that this option is only available where the Health Records feature is available, which is currently in the US, UK, and Canada. Users also have the options of downloading a file with a .smart-health-cards extension or scanning a smart-health-card file-embedded QR code. This will display the following sheet in the Health app, where users can view details and choose whether to download these records. These two options are available internationally.
Finally, how can you request access to this data with HealthKit? We are introducing a new HealthKit query, HKVerifiableClinicalRecordQuery, and a new HKSample subclass, HKVerifiableClinicalRecord for iOS 15. Unlike other HKQuery subclasses, in order to use this query, you must request for a HealthKit access entitlement for Verifiable Health Records. While you will still have access to the public headers in the SDK, the results handler will return an authorization denied error without the entitlement. Please refer to the links associated with this session for information on how to get started. Before we dive deeper into the query and sample classes, let's look at authorization for Verifiable Health Records, which is slightly different from what you might be used to with Health Records.
First, Verifiable Health Records observes a per-sample authorization pattern, where the user selects individual samples, not types, to share with your app. This type of authorization provides users with more granular control over their data and is important in protecting their privacy. The current HealthKit authorization pattern consists of two steps: First requesting user authorization for types to read and share, and then querying for samples once authorization is granted. With Verifiable Health Records, this first step is not needed. Instead, once an instance of HKVerifiableClinicalRecordQuery is created and executed, an authorization sheet will be presented, allowing the user to select which individual records to share with your app.
After sharing, the chosen records are then returned to the query's results handler.
Furthermore, authorization for Verifiable Health Records is one-time only.
Sharing these records with a third-party app does not set up long-term access authorization. Each execution of a HKVerifiableClinicalRecordQuery will result in the presentation of the following authorization sheet and the return of user-selected records to the results handler. Now that we've gone over authorization, let's look at HKVerifiableClinicalRecordQuery in more detail.
To initialize the HKVerifiableClinicalRecordQuery, you will first have to provide a string array of record types, which we defined earlier as the type of data present in a verifiable health record.
Only records with all provided record types present will be shown in the authorization sheet. Like other HealthKit queries, you also have the option to add a predicate to further filter the set of samples for authorization. We are providing a convenience constructor to create a predicate for records with relevant dates within a date interval. Once the query is executed and the user taps "Share Once," the authorized HKVerifiableClinicalRecords are then returned in the results handler. The record includes basic information about the subject and issuer, and, of course, the JWS property, which will contain the raw data that you will verify and decode. Now that we've gone through the Verifiable Health Records feature and what its API looks like, I'm going to hand it off to Cary, who will walk you through how to import test data, use the API to request records, and verify a record's JWS. Thanks, Annie. Hi, everyone. My name is Cary, and I'm also an engineer on the Health Records team. Today, I'll be walking you through how to work with Verifiable Health Records. So the best way to start working with this new feature is to load some test data into HealthKit. As Annie mentioned, there are three different methods you can use to get started. If you're in the United States, Canada, or the United Kingdom, you can simply connect to a provider using the existing Health Records feature in the Health App.
Another new way we've added is by downloading a .smart-health-card file, an example of which you can find on the smarthealth.cards specification website. The third method is by scanning a QR code. So let's go get some data. For this demo, you can start by trying out the new QR code import. You'll start with a device running iOS 15 and scanning this QR code. As soon as you scan it, you will receive a prompt to open directly into the health app. Once you switch to the health app, it will immediately begin guiding you through importing this health card. After selecting the "Add to Health" option, the import process will complete.
You can scroll over to the browse tab and see the imported records under the immunizations section. If you go ahead and tap into this category, you can now see that you have a vaccine and that its signature has been verified by the Health app. Now that you have some signed records imported into the Health app, you're ready to go ahead and request these records from your own app. Now, let's look at an example of how your app can access this new API.
First, you'll start by importing HealthKit. Next, you'll create a HealthStore, which you may already be familiar with doing if you've used the standard HealthKit authorization API. You will also create a list of record types to specify the type of data that you would like the user to share with you. When authorizing, you will be presented with a list of data to share that only match these record types. Next, you will create a predicate specifying the valid date range for samples to share. In this example, you can see labs conducted in the past seven days are being requested. Then you will create a query using the constructed recordTypes and predicate, using a completion to check whether the samples presented were shared with your app. Finally, executing the query will cause an authorization sheet to be presented where results may be selected to share with your app.
This dialogue will be presented every time this method is executed.
Now that you have retrieved a piece of signed clinical data, you can write some code to verify its signature within your app.
To review, digital signatures are a mathematical way to verify the authenticity of digital information. This process may be performed by any device, such as a server or another mobile device, but let's go ahead and take a look at how to do it from within your app. You can accomplish this using four main steps. First, you will parse the clinical data using a Codable. Second, you will decompress the signed JSON Web Token payload. Third, you will download the issuer's public key from their website.
And finally, you will use CryptoKit to verify the signature.
Let's start by looking at how to define the data model for a signed record, which is formally defined in the JSON Web Signature specification. This struct is defined in three main parts: A header, payload, and signature. You could use Swift Codable to do the parsing for you, and you should keep an original representation of the header and payload around for use during signature verification.
Most importantly, you'll add an initializer to parse the JWS from its compact serialized form. Next, you'll create a struct used to represent the JWS Header. You will need this during the initialization from compact serialization, as well as during the signature verification process. You can see here in the header that you can expect to find a specified signature algorithm, a key-ID field used to hold a public key thumbprint, and a compression algorithm. The public key thumbprint is used to identify which of the issuer's keys was used to sign the JWS. This process requires the ES256 Signature Algorithm and the DEF compression algorithm, both of which are supported by first-party Swift libraries. Now let's look at the code needed to parse the JWS from its compact serialization.
You'll start by splitting the serialized string into its constituent pieces, each of which is Base64URL encoded. After creating a JSONDecoder and Base64URL decoding the header and payload, you can check to see if the header indicates that the payload is compressed correctly. After checking the compression, you will use a utility method that you can find in the provided sample project to decompress the payload. The next piece you'll need is a struct to model the health card payload.
The two most important pieces here are the issuer field, which will hold the issuer's web identifier that you can use to fetch their keys, and the other is the VC field, which holds all the patient data. For more information on how to parse the VC field and analyze its contents, check out the "Handling FHIR without getting burned" talk from WWDC 2020. Now that you've seen how to parse a JWS, you are ready to verify its authenticity.
Let's look at creating an extension that will add a verification method to your JWS struct.
Since you'll be reaching out to the issuer's website directly to retrieve their signing keys, you can use Combine to help with the asynchronous aspects of this process, and you can use CryptoKit to help with the signature verification. Inside the verification method, you'll begin by pulling out the issuer's identifier, which is always the issuer's web address. Here, you can add your own method to check if the URL is one that you personally trust and abort if it's one that you don't want to accept results from. Next, you will create a URL to retrieve the issuer's keys by appending the standard .wellknown/jwks.json URL path.
This path suffix is defined in the specification and should be the same for all issuers.
Now that you have constructed the well-known URL, you can connect to the issuer, download their keys, and finish verifying the signature. You'll finish writing this method by creating a URLSession dataTaskPublisher that will connect to the issuer's well-known jwk endpoint. You can then map it to data, decode it using a jsonDecoder to a set of JWKs, and finally map it to a boolean, indicating whether or not the signature is valid. Inside the final map block you will select the issuer's key based on what the JWS header indicates.
The final piece involves using CryptoKit to verify the signature. According to the JWS specification, the signed portion of data is formed by concatenating the Base64URL-encoded header and payload joined by a period. Next, you'll convert the downloaded JWK keys to CryptoKit P256 signing keys, using a utility method that you can find in the sample project accompanying this talk. Finally, you'll use CryptoKit's isValidSignature method to do the verification of the signature. Now that your data model and all logic needed to verify its authenticity is defined, you can put everything together. Taking a look at the earlier example of how to call the new API, you can replace this comment with a call to your new verification method.
Since the API returns a list of all selected samples, you can select the first one from the list.
Next, parse it from data to a String. Then parse it using your compact serialization initializer. Finally, call your new verification method, and listen for emitted boolean results.
And just like that, we've seen how to take a signed piece of clinical data download the issuer's signing keys, and validate the signature of these records.
Now, I'll hand things back to Annie to summarize.
Before we wrap up, I want to talk about privacy with respect to Verifiable Health Records. Hopefully, I've made it clear throughout this session that the Verifiable Health Records feature is built with user privacy in mind. First of all, the SMART Health Card specification uses data profiles that minimize the information an issuer should include in each record. The Health app is designed to protect sensitive health data, so when a phone is locked, all health and fitness data in the Health app is encrypted, which includes verifiable health records. Access to verifiable health records requires an application to an entitlement that will come with additional obligations to ensure that this sensitive health data is not misused.
And finally, users will have additional control over how to share their verifiable health records with your app. Let's review what we've covered today.
In iOS 15, building on top of Health Records, we are bringing the capability to import verifiable records into the Health app and for developers to read this data with a new API.
To wrap up, here's what you can do next. First, to learn more about the verifiable health data ecosystem, check out the SMART Health Cards specification.
Import test records into the Health app to try it out yourself.
Download the sample app, which shows how you can query for and verify those test records. And finally, when you're ready to ship your app, request the HealthKit access entitlement for verifiable health records. Thank you for watching, and have a great WWDC.
Looking for something specific? Enter a topic above and jump straight to the good stuff.
An error occurred when submitting your query. Please check your Internet connection and try again.