Cortex employs session-based (using cookies) and signature-based authentication. Within each org, applications can be created which provide the necessary key and secret for API access. See Generate API Key for more details.
Session-Based Authentication
A typical use of session-based authentication would be for a client application, such as a mobile or web app, whereby individual users authenticate using their credentials to initiate a new session.
The session is unique to the authenticated user and will timeout after a period of inactivity, based on org settings. Account registration or logging in both initiate authenticated sessions.
When employing session-based authentication, Two Factor Authentication also applies.
Signature-Based Authentication
A signing key allows a third-party to make privileged calls to the API using a secure request signing mechanism. These keys can be configured for single or multiple user accounts, timed expiry, etc. Such a mechanism can be useful for scenarios such as back-end system integrations (e.g. Integrations with an EMR, or HL7 messaging appliance).
Signed Request Headers
Name | Value |
---|---|
| The calculated request signature. |
| Client Unix Timestamp in milliseconds. The server issues a |
| A client-generated nonce matching |
| Optionally sets the calling principal by account id (if the app is configured to allow principal override). |
Example signed request
// Example signature-authenticated request using nodejs
'use strict';
function generateNonce() {
var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789',
maxC = chars.length-1,
nonce = '',
length = 16;
while(length--) {
nonce += chars[Math.round(Math.random() * maxC)];
}
return nonce;
}
var crypto = require('crypto'),
request = require('request');
var command = '/connections', // use only the path component (no query strings)
method = 'GET', // uppercase http method
apiKey = 'GsAqlhnIMzrDeD8V2MBQWq',
apiSecret = 'Kzc8nwCQiKOgiwTIobaI3H6ryK4C5fC0QcYsuXCSbEMKcJPz8EU2phcj6akDB9VP',
timestamp = Date.now(),
hmac = crypto.createHmac('sha256', apiKey+apiSecret),
signature;
hmac.update(command + ';' + method + ';' + timestamp);
signature = hmac.digest('hex');
var options = {
url: 'https://api.dev.medable.com/example/v2/connections',
headers: {
'Medable-Client-Key': apiKey,
'Medable-Client-Signature': signature,
'Medable-Client-Timestamp': String(timestamp),
'Medable-Client-Nonce': generateNonce()
}
};
request(options, function(error, response) {
// ...
});
Authentication
Token Authentication and Scoping
Beta FeatureFor now, generating and revoking auth tokens is limited to the scripting interface. See the Scripting Accounts Module section for more examples and full token options.
Applications can leverage JWT (JSON Web Tokens) to access Cortex. Once a key-pair has been generated, tokens can be generated from either session-based or signature-based apps.
- To enable token signing, Generate RSA key pairs for each app you wish to authenticate.
- The default limit of permanent/limited-use authentication tokens per account in the app is 10.
- Check
Expose Keys
to add this app's JWK to the list produced in GET /auth/certs. Useful if you want third-parties to validate tokens generated by your app.

To invalidate all tokens for an app you must regenerate the key pair or delete the app altogether. You can invalidate permanent/limited-use tokens per subject using Account.revokeAuthToken() and Account.revokeSubjectTokens(). Ephemeral tokens (those without a jti) can only be revoked by deleting the application or regenerating the key-pair.

The following table outlines the JWT claim set supported in Cortex.
Claim | Description |
---|---|
aud | The audience. This is always |
iss | The issuing application's api key. |
iat | The unix timestamp when the token was generated. |
nbf | When present, the unix timestamp when the token becomes usable. |
exp | When present, the unix timestamp when the token expires. |
sub | The account identifier of the token's calling principal. The token will authenticate as this account when used with Cortex. |
jti | When present, represents the unique token identifier. This will exist for permanent and limited-use tokens. |
cortex/scp | The token scope. Scopes limit token access to sections listed in the claim. See Authentication Scoping. |
cortex/rls | An array of roles granted to the token, and merged with the token subject's existing roles. |
cortex/skp | When set, the token skips access controls for the purpose of limiting matches. The scripting analogue to this claim is |
cortex/skc | When set, the token skips access controls for instance creation. The scripting analogue to this claim is |
cortex/gnt | When set, the token grants this level of access to any and objects accessible to the token. The scripting analogue to this claim is |
cortex/cnt | When set, represents the maximum number of times the token can authenticate before invalidating. |
Scoping
A powerful new scoping feature has been added that allows you to limit access to a subset of functionality, like object access, deployments, script and view execution, and administration functions.
All access tokens are scoped, and adding scope is done by adding one or more scope chains to the token in the cortex/scp
claim, or inheriting them from subject roles and those added with cortex/rle
claims.
Valid scope examples:
- object.read.account.*.name
- script.execute (same as script, script.execute.*)
- view.execute.c_daily_report
Invalid scope examples:
- object.read.account.name (missing identifier or * before property)
- deployment.create (use object.create.deployment instead)
Scope Chain | Level |
---|---|
| The token can perform all operations as the calling subject. |
| Object operations, such as reading accounts, creating connections, etc. The last element in the chain (fqpp) can be used to limit access to individual properties (e.g. c_set[]#c_foo.c_string_arr[]). You can see the fqpp (fully-qualified property path) in your schemas (GET /schemas). Examples:
|
| This scope is required if the token is used to run script routes. An Identifier can be added to the chain to limit use to a single route. |
| This scope is required if the token is used to run script routes. An Identifier can be added to the chain to limit use to a single view. |
| This scope is required if the token is used to execute deployments via scripted deployment automation.An Identifier can be added to the chain to limit use to a single deployment. |
| This scope is required if the token is used to call administrative routes, such as user provisioning, deactivating accounts, etc. |
Scopes have been added to custom roles. Tokens inherit the scopes from roles held by the principal, as well as any roles granted by the cortex/rls
claim. In the image below, the Scoped role add the ability to execute any view and a single scripted route.

const Account = org.objects.account;
const account_id = Account
.find({email: '[email protected]'})
.skipAcl()
.grant(4)
.next()
._id;
// create an ephemeral token that allows the caller to act as account_id and
// read the account name and the subject of all c_message instances, skipping
// access control.
return org.objects.account.createAuthToken(
'yk8XJmrMKWzhinCl4Ww99A',
account_id,
{
scope: [
`object.read.account.${account_id}.name`,
"object.read.c_messages.*.c_subject"
],
skipAcl: true,
grant: consts.accessLevels.read
}
)
'use strict';
const request = require('request'),
token = 'eyJhCiJSUz...c4DiCkVvNA',
options = {
url: 'https://api.dev.medable.com/example/v2/c_messages',
headers: {
Authorization: `Bearer ${token}`
}
};
request(options, function(error, response) {
// ...
});