We’ve gotten a lot of questions about how users can connect to and use their application for multiple HealthVault records from partners lately; we’ve also found a number of partners who didn’t consider the scenario of an existing user wanting to switch which HealthVault record is currently connected and authorized for their application. These are all very important scenarios, and every HealthVault application should carefully consider how to handle them effectively.
The nice part is that it’s very easy to do so. HealthVault has been designed to manage switching records and using multiple records with an application in a very lightweight way that requires very little work on the part of the application developer.
This is going to be a pretty long post—so if you want to skip over all the scenarios and context, and head straight to the implementation details and code, just scroll to the bottom. There’s not much code required, and it’s down at the end.
Also, keep in mind that there are still two types of applications to consider here—“Single Record Applications” (or SRAs) that only work with one HealthVault record at a time, and “Multi Record Applications” (or MRAs) that understand how to work with multiple records simultaneously. For the purposes of this post, we’re going to talk only about SRA applications, which are the most common type of application. We’re also only talking about applications with an online user experience—so no PatientConnect or Drop-off-Pick-Up applications apply here either.
So, let’s get started. Before we get to how to do this, we want to make sure everyone understands why this is important and why effectively every application should include this functionality. Let’s start from the beginning: the first time a user goes to your application and connects to HealthVault, they are redirected to HealthVault where they:
1. Create a new account, or sign into an existing account
2. Create a new health record, or select an existing health record
3. Review the application authorization request, and accept/reject it, and are then redirected back to the application
In #1 and #2, it’s important to note that a HV account can create and manage multiple health records (where each health record is for a single individual)… and that a record can be managed by multiple accounts. There is a many-to-many relationship there. If my wife and I both create accounts, and create records, and then share custodial access to our records with each other… then there will be two accounts, each with access to the same two records. Or, if my wife creates an account, she can then create a record for herself, and a record for her mother, and another for a child (for example). She would have one account that then has three records for three individuals… all using the same account sign in credentials.
In general, HealthVault manages this complexity for applications. You simply direct users off to authorize your application, and at the end you get either a wctoken to represent the signed in online session, or a PersonID/RecordID that can be used for offline communication to/from a user’s HealthVault record.
However, there are a few user scenarios that make it a good idea to display to the user which record is currently linked (or “active”), and to display a link or button to change the current linked/active record. This is because with the default parameters, returning users will automatically be using the previously active record (for online connections) or for applications using offline connections the PersonID and RecordID are stored by the app and don’t change unless the app lets the user change them. The most relevant scenarios in which the app needs to let the user change active records are:
1. If it’s been a few months since the user set things up, he may want to verify that the app is set up to read/write data to the correct HV record (he may have forgotten which record he authorized, for example), or may just want to head back to the app to make sure things are set up properly. The user would click on the link on the HV side to go back to the application, and that would go to the Action URL’s HOME target. The user would expect to be able to verify that they’ve already set up a connection, and to see the name of the HealthVault record currently receiving data.
2. If the user accidentally selected the wrong record when authorizing your app (example: user has a HV account that manages 4 records, they intended to pick the 4th record but instead left the default/1st record selected), they may want to go back to the application and switch which record is set up to receive data. This would change the association between the application user ID and the HealthVault record.
3. If the user decides to create a new HealthVault account or record, and abandon the old one, they may want to go back to the application and either remove access (and then later add it to the new record), or switch from the old to the new in a single operation.
Some of these can be handled somewhat by severing the connection from the HealthVault side (and then connecting from the other record), but that isn’t always optimal, and many users have expressed their expectation that they would do this by going to the application to make changes, not HealthVault’s shell UI. We strongly suggest supporting it from both sides… plus, exposing it from the app allows the application to handle the change gracefully, rather than having to catch Access Denied errors from HealthVault after the user breaks the connection from the HV side.
Anyhow—the functionality to do all this is pretty simple, thankfully. For returning users, you just need to:
1. Read and display the active record name (so users know which record is active, and can decide if they want or need to change the active record)
2. Display basic HTTP links for the switch record and (less important, but also helpful) remove access functions.
a. Those are static links that go to HealthVault and pass in some parameters to tell HV to send the user through the appropriate workflow (using the Shell Redirect feature, the server-side equivalent of the client’s Action URL Redirect—using the AUTH target and the FORCEAPPAUTH parameter).
3. Handle the “SelectedRecordChanged” Action URL target (which can point to an existing target as long as your code handled the record change properly there)
In practice, for those using the .NET SDK, this involves:
1. Editing your web.config and adding a new entry like the following, replacing src.aspx with an appropriate page to handle the SELECTEDRECORDCHANGED target (if your code needs custom logic to grab the new PersonID/RecordID and save it to a local DB, for example… native apps can likely just use their normal default page):
a. <add key="WCPage_ActionSelectedRecordChanged" value="src.aspx"/>
2. Handling the “Change User” or “Switch Record” button/link with the following code:
this.RedirectToShellUrl("AUTH", "appid=" + this.ApplicationId.ToString() + "&forceappauth=true", "passthroughParam=optional");
For non .NET SDK apps, or those who prefer a static link, you just need to add a hyperlink of the following form:
https://account.healthvault-ppe.com/redirect.aspx?target=AUTH&targetqs=appid%3D82d47a5a-d435-4246-895a-746c475090d3%26forceappauth%3Dtrue
As well as handling the SELECTEDRECORDCHANGED target on your Action URL redirect handler.
I’ll include one example here to illustrate a bit. If you go to the (free) American Heart Association Heart 360 app (http://www.heart360.org), the first page lets you get started, which has the user sign in, and authorize the app if that hasn’t already happened. If it has, then the user just signs in to an online session. If the user has an account with multiple records, and has already authorized one of the records, the next time they go back and sign in, that previous record is automatically selected.
The next page includes a “Welcome back, <FIRST NAME>” string in the upper right that is using the HealthVault selected/active record name, and to the right of that has a “Change User” link. The “Change User” link uses the HealthVault Shell URL redirect with the AUTH target and the FORCEAPPAUTH=true parameter in order to allow the user to either connect and use an additional record, or just to switch the current record.
And, for reference, the record select screen you should see on the HealthVault side looks like:
You will see the error above if you try to update an instance of the BasicDemographicInformation HealthRecordItem type that is stored in the v2 type schema from an application that “speaks” the v1 type schema.
In our recent 0910 release we updated the Basic Demographic Information HealthRecordItem type to use Codable Values for State/Province and Country. This helps make the data more consistent across applications, but it means that we had to version the data type.
Eric Gunnerson’s blog post describes how data type versioning is handled within the HealthVault platform and my blog post three months ago elaborates a bit on this topic. But the recent revision of Basic Demographic Info gives me a chance to expand a bit more, discuss some corner cases and give some examples.
Data Type Details
You can see the type details in our HealthRecordItem Type Schema Browser:
You can also see the type details in our MSDN Library, which always documents our most recent SDK version (as of right now, version 1.1.2193.4712).
How this impacts you
If you have a HealthVault application which is already live and you interact with Basic Demographic Information then you are almost certainly using the older version of the schema. Per the descriptions in Eric’s and my blog posts, if you ask the HealthVault platform for Basic Demographic Information then it will return all of the instances of both v1 and v2 of the data type. Any v2 instances will be downconverted into v1 of the schema. These downconverted instances will be read-only to your application. If you try to update any of these downconverted instances then you will see the error message above.
The HealthVault Shell has been updated to use the new version of Basic, 3b3e6b16-eb69-483c-8d7e-dfe116ae6092. This means that any new account will have its Basic Demographic Information stored in the new schema. It also means that any user who updates their Basic Demographic Info in the Shell will get moved to the new schema.
You have two options in order to avoid this error, and both require a code change:
- Stay on the 1.0.2145.4504 and don’t update BasicV2 instances. You can accomplish this by capturing this exception and letting the user know that an update of this item was not possible, but I’d rather not wait until the user tries to save changes. Instead, look at the IsDownversioned property on each instance of basic and tell the user when an instance is read-only using your own UI cues. You should also give the user a workaround for updating this information, such as using the HealthVault Shell.
- Move to the 1.1.2193.4712 SDK
Moving Forward
We don’t update data types all that often, and few of our data types are singletons so it is unlikely that you’ll see this issue again in the near future. But you can “future-proof” your application by looking for the IsDownversioned flag on each HealthRecordItem instance before you allow the user to attempt an update.
We are considering some revisions to our approach to data type versioning that may simplify this process for developers. Keep an eye on the HealthVault Developer Blog for announcements about updates to the HealthVault platform.
You may notice that the newest version of the Application Configuration Center contains fields for configuring your IP Filtering settings. This extra security measure is not generally needed for HealthVault applications, except in special cases. If you enter a non-null address range in this field then you will effectively turn on filtering for your application in HealthVault-PPE. If you do this accidentally then you can turn IP filtering off again by clearing out this field.
If you have an application which is live in the production HealthVault environment and are using IP Filtering there, you still need to go through the Microsoft team in order to update your Production settings as the settings in the config tool are for PPE. You can contact Microsoft for these types of updates via the hsg-pn@microsoft.com email alias. Expect the update to take a few business days.
You can create and update your ApplicationId configuration in our Developer/PreProduction environment by accessing https://config.healthvault-ppe.com. The App Manager tool in our HealthVault SDK also connects to this config site.
But this configuration site is only available in non-Production environments. Microsoft reviews the data access for each application before it goes live, in an effort to ensure that the data set is reasonable for the implied intent of the application. Therefore any updates to a Production configuration can only be performed by the Microsoft team. You must make the config changes in PPE first and then test/verify that they work with your app. Then you submit an email request to HvGoLive@microsoft.com so that we can review the changes and then push the changes for you.
If you are making major changes to your configuration and want the config updates to coincide with the deployment of new code, we are generally able to schedule the push of a config update to coincide with your deployment. We ask that you give us a few business days' notice for such a push, or a bit longer if the push will be outside of west-coast US business hours.
Applications are expected to Handle the HealthServiceCredentialTokenExpiredException. The best way to handle this error is to Redirect the user to sign-on again, the best place to implement it in your basepage’s page error handler. Here is some sample code which accomplishes the same, please note this code should be in our basepage or any page deriving from HealthServicePage :
/// <summary>
/// This error block handles the case of an expired user token. User
/// tokens expire after 24 hours or time set by persistent tokens
/// for the "keep me sign-in" feature. These tokens and are stored in the cookie,
/// which has no default timeout. Developers are encouraged to expire cookies
/// sooner, but this code handles the case where the cookie does not
/// expire and the browser window is left open for over 24 hours.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Page_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
if (ex is HealthServiceCredentialTokenExpiredException)
{
WebApplicationUtilities.RedirectToLogOn(HttpContext.Current);
}
ShowError(ex);
}
Your page will need to implement ShowError method or you comment it and re-throw the un-caught exceptions.
Here is some help on diagnosing some of the common modes of failure with HealthVault certificate management.
Access denied
The most common cause of this error is that the application can find the certificate but the account running the app does not have the proper permissions to utilize its private key at run-time. See the end of this article for more information on giving permissions manully, or use the App Manager tool in the SDK.
This error can also be triggered by attempting a read/write which the user has not authorized, or a number of other authorization-related errors. You may need to look at the stack trace in order to figure out where your error lies. But if you have never successfully connected to HealthVault from Machine X with AppId Y, the certificate is the best place to start.
Keyset does not exist
I have seen this in two different scenarios:
- Application certificate is in the cert store, but that certificate only contains a public key. So the app finds the cert but can't find the keyset that it wants.
- Application certificate is in the file system but the application's service account doesn't have permission on this folder or file. Having not run this scenario myself, I would have expected this to be another "access denied" but I learned today that it gives a "keyset does not exist" error.
Got a good question today from a long-time HealthVault Developer -- at least they are long-time in terms of the short history of HealthVault.
If your application certificate is not configured correctly, either on your app server or on the HealthVault server, the first time that you will see an error is the first time that your application tries to read or write data to/from the HealthVault platform. This means that your end users (or more likely your test accounts) can successfully go through Application Authorization before they will see this error.
The fact that you get through App AuthZ without seeing any errors may lead you to believe that your certificate must be correct, but that is not the case. If you see an "Access Denies" error the first time that you try to load a page containing health information, then the most likely cause of your issue is an improperly configured application certificate.
See the post linked above for more information on certificate management.
There is a parameter on the HealthRecordFilter object called MaxFullItemsReturnedPerRequest and it is set to 240 by default. If your search returns more than MaxFullItemsReturnedPerRequest items then you will get full items for the first max items and then HealthRecordItemIds for the remaining items. You can then retrieve the other items by ID.
If you are using the .NET HealthVault SDK then all of this logic is handled for you. The HealthRecordItemCollection returned by GetMatchingItems will appear to contain all of the items. Attempts to work with the guts of an item that is still on the server will trigger the retrieval of that information.
(update on 10/21/2009: the default for MaxFullItemsReturnedPerRequest is 240. I originally said 30 and that is incorrect.)
We get a lot of questions about managing your HealthVault application certificate. When your application initiates a connection to HealthVault, it uses its unique private key to encrypt the first handshake message that it sends. HealthVault then uses a public key to verify that the sender of this message is indeed a trusted host. This public key must be registered with HealthVault before such connections can be made.
Generating your key pair and installing your private key
You can generate your key pair in one of two ways:
- Use the MakeCert.exe tool which ships with the HealthVault SDK and is available on MSDN. This doc on MSDN has more information on using MakeCert.
- Use the Application Manager tool which ships with the HealthVault SDK. Application Manager makes it easy to create a new AppId and certificate pair at the same time. Application Manager does not handle the scenario of creating a new certificate pair for an existing application.
Once you have generated your key pair, you can install it for use by your application in one of three ways:
- Cert store, default name. Application Manager gives your certificate the default name, "WildcatApp-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx," and places it in the default location. The HealthVault instructions on using MakeCert also give the cert the default name and place it in the default location.
- Cert store, non-default name. To specify your certificate name, specify the full subject name of the cert (CN=) in the <appSettings> section of your web.config file as follows:
<appSettings>
<add key="AppCertSubject" value="[full_cert_subject_name]"/>
</appSettings>
- File system. If you choose to take this approach then make sure that the following two things are true:
- The IIS worker process has access to read the file
- The file is not stored in a location where it is visible on your web site. Don't put it inside your web application path but in a secure file path not visible in IIS.
- To store your certificate on the local file system, you must add an additional entry in your web.config to tell the HealthVault SDK code where to look for the certificate, as follows. (This is also added to the <appSettings> section.)
<add key="ApplicationCertificateFileName" value="C:\someFolderName\cert\HelloWorld-SDK_ID-05a059c9-c309-46af-9b86-b06d42510550.pfx" />
- Note: the file path is an absolute local file path; relative path names are not allowed
- If you configured your private key to require a password for use, then you can/must specify this password in your web.config by using the ApplicationCertificatePassword parameter
Registering your Public Key
In PPE, you can register your public key in one of two ways:
- Use the Application Manager tool in the .NET HealthVault SDK to upload a new ApplicationId and its certificate. Note that this workflow is only for creating new applications. You cannot add a new certificate to an existing application this way.
- Login to the Application Configuration Center at https://config.healthvault-ppe.com and click on the "Public Certs" tab. You can upload additional CER files here.
Note that each application in the HealthVault-PPE environment has exactly one HealthVault-PPE account that has been set up with config access. If you need to set up a new account with config access, you can request this access via a link on the App Config Center home page.
In Production, the only way to register your public key is to go through the HealthVault Go-Live process. If your application is already live and you need to update or replace its public key, you can file a request with HealthVault Developer Support here.
We've had a few forum questions lately on email addresses so I figured I would send out some clarifying info.
Most of you are aware that the Account and the Record are separate but related entities in HealthVault. Each user account can have varying degrees of access to one or many records. So when you start thinking "email" you have to wonder whether you want to email the account-holder or the record-holder. The account-holder's email can be retrieved by an application by calling PersonInfo.GetSelfRecord() and then looking for the PersonalContactInfo singleton object. The record-holder's email is in the record at PersonInfo.SelectedRecord.
You login to an account with a credential that is usually a LiveId but the email address / login name for this LiveId is not stored in HealthVault nor is it directly available via the HealthVault API. This LiveId email address is often the same as the account-holder email in HealthVault but there is no guarantee that this will be the case.
When a user creates a record in the HealthVault UI, they must specify an email address. If they just signed up for an account and this record is their Self record then the email address that they used at account sign-up may get copied into the input form but the user can change this. After record creation, a user can change any record email address to which they have access by using the "Edit Profile" link in the HealthVault shell.
Once you have that email address, you can use your own server to send email or you can use the HealthVault API to do so. If you elect to use the HealthVault API, note that you will need to make two config changes first, via https://config.healthvault-ppe.com:
- Add the Send Email method group on the Method Mask tab
- Specify the Email Domain from which your application's emails will originate. You will set the sender address when calling our email-sending methods and the HealthVault platform will check that this sender address matches the domain on file.
HealthVault provides a way for applications to email their end users without requiring access to PersonalContactInfo. This is a privacy feature. Regardless of whether this approach is used, an applications Terms of Use must make it clear to end users that the application may send them email.
SendInsecureMessageFromApplication has two signatures:
-
The
first signature lets you specify a list of recipients by (DisplayName, EmailAddress). You can use this in the case where you know the recipient email addresses via any of the data types mentioned above.
-
The
second signature lets you specify a list of recipients by PersonId. You can use this in the case where you don't know the account-holder's email address but you still want to contact them. This protects the user's privacy by never sharing their address. This function leverages the email address in the PersonalContactInfo field of the first Self record of the user.
SendInsecureMessageToCustodiansFromApplication takes a RecordId as a parameter. This method will send your note to every custodian of the record in question.
Note that all of these methods are called "SendInsecureMessage..." because email is not a secure protocol and you should not send health information through email. (Consult a lawyer if this is unclear.)
Questions on any of this? Please ask on the forum in order to get the quickest possible response.
It is possible for end users to delete a HealthVault record but this function is not featured prominently in the user interface. This is by design, as we don't want end users to accidentally delete a record.
In order to delete a record:
- Go to the HealthVault Shell: https://account.healthvault.com in the Consumer environment or https://account.healthvault-ppe.com in the Developer environment
- Use the left sidebar menu to "switch to" the record that you wish to delete
- Click the "Edit Profile" link under the record picture/folder icon near the upper left corner of the screen
- Below the [save] and [cancel] buttons, you will see this text:
"If you wish, you can delete this health record. Warning: This will delete all the information in the record. Learn more."
I didn't make the text above a hyperlink but it takes you to the DeleteRecord page in the Shell.
If an end user erroneously deletes a record, it is often possible to recover the deleted data by contacting HealthVault customer support.
This is a supplement to Eric's blog post on Data Type Versioning in HealthVault.
The HealthVault platform is capable of converting V2 items into the V1 schema and vice versa. In order to simplify things for app developers, the platform will convert all versions of a type into versions that it believes this app understands. It makes this assessment by looking at the base authz that has been configured for the app. If the app asks the user to authorize V1 then the platform will convert all instances to V1. Replace "V1" with "V2" or "both versions" in that previous sentence at it remains true.
Note that what the app asks for in its auth rules may sometimes differ from what the user has granted, especially in the case where you have changed your auth rules from including Type-V1 to Type-V2. If a user has already authorized Type-V1 and you change your auth rules to use Type-V2 then the platform will recognize that your app now uses Type-V2 and the user will not be prompted to re-authorize.
So if you change your code to deal with V2 instead of V1 but don't change your authorization rules then the platform will continue converting the elements into V1s. A typecast error will occur when you try to load these V1 instances into a V2 object.
The platform treats different versions of a single type a bit differently than it treats different types. When you query the platform for instances of a versioned type, the platform's default behavior is to treat this as a request for all versions of the type and to ignore the fact that your request implicitly specified a version. You can override this behavior by using the TypeVersionFormat property of the View property of your HealthRecordFilter when you search, i.e.
filter.View.TypeVersionFormat.Add(Encounter.TypeId);
If you are searching across multiple types then this TypeVersionFormat collection can contain multiple elements, each of which will only be applied when relevant.
So if you are moving from an older version to a newer version of one or more data types then you have two options to choose from:
1. Change your authorization rules such that the new versions are requested. The platform will see this at run-time and will therefore convert instances into the version that you want.
-OR-
2. Add one or more types to your HealthRecordFilter.View.TypeVersionFormat collection.
If you fail to do either of these options then you will get a typecast error at run time.
There are two different elements that you are likely to think about when you think "authorization:"
* the amount of access that your app requests -- required and optional auth rules
* the amount of access that any particular user has granted
Think of the first as a domain table, perhaps, and the latter as a collection of values or instances.
The authorization state that the HealthVault platform references at run-time is a set of access that has been approved for a particular application by a particular user on a particular record. With optional auth, different amounts of access can get approved for different (application, user, record) triples. This bundle of access is stored in the HealthVault authorization system along with the (app, user, record) triple.
The bundle of data access gets presented to the user during app auth is a function of what is configured in the online and offline auth rules in ACC. Changing what is configured in ACC does not change what users have already authorized. But if a user logs into a HealthVault-connected app and the platform sees that their currently-granted access doesn't match the required minimum that was configured in ACC, then the user is prompted to re-auth.
When a you add an optional auth rule to your application, by default it will not be shown to users in App Auth. There is a parameter to the Shell Redirect call which lets you specify one or more rules to be displayed – onopt#.
On the back end, the application configuration also supports setting this default show/hide behavior, or the checked/unchecked state of the checkbox next to your optional rule. We have not yet added this feature to the Application Configuration Center, so if you would like to set this parameter then you must contact the HealthVault team. Please send us the following:
ApplicationId
Rule Name(s)
preferred show/hide behavior for each rule
preferred check/uncheck behavior for each rule
If you aren't already working with the HealthVault team then you can reach us via either of the support options listed at http://msdn.microsoft.com/en-us/healthvault/bb870258.aspx.
If you are using Optional Access in your HealthVault authorization rules, then at times you may want to know whether a particular user has authorized an optional piece of the data type access. You can find out what actions are currently authorized for a {user, record, app} by calling PersonInfo.SelectedRecord.QueryPermissions. You can read more about this method at http://msdn.microsoft.com/en-us/library/microsoft.health.healthrecordinfo.querypermissions.aspx. You can also use a similar method, QueryPermissionsByType, http://msdn.microsoft.com/en-us/library/microsoft.health.healthrecordaccessor.querypermissionsbytypes.aspx.
HealthVault does *not* allow an application to ask what access a particular user has to a particular record, but it can ask what that user has authorized for use in this application.
I hope that clears things up a little.