Partner API - Context Management
Our Partner API context management capability allows our users to initiate your application so that it can obtain tokens in order to call our APIs on behalf of the user.
How it works
Section titled “How it works”Point of operation:
Desktop - your application is desktop based and we initiate it to provide Patient/User context
How we launch your application for users
Section titled “How we launch your application for users”Firstly we need to get you configured, so see Onboarding. When we get you onboarded, we will give you some details that you need to use later.
As part of onboarding we also will ask you to provide us a couple of URLs:
launchUrl- address of the launch handler that you implement, you will need to make some calls back to us using the details we provided in this handler in order to obtain tokens.redirectUri- address of the auth handler that you implement to complete the authorization code flow.
As an example, if your custom URI scheme handler was available at
partnerapp://partnerapplication, your launch handler was /launch and your
auth handler was /auth then your registered URLs would be:
launchUrl-partnerapp://partnerapplication/launchredirectUri-partnerapp://partnerapplication/auth
When one of our users initiates your application, we will append some query
parameters to the launchUrl:
partnerapp://partnerapplication/launch?iss=XXXXXXX&launch=XXXXXXX and then
make a GET request to it.
How you obtain the tokens
Section titled “How you obtain the tokens”You will need to use the Authorization Code + PKCE flow. This flow consists of these key steps:
- Generate a code verifier and code challenge
- Request an authorization code
- Exchange the authorization code for tokens
Step 1: Generate a Code Verifier and Code Challenge
Section titled “Step 1: Generate a Code Verifier and Code Challenge”The Authorization Code + PKCE flow requires a code verifier (a random string) and a code challenge (a transformed version of the verifier). The code verifier should be:
- A cryptographically random string
- Between 43 and 128 characters long
- Can contain letters, digits, underscores, hyphens, and periods
# Example of generating a code verifier with OpenSSLCODE_VERIFIER=$(openssl rand -base64 96 | tr -d '\n' | tr '+/' '-_' | tr -d '=' | cut -c 1-96)# Example result: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXkThe code challenge is derived from the code verifier using SHA-256 and Base64URL encoding:
# Example of generating a code challenge with OpenSSLCODE_CHALLENGE=$(echo -n "$CODE_VERIFIER" | openssl dgst -sha256 -binary | base64 | tr '+/' '-_' | tr -d '=')
# Example result: E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cMStep 2: Request an Authorization Code
Section titled “Step 2: Request an Authorization Code”Construct an authorization URL with your application details and the code challenge:
curl -X POST "https://XXXXXXX.com/XXXXXXX/XXXXXXX/oauth2/v2.0/authorize" \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "client_id=XXXXXXX" \ --data-urlencode "response_type=code" \ --data-urlencode "redirect_uri=partnerapp://partnerapplication/auth" \ --data-urlencode "response_mode=query" \ --data-urlencode "scope=XXXXXXX" \ --data-urlencode "code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM" \ --data-urlencode "code_challenge_method=S256" \ --data-urlencode "state=XXXXXXX" \ --data-urlencode "launch=XXXXXXX"We will validate the launchID and other parameters, and if valid, will redirect back to your auth handler with an authorization code:
partnerapp://partnerapplication/auth/auth?code=XXXXXXX&state=XXXXXXXStep 3: Exchange the Authorization Code for Tokens
Section titled “Step 3: Exchange the Authorization Code for Tokens”Once you have the authorization code, you can exchange it for tokens using a POST request to the token endpoint:
curl -X POST '<https://XXXXXXX.com/XXXXXXX/XXXXXXX/oauth2/v2.0/token>' \ -H "Content-Type: application/x-www-form-urlencoded" \ --data-urlencode "grant_type=authorization_code" \ --data-urlencode "client_id=XXXXXXX" \ --data-urlencode "scope=XXXXXXX" \ --data-urlencode "code=XXXXXXX" \ --data-urlencode "redirect_uri=partnerapp://partnerapplication/auth" \ --data-urlencode "code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"The response will include the tokens:
{ "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsI...", "token_type": "Bearer", "expires_in": 3600, "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp...", "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsI..."}Patient Context
Section titled “Patient Context”Within the ID token we issue to your application, we embed patient context so that you can use it as required. An example base64 decoded payload of an ID token:
{ "exp": 1744876486, "nbf": 1744872886, "ver": "1.0", "iss": "https://XXXXXXXXXXXXX/v2.0/", "sub": "XXXXXXXXXXXXX", "aud": "XXXXXXXXXXXXX", "acr": "XXXXXXXXXXXXX", "nonce": "204b601d-a7fc-440a-b863-0667d2307131", "iat": 1744872886, "auth_time": 1744872885, "email": "luke.smith@emishealth.com", "userERN": "ern:emis:user:user:XXXXXXXXXXXXX", "title": "Mr", "firstName": "Luke", "familyName": "Smith", "orgERN": "ern:emis:org:org:XXXXXXXXXXXXXd", "orgName": "BURNHAM SURGERY", "cdb": "50002", "ods": "XXXXXXXXXXXXX", "authorizations": [ "agmt-agmt.read", "clinical-cr.read", "clinical-cr.write", "doc-app.addcode", "doc-app.create", "term-prep.read" ], "authorizationDetails": "[{\"forenames\":\"Ray\",\"surname\":\"Keates\",\"gender\":\"Male\",\"dob\":\"12-Oct-1955\",\"title\":\"Mr\",\"type\":\"patient-context\",\"version\":\"1.0.0\",\"patient-identifiers\":[{\"identifierType\":\"GUID\",\"identifierValue\":\"XXXXXXXXXXXXX\"},{\"identifierType\":\"EHR Partner ID\",\"identifierValue\":36},{\"identifierType\":\"NHS Number\",\"identifierValue\":\"XXXXXXXXXXXXX\"}],\"address-details\":[{\"addressLine1\":\"\"}],\"contact-details\":[]}]", "at_hash": "aSTOTTzIbbMNHsrdmGe39g"}Note the authorizationDetails claim.
Expected flow
Section titled “Expected flow”The sequence diagram below depicts the expected flow:
sequenceDiagram
actor User as User
participant Source as EMIS-X Application
participant Client as Partner Application
participant IdP as EMIS-X Identity Provider
participant Resource as EMIS-X API
%% User initiates launch
activate User
User->>Source: Initiates launch
deactivate User
activate Source
%% Launch sequence
Source->>IdP: Request launchID
activate IdP
IdP->>IdP: Generate launchID
IdP-->>Source: Return launchID
deactivate IdP
Source->>Client: GET {launchURL}?launch={launchID}
activate Client
deactivate Source
Note over Client: Store launchID
%% PKCE preparation
Client->>Client: Generate Code Verifier
Client->>Client: Create Code Challenge
(SHA-256 hash of Code Verifier)
%% Authorization request
Client->>IdP: POST /authorize
with client_id, redirect_uri, code_challenge, launchID
activate IdP
Note over IdP: Validate launchID
(and other params)
IdP-->>Client: Return Authorization Code
deactivate IdP
%% Token exchange
Client->>IdP: POST /token
with code, code_verifier, client_id
activate IdP
Note over IdP: Verify Code and
Code Verifier match
IdP-->>Client: Return Access, ID & Refresh Tokens
deactivate IdP
Client->>Client: Decode ID token to get patient context
%% User interacts with client after auth
Client-->>User: Display authenticated interface
activate User
User->>Client: Interact with application
Note over Client: User initiates resource request
%% Using the tokens
Client->>Resource: Request with Access Token
activate Resource
Note over Resource: Validate token
Resource-->>Client: Protected Resource Response
deactivate Resource
Client-->>User: Display resource response
deactivate User
deactivate Client 
