TEFCA Individual Access Services (IAS) Integration Guide

Configure your application to support TEFCA Individual Access Services (IAS) with ID.me.

Overview

The Trusted Exchange Framework and Common Agreement (TEFCA) is a U.S. healthcare interoperability framework that enables individuals to securely access their own health records across disparate networks. ID.me supports the TEFCA Individual Access Services (IAS) exchange purpose, empowering patients to request and receive their health information through apps of their choice using NIST 800-63-3 Identity Assurance Level 2 (IAL2) identity proofing.

Intended audience
Developers and IAM administrators responsible for configuring federation between a TEFCA IAS application and ID.me.

What you will build
A TEFCA-compliant patient access workflow where ID.me serves as the Credential Service Provider (CSP) issuing IAL2-verified identity tokens to your IAS application.

Result
A patient logs into your app, verifies their identity via ID.me, and grants consent to retrieve their clinical records with a single IAS request across the TEFCA network.

Authentication flow

The following steps outline the TEFCA IAS authentication flow between the patient, your application, the Qualified Health Information Network (QHIN), and ID.me:

1

The patient initiates a login to your application

2

Your application redirects the patient to ID.me for identity verification, including the TEFCA IAS policy scope

3

The patient completes authentication and NIST IAL2 identity proofing at ID.me

4

The patient reviews and grants consent to share their verified identity attributes with your application

5

ID.me issues a signed OIDC identity token containing verified demographic claims

6

Your application relays the ID.me identity token to the QHIN as part of the IAS query

7

The QHIN validates the token against ID.me’s published JWKS endpoint and recognizes the patient’s verified identity and consent

8

The QHIN securely transfers the patient’s health records (USCDI v1 minimum) back to your application

Prerequisites

Before beginning this integration, ensure you have the following:

  • Communicate to your ID.me Solution Consultant that you intend to use the Individual Access token — an IAL2-level policy will be provisioned for your application
  • A registered OID (Object Identifier) registered with HL7, required before a production client can be created
  • A registered application capable of handling the Authorization Code Grant type
  • A publicly accessible redirect URI (HTTPS required in production)
  • Familiarity with OIDC flows, JWT validation, and token-based authentication concepts

Environments

ID.me provides two environments:

Sandbox

https://api.idmelabs.com/

Production

https://api.id.me/

All ID.me OIDC endpoints are derived from the base URL above. Replace the sandbox base URL with the production base URL in every endpoint you configure before going live.

OIDC discovery endpoints

EndpointSandboxProduction
OIDC Issuerhttps://api.idmelabs.com/oidchttps://api.id.me/oidc
Discovery documenthttps://api.idmelabs.com/oidc/.well-known/openid-configurationhttps://api.id.me/oidc/.well-known/openid-configuration
Authorization endpointhttps://api.idmelabs.com/oauth/authorizehttps://api.id.me/oauth/authorize
Token endpointhttps://api.idmelabs.com/oauth/tokenhttps://api.id.me/oauth/token
JWKS endpointhttps://api.idmelabs.com/oidc/jwkshttps://api.id.me/oidc/jwks
UserInfo endpointhttps://api.idmelabs.com/api/public/v3/attributes.jsonhttps://api.id.me/api/public/v3/attributes.json

Configure your application

Register your application with ID.me

Before configuring your identity provider settings, you must register your application with ID.me to obtain OAuth 2.0 credentials.

1

Contact your ID.me Solution Consultant and communicate that you intend to use the Individual Access token. Provide the following details:

  • Application name: The display name patients will see during the consent screen
  • OID: Your HL7-registered Object Identifier (required for production)
  • Redirect URIs: One or more HTTPS callback URLs your application will use after authentication (see Redirect URIs below)
  • Requested scopes: At minimum openid plus the TEFCA IAS policy scope handle (provided by your Solution Consultant)
  • Environment: Sandbox, production, or both
2

ID.me will provision an IAL2-level project and policy for your application, then return:

  • client_id: Your application’s unique identifier, formatted as a URN OID (e.g., urn:oid:1.2.3.4.5.6)
  • client_secret: Your application’s secret credential (store securely — never expose in client-side code)
3

Store your credentials securely in your application’s secrets management system. You will use these in the token exchange step of the Authorization Code flow.

Redirect URIs

Redirect URIs must be pre-registered with ID.me. ID.me will reject any authorization request that specifies an unregistered redirect_uri.

Requirements:

  • Must use HTTPS in production (HTTP is permitted in sandbox for localhost only)
  • Must be an exact match — wildcard URIs are not supported
  • Mobile apps may use custom URI schemes (e.g., com.example.app://callback) if registered

Configure ID.me as an OpenID Connect identity provider

With your client_id and client_secret in hand, configure your application to initiate the Authorization Code flow against ID.me.

Authorization request

Direct the patient to ID.me’s authorization endpoint with the following parameters:

Example
$GET https://api.idmelabs.com/oauth/authorize
$ ?response_type=code
$ &client_id=urn%3Aoid%3A1.2.3.4.5.6
$ &redirect_uri=https%3A%2F%2Fyourapp.example.com%2Fcallback
$ &scope=openid+YOUR_TEFCA_IAS_POLICY_SCOPE
$ &state=RANDOM_STATE_VALUE
$ &nonce=RANDOM_NONCE_VALUE
ParameterRequiredDescription
response_typeYesMust be code for Authorization Code flow
client_idYesYour OID-formatted client ID (e.g., urn:oid:1.2.3.4.5.6)
redirect_uriYesMust match a pre-registered URI exactly; URL-encode the value
scopeYesSpace-separated list including openid and your TEFCA IAS policy scope handle
stateYesRandom opaque value used to prevent CSRF attacks; validate on return
nonceRecommendedUnique per-session value embedded in the id_token; validate to prevent replay attacks

Upon completion of the verification flow, ID.me redirects the patient back to your redirect_uri with an authorization_code. Parse this code from the URL and use it to perform the token exchange.

Token exchange

Exchange the authorization code for tokens using the token endpoint:

Example
$curl --request POST \
> --url https://api.idmelabs.com/oauth/token \
> --header 'Content-Type: application/x-www-form-urlencoded' \
> --data-urlencode 'grant_type=authorization_code' \
> --data-urlencode 'code=AUTHORIZATION_CODE' \
> --data-urlencode 'redirect_uri=https://yourapp.example.com/callback' \
> --data-urlencode 'client_id=urn:oid:1.2.3.4.5.6' \
> --data-urlencode 'client_secret=YOUR_CLIENT_SECRET'
ParameterDescription
grant_typeMust be authorization_code
codeThe authorization code returned to your redirect URI
redirect_uriMust exactly match the URI used in the authorization request
client_idYour OID-formatted client ID
client_secretYour application secret

Verification result types

The token response varies depending on whether identity verification was successful.

Successful verification — ID.me returns an access_token, id_token, and refresh_token. The id_token is only issued when the patient’s demographics have been fully validated to IAL2. Use the id_token to relay the patient’s identity to the QHIN.

Example
1{
2 "access_token": "eyJ...",
3 "token_type": "Bearer",
4 "expires_in": 300,
5 "id_token": "eyJ...",
6 "refresh_token": "eyJ..."
7}

OIDC identity token (id_token)

ID.me issues an id_token signed using RS256. The token payload follows standard OIDC claim names and includes TEFCA-required demographic attributes. The aud claim reflects your OID-formatted client_id.

Example
1{
2 "iss": "https://api.idmelabs.com/oidc",
3 "sub": "AbCdEfGhIjKlMnOpQrStUvWxYz0123456789",
4 "aud": ["urn:oid:1.2.3.4.5.6"],
5 "iat": 1700000000,
6 "exp": 1700003600,
7 "auth_time": 1700000000,
8 "nonce": "RANDOM_NONCE_VALUE",
9 "jti": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
10 "sid": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
11 "ial": "2",
12 "given_name": "JANE",
13 "middle_name": "MARIE",
14 "middle_initial": "M",
15 "family_name": "DOE",
16 "birthdate": "1985-04-12",
17 "gender": "F",
18 "email": "jane.doe@example.com",
19 "phone_number": "+15551234567",
20 "phone_number_verified": true,
21 "address": {
22 "country": "US",
23 "formatted": "123 MAIN ST, APT 4B, SPRINGFIELD, IL 62701 US",
24 "locality": "SPRINGFIELD",
25 "postal_code": "62701",
26 "region": "IL",
27 "street_address": "123 MAIN ST, APT 4B"
28 },
29 "historical_address": [
30 {
31 "formatted": "456 OAK AVE, CHICAGO, IL 60601",
32 "locality": "CHICAGO",
33 "postal_code": "60601",
34 "region": "IL",
35 "street_address": "456 OAK AVE"
36 }
37 ]
38}

The exact claim names returned depend on your ID.me policy configuration. Work with your ID.me Solution Consultant to confirm which attributes your policy handle returns and how they map to the TEFCA required demographics.

Historical addresses

The historical_address claim returns an array of previously verified addresses for the patient. This data can improve patient matching rates with QHINs and health systems that cross-reference multiple address records. To enable historical addresses in your token, contact your ID.me Solution Consultant.

Required TEFCA IAS demographics

Per the Sequoia Project IAS SOP, the following patient demographics must be validated to IAL2 and present in the token:

AttributeOIDC ClaimRequiredNotes
Legal first namegiven_nameYes
Legal last namefamily_nameYes
Date of birthbirthdateYesYYYY-MM-DD format
Street addressaddress.street_addressYes
Cityaddress.localityYes
Stateaddress.regionYes
ZIP codeaddress.postal_codeYes
Sex / gendergenderNoRecommended
Middle namemiddle_nameNoRecommended
Middle initialmiddle_initialNoRecommended
Email addressemailNoRecommended
Mobile phone numberphone_numberNoRecommended
Historical addresseshistorical_addressNoRecommended; improves matching

Validate the token

Before relaying the identity token to a QHIN, validate it against ID.me’s public key. Fetch the JWKS from the endpoint below and use a standard JWT library to verify the token signature using the key matching the token’s kid header value.

Example
$GET https://api.idmelabs.com/oidc/jwks

When validating, confirm all of the following:

  • The token signature verifies against the JWKS public key
  • The iss claim matches ID.me’s issuer URI for the environment (sandbox or production)
  • The aud claim matches your registered client_id
  • The exp claim has not elapsed (token has not expired)
  • The iat claim is in the past
  • The nonce claim matches the value sent in your authorization request
  • The ial claim equals "2", confirming IAL2 identity proofing

Never relay an identity token to a QHIN without first completing full token validation. An invalid or expired token will be rejected by the QHIN and may expose your integration to replay attacks.

Test the integration

Use the ID.me sandbox environment (https://api.idmelabs.com/) to validate your integration end-to-end before promoting to production.

1

Initiate an authorization request

Do this from your application using your sandbox client_id (OID format) and your TEFCA IAS policy scope. Confirm the redirect correctly lands on the ID.me sandbox login page.

2

Complete identity verification

Use the ID.me sandbox test credentials provided by your Solution Consultant. The sandbox simulates the full IAL2 proofing flow without requiring real identity documents.

3

Inspect the token response

Confirm that id_token and refresh_token are present, indicating a successful verification. Decode the id_token (e.g., at jwt.io) and verify:

  • iss matches https://api.idmelabs.com/oidc
  • aud matches your OID-formatted client_id
  • ial claim equals "2"
  • All required TEFCA demographics are present (name, DOB, address claims)
  • The nonce matches the value sent in the authorization request
4

Validate the token signature

Fetch the JWKS from https://api.idmelabs.com/oidc/jwks and confirming the signature verifies against the key matching the token’s kid header value.

5

Submit a test IAS query

Send this to your QHIN’s sandbox or test environment with the token relayed in the appropriate format (SAML attribute or tefca_ias FHIR extension). Confirm the QHIN accepts the token and returns a successful query response.

6

Test a failed verification

Confirm your application handles an unsuccessful response (no id_token in the token response) gracefully, without attempting to relay a non-existent token to the QHIN.

7

Test edge cases

Test multiple cases including expired tokens, mismatched nonce values, and historical_address arrays with multiple entries, to confirm your parsing and error handling behaves as expected.

Token expiry defaults to 5 minutes (expires_in: 300). Request a fresh token for each IAS query rather than caching tokens across sessions.

Security best practices

When extracting claims from the identity token, follow these best practices to maintain a secure and reliable integration:

Validate the token — Always validate the token before extracting claims. Verify the signature, and confirm the exp and iat claims ensure the token is still valid. Never relay an unvalidated token.

Check the audience (aud) — Confirm the aud claim matches your client_id. This ensures the token was issued for your application and not for a different relying party.

Verify the issuer (iss) — Ensure iss matches ID.me’s issuer URI for the active environment. Tokens from the sandbox issuer must never be accepted in production and vice versa.

Handle claims securely — Extract only the claims your application needs to minimize data exposure. Treat demographic attributes (email, phone, address) as sensitive data and ensure compliance with applicable privacy regulations.

Use the state parameter — Always generate a unique state value per session in your authorization request and validate it on return. This prevents cross-site request forgery (CSRF) attacks.

Use the nonce parameter — Embed a unique nonce in the authorization request and validate it in the returned id_token. This prevents token replay attacks.

Do not cache JWKS keys indefinitely — Rotate your cached public keys periodically by re-fetching the JWKS endpoint. ID.me may rotate signing keys, and stale cached keys will cause signature validation failures.

Log and monitor — Log token validation attempts and monitor for unusual patterns, such as repeated validation failures or tokens with unexpected aud or iss values, which may indicate an attack.

Troubleshooting

Token validation failures

If the QHIN rejects the identity token, check the following:

SymptomLikely causeResolution
Signature verification failedStale cached JWKS keysRe-fetch JWKS from ID.me’s endpoint; do not cache keys indefinitely
iss mismatchEnvironment mismatch (sandbox vs. production)Ensure token issuer and QHIN environment match
aud mismatchToken issued for a different clientConfirm your OID-formatted client_id matches what is registered with ID.me
Missing ial claim or value is not "2"Policy scope not configured for IAL2Verify policy handle with your ID.me Solution Consultant
invalid_grant from QHINQHIN authorization server does not support IAS workflowContact your QHIN to confirm IAS support is enabled
id_token absent from token responseVerification was unsuccessfulCheck verification session endpoint for failure reason; do not proceed with QHIN query

If patients are not presented with a consent screen to share identity attributes, confirm that your authorization request includes the correct TEFCA IAS policy scope handle. The scope drives which attributes are requested and triggers the consent screen. Contact your ID.me Solution Consultant to verify your policy scope configuration.

Reference