A description of the identification and authorization process. Authorization is required to receive data from ITMED.
The access token is only issued to the end user (the medical professional). There is currently no role schema, so anyone with an access token will be considered a health care provider.
base_path
To get a token, you need to send a POST request:
curl --location --request POST 'https://base_path/oauth/token' \
--form 'grant_type="password"' \
--form 'client_id={client_uuid}' \
--form 'client_secret={client_secret}' \
--form 'username={username}' \
--form 'password={password}'
If the request is successful, the response body will contain a JSON object:
{
"token_type": "Bearer",
"expires_in": 10800,
"access_token": "iLCJhbGciO....vEGit7M_0cM",
"refresh_token": "d50a225f1d2260....54ce411cb3a44aec2"
}
The access token contains the following information (example, snipped for brevity):
HEADER:
{
"typ": "JWT",
"alg": "RS256"
}
“PAYLOAD”:
{
"aud": "955a889f....4fd2c6ea081f",
"jti": "80ce....cc7bc6ef386",
"iat": 1700202141,
"nbf": 1700202141,
"exp": 1700212941,
"sub": "ba417....a54e201",
"scopes": [],
"user_info": {
"name": "Last_name First_name Patronymic",
"organization": [
{
"uuid": "1ab24....73ee5",
"name": "Organization full name",
"active": true,
"practitioner_role": [
{
"uuid": "6d38....b268eab",
"code": "367"
}
]
}
],
"practitioner": "33c176....035e1",
"roles": [
"head_inpatient",
"head_outpatient"
]
},
"client_id": "ba417....a54e201"
}
Where:
The validity of the access token is three hours from the moment of generation The refresh token is valid for 7 days.
To get a new pair of tokens, you must send a POST request:
curl --location --request POST 'base_path/oauth/token' \
--form 'grant_type="refresh_token"' \
--form 'client_id={client_id}' \
--form 'client_secret={client_secret}' \
--form 'refresh_token="...052ec29d13dc"'
A new pair of tokens will be sent in response (access and refresh)
Note that Resource owner grant type authorization is only available in the test environment.
Authorization code grant is available in test and production environments.
Attention! To authorize production applications, you must use only the Authorization code flow!
In this flow, the interaction between the client and the resource owner goes through the user-agent (browser). The user-agent has one requirement: it must be able to work with HTTP redirects. Without this, the resource owner will not be able to get to the authorization server and return back with a grant.
Proof Key for Code Exchange (abbreviated PKCE, pronounced “pixie”) is an extension to the authorization code flow to prevent CSRF and authorization code injection attacks. The technique involves the client first creating a secret on each authorization request, and then using that secret again when exchanging the authorization code for an access token. This way if the code is intercepted, it will not be useful since the token request relies on the initial secret.
PKCE was originally designed to protect the authorization code flow in mobile apps, and was later recommended to be used by single-page apps as well. In later years, it was recognized that its ability to prevent authorization code injection makes it useful for every type of OAuth client, even apps running on a web server that use a client secret. Because of its history in the use of mobile apps and single-page apps, it is sometimes incorrectly thought that PKCE is an alternative to a client secret. However PKCE is not a replacement for a client secret, and PKCE is recommended even if a client is using a client secret, since apps with a client secret are still susceptible to authorization code injection attacks.
The full spec is available as RFC7636.
When the native app begins the authorization request, instead of
immediately launching a browser, the client first creates what is known
as a “code verifier“. This is a cryptographically random string using
the characters A-Z, a-z, 0-9, and the punctuation characters -._~
(hyphen, period, underscore, and tilde), between 43 and 128 characters
long.
Once the app has generated the code verifier, it uses that to derive the
code challenge. For devices that can perform a SHA256 hash, the code
challenge is a Base64-URL-encoded string of the SHA256 hash of the code
verifier. Clients that do not have the ability to perform a SHA256 hash
are permitted to use the plain code verifier string as the challenge,
although that provides less security benefits so should really only be
used if absolutely necessary.
Base64-URL-encoding is a minor variation on the typical Base64 encoding
method. It starts with the same Base64-encoding method available in most
programming languages, but uses URL-safe characters instead. You can
implement a Base64-URL-encoding method by taking a Base64-encoded string
and making the following modifications to the string: Take the
Base64-encoded string, and change + to -, and / to _, then trim the
trailing = from the end.
function base64_urlencode(str) {
return btoa(String.fromCharCode.apply(null,
new Uint8Array(str)))
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
}
Now that the client has a code challenge string, it includes that and a parameter that indicates which method was used to generate the challenge (S256) along with the standard parameters of the authorization request. This means a complete authorization request will include the following parameters.
response_type=code – indicates that your server expects to receive an authorization code
client_id= – The client ID you received when you first created the application
redirect_uri= – Indicates the URL to return the user to after authorization is complete, such as org.example.app://redirect
state=1234zyx – A random string generated by your application, which you’ll verify later
code_challenge=XXXXXXXXX – The code challenge generated as previously described
code_challenge_method=S256 – either plain or S256, depending on whether the challenge is the plain verifier string or the SHA256 hash of the string.
The authorization server can require that public clients must use the PKCE extension. This is really the only way to allow native apps to have a secure authorization flow without using the client secret, especially without the redirect URI security that’s available with web-based clients. Since the authorization server should know that a specific client ID corresponds to a public client, it can deny authorization requests for public clients that do not contain a code challenge.
If the authorization server requires public clients to use PKCE, and the authorization request is missing the code challenge, then the server should return the error response with error=invalid_request and the error_description or error_uri should explain the nature of the error.
The application will then exchange the authorization code for an access token. In addition to the parameters defined in Authorization Code Request, the client will also send the code_verifier parameter. A access token request will include the following parameters:
grant_type=authorization_code – Indicates the grant type of this token request
code – The client will send the authorization code it obtained in the redirect
redirect_uri – The redirect URL that was used in the initial authorization request
client_id – The application’s registered client ID
code_verifier – The code verifier for the PKCE request, that the app originally generated before the authorization request.
Since the code_challenge and code_challenge_method were associated
with the authorization code initially, the server should already know
which method to use to verify the code_verifier.
The authorization server should take the provided code_verifier and
transform it using the same hash method, then comparing it to the stored
code_challenge string.
If the verifier matches the expected value, then the server can continue
on as normal, issuing an access token and responding appropriately. If
there is a problem, then the server responds with an invalid_grant
error.
The PKCE extension does not add any new responses, so clients can always
use the PKCE extension even if an authorization server does not support
it.
kceChallengeFromVerifier(v) {
const hashed = await this.sha256(v);
return this.base64urlencode(hashed);
},
// Base64-url encodes the input string
base64urlencode(str) {
// Convert the ArrayBuffer to string using Uint8 array to convert to what btoa accepts
// btoa accepts chars only within ascii 0-255 and base64 encodes them.
// Then convert the base64 encoded to base64url encoded
// (replace + with -, replace / with _, trim trailing =)
return btoa(String.fromCharCode.apply(null, new Uint8Array(str)))
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=+$/, "");
},
sha256(plain) {
const encoder = new TextEncoder();
const data = encoder.encode(plain);
return window.crypto.subtle.digest("SHA-256", data);
},
generateRandomString() {
const array = new Uint32Array(28);
window.crypto.getRandomValues(array);
return Array.from(array, (dec) =>
("0" + dec.toString(16)).substr(-2)
).join("");
},
redirectToSSO() {
// Create and store a random "state" value
const state = this.generateRandomString();
localStorage.setItem("pkce_state", state);
// Create and store a new PKCE code_verifier (the plaintext random secret)
const code_verifier = this.generateRandomString();
localStorage.setItem("pkce_code_verifier", code_verifier);
// Hash and base64-urlencode the secret to use as the challenge
const code_challenge = await this.pkceChallengeFromVerifier(code_verifier);
// Build the authorization URL
const url =
config.SSO_AUTHORIZATION_ENDPOINT
+
"?response_type=code" +
"&client_id=" +
encodeURIComponent(config.SSO_CLIENT_ID) +
"&state=" +
encodeURIComponent(state) +
"&redirect_uri=" +
encodeURIComponent(config.SSO_REDIRECT_URI) +
"&code_challenge=" +
encodeURIComponent(code_challenge) +
"&code_challenge_method=S256";
// Redirect to the authorization server
window.location = url;
}
getToken(
config.SSO_TOKEN_ENDPOINT,
{
grant_type: 'authorization_code',
code: q.code,
client_id: config.SSO_CLIENT_ID,
redirect_uri: config.SSO_REDIRECT_URI,
code_verifier: localStorage.getItem('pkce_code_verifier')
},
);
First of all, you need to check that the current time is less than
the value of payload.exp
A public key can be used to verify the signature.
Manual verification
Option 1
The public key is a string. You can get a public key by sending a GET request
curl --location --request GET 'base_path/public-key'
A public key will be sent in response
-----BEGIN PUBLIC KEY-----
MIICIjAN
.......
0CAwEAAQ==
-----END PUBLIC KEY-----
Option 2
JSON Web Key (JWK) A public key as an object JWK can be obtained by sending a request to
curl --location --request GET ' base_path/jwks.json
To get information about the user, send a GET request
curl --location --request GET 'base_path/api/user' \
--header 'Authorization: Bearer eyJ0eXAiOiJKV1QiLC....WqMvEGit7M_0cM' \
--form 'client_id={client_id}' \
--form 'client_secret={client_secret}'
The body of the response message will contain a JSON object
{
"identify": "ba417bf3-b9d1-40a1-91f1-31320a54e201",
"username": "user_login",
"email": "email",
"phone": "phone",
"status": "active",
"name": "Last_name First_name Patronymic",
"organization": [
{
"uuid": "1ab24c....f73ee5",
"name": "Organization name",
"active": true,
"practitioner_role": [
{
"uuid": "6d38179e-9790-4f9f-81e8-cbe76b268eab",
"code": "367"
}
]
}
]
}
Please note: User info is also available in the
access token (see payload.user_info).
In their systems, you can create and store user information in any way. In doing so:
For more information about oAth2 see: