Thursday, April 19, 2018

Additional SMART on FHIR Authorizations


I sent something similar to this to the HL7 Security workgroup today:

This strikes me as an interesting use case for SMART or similar OAuth2 based authentication.

The use case is thus:

Through an API, a user wants to include certain kind of information into the record.  A given example might be an uncoded medication.  The provider organization wants to ensure that any uncoded medications have a SEPARATE provider authorization before entry, specific to the API call that attempted the request.  For example, in a system that I am familiar with, if using the EHR client, when an uncoded medication is attempted to be entered, the provider is requested to verify their intent (via a button click).  In another case, to “sign” a document, it requires them to re-enter their password information.

An authorization protocol that might work with a server and 3rd party applications wishing to adhere to these rules would be one in which:

API call which failed due to these sorts of rules would generate a user override authorization request token and pass it back to the caller in an extension to the OperationOutcome resource.  Such a token may also need to be communicated in an out-of-band manner to an authorization endpoint (but could also just be a signed JWT).  The code associated with the OperationOutcome might indicate that additional user authorization is required.

The application would call the authorization server with this token to request user authorization to complete the API request via a web-based UI that used redirection much in the same way that SMART on FHIR does today.  It might also include its current user's access token, although that seems unnecessary since the token supplied by the application would seem to be enough validation of the user.

The authorization server would interact with the user to request their authorization to complete the request.  On receipt of the user authorization (either by clicking yes, or reauthenticating, or whatever), it would provide an override token to the calling application.  Such token could then be passed in to a repeat of the API call.  The API call would find the same problem, but having been given the override token, would be able to accept the request.

Such a protocol might also include in the given override request token a signal with regard to the degree of user reauthentication/authorization necessary.  For example, many systems that have a physician “sign” a document, require the physician to reauthenticate to the application, whereas other override responses are simply a button click (e.g., uncoded medication).

An application that is aware of these special cases might also want to have a way to short circuit the API failure part of the request loop, and preemptively obtain provider authorization.



Your thoughts?  Is this a reasonable thing to consider?



   Keith

Monday, April 16, 2018

If I'm baffled by this, how can patients possibly manage?

I have passwords I use about once a quarter for five different health accounts so that I can track what is going on with myself and my family.  Two Patient portals, one mail-order prescription company, my insurer, my HSA and God alone only knows what else.  I sit through five different sets of menu options whenever I have to call these people one the phone.

Sometimes I can access my children's data, sometimes I cannot.  Usually the latter unless my children have remembered to allow me access (which they willingly do), because one is an adult and the other is over 13.  I could probably have any person in my house claim to be them when I call one of these organizations AND FINALLY GET to a human, but always make sure to have them available.

In trying to follow up on a refill where I know the drug, the dose, the quantity, the ordering provider, and even the providers order number, I cannot get access to whether an order has been placed.  My children's healthcare providers are almost as baffled as I am by the systems they must use.

If trained and aware people are challenged by this system, how could the average patient cope?  How would a disabled patient have any ****ing chance of working through this?  This ISN'T about technology.  Technology could solve these problems.  It's about interpretations of policy that make it difficult for anyone to do anything even remotely difficult.  It's about appropriate use of technology that makes my life better, not yours.  It's NOT about paying attention to your stuff first instead of mine.

Does anyone EVER LIVE test tree menus on automated call systems?  I've had to enter account number and zip code twice, enter my phone number (even though I'm not blocking them from receiving that information), and then when I finally get to a human, repeat the same information, and then be routed to the right person to help me and repeat it AGAIN. Then I have to go through a standard set of three questions they ask on every call that have NOTHING to do with the reason I'm calling.  Yes, this is my address, no it has not changed, and yes you have my current information.  Where is the patient service here?  It's not in my immediate service to make sure you have up to date information, that is NOT my #1 reason for calling you today.  Can we solve my problem first before we make sure that everything else is ok?

My God, today, when I'm home sick and still have to deal with their mess, my patience for how patients are treated is 0, and I just want to )_*(&*(&%*!~ scream.

Payers and providers: here are some things that you can do to fix your systems:

  1. Make your call 911 message as short and direct as possible.  I understand and appreciate why it is there, but I don't call you when I should call 911, and having to wait for you to draw it out drives me crazy.
  2. Let me hit the button I know I need before you finish your first message.  It should be: If you are a current patient/member please press 1.  Get me in the door quickly to solving MY problem.
  3. Solve my problem first, then yours (e.g., has my contact info changed), and NOT the other way around.
  4. If I ask for an operator or hit 0, take me to an operator, not another menu.
  5. Yes, I know you have an online system, IT DIDN'T HELP ME, which is why I'm calling you.  Don't frustrate me further with that information until I'm on the path to queue I need, or at least let me get there quickly.  Making me wait through that message doesn't solve my problem.
  6. Make it easy for me and my family to allow me (or some other individual) to act on their behalf.
  7. Thank you for trying to protect my privacy.  Stop gloating about the great job you are doing and help me solve my problem.  Another message I want to hear AFTER I'm in the right queue and have to wait for the next available operator.


   Keith

I swear I want one of those answering machines I can program myself, just to make you feel my pain when you call me.

Thank you for calling the Boone residence.  If you are a family friend you have called the wrong unpublished number.  Please hang up and call the other number we gave you, or text the person you wish to reach.  If you are a member of the police or fire or other public safety department, please press 1.  If you are a vendor with whom we have an account please press 2. If you are a vendor or charitable society that we have not contacted, please hang up and do not call again.  This phone is on a do not call list, and repeated attempts to call this number will result in criminal prosecution. 3. If you are a healthcare provider, a health insurer, a pharmacy or PBM program with whom a member of this household has an affiliation please press 8 now.  *8*  If you need immediate ...

Thank you for contact the Boone family.  If you are a healthcare provider or pharmacy please press 1.  If you are a payer or PBM please ... *1*

Please enter the extension of the person you wish to reach.  If you would like a dial by name directory, please press 1.  Or press 0 for immediate... *0*

Then we'll ask for you name, phone number and 10 digit NPI number, and if you don't have it immediately, we'll wait a few minutes and then ask you to hang up and call us back when you have it.

How will that work for you?



And to follow up, today, I had to call my homeowners.  They knew who I was BEFORE I told them.  Their claims specialist already had my policy available because they KNEW who was calling them.  And then contacts a water damage specialist for me who called me withing 15 minutes for a same day appointment.  Thank you Liberty Mutual for getting it COMPLETELY right.

Sunday, April 8, 2018

In which I finally figure out dates and timestamps in FHIR

The key is in the last sentence of my last post (half the reason I do these things is to think out loud).  Two DIFFERENT things.  A timestamp is a date and a timestamp.  A date is just a date.  When you compare a date to a timestamp, you are comparing ONLY the date aspect.  When comparing times, you are getting into the timestamp aspect.

User expectations met, problem resolved.  I may have to think about saving the date only representation of a timestamp for efficiency reasons.

    Keith

Right, Wrong and Right again about TimeZones in FHIR

So I am back to dates again.  I forgot to check in the change for setting the servers default time zone, and almost all my unit tests failed on a build machine located outside of my home zone.  Dang it.  I KNEW that would happen.

So I made the fix (following the advice of reasonably smart people), and set the server's default time zone to UTC.  And my date tests are still failing.

Here's the failure:  There's a record that updated on 2017-06-26T17:51:45.233-07:00 (the source of that record is on the left coast, it doesn't matter where the server is).

And I query for it by asking for records updated on that date using _lastUpdated=2017-06-26.

What should happen?

Well, everyone I asked who answered without much thought say that these should match. But they don't.

Because 2017-06-26T17:51:45.233-07:00 is 2017-06-27T00:51:45.233Z in UTC.  And clearly 2017-06-26 is not the same date once you transform the timestamp to UTC, and therefore _lastUpdated=2017-06-26 fails.

The principle of least surprise should apply, and if we use a fixed time zone for the server, it surely doesn't.  Especially a case where the server time zone isn't one my users care about.

So, the conclusion I've come to is, when comparing two dates:

IF both specify precision, the comparison proceeds based on the time as reflected by each date, as it was specified.  There's no problem here.  If neither specifies a time zone, any time zone will do, as long as it is the same for both.

Here's the tricky bit: if A does not specify a precision but B does, the comparison should be based on a common time frame, either A specified a TimeZone and B didn't, or A didn't and B did.  In the presence of a time zone in A or B but NOT both, they should be compared using the same zone.  Otherwise, someone is going to get an unexpected result.

It's that odd duck case where the user asked for a particular date, the server has a specific date and time zone specified for the date (which may be different from where the server itself is located), and boom.

Otherwise, imagine what could happen.  Any test without a time zone is by necessity, imprecise.  I think it's better to specify what should happen in these cases quite specifically, so that time based comparisons don't surprise anyone.

Date has a legal function, time stamps a technical one.  Comparing a DateTime might be used for either case.  So, in my view, even though it seems weird, one has to consider that a DateTime comparison WILL behave differently when using 2017-06-27T00:00+00:00 as compared to 2017-06-27.  The questions are different.  The first is about time, the second about a date, and the two are DIFFERENT but related things.

Ah well.  Another day goes by.

     Keith

P.S. A similar problem will show up when comparing durations. Did it happen in the last two days or in the last 48 hours are two DIFFERENT questions, just as when I speak to my colleagues in India yesterday, today and tomorrow mean different things to each of us, depending on where we are in the world.  Let's not even talk about business days ...

Friday, April 6, 2018

The FHIRWalker Interface

The DOM has the TreeWalker interface, I would suggest that FHIR would benefit from a FhirWalker interface which would allow a resource, datatype or component to be walked in a traversal and processed in certain ways.  Definition of such an interface would benefit many.  Already such interfaces about in implementation code, e.g., in HAPI there are multiple parsers and readers that do the very thing that my FhirWalker interface would support.

Two such walkers would be valuable, one which walked a structure definition for a prospective resource, and the other which walked an actual resource.

Such walkers could be used for reporting, query implementation, et cetera.

I've personally written three, four, (I've now lost count) of these for various purposes.  I got fed up with writing one more and wrote a Walker class for myself.  It looks something like this (though I must admit to some artistic license in the names of things.

interface FhirWalker() {

   // Step describes the interface of the methods that can be
   // performed with the Victim of the FhirWalk.  It's not really
   // used in any way by walk or walkBackwards, but might find 
   // some use in implementations.
   interface Step implements BiFunction<String, Any, Boolean>;

   // Walk defines the interface for a traversal. Like 
   interface Walk implements BiConsumer<Any, Victim>;

   interface Victim {
       // tremble is called before walking on any coal.
       // if tremble returns true, the processor chickened out.
       boolean tremble(String path, Any coal) throws FhirWalkerTrippedException;

       // stumble is called on anything that caused a problem.
       // think of it an an exception handler.  If it returns
       // true, the victim recovered.  Otherwise, the victim 
       // fell down and help needs to be called (an exception thrown)
       // immediately.
       boolean stumble(String path, Any coal) throws FhirWalkerTrippedException;

       // burned is called after walking on a coal.
       boolean burned(String path, Any coalthrows FhirWalkerTrippedException;
   }

   public class FhirWalkerTrippedException {
       public FhirWalkerTrippedException(
           String exclamation, Any coalStumbledUpon, Throwable excuse
       );
   }

   // Walk the FHIR tree in order
   public void walk(Any startingCoal, Victim victim) 
      throws FhirWalkerTrippedException;

   // Walk the FHIR tree in reverse order
   public void walkBackwards(Any startingCoal, Victim victim)
      throws FhirWalkerTrippedException;

   public void walkCanonically(Any startingCoal, Victim victim)
      throws FhirWalkerTrippedException;
}


The walk() method implements a NLRN traversal, calling tremble before processing a node, then processing the children left to right, then calling burned after processing the children.  The walkBackwards() method implements an NRLN traversal, calling tremble first, and burned last. 

Tremble and burned have the same signature even though the return on burned is ignored (once burned it twice shy?)  The reason for that is so that a function used for burned in one place can be
used for tremble in a different traversal.

For what it is worth, something implementing the FhirWalker interface need not traverse an entire tree of a FHIR Resource.  It could implement a traversal of selected subnodes.  For example, a DataTypeWalker might only call tremble and burned on FHIR Data types found, a PrimitiveWalker only on primitives.  A whole host of walkers could be created with great benefit.

Somewhere in the back of my head is a BlindfoldedWalker but I don't know where that's going.

Anyway, enough amusement for the day.  I have my own walker now, time to go for a stroll.

   Keith


Tuesday, April 3, 2018

FHIR date Equality in a Global Environment

Under what circumstances can someone ask a question that can mean one thing in one part of the country and yet something entirely different in another?  When what they are talking about is time.  Just ask your colleague from the left or right coast what time it is, and you'll see they come up with a different answer than you do.

Here's the challenge:  A FHIR Server hosts a number of resources, and each of them have an associated timestamp in Resource.meta.lastUpdated.  So what should you get when you ask for resources that have been updated recently?  Well, frankly it depends upon how you ask.

If you give a date only with no time zone, then the answer you SHOULD get back is the based on the date you supply, as interpreted in the context of the resource that has the timestamp.

Consider: Resource A has timestamp given in terms of Eastern Standard Time.  Resource B has the same timestamp, but using Pacific Standard Time.  Resource C has the same timestamp, but using India Standard Time.  If C was updated today, is it also true that A and B were?  Not for certain, at least according to my thinking.

The answer is, it depends.  First of all, it cannot matter to the server where you are if you don't tell it, so if you give a query based on a date (and thus without a timestamp), it will have to use what it knows, which is whatever timezone it uses for local reference.  Some servers may set local reference time to UTC, others while most would use the local time zone, yet others may have standardized on the local time at wherever headquarters happens to be.  Even so, the variations don't matter, what matters is if you say nothing, the server has to interpret the value. If you do happen to tell the server where you are (e.g., by giving a timestamp with a timezone -- and thus hours and minutes at the very least), then it should interpret time according to what you told it.  So far, so good.

Next up.  But what about what the resource say?  Why does this matter you might ask.  Well, the server and the resource may not agree on what should be in the timezone of a timestamp.  In fact, if your server is just a raw FHIR repository of data, with some applications storing data, and others reading it, then it is the application which decides the timezone associated with the timestamp.  So, if you are with me so far:

Resource A was created a T in EST, B at T - 3 hours measured in PST, and C at T + 10:30 hours.  A time span measuring 13 and 1/2 hours.  More than half the time of the day, the crossover from one day to the next will be between resource B and Resource C's location.  And Resource A will be on one side or the other (it can get worse: If you have your resources on Baker Island, New Zealand and the Line Islands, where for a couple of hours, each could be stamped with a different day).  So NOW what?

If comparing by date alone, the resource date could actually be the one to rule.  If comparing my day to the resource's day, where we disagree on what the time zone is, how is a body (or a server) to compare the two.  We could agree to use the servers time as the point of arbitrage, and the problem would be solved. 

Or would it? How do you commit baseline test results for your server codes unit tests when you have developers in all three zones running them on a local server?

**** if I know anything other than a completely arbitrary answer.  If you have a better one, I'd love to hear it.

    Keith





Monday, April 2, 2018

:not in the presence of multiple values in FHIR

When a resource can have multiple values of a particular type (e.g., identifier, code, _tag, _security), one of the questions that has come up for me was the interpretation of :not.

This is made more challenging by virtue of the fact that different implementations handle it differently.

From a use case perspective, which is more interesting:

Given that patients often have multiple identifiers would you rather than:

Patient?identifier:not=999999999

Return a patient that did not have any identifier=999999999, or would you rather it returned a patient if any one of their identifiers was not 999999999?  Frankly, I want the former.

Similarly for code, if Condition has an ICD-10 and SNOMED code, would you ask for Conditions that are not SNOMED code for Heart Attack to include all conditions that had any other code, or that ensured NO value of Code was the SNOMED code for heart attack.  I want the latter.

This is the rationale behind a recent clarification in FHIR STU4, coming hot and heavy off the presses (see GET [base]/Composition?section:not=48765-2).

I'm all in favor of this, although I must admit to having had some struggles interpreting it.  That's what ballots are for though.

     Keith