ЕСОЗ - публічна документація

Access tokens JWT format

Purpose and requirements

  1. Access tokens generation in JWT format is implemented to reduce load of mithril service, that exists on any call to eHealth API that uses Authorization header with access token.

  2. Only access tokens that are used to obtain access to eHealth endpoints (i.e. Create/Update Person Request, Create Declaration Request, Get Person Verification Details etc) can be used in JWT format. Any other tokens that are used in OAuth flow (i.e. authorization_code, refresh_token, session_token) are still generated in existing format.

  3. Access token generation format is controlled by a configuration parameter - ACCESS_TOKEN_JWT.

  4. Existing access token generation format validation must be active and operational.

  5. Access token in JWT format contains only user and client related data, broker data is still validated with api-key validation through Mithril service using existing process, described here MIS authorization

  6. Access tokens in JWT format are not saved to mithril database, as they are only validated on Kong service side.

Implementation

Access token in JWT format generation

Following methods that are used to generate access token for user:

Exchange oAuth Code Grant to Access Token

https://e-health-ua.atlassian.net/wiki/spaces/PCAB/pages/17415667759

https://e-health-ua.atlassian.net/wiki/spaces/PCAB/pages/17415798881

https://e-health-ua.atlassian.net/wiki/spaces/PCAB/pages/17415340883

https://e-health-ua.atlassian.net/wiki/spaces/PCAB/pages/17416060933

Methods check value of ACCESS_TOKEN_JWT configuration parameter:

  • true - access tokens for users are generated in JWT format;

  • false - access tokens for users are generated in opaque format (hashed string of record from mithril database, tokens table).

Methods responses for generated token are not changed, token is still returned in value field.

JWT structure

Access token in JWT format consist of three base64 encoded strings that are separated by dot symbol:

  • Header

  • Payload

  • Signature

eyJhbGciOiJSUzUxMiIsImtpZCI6IlZ1SGVPX3Nyemhua3h6b1RKYzNURW1CYUZHdkhWcXFXR0V5NzRzM3hWak0iLCJ0eXAiOiJKV1QifQ.eyJhY2Nlc3NfdHlwZSI6IkJST0tFUiIsImFwcGxpY2FudF9wZXJzb25faWQiOiJiZjExNDU0Zi1hMmIwLTRhYjQtYjhiZC1jMmFiZjhkYWM0NDAiLCJhcHBsaWNhbnRfdXNlcl9pZCI6IjE1YzcyZTBiLWM2MDktNGViZC1iZmM5LWE0NWQzM2E3M2JjNSIsImF1ZCI6IkVIZWFsdGgiLCJjbGllbnRfaWQiOiI4YTk5ZmZkZi0zMTRlLTQ0MTktOTMxZC1hNzZmNDFmOGM0NTYiLCJleHAiOjE3MTc4NzQzODQsImdyYW50X3R5cGUiOiJwaXNfYXV0aCIsImlhdCI6MTY4NjMxNzQ1OCwiaXNzIjoiRUhlYWx0aCIsImp0aSI6ImQ3NzFmN2M0LTNkZjUtNGRmYy1iNDQ2LTYwMzk1MGU3ZTFhMiIsIm5iZiI6MTY4NjMxNzQ1NywicGVyc29uX2lkIjoiYmYxMTQ1NGYtYTJiMC00YWI0LWI4YmQtYzJhYmY4ZGFjNDQwIiwicmVmcmVzaF90b2tlbiI6ImJucFNORVY2WW10QlVEWmhNM2xxTHpaelUwRjFaejA5Iiwic2NvcGUiOiJhcHA6cmVhZCBwcm9maWxlOnJlYWQiLCJzdWIiOiIxNWM3MmUwYi1jNjA5LTRlYmQtYmZjOS1hNDVkMzNhNzNiYzUiLCJ0eXAiOiJhY2Nlc3NfdG9rZW4ifQ.dxurQjxcv_LQR9YiG7we0vZl8CKd1c2dZ2i8XhDUt7CNxLINFoLgpmcVnUSMf_Slk-YkXOELgT4sjyWkaeoZpWhcWfTeHMNh7KQzMH9Mnn_8_oGGc4yt3ZPYiHxng4KY2BqrLniPYel_FtaZoBKrfpwh6zh7RUdau_irOLIF6C_RTQ7BD-wOyCEeRjc2CchBFk5ZN3Cagwx5RbA_Y__nUpe4nK00HbRENBgDySq4k2lqfkfUUGG7HWMs0r9Ik7neRB1holb0-RrkYFs4NVVkl1N078ryqDhTXoHM8_9Z9FiU9eEE53QGRchVO2Tmo85gynPqfsXVJghykMH56hsvag.eyJhY2Nlc3NfdHlwZSI6IkJST0tFUiIsImFwcGxpY2FudF9wZXJzb25faWQiOiJiZjExNDU0Zi1hMmIwLTRhYjQtYjhiZC1jMmFiZjhkYWM0NDAiLCJhcHBsaWNhbnRfdXNlcl9pZCI6IjE1YzcyZTBiLWM2MDktNGViZC1iZmM5LWE0NWQzM2E3M2JjNSIsImF1ZCI6IkVIZWFsdGgiLCJjbGllbnRfaWQiOiI4YTk5ZmZkZi0zMTRlLTQ0MTktOTMxZC1hNzZmNDFmOGM0NTYiLCJleHAiOjE3MTc4NzQzODQsImdyYW50X3R5cGUiOiJwaXNfYXV0aCIsImlhdCI6MTY4NjMxNzQ1OCwiaXNzIjoiRUhlYWx0aCIsImp0aSI6ImQ3NzFmN2M0LTNkZjUtNGRmYy1iNDQ2LTYwMzk1MGU3ZTFhMiIsIm5iZiI6MTY4NjMxNzQ1NywicGVyc29uX2lkIjoiYmYxMTQ1NGYtYTJiMC00YWI0LWI4YmQtYzJhYmY4ZGFjNDQwIiwicmVmcmVzaF90b2tlbiI6ImJucFNORVY2WW10QlVEWmhNM2xxTHpaelUwRjFaejA5Iiwic2NvcGUiOiJhcHA6cmVhZCBwcm9maWxlOnJlYWQiLCJzdWIiOiIxNWM3MmUwYi1jNjA5LTRlYmQtYmZjOS1hNDVkMzNhNzNiYzUiLCJ0eXAiOiJhY2Nlc3NfdG9rZW4ifQ.dxurQjxcv_LQR9YiG7we0vZl8CKd1c2dZ2i8XhDUt7CNxLINFoLgpmcVnUSMf_Slk-YkXOELgT4sjyWkaeoZpWhcWfTeHMNh7KQzMH9Mnn_8_oGGc4yt3ZPYiHxng4KY2BqrLniPYel_FtaZoBKrfpwh6zh7RUdau_irOLIF6C_RTQ7BD-wOyCEeRjc2CchBFk5ZN3Cagwx5RbA_Y__nUpe4nK00HbRENBgDySq4k2lqfkfUUGG7HWMs0r9Ik7neRB1holb0-RrkYFs4NVVkl1N078ryqDhTXoHM8_9Z9FiU9eEE53QGRchVO2Tmo85gynPqfsXVJghykMH56hsvag
{ "alg": "RS512", "kid": "VuHeO_srzhnkxzoTJc3TEmBaFGvHVqqWGEy74s3xVjM", "typ": "JWT" } . { "access_type": "BROKER", "app_id": "ce21628e-317b-4edb-bda6-0de661f24666", "applicant_person_id": "bf11454f-a2b0-4ab4-b8bd-c2abf8dac440", "applicant_user_id": "15c72e0b-c609-4ebd-bfc9-a45d33a73bc5", "aud": "EHealth", "client_id": "8a99ffdf-314e-4419-931d-a76f41f8c456", "exp": 1717874384, "grant_type": "pis_auth", "iat": 1686317458, "iss": "EHealth", "jti": "d771f7c4-3df5-4dfc-b446-603950e7e1a2", "nbf": 1686317457, "person_id": "bf11454f-a2b0-4ab4-b8bd-c2abf8dac440", "redirect_uri": "https://example_uri.com/redirect", "refresh_token": "bnpSNEV6YmtBUDZhM3lqLzZzU0F1Zz09", "scope": "app:read profile:read", "sub": "15c72e0b-c609-4ebd-bfc9-a45d33a73bc5", "typ": "access_token" } . dxurQjxcv_LQR9YiG7we0vZl8CKd1c2dZ2i8XhDUt7CNxLINFoLgpmcVnUSMf_Slk-YkXOELgT4sjyWkaeoZpWhcWfTeHMNh7KQzMH9Mnn_8_oGGc4yt3ZPYiHxng4KY2BqrLniPYel_FtaZoBKrfpwh6zh7RUdau_irOLIF6C_RTQ7BD-wOyCEeRjc2CchBFk5ZN3Cagwx5RbA_Y__nUpe4nK00HbRENBgDySq4k2lqfkfUUGG7HWMs0r9Ik7neRB1holb0-RrkYFs4NVVkl1N078ryqDhTXoHM8_9Z9FiU9eEE53QGRchVO2Tmo85gynPqfsXVJghykMH56hsvag

Header

JWT header contains following fields:

Field

Description

Example

Field

Description

Example

alg

signature or encyption algorithm that is used to generate JWT

RS512

kid

identifier of private key that is used to sign JWT

VuHeO_srzhnkxzoTJc3TEmBaFGvHVqqWGEy74s3xVjM

typ

token type

JWT

Mithril service uses RS512 JWT signature algorithm with JWT token type.

Payload

JWT payload contains following fields:

Field

Description

Example

Field

Description

Example

aud

audiene (who or what the token is intended for)

EHealth

exp

token expiration date and time in unix-format

1714840995

iat

token generation date and time in unix-format

1680685233

nbf

token not valid before date and time in unix-format

1680685232

jti

unique token identifier in uuid format

481aa86b-7bfa-462c-8bcb-1a9e9edff192

sub

subject of token (user_id) in uuid format

9cf6f222-2a6a-4ef2-b7f0-6331e1d706ae

iss

name of token issuer

EHealth

access_type

access type of client, if BROKER - additional api-key validation is needed, if DIRECT - no api-key validation needed

BROKER

app_id

identifier of approval between user, applicant user and client that was used to issue access token

ce21628e-317b-4edb-bda6-0de661f24666

applicant_user_id

identifier of user that initiated access token generation for subject (user) in uuid format

9cf6f222-2a6a-4ef2-b7f0-6331e1d706ae

applicant_person_id

identifier of person in uuid format that is accosiated with user who initiated access token generation for subject (user), if exists

38a72dcc-8cb4-4204-ab3a-913fe1dadb8f

client_id

identifier of client that initiated access token generation for subject (user) in uuid format

8a99ffdf-314e-4419-931d-a76f41f8c456

grant_type

grant type that was used to issue access token, if exists

authorization_code

person_id

identifier of person in uuid format that is accosiated with token subject (user), if exists

38a72dcc-8cb4-4204-ab3a-913fe1dadb8f

redirect_uri

redirect uri that was used by client for access token generation, if exists

https://example_uri.com/redirect

refresh_token

refresh token that was generated with access token

Tm5hRkx2U0hIOUxPMjg5ZlFlK3gzQT09

scope

list of scopes that are allowed by issued access token, separated by space

app:read profile:read

typ

the type of the token

access_token

Mithril service forms payload field based on data that is used for token generation (user_id, client_id, requested scope list, session token, etc).

Signature

JWT signature contains signature of token content using algorithm described in alg field from header.

Mithril service uses asymmetric signature algorithm – token content is signed using private key stored in JWT_PRIVATE_KEY parameter.

Access tokens in JWT format validation

Access token in JWT format must be submitted to the same Authorization header for any method that required access token.

curl --location 'https://api.dev.edenlab.com.ua/api/persons/5865b87e-0da0-4d28-8d3c-e24e0d4813e3/verification' \ --header 'api-key: b1lPV3d3blRHRHdOYVYrYmNPU0tJdz09' \ --header 'Authorization: Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJFSGVhbHRoIiwiZXhwIjoxNjg1NDU4MDkxLCJpYXQiOjE2ODMwMzg4OTEsImlzcyI6IkVIZWFsdGgiLCJqdGkiOiI5OTU2NjVmYS1iYjAyLTQyZWMtYWYyYi1iOWJhMWQyZDkyNDQiLCJuYmYiOjE2ODMwMzg4OTAsInN1YiI6eyJjbGllbnRfaWQiOiI0ZjEyMDUzYi00NTBlLTQyNzUtOWJlYi0yNjU1YjgxNjAxM2YiLCJncmFudF90eXBlIjoiYXV0aG9yaXphdGlvbl9jb2RlIiwicmVkaXJlY3RfdXJpIjoiaHR0cHM6Ly9leGFtcGxlNzMuY29tIiwic2NvcGUiOiJkZWNsYXJhdGlvbjpyZWFkIGRlY2xhcmF0aW9uX3JlcXVlc3Q6YXBwcm92ZSBkZWNsYXJhdGlvbl9yZXF1ZXN0OnJlYWQgZGVjbGFyYXRpb25fcmVxdWVzdDpzaWduIGRlY2xhcmF0aW9uX3JlcXVlc3Q6d3JpdGUgZGl2aXNpb246cmVhZCBlbXBsb3llZTpyZWFkIGVtcGxveWVlX3JlcXVlc3Q6YXBwcm92ZSBlbXBsb3llZV9yZXF1ZXN0OnJlamVjdCBlbXBsb3llZV9yZXF1ZXN0OnJlYWQgbGVnYWxfZW50aXR5OnJlYWQgb3RwOnJlYWQgb3RwOndyaXRlIHBlcnNvbjpyZWFkIHBhdGllbnRfc3VtbWFyeTpyZWFkIGVuY291bnRlcjp3cml0ZSBlbmNvdW50ZXI6cmVhZCBlbmNvdW50ZXI6Y2FuY2VsIGVwaXNvZGU6d3JpdGUgZXBpc29kZTpyZWFkIGpvYjpyZWFkIGNvbmRpdGlvbjpyZWFkIGNvbmRpdGlvbjp3cml0ZSBvYnNlcnZhdGlvbjpyZWFkIG9ic2VydmF0aW9uOndyaXRlIGltbXVuaXphdGlvbjpyZWFkIGltbXVuaXphdGlvbjp3cml0ZSBhbGxlcmd5X2ludG9sZXJhbmNlOnJlYWQgYWxsZXJneV9pbnRvbGVyYW5jZTp3cml0ZSBtZWRpY2F0aW9uX3N0YXRlbWVudDpyZWFkIG1lZGljYXRpb25fc3RhdGVtZW50OndyaXRlIGRldmljZTpyZWFkIGRldmljZTp3cml0ZSByaXNrX2Fzc2Vzc21lbnQ6cmVhZCByaXNrX2Fzc2Vzc21lbnQ6d3JpdGUgbWVkaWNhdGlvbl9kaXNwZW5zZTpyZWFkIGRydWdzOnJlYWQgbWVkaWNhdGlvbl9yZXF1ZXN0OmRldGFpbHMgbWVkaWNhdGlvbl9yZXF1ZXN0OnJlYWQgbWVkaWNhdGlvbl9yZXF1ZXN0OnJlamVjdCBtZWRpY2F0aW9uX3JlcXVlc3Q6cmVzZW5kIG1lZGljYXRpb25fcmVxdWVzdF9yZXF1ZXN0OnJlYWQgbWVkaWNhdGlvbl9yZXF1ZXN0X3JlcXVlc3Q6cmVqZWN0IG1lZGljYXRpb25fcmVxdWVzdF9yZXF1ZXN0OnNpZ24gbWVkaWNhdGlvbl9yZXF1ZXN0X3JlcXVlc3Q6d3JpdGUgZGlhZ25vc3RpY19yZXBvcnQ6cmVhZCBkaWFnbm9zdGljX3JlcG9ydDp3cml0ZSBkaWFnbm9zdGljX3JlcG9ydDpjYW5jZWwgcHJvY2VkdXJlOnJlYWQgcHJvY2VkdXJlOndyaXRlIHByb2NlZHVyZTpjYW5jZWwgc2VydmljZV9yZXF1ZXN0Om1ha2VpbnByb2dyZXNzIHNlcnZpY2VfcmVxdWVzdDpjb21wbGV0ZSBzZXJ2aWNlX3JlcXVlc3Q6cmVjYWxsIHNlcnZpY2VfcmVxdWVzdDpjYW5jZWwgc2VydmljZV9yZXF1ZXN0OndyaXRlIHNlcnZpY2VfcmVxdWVzdDpyZWFkIHNlcnZpY2VfcmVxdWVzdDp1c2UgYXBwcm92YWw6Y3JlYXRlIHByb2dyYW1fc2VydmljZTpyZWFkIG1lZGljYXRpb25fYWRtaW5pc3RyYXRpb246cmVhZCBtZWRpY2F0aW9uX2FkbWluaXN0cmF0aW9uOndyaXRlIGhlYWx0aGNhcmVfc2VydmljZTpyZWFkIGVtcGxveWVlX3JvbGU6cmVhZCBwZXJzb25fcmVxdWVzdDp3cml0ZSBwZXJzb25fcmVxdWVzdDpyZWFkIGF1dGhlbnRpY2F0aW9uX21ldGhvZF9yZXF1ZXN0OndyaXRlIGNhcmVfcGxhbjpyZWFkIGFwcHJvdmFsOnJlYWQgYXBwcm92YWw6Y2FuY2VsIGNhcmVfcGxhbjp3cml0ZSBjb21wb3NpdGlvbjpjcmVhdGUgY29tcG9zaXRpb246c2lnbiBjb21wb3NpdGlvbjpjYW5jZWwgY29tcG9zaXRpb246cmVhZCBjb21wb3NpdGlvbjpzZWFyY2ggbGljZW5zZTpyZWFkIGNsaW5pY2FsX2ltcHJlc3Npb246cmVhZCBydWxlX2VuZ2luZV9ydWxlOnJlYWQgbWVkaWNhbF9ldmVudF9jb250ZXh0OnJlYWQgY2xpbmljYWxfaW1wcmVzc2lvbjp3cml0ZSBwZXJzb25fdmVyaWZpY2F0aW9uOmRldGFpbHMgcGVyc29uX3ZlcmlmaWNhdGlvbjp3cml0ZSBwZXJzb25fZW1lcmdlbmN5X2NvbnRhY3Q6cmVhZCIsInVzZXJfaWQiOiIzNWM4YTFhOS05YzkzLTRmMDEtYmNlZS1jNTJjZjU3NjI2YzQifSwidHlwIjoiYWNjZXNzIn0.vfV6CbHmMOisqbUyLqtaau5gxN2UcPwq8rdg2pQLpvAAAT0mZ0PWO9DtveTjArkDi0x5bU5uf8yOKo882zCAuFip1mWOJNhu0qSHr3uMQMCwvwrtY0JGavuf0wA81hlSCah4mE3U5iPBPuPYgfqMiELxxh1-Mz1OsYQsJfS_KTK_8LSW41eBSmcm-3piRXFeDIOiE2REQDG3GNvkXEJ37SCBJTMv8r_ypVcjymRkWk32_ajrqWS17Y66ax-D59Cp2bf1TWNtk8KKIqnIf-g-nG-sSzruoZGtbNzp6mW4yPZkZ-zG5hVpv0u_i50BU8FittPwNLa-EjX3IDqUUrWwpw'

Access tokens in JWT format are validated only by Kong service, if all token validations are passed – scope list from scope field is used to validate access to requested endpoint.

JWT is validated by following steps:

  1. Validate Authorization header format

    1. in case token parses as JWT – validate it as JWT

    2. else – validate it using existing token format validation (by token verify mithril method)

  2. Verify JWT signature with public key stored in JWT_PUBLIC_KEY and JWT_PUBLIC_KEY_OLD parameters

    1. in case JWT signature is not verified by stored public key - return 401 ('access_denied')

  3. Check token expiry date from exp field is greater then now()

    1. in case exp field does not exist or expiry date is in the past - return 401 ('access_denied')

  4. Check token issuer from iss field equals to ‘EHeath’ value

    1. in case iss field does not exist or contains any other data - return 401 ('access_denied')

  5. Check token audience from aud field equals to ‘EHealth’ value

    1. in case aud field does not exist or contains any other data - return 401 ('access_denied')

  6. Check token is not blacklisted in Redis cache database by values of fields from payload (jti, sub, client_id):

    1. check key blacklist_jti_<<jti>> does not exist (for example, blacklist_jti_481aa86b-7bfa-462c-8bcb-1a9e9edff192);

    2. check key blacklist_user_id_<<sub>> does not exist (for example, blacklist_user_id_481aa86b-7bfa-462c-8bcb-1a9e9edff192);

    3. check key blacklist_client_id_<<client_id>> does not exist (for example, blacklist_client_id_8a99ffdf-314e-4419-931d-a76f41f8c456);

    4. check key blacklist_user_id_client_id_<<sub>>_<<client_id>> does not exist (for example, blacklist_user_id_client_id_481aa86b-7bfa-462c-8bcb-1a9e9edff192_8a99ffdf-314e-4419-931d-a76f41f8c456);

    5. check key blacklist_app_id_<<app_id>> does not exist (for example, blacklist_app_id_ce21628e-317b-4edb-bda6-0de661f24666);

      1. in case any of the keys exist in Redis cache database - return 401 ('access_denied')

ЕСОЗ - публічна документація