Authentication
EMIS-X utilises modern authentication (authN) and authorization (authZ) using two mechanisms:
- SAML
- OAuth/OIDC
Both of these mechanisms are provided by the EMIS-X AuthN & AuthZ solution (also referred to as EMIS-X Users, a misnomer).
How we determine the authentication mechanism
Section titled “How we determine the authentication mechanism”There are various options and flows for each mechanism. The following process flow is how we determine which of them to use.
flowchart TD
start([Authentication Protocol Selection]) --> app_type{Application Type?}
%% Define implementation node (replacing reserved 'end' keyword)
implementationNode([Protocol Implementation])
%% Initial application type decision
%% Simplified application type paths
app_type -->|Web| api_access{API Access Needed?}
app_type -->|Mobile/SPA/Desktop| api_access{API Access Needed?}
%% Common path for all user application types
api_access -->|Yes| oidc[OpenID Connect]
api_access -->|No| sso_needed{SSO Needed?}
sso_needed -->|Yes| sso_type{Only SAML supported?}
sso_type -->|Yes| saml[SAML 2.0]
sso_type -->|No| oidc[OpenID Connect]
sso_needed -->|No| oidc[OpenID Connect]
%% Machine-to-Machine paths - kept separate
app_type -->|Machine-to-Machine| oidc[OpenID Connect]
%% Protocol descriptions - removed intermediate "Consider" nodes
%% Keep description annotations for protocols
saml:::protocol
oidc:::protocol
%% SAML Flows
saml --> saml_initiated{Initiation Type?}
saml_initiated -->|"SP-Initiated
(More secure, user starts at SP)"| saml_sp[SP-Initiated Flow]
saml_initiated -->|"IdP-Initiated
(Simpler, user starts at IdP)"| saml_idp[IdP-Initiated Flow]
%% OIDC Flows
oidc --> oidc_flow{Client Type & Requirements?}
oidc_flow -->|"Web App
(User)
(Confidential Client)"| oidc_auth_code[Authorization Code Flow]
oidc_flow -->|"Mobile/SPA/Desktop
(User)
(Public Client)"| oidc_pkce[Authorization Code Flow with PKCE]
oidc_flow -->|"Machine-to-Machine
(User)
(Confidential Client)"| oauth_password[Resource Owner Password Flow]
oidc_flow -->|"Machine-to-Machine
(No User)
(Confidential Client)"| oauth_client_cred[Client Credentials Flow]
%% Decision factor annotations - using implementationNode instead of 'end'
oauth_client_cred -->|"Secure: Yes
Token Types: Access only
User Present: No"| implementationNode
oauth_password -->|"Secure: No
Token Types: Access, Refresh
User Present: No"
For automated testing only| implementationNode
oidc_auth_code -->|"Secure: Yes
Token Types: ID, Access, Refresh
User Present: Yes"| implementationNode
oidc_pkce -->|"Secure: Yes
Token Types: ID, Access, Refresh
User Present: Yes"| implementationNode
saml_sp -->|"Secure: Yes
Token Types: SAML Assertion
User Present: Yes
Initiation: Service Provider"| implementationNode
saml_idp -->|"Secure: Moderate
Token Types: SAML Assertion
User Present: Yes
Initiation: Identity Provider"| implementationNode
For the vast majority of partners, we expect to use OAuth/OIDC and the
Authorization Code Flow with PKCE and Client Credentials Flow.
OAuth/OIDC
Section titled “OAuth/OIDC”We will not detail OAuth/OIDC here, there are plenty of resources available for that. Instead we will detail the pertinent points of our implementation.
Tokens
Section titled “Tokens”Our tokens are transparent JSON Web Tokens (JWT) that are self-contained:
- They contain all of the necessary information within the tokens in claims.
- The information within them can be trusted as they can be cryptographically verified to ensure they have been issued by us and have not been tampered with.
- The tokens are signed, but not encrypted. They can be decoded and the claims viewed.
Our authN & authZ is therefore stateless and does not require further lookups to other services to make authentication decisions.
Depending on the flow used and requests made, we will potentially issue 3 types of token:
- ID - can be used by applications as proof of authN
- Access - passed to our APIs when making requests
- Refresh - used to obtain new ID and/or Access tokens when they have expired
Token Expiry
Section titled “Token Expiry”The ID & Access tokens we issue currently have a 1 hour validity but we may change this at any time.
Application Restricted vs User Restricted
Section titled “Application Restricted vs User Restricted”Our authentication solution will issue one of two kinds of access token depending on the flow used to obtain it. We class these as User Restricted & Application Restricted. The key difference between these is that one is context of the user using the application that obtained the token and the other is in context of the application itself.
- User Restricted - the requester ends up with an access token that has user context, the bearer of the access token is acting on behalf of the user.
- Application Restricted - the requester ends up with an access token that does not have user context, the bearer of the access token is acting on behalf of itself.
The payload of an example User Restricted access token:
{ "aud": "XXXXXXXXXXXXX", "iss": "https://XXXXXXXXXXXXX/v2.0/", "exp": 1741106843, "nbf": 1741103243, "sub": "XXXXXXXXXXXXX", "email": "luke.smith@emishealth.com", "givenName": "Luke", "familyName": "Smith", "userERN": "ern:emis:user:user:XXXXXXXXXXXXX", "title": "Mr", "authorizations": [ "agmt-agmt.read", "clinical-cr.read", "clinical-cr.write", "doc-app.addcode", "doc-app.create", "term-prep.read" ], "orgERN": "ern:emis:org:org:XXXXXXXXXXXXX", "orgName": "BURNHAM SURGERY", "roleERNs": ["ern:emis:user:role:XXXXXXXXXXXXX"], "roleNames": ["Test 50002 role"], "ods": "XXXXXXXXXXXXX", "cdb": "50002", "emisWebUserInRoleId": 28299, "nonce": "019561d7-006b-71d1-b704-3a79d618a17c", "scp": "emisweb cdb ods emis-x", "azp": "XXXXXXXXXXXXX", "ver": "1.0", "iat": 1741103243}Application Restricted access token payload:
{ "iss": "https://XXXXXXXXXXXXX/v2.0/", "exp": 1675855606, "nbf": 1675852006, "aud": "XXXXXXXXXXXXX", "appERN": "ern:emis:user:app:XXXXXXXXXXXXX", "appName": "Partner XXXXXXXXXXXXX", "orgERN": "ern:emis:org:org:XXXXXXXXXXXXX", "orgName": "BURNHAM SURGERY", "authorizations": ["clinical-cr.read", "doc-app.create", "term-prep.read"], "azpacr": "1", "sub": "XXXXXXXXXXXXX", "oid": "XXXXXXXXXXXXX", "tid": "XXXXXXXXXXXXX", "ver": "2.0", "azp": "XXXXXXXXXXXXX", "iat": 1675852006}Note the key differences, the User Restricted token includes details about the
user (userERN, roleERN, name) with the Application Restricted token only
including details about the application (appERN, appName). Both tokens
contain the authorizations claim, this array contains the permissions that our
APIs look out for in order to make authorization decisions.
Obtaining tokens
Section titled “Obtaining tokens”How you obtain tokens depends on what your solution is and how it has been configured in our authentication solution.
We highly recommend that you use the Microsoft Authentication Library (MSAL) as it performs many necessary steps for you by providing it with the necessary config.
For a quick example of how you can obtain a token in the Client Credentials Flow which can only be used for an Application Restricted access token:
curl -X POST \ 'https://XXXXXXX.com/XXXXXXX/XXXXXXX/oauth2/v2.0/token' \ --header 'Accept: */*' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'grant_type=client_credentials' \ --data-urlencode 'client_id=XXXXXXX' \ --data-urlencode 'scope=XXXXXXX' \ --data-urlencode 'client_secret=XXXXXXX'You will get a response similar to:
{ "access_token": "eyJhbGciOiJSUzI1NiIs...", "token_type": "Bearer", "not_before": 1744706741, "expires_in": 86400, "expires_on": 1744793141, "resource": "XXXXXXX"}Further reading
Section titled “Further reading”- Read the OpenAPI specification for the Partner API.
- If you are developing an application that needs to consume our APIs on behalf of our users consider looking at our application launch process.
We look forward to collaborating with you to revolutionize healthcare technology. If you have any questions or need assistance, please don’t hesitate to reach out.

