Coder Social home page Coder Social logo

openmrs-module-oauth2login's Introduction

OpenMRS OAuth 2.0 Login Module

Description

This module delegates user authentication to an OAuth 2.0 resource provider. In effect it turns OpenMRS into an OAuth 2.0 client as soon as the module is installed and running.

Overview

It suffices to install the module for OpenMRS' default authentication scheme to become inactive and for the module custom authentication scheme to take over.

The module consumes a configuration file oauth2.properties that must be dropped in the OpenMRS app data directory:

.
├── modules/
├── openmrs.war
├── openmrs-runtime.properties
├── ...
└── oauth2.properties

The properties configuration contains two separate sets of settings:

  1. The usual OAuth 2 properties:

    • The client ID and secret.
    • A couple of URIs to transact with the OAuth 2 provider: user authorization URI, access token URI and user info URI.
  2. OpenMRS users properties mappings with the OAuth 2 'user info'.
    For new users the master information is first maintained with the OAuth 2 provider, starting with their username. This information is obtained through a JSON response from the user info URI. A simple one-to-one mapping between what is needed from an OpenMRS user's perspective and what is given by the OAuth 2 provider can be provided through the OAuth 2 properties file.

The module ships with sample test resources that show how the OAuth 2 properties file should look like when using JBoss' Keycloak and Google API as OAuth 2 providers, see here.

Authentication Mechanism

Overview

OpenMRS requires persisted OpenMRS users with roles to perform actions within the application. For the OAuth 2 provider to be able to take care of authentication there has to be a duplication of users in both systems. A user will exist both with the OAuth 2 provider and the corresponding user will also exist within OpenMRS *.

The authentication is based on the username.

* This duplication of users could be avoided if OpenMRS was fully leveraging Spring Security. This is not yet the case and as of now authorization is made based on users that are persisted and accessed through the DAO layer.

On-the-fly user creation

However at first the user might not exist yet in OpenMRS and as a convenience the module will create a new OpenMRS user on the fly. This is why a mapping mechanism must exist between the OAuth 2 provider user infos and the OpenMRS users, at minima to find out what the OpenMRS username will be.

Keeping identities in sync with OpenMRS

The main use case is to help support the management of users and roles outside of OpenMRS, with the identity provider. The User pieces of information that are mapped to OpenMRS user properties and that can be updated at each authentication are listed here:

* Username and system ID cannot be updated, they are set once and for all at the first authentication of the user.

Externalised role management

A list of OpenMRS roles can be provided through the user info JSON. This can be done through leveraging the openmrs.mapping.user.roles mapping property that holds a pointer to the user info JSON key whose value is a comma-separated list of OpenMRS role names.

For instance a basic user info JSON might look like that:

{
  "sub": "4e3074d6-5e9f-4707-84f1-ccb2aa2ab3bc",
  "email": "[email protected]",
  "roles": ["Nurse", "Clinical Advisor"]
}

With an OAuth 2 properties mapping set as such:

openmrs.mapping.user.roles=roles

Where "Nurse" and "Clinical Advisor" are expected to be OpenMRS role names. Those role names are then used to fetch OpenMRS roles to be assigned to the user. All the role names that do not point to existing OpenMRS roles are skipped.

This requires using an identity provider that allows the configuration of the user info JSON with custom members, such as this "roles" member in the above example.

Sample mapping

Let us start from a sample JSON to understand how the mappings should be set.

1) Sample user info JSON:
{
  "sub": "4e3074d6-5e9f-4707-84f1-ccb2aa2ab3bc",
  "preferred_username": "tatkins",
  "given_name": "Tommy",
  "family_name": "Atkins",
  "email": "[email protected]",
  "roles": [
    "Provider",
    "Nurse"
  ]
}
2) Corresponding mappings needed in oauth2.properties:
openmrs.mapping.user.systemId=sub
openmrs.mapping.user.username=preferred_username
openmrs.mapping.person.givenName=given_name
openmrs.mapping.person.familyName=family_name
openmrs.mapping.user.email=email
openmrs.mapping.user.roles=roles

Example

If a user authenticates as 'jdoe' with the OAuth 2 provider, OpenMRS will attempt to fetch the user 'jdoe'.

  • If a 'jdoe' user can be found in OpenMRS, then it will updated as per the user info JSON and become the authenticated user.
  • If a 'jdoe' user cannot be found in OpenMRS, it will be created as per the user info JSON and become the authenticated user.

Redirect URL after successful login

By default the user will be redirected to the root URL / after a successul login. The redirect URL can be modified by using the global property (GP) oauth2login.redirectUriAfterLogin. For example when the module is used within the Reference Application with the two-screen login enabled, this GP can be used to enforce a redirect to the login GSP page (hence kicking in its Java controller logic):

/referenceapplication/login.page?redirectUrl=/index.html

Two-step login with OpenMRS 2.x

In OpenMRS 2.x it is necessary to explicitely enable the two-step login for the OAuth2 delegated authentication to work properly. To do so make sure that the following global property exists with a non-blank value: referenceapplication.locationUserPropertyName.

Service Account Authentication

Not all interaction with OpenMRS is by human users, some is server-to-server interaction, third party systems need to authenticate with OpenMRS in order to access desired resources, these applications should be able to provide a token obtained from an identity provider that can be trusted by OpenMRS to authenticate and authorize them to access restricted resources, service account authentication is aimed at addressing this requirement.

How it Works

The third party system obtains a JWT token from an identity provider and then for any subsequent requests, it sets it as the value of the authorization header with the scheme set to Bearer or a special header named X-JWT-Assertion as shown below,

Authorization: Bearer YOUR-JWT-TOKEN

OR

X-JWT-Assertion: YOUR-JWT-TOKEN

When OpenMRS receives the request, it reads the JWT from the header, parses and verifies its signature, if all is good, it goes ahead to read the username from the JWT payload and uses it to authenticate the request using this module's Oauth2 authentication scheme, this assumes a user account already exists in OpenMRS with the specified username.

Configuration

In order for OpenMRS to verify the signature of a JWT, it needs a key to do so. For enhanced security, the module only supports asymmetric algorithms. Currently, only RSA based algorithms(RS256, RS384, RS512, PS256, PS384, PS512) are supported. Therefore, you need to provide a public key from the identity provider to be used to verify the JWT signatures. The public key can be configured in 3 ways and below is the lookup order,

  1. From the oauth2.properties file as the value of the publicKey property
  2. From a specific file located in the application data directory or its subdirectories, this file is configured via the oauth2.properties file as the value of the publicKeyFilename property
  3. The module fetches all known keys from the identity provider at the url configured as the value of the keysUrl property in the oauth2.properties file

Configuration Guides

  1. Guide for Keycloak
  2. Guide for Google API

Requirements

OpenMRS Core 2.2.1 or Core 2.3.0 and above.

openmrs-module-oauth2login's People

Contributors

mks-d avatar icrc-loliveira avatar ibacher avatar icrc-fdeniger avatar wluyima avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.