On this page:

11.17Federated OAuth2/OIDC Login

 

By default, when a SMART Application requests a new authorization from the SMART Outbound Security module, the module will prompt the user for credentials in order to authenticate them, and will use an Inbound Security Module to verify the credentials directly.

Instead of prompting the user for credentials however, it is also possible to redirect the user to a third-party "Federated" OAUth2/OIDC provider. This allows SMART authentication against a standards compliant OIDC provider that is able to complete the OAuth2 Authorization Code flow, but is not able to support the SMART on FHIR extensions to the base OIDC specification.

For example, this method could be used to:

  • Support authenticating users via social logins such as Google, Twitter, and Facebook.
  • Support authenticating users against cloud identity providers such as Okta or Ping, while leveraging SMART Client Management in Smile CDR and potentially injecting additional details such as launch contexts that would not be available to the cloud provider.

11.17.1Federated Flow

 

The following diagram shows how the Federated OAuth2/OIDC Login flow works.

Federated OAuth2 Flow

11.17.2Considerations

 

When using federated flow, a few key points to consider include:

  • When using this flow, an access token is generated by the Federated OIDC Provider, but this token is only used by Smile CDR to perform introspection. To the Federated OIDC Provider, your Smile CDR installation is the client requesting access. This means that even if many SMART Applications are registered in Smile CDR and then used, a single client definition will be used by the Federated OIDC Provider.
  • All SMART Applications are authorizing against the Smile CDR SMART Outbound Security module. These applications are unaware of the Federated OIDC Provider.
  • Access tokens and refresh tokens used to access your FHIR APIs are managed by Smile CDR and not by the Federated OIDC Provider.

Using Federated OAuth2/OIDC Login with the SMART Outbound Security module is not the only way to integrate with a third-party OIDC provider however. See Models for Using External OIDC Servers for a discussion on available options.

11.17.3Setting Up Federated OAuth2/OIDC Login

 

To enable Federated OAuth2/OIDC Login, use the following steps:

1. Create a Client Definition for Smile CDR

In the federated OAuth2 provider, create a client definition for Smile CDR. Note that even if you will be registering multiple client (SMART App) definitions in Smile CDR, you will only need to create one definition in the federated provider.

This provider should:

  • Support the Authorization Code flow (required)
  • Have a client secret defined (required)
  • Have reachable endpoints for user authorization (Authorization Endpoint), token exchange (Token Endpoint), and user introspection (UserInfo Endpoint)
  • Support OpenID Connect with the openid and profile scopes (optional but recommended)
  • Support PKCE if possible (optional but recommended)

2. Create/Configure a SMART Outbound Security Module

If one hasn't already been created, create a new module of type SMART Outbound Security.

In the module definition, enable the Federated OAuth2/OIDC Login option. Note that if this is enabled, the module will no longer support direct user authentication (i.e. prompting a user for username and password from within Smile CDR and verifying the credentials directly against a database/LDAP/etc).

3. Create an OIDC Server Definition

In the Web Admin Console, navigate to Config -> OpenID Connect Servers, select your SMART Outbound Security module from the dropdown and click Create. The Server definition refers to the Federated OAuth2/OIDC Provider itself.

You can also create your definition programatically at runtime using the OpenID Connect Servers Endpoint, or automatically at startup using Pre-Seeding OpenID Connect Servers.

This definition should include all of the relevant endpoint URLs for the provider, and also needs to include an Authorization Script as described below.

At least one definition must be created, but you can also create more as necessary. If only one federated definition is created, users will automatically be directed to this provider when a new authorization is initiated. If multiple definitions are created, the user will first be prompted for which provider they wish to authorize against.

4. Create at least one OIDC Client Definition

Create a definition for the SMART Application you wish to authorize, or multiple definitions if you have multiple applications.

5. Configure your FHIR Endpoint module

In your FHIR Endpoint module configuration, enable OpenID Connect Security and add an OIDC module dependency from your FHIR Endpoint module to your SMART Outbound Security module.

11.17.4Authorization Script

 

Each server definition used for federated login must include an Authentication Callback Script that implements the onAuthenticateSuccess function. This function is described here.

The purpose of this function is to take the user session created by the federated provider, and enhance it by adding any necessary session information including:

  • User demographics
  • Smile CDR permissions
  • Launch context details if known

Example

The following example shows an authentication callback script for Federated OAuth2/OIDC Login.

/**
 * This is a sample authentication callback script for the
 * SMART Outbound Security module, showing federated OAuth2/OIDC Login 
 *
 * @param theOutcome The outcome object. This contains details about the user that was created
 * 	in response to the incoming token.
 * @param theOutcomeFactory A factory object that can be used to create a new success or failure
 * 	object
 * @param theContext The login context. This object contains details about the authorized
 * 	scopes and claims
 * @returns {*} Either a successful outcome, or a failure outcome
 */
function onAuthenticateSuccess(theOutcome, theOutcomeFactory, theContext) {
   
   // In this example we are demonstrating a patient-facing app, where the user corresponds to a FHIR Patient, and the
   // ID of that patient is passed back from the federated provider via a claim in the JWT-encoded access token. Instead
   // the ID might be fetched using an HTTP call, or derived from something else.
   var patientId = theContext.getStringClaim('patientId');

   // Add a log line for troubleshooting
   Log.info("User " + theOutcome.getUsername() + " has authorized for " + patientId + " with scopes: " + theContext.getApprovedScopes());

   // All users can use the FHIR CapabilityStatement operation
   theOutcome.addAuthority('FHIR_CAPABILITIES');

   // Assign appropriate Smile CDR permissions
   theOutcome.addAuthority('FHIR_READ_ALL_IN_COMPARTMENT', 'Patient/' + patientId);
   theOutcome.addAuthority('FHIR_WRITE_ALL_IN_COMPARTMENT', 'Patient/' + patientId);

   // Set the launch context (in case the application has requested a SMART launch context scope). This should only
   // be set if the patient referenced by the ID is actually in context for this launch.  
   theOutcome.addLaunchResourceId('patient', patientId);

   return theOutcome;
}