On this page:

7.5Scripted Inbound Security

 

The Scripted Inbound Security module uses a JavaScript script to perform user authentication.

With this module, the script receives credentials (a username and password), and validates them. If the credentials are not valid, the script returns an error and the user is not permitted to authenticate.

If the credentials are valid, the script returns information about the user logging in.

7.5.1Script Format

 

Authentication scripts used by this module must implement a JavaScript function called authenticate. Scripts may take advantage of the full Smile CDR JavaScript Execution Environment.

Method: authenticate

This section describes the API used for the authenticate function.

Input Parameter: theRequest

This object contains the credentials of the authenticating user. It may be used to request the following details:

  • Username – The username supplied by the user logging in.
var username = theRequest.username;
  • Password – The password supplied by the user logging in.
var password = theRequest.password;
  • Remote Address – The IP of the user/client making the authentication request.
var ip = theRequest.remoteAddress;

Input Parameter: theOutcomeFactory

The theOutcomeFactory object is used as a factory for the return value that is returned from the authenticate method. It creates a success or failure object.

See returning failure for information on how to abort a login.

7.5.2Script Examples

 

This section shows several examples of authentication scripts.

Simple Hard-Coded Example

The following example shows a simple authentication function that uses a hardcoded username and password defined in the script. This is only useful for basic testing, and would generally not be considered secure for any real scenarios.

/*
 * This is an authentication script for the script-inbound-security
 * module. It is required to contain a function called "authenticate".
 *
 * This function takes two parameters:
 *   - theRequest: This object contains the incoming credentials.
 *   - theOutcomeFactory: This is a factory for the success/failure responses
 *              to be returned by this function.
 */
function authenticate(theRequest, theOutcomeFactory) {

   // Create a log entry
   var ip = theRequest.remoteAddress;
   Log.info("User " + theRequest.username + " is authenticating from " + ip);

   /*
    * If the username is "myadmin" and the password is "password123"
    *
    * This is an admin user with a hardcoded username and password.
    * In a real system you would probably want to do something more
    * secure and scalable, but this is a good first introduction.
    */
   if (theRequest.username == 'myadmin') {
      if (theRequest.password == 'password123') {

         // Create a successful login response
         var success = theOutcomeFactory.newSuccess();
         success.username = 'myadmin';
         success.familyName = 'Smith';
         success.givenName = 'John';

         // Grant admin user full authority
         success.addAuthority('ROLE_SUPERUSER');
         return success;

      }
   }

   // Create a failing response if we haven't successfully
   // authenticated
   var failure = theOutcomeFactory.newFailure();
   failure.message = 'Bad password!';
   failure.incorrectPassword = true;
   return failure;

}

Calling an External REST Authentication Service

The following example shows a script that authenticates against an external authentication service. This example uses the Okta authentication API (reference) to validate credentials, and it treats the logging-in username as a Patient ID for a Patient user who is logging in.

/*
 * This is an authentication script for the script-inbound-security
 * module. It is required to contain a function called "authenticate".
 *
 * This function takes two parameters:
 *   - theRequest: This object contains the incoming credentials.
 *   - theOutcomeFactory: This is a factory for the success/failure responses
 *              to be returned by this function.
 */
function authenticate(theRequest, theOutcomeFactory) {

   /*
    * Authenticate using Okta
    */
    var username = theRequest.username;
    var password = theRequest.password;

	// Prepare an HTTP POST (JSON payload)
	var post = Http.post('http://localhost:30000/api/v1/authn');
	var authRequest = new Object();
	authRequest.username = username;
	authRequest.password = password;
	post.setContentJson(authRequest);

	// Invoke the auth service
	post.execute();

    // If this method returns false, we either had a connection
    // failure, or a non-200 HTTP response
	if (!post.isSuccess()) {
	    var failure = theOutcomeFactory.newFailure();
	    failure.message = post.getFailureMessage();
	    return failure;
	}

	// Parse the response
	var responseJson = post.parseResponseAsJson();
	if (responseJson.status == 'SUCCESS') {

		// Create a success login
		var success = theOutcomeFactory.newSuccess();
		success.username = responseJson._embedded.user.profile.login;
		success.familyName = responseJson._embedded.user.profile.lastName;
		success.givenName = responseJson._embedded.user.profile.firstName;

		// We'll treat the username as the patient scope in this
		// example
		var patientId = success.username;

		// Set the current patient ID as the launch context so that
		// the "launch/patient" scope can be handled correctly by
		// the auth server
		success.addLaunchResourceId('patient', patientId);

		// Grant the user any authorities they should be allowed to
		// have. In this case, we're granting READ ONLY access to
		// anything in the Patient's own compartment.
		success.addAuthority('FHIR_READ_ALL_IN_COMPARTMENT', 'Patient/' + patientId);

		// Retudn the successful authentication
		return success;
	}

	// Fail by default
    var failure = theOutcomeFactory.newFailure();
    failure.message = 'Bad username!';
    failure.incorrectUsername = true;
    return failure;
}