# Security Recipes

This page provides examples of how to configure Smile CDR for specific security designs.

# App: Local Users with HTTP Basic Authentication

This example illustrates how to set up a simple application. In this example, we're using Smile CDR's database for both resource storage as well as user storage and authentication. We will set up two users:

- A Regular User – with read-only access to data for a single patient
- An App Admin – with access to create and maintain data on the server

The following diagram illustrates this setup.

## Setting Up Modules

- Create Security Module

If your configuration does not already have one, first you should create a **[Local Inbound Security](/content/docs/security/local_inbound_security_module.html)** module. Configure this module with appropriate password encoding settings for your needs.

- Create FHIR Storage

Create a **FHIR Storage (Relational)** module for the version of FHIR you are looking to support.

- Create FHIR Endpoint

Create a **FHIR Listener** module for the version of FHIR you are looking to support.

The FHIR REST endpoint should be configured with HTTP Basic security enabled, and the `Username/Password Authentication` dependency pointed to your Inbound Security module. This is shown in the screenshot below.

## Defining Permissions: App Admin

We will define permissions for an administrative user, who is allowed to perform any function on the repository. This user can create, update, and delete resources as required.

To set this up, grant the user the following permissions:

| Permission | Argument | Explanation |
| --- | --- | --- |
| [`ROLE_FHIR_CLIENT`](/content/docs/security/roles_and_permissions.html#ROLE_FHIR_CLIENT/index.html) | (n/a) | Allow user to be a FHIR client. |
| [`ROLE_FHIR_CLIENT_SUPERUSER`](/content/docs/security/roles_and_permissions.html#ROLE_FHIR_CLIENT_SUPERUSER/index.html) | (n/a) | Allow user to perform any FHIR tasks. |

## Defining Permissions: Regular User

We will then define permissions for a user who is authorized to be a FHIR Client, and who can read any resource of types `ValueSet` and `CodeSystem`. Aside from that, the user can only read resources within a specific Patient compartment. This means that the user can access general administrative data (`ValueSet` and `CodeSystem` do not not typically contain sensitive data).

The user is blocked from reading other data that isn't in a specific Patient compartment to which they have access. The Patient compartment belongs to a specific Patient resource (e.g. `Patient/123`), and the only resources that can be read are limited to that specific patient instance as well as other resources with an explicit reference to that resource from one of the reference types defined by FHIR's Patient compartment (e.g. "Observations with the given patient as the subject" and "MedicationRequests with the given patient as the subject").

To set this up, grant the user the following permissions:

| Permission | Argument | Explanation |
| --- | --- | --- |
| [`ROLE_FHIR_CLIENT`](/content/docs/security/roles_and_permissions.html#ROLE_FHIR_CLIENT/index.html) | (n/a) | Allow user to be a FHIR client. |
| [`FHIR_READ_ALL_OF_TYPE`](/content/docs/security/roles_and_permissions.html#FHIR_READ_ALL_OF_TYPE/index.html) | `ValueSet` | Allow user to read any `ValueSet` resources. |
| [`FHIR_READ_ALL_OF_TYPE`](/content/docs/security/roles_and_permissions.html#FHIR_READ_ALL_OF_TYPE/index.html) | `CodeSystem` | Allow user to read any `CodeSystem` resources. |
| [`FHIR_READ_ALL_IN_COMPARTMENT`](/content/docs/security/roles_and_permissions.html#FHIR_READ_ALL_IN_COMPARTMENT/index.html) | `Patient/123` | Allow user to read any `Patient`, `Observation`, `QuestionnaireResponse`, `ServiceRequest`, etc. that belong to the patient `Patient/123`. |

## Calling the API

Now that you have a FHIR Endpoint that has been secured using HTTP Basic Authentication, you will need to add an `Authorization` header to your requests.

Here is an example request showing the Authorization header:

```http
GET /Patient/123
```

Copy

The `[credentials]` part above is a base64 encoded string in the format `username:password`. For example, the username `admin` and password `password` you would result in the following header:

```http

```

Copy

The `base64` command can be used to base64 encode the credentials. For example:

```bash
echo -n "admin:password" | base64
YWRtaW46cGFzc3dvcmQ=
```

Copy

# Integrating SMART Apps With a 3rd-Party OIDC Server

In this example we are authorizing data requests to Smile CDR using a 3rd-party Identity Provider, or more specifically a 3rd-party OpenID Connect server. This might be a software package such as [MitreID Connect](https://github.com/mitreid-connect/) or [Keycloak](https://www.keycloak.org/), or it could be a cloud service such as [Okta](https://www.okta.com/) or [Ping Identity](https://www.pingidentity.com/en.html).

The proposed architecture here is shown below:

## Background And Terminology

To keep things simple, we'll refer to 3 roles in the example that follows (see [SMART on FHIR Participants](/content/docs/smart/smart_on_fhir_introduction.html#participants/index.html) for more details on these roles):

- The **Auth Server** is the 3rd party Identity Provider. It stores information about the user account and makes assertions about who they are and what they can do.
- The **Resource Server** is the Smile CDR FHIR Endpoint with OpenID Connect security enabled.
- The **App** is a standard SMART on FHIR compliant application.

The OpenID Connect protocol relies heavily on cryptographically signed assertions about the user that are included in the token(s) which are passed from system to system. A simple way of thinking about these assertions is follows:

1. The _Auth Server_ creates a small JSON document with elements that states "The App that I gave this document to is logged in as John Smith, which corresponds to the FHIR Resource `Patient/A123`. John has granted the app permission to perform **read** operations but not **write** operations."
2. The _Auth Server_ uses a key to digitally sign the document. This same key is also distributed to the _Resource Server_ so that it can verify the signature (this form of verification is known as "Shared Secret" verification and is an alternative to other forms such as RSA public/private keypairs which are also often used). This means that anyone who holds this token can digitally verify that the token was actually created by the Auth Server and that it was not modified.
3. The _Auth Server_ gives this signed document (which is in fact a [JSON Web Token](http://jwt.io/) to the **App**. This signed document is now called a _token_. The app needs to keep the token secure since it was issued specifically for that app.
4. The **App** includes the token in FHIR requests that it makes to the **Resource Server**.
5. The **Resource Server** uses the public key from the _Auth Server_ to verify that the token is genuine and has not been tampered with and uses the data contained within it to decide what operations should be allowed by the user.

## Scopes and Claims

The token, which is a signed JWT, contains a number of specific "claims", which are actually just fields within the JSON document. These fields identify the system that issued the token, metadata about the token and the session, and often details about the user.

A very simple example of a JWT follows:

```json
{
    "sub":"myusername",
    "azp":"my-client-id",
    "scope":"openid profile patient\/*.read",
    "iss":"http:\/\/localhost:19202",
}
```

Copy

When this JWT is signed for sharing, it is also base64 encoded, resulting in a string resembling the following (although the real string is generally much longer):

```none
yJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJteXVzZXJuYW1lIi.9w3r9fy8czoy8YYUy
```

Copy

The OIDC protocol specifies that apps may request scopes, which are a mechanism for declaring specific permissions that the **App** has (in other words, they declare that the App has the ability to do specific things on the user's behalf). The SMART on FHIR specification declares a set of healthcare specific scopes, such as `patient/*.read` (app may read but not write any resource data about the current patient). These scopes are placed in a claim called `"scope"`.

While scopes are used to convey App permissions, the OIDC protocol does not mandate any specific mechanism for conveying User permissions. In our example here we will use additional custom claims that are used to convey the linkage between the user and the FHIR resource they are allowed to access.

## User Permissions

Conveying the details of what the user can do is probably the least intuitive part of this flow. This is mostly because the OIDC and SMART on FHIR specs aim to cover the authorizing of Apps, but leave the authorizing of Users up to the individual implementation.

In our example, we are going to add a single custom claim to the generated token called `"patient"` containing the resource ID that corresponds to the logged in user. In other words, the Auth Server will include a custom claim such as `"patient": "A123"` and this will be interpreted by the Resource Server as an assertion by the Auth Server that the logged in user is allowed to access any data belonging to the FHIR resource `Patient/A123`. This might include the Patient resource itself, but also Observations, Encounters, etc. that refer to the given patient.

This type of authorization is simplistic and can be expanded upon in many ways, but it serves to demonstrate the concept of authorizing patient-facing apps to allow patient users to access their own data without granting access to other users' data.

## Step 1: Configure The Auth Server

For our example, the Auth Server is configured with the following settings. The mechanism for achieving these settings will vary from provider to provider but the general concepts should exist in every provider.

**Key**

In our example here, we will use the following HS256 shared secret key. There is no requirement to use this form of signature, and you should absolutely generate your own key for any real scenarios but this key will work for testing. The format below is known as a JSON Web Key (JWK), and the value of the `"k"` attribute is the actual key contents.

```json
{
  "kty": "oct",
  "use": "sig",
  "kid": "my-app-key",
  "k": "cdl6q83k7VNc-LtSzi_VzR3BvnTWmFGuQPuscZ45IOtVIb178ZwWwc65M1yhd-3Mmp_kxoe8_r_s5TN3pLOHYHynkAgavmJJHG-YDc6ksVD1cAQMXlL90BXfFuL0ewDCqgghkTd-qCGavGbi4rwqFOQp5QxbfT1-SK9q7-IgGOANVKiPIAS-bup1f0QwPUU3k9hNP5gF9dWHk4nK9KBZkO_iGXjrc1tv5gCGi1kKk9R4BAK0bHvDfQjR1ryl_Jkt_qdodd9cuyFr_lS6Rd7VsmnnEYEpwbleYtkcjqBJRvR86Ka6BQgzdSyBp3Lel2mL23X6gAw1TVT_jERPBmqBpg",
  "alg": "HS256"
}
```

Copy

**Issuer**

Any OpenID Connect server needs an Issuer URL. This URL serves two purposes: First, it is used as a unique identifier to actually indicate to the Resource Server which Auth Server actually issued the token. Second, it can be used as an actual resolvable URL by the App or Resource Server to _introspect_ the token, meaning to invoke service endpoints on the Auth Server to query for more information about the token or the server.

In our example, we will use the issuer URL of: `https://identity.example.com:9200`

**Client Definition**

We will need to create a definition for the client (i.e. the **App**) in the identity server. This definition should include the following details:

- The App should be approved to request the scopes `openid` and `profile` (these are standard OIDC scopes) as well as `patient/*.read` (a SMART on FHIR scope that is automatically understood by Smile CDR)

- The Auth Server should also include the `"patient"` claim in tokens it generates. You might start by simply manually hard coding this claim, and then refining this once the rest of the flow is working.

## Step 2: Create an Authentication Script

The following is an example of an [Authenticaton Script](/content/docs/smart/smart_on_fhir_inbound_security_module.html#accepting-external-access-tokens/index.html).

In this script, we examine the "patient" claim, and if it exists we will grant the user full read/write permissions for any data in the given patient's "compartment" (compartments are a FHIR concept indicating resources that are deemed to "belong" to the given Patient). We will use scopes to indicate whether a given app should be allowed to read and/or write, so despite giving the user full permissions here, these rights will be reduced when a token is processed based on the approved scopes.

```javascript
function onAuthenticateSuccess(theOutcome, theOutcomeFactory, theContext) {

// Just log a bit about the user logging in.
   var username = theOutcome.username;
   var remoteAddress = theContext.remoteAddress;
   Log.info('User ' + username + ' is logging in from ' + remoteAddress);

// Authorize access based on the "patient" claim.
   var patient = theContext.getClaim("patient");
   Log.info("Have access to patient: " + patient);
   theOutcome.addAuthority('FHIR_READ_ALL_IN_COMPARTMENT', 'Patient/' + patient);
   theOutcome.addAuthority('FHIR_WRITE_ALL_IN_COMPARTMENT', 'Patient/' + patient);

return theOutcome;
}
```

Copy

## Step 3: Create a SMART Inbound Security module

If you do not already have one created, you should now create a SMART Inbound Security module in your Smile CDR module configuration. Give this module an ID, and then paste the script from step 2 into the **Authentication Callback Script (Text)** box. No other settings are required.

## Step 4: Configure the FHIR Endpoint module

In your FHIR Endpoint module you will want to:

- Enable OpenID Connect Security
- Set a module dependency on your new SMART Inbound Security module
- Restart the module

## Step 5: Create an OIDC Server Definition

Under _Config_ _OpenID Connect Servers_ you should create a server definition. This definition corresponds to your _Auth Server_.

Grant the server an Issuer ID of `https://identity.example.com:9200` and paste the JWK (the full JSON document) from step 1 above into the text box labelled **Key (Text)** and then hit save.

No other settings are required.

## Testing This Setup

If you are able to, you can now request a real token from your _Auth Server_. For the purposes of demonstration, we'll go through an example that should also work with the given configuration.

The following JSON document is a JWT claim set containing the required claims for the setup described here:

```json
{
    "iss": "https://identity.example.com:9200",
    "iat": 1573065974,
    "exp": 1604688374,
    "aud": "https://fhir.example.com:8000",
    "sub": "jsmith123",
    "scope": "openid profile patient/*.read",
    "family_name": "Smith",
    "given_name": "John",
    "patient": "A123"
}
```

Copy

When signed using the key above, this claim set results in the token below. Note that if you want to try modifying the claim set above and generating your own new token for testing you can do so using [this tool](http://jwtbuilder.jamiekurtz.com/). If you want to try parsing and verifying the signed token below, you can do so at [JWT.io](https://jwt.io/).

```none
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2lkZW50aXR5LmV4YW1wbGUuY29tOjkyMDAiLCJpYXQiOjE1NzMwNjU5NzQsImV4cCI6MTYwNDY4ODM3NCwiYXVkIjoiaHR0cHM6Ly9maGlyLmV4YW1wbGUuY29tOjgwMDAiLCJzdWIiOiJqc21pdGgxMjMiLCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIHBhdGllbnQvKi5yZWFkIiwiZmFtaWx5X25hbWUiOiJTbWl0aCIsImdpdmVuX25hbWUiOiJKb2huIiwicGF0aWVudCI6IkExMjMifQ.un-o5De6VvmCUcuA8usKYzQmIGP95PTUYs78pxxm8gs
```

Copy

You may now invoke your FHIR endpoint using a request such as the following:

```http
GET Patient/123
```

Copy

## Extra Authorization Script Features

The following snippet shows the authorization script above with a few more examples of possible interactions.

Note that method `getClaim()` can be used on `theContext` object to get any claim in the JWT, of any data type.
Null will be returned, if no claim exists.

Alternatively, method `getClaims()` can be used to get all claims from the token as a Map object.

```javascript
function onAuthenticateSuccess(theOutcome, theOutcomeFactory, theContext) {

// Example of granting permissions based on specific claims. We'll
   // give users with the "ehr_admin" claim full superuser rights, and
   // we'll give users with the "ehr_user" claim access to all patient
   // data.
   if (theContext.getClaim("ehr_admin") === true) {
      theOutcome.addAuthority('ROLE_FHIR_CLIENT_SUPERUSER');
   }
   if (theContext.getClaim("ehr_user") === true) {
      theOutcome.addAuthority('FHIR_READ_ALL_OF_TYPE', 'Patient');
   }

// Example of adding permissions based on scopes
   if (theContext.getApprovedScopes().contains('view-claims')) {
       theOutcome.addAuthority('FHIR_READ_ALL_OF_TYPE', 'Claim');
   }

// Authorize access based on the "patient" claim
   var patient = theContext.getClaims()["patient"];
   Log.info("Have access to patient: " + patient);
   theOutcome.addAuthority('FHIR_READ_ALL_IN_COMPARTMENT', 'Patient/' + patient);

return theOutcome;
}
```

Copy

[Consent Service: Java API](/content/docs/security/consent_service_java.html)

Security Recipes

Security

[Authentication Protocols](/content/docs/security/authentication_protocols.html)
[Authorization and Consent](/content/docs/security/authorization_and_consent.html)
[Inbound Security Module](/content/docs/security/inbound_security_module.html)
[Local Inbound Security Module](/content/docs/security/local_inbound_security_module.html)
[LDAP Inbound Security Module](/content/docs/security/ldap_inbound_security_module.html)
[Scripted Inbound Security Module](/content/docs/security/script_inbound_security_module.html)
[SAML Inbound Security Module](/content/docs/security/saml_inbound_security_module.html)
[Trusted Client Mode](/content/docs/security/trusted_client_mode.html)
[Roles and Permissions](/content/docs/security/roles_and_permissions.html)
[Callback Scripts](/content/docs/security/callback_scripts.html)
[Anonymous Access](/content/docs/security/anonymous_access.html)
[Consent Service](/content/docs/security/consent_service.html)
[Consent Service: JavaScript API](/content/docs/security/consent_service_javascript.html)
[Consent Service: Java API](/content/docs/security/consent_service_java.html)
[Security Recipes](/content/docs/security/security_recipes.html)
[Two Factor Authentication](/content/docs/security/two_factor_auth.html)
[Password Hashing Algorithms](/content/docs/security/password_hashing_algorithms.html)
[JWT Signature Algorithms](/content/docs/security/jwt_signature_algorithms.html)
[Troubleshooting Security](/content/docs/security/troubleshooting_security.html)

[Two Factor Authentication](/content/docs/security/two_factor_auth.html)
