My GSoC Experience So Far With Data for the Common Good
July 17, 2026 • 6 min read
If you live in the United States, your medical records are scattered across every hospital, clinic, and lab you've ever walked into. Each one keeps its own copy in its own system, and if you wanted your complete history in one place tomorrow, you would have no easy way to get it. That isn't a technology gap, because the standards for moving this data already exist. Somebody just has to do the work of speaking to every one of those systems.
I'm halfway through Google Summer of Code, a program that matches contributors with open source organizations for a summer of focused work, and I'm spending mine with Data for the Common Good, a group at the University of Chicago that builds platforms to pool health data so researchers can actually use it. Their Pediatric Cancer Data Commons holds the largest harmonized set of childhood cancer data in the world.
I'm building the backend that lets a patient pull their own records out of whatever US hospital system happens to hold them, with their permission. I say building, but rebuilding is closer to the truth, because what I inherited was a proof of concept that logged a patient in and then threw away everything it got.
What my mentor, Luca Graglia, and I decided to build around that login, and especially the code we chose not to write, is the most interesting part of this project.
The standard that makes this possible
Before the story continues, let me introduce the three terms everything else stands on.
An EHR (Electronic Health Record) is the software a hospital keeps its patients' charts in, covering diagnoses, medications, lab results, and visit history. A few dozen vendors dominate the US market: Epic, Oracle Health (formerly Cerner), athenahealth, and a long tail behind them.
Just like every email program agreed on what an email looks like, FHIR (say "fire") is that same agreement for medical data. It breaks a chart into resources, each one a JSON object of a known type. Examples of these objects are a Patient, a Condition for a diagnosis, or an Observation for a lab result. Each hospital serves its FHIR data from its own web address, and that address is called an endpoint.
SMART on FHIR is the login layer on top of that format. It is OAuth 2.0 with healthcare rules bolted on. It's the "Log in with Google" pattern you've used a hundred times, except this time you're logging into your hospital. The app sends you to the hospital's own login page, you approve what's being asked for, and the hospital hands back a short one-time code that the backend quietly swaps for an access token. That final exchange is what the whole flow exists for: the backend ends up holding a token, never your password, and you approved exactly what it can read.
Here's that flow from start to finish, with the part I built:
One adapter, not one per vendor
There are tens of thousands of those endpoints in the United States, so how many separate integrations should it take to talk to all of them? The obvious answer that will come to one's mind is one per vendor: an EpicProvider class, a CernerProvider class, and a new one every time somebody asks for their hospital. However, I didn't write any of them.
I could get away with that because those endpoints aren't tens of thousands of different systems. A few dozen vendors build the software behind all of them, and every one of those vendors implements the same standard. That standard requires each server to publish a discovery document at a fixed address, /.well-known/smart-configuration, where it describes itself: its login URL, its token URL, the authentication methods it accepts, and whether it wants PKCE (more on that shortly).
So I wrote one adapter, a class called GenericSMARTProvider, that reads that document at connection time and adapts to whatever it finds. Nothing about any vendor is hardcoded anywhere, and adding a new hospital system is a configuration entry rather than new code.
Does the idea survive contact with real servers? So far it has. I tested it against three servers chosen to be as different from one another as possible. Epic is a confidential client, meaning it holds a secret it must present when it trades the code for a token, while the SMART Launcher is a public client with no secret at all, leaning entirely on PKCE. That's about as structurally different as two SMART servers get, and both run live end to end through the same unchanged code. Oracle Health's sandbox follows the same path against its real discovery document, though its final token exchange is simulated in my tests, because a live login needs an Oracle registration I don't have yet. Three servers, zero vendor classes.
PKCE turns on because the server says so
PKCE (say "pixie") protects the moment the hospital hands back that one-time code. At the start of a login, my backend invents a big random secret called the verifier and sends out only a fingerprint of it, a hash that can't be run backwards to reveal the secret. At the token step, it presents the original verifier, the server re-computes the fingerprint, and the exchange is refused unless the two match, so intercepting the code alone gets an attacker nothing.
Here's the part I like the most. My code never decides whether to use PKCE - the server does. I gate it on whether S256 shows up in the discovery document's code_challenge_methods_supported list, and if the server advertises it, PKCE is on.
I made a smaller decision in there that mattered more than it looks. The verifier is born when the login starts but isn't needed until the token exchange, a separate request entirely, so it has to survive in between. I have build_auth_url hand back an AuthorizationRequest carrying the URL and the verifier together, which lets the backend park that secret in its own database and never let it out. If the function returned just the URL, the verifier would have to make the trip some other way, and the tempting answer is to send it along with the user, which would defeat the entire point of the protection.
All of that back-and-forth exists to win a prize - the tokens. The original backend I inheritedused to throw that prize away. There was no form of persistence.
The tokens survive now
The proof of concept I inherited would finish the entire login correctly, return {"success": true} to the browser, and drop the tokens, so every login started from nothing.
I store them now, encrypted with Fernet, and stored in the database. The EncryptedString column encrypts on write and decrypts on read, so the rest of the code just sees ordinary strings.
The encryption key also has no default, so leaving it unset means the app refuses to start, loudly, rather than do something unsafe in silence.
Access tokens expire, and a refresh token is how you get a new one without dragging the patient back through a login. I wrote a refresh_token() method for exactly that, and it works, but nothing calls it yet because we have no production systems ready to test😅.
The government feed went down
One more story from this half comes from the step before any login begins: a patient has to tell us which hospital they're with, so we hand them a list to choose from. That list comes from LANTERN, a directory of FHIR endpoints maintained by the US government. At a point, their download URL started returning 404, the web's not-found error, so I repointed the backend at the GitHub mirror, and then the mirror was cleaned out too a few days later.
That experience taught me not to treat a data source I don't control as though it were reliable. The final fix we ended up going with to ensure robustness was to make the backend serve the newest file it had so it could degrade gracefully instead of failing outright.
What's next
The biggest piece left is normalization. Every EHR returns the same resource type in a slightly different shape, and a researcher pulling data shouldn't have to know or care which hospital it came from. That means parsing everything into one consistent shape and fetching the US Core mandatory set first, instead of all sixty resource types every time, which is what the code does today. After that comes smarter provider search, and then more hospital systems, each one arriving as a configuration entry rather than new code.
None of this would have gone the way it did without Luca, who's been consistently willing to let me take full ownership of a design decision and discuss with him afterwards..
I am looking forward to the second half!