How to connect openmrs-module-oauth2login to OpenMRS via Keycloak and enable auto-user creation?

Hi everyone,

I am trying to integrate OpenMRS 2.12.2 RefApp with Keycloak using the openmrs-module-oauth2login module → GitHub - openmrs/openmrs-module-oauth2login: Delegates user authentication to an OAuth 2.0 authentication provider. . Here is what I have done so far:

Steps Taken:

  1. Installed Keycloak 25.0.1 (using Docker) and created a new realm demo for OpenMRS users.
  2. Created a Keycloak client for OpenMRS with the following settings:
  • Client ID: openmrs
  • Access type: Confidential (in keycloak am using this is by default once i enabled Client authentication)
  • Direct Access Grants: Enabled
  • Valid Redirect URI: http://localhost:8080/openmrs/*
  • Web Origins: http://localhost:8080
  1. Configured Keycloak client scopes and mappers to include the following claims in the token: sub, preferred_username, given_name, family_name, email.
  2. Generated a client secret in Keycloak and added it to the OAuth2 module properties file (OAuth2.properties) in OpenMRS app data directory.
  3. Mapped Keycloak claims to OpenMRS fields:
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.systemId=sub
  1. Enabled login flow and tested manually via Postman – I can successfully retrieve the user info from Keycloak using the access token.

Current Problem:

  • When attempting to login through the OpenMRS OAuth2 login page, I am redirected to Keycloak and can login successfully.
  • However, after sign-in, OpenMRS does not redirect to the home page, and the logs show:
The user 'temp' could not be authenticated with the identity provider.
  • It seems OpenMRS is receiving the correct user info JSON from Keycloak, but the user is not being authenticated.

Questions:

  1. How can I properly connect openmrs-module-oauth2login to OpenMRS via Keycloak so that after Keycloak login, the user is redirected to OpenMRS home?
  2. How can I enable auto-creation of users on first login using the OAuth2 module?

Any guidance, sample configurations, or tips on debugging this issue would be greatly appreciated!

Thanks in advance.

cc @ruhanga @mksd @wyclif @ibacher

1 Like

Might be helpful if you can post the server log around one of these events happening.

here is the logs

cc @ibacher

Should it be that the user I use should be in openmrs by default? For more context, I created a temp user in keycloak and am able to login in directly but unable to link

Also here is my full oath2.properties

clientId=openmrs

clientSecret=SOMETHING

publicKey=SOMETHING

publicKeyFilename=publicKey.txt

userAuthorizationUri=http://localhost:8081/realms/demo/protocol/openid-connect/auth

accessTokenUri=http://localhost:8081/realms/demo/protocol/openid-connect/token

userInfoUri=http://localhost:8081/realms/demo/protocol/openid-connect/userinfo

logoutUri=http://localhost:8081/realms/demo/protocol/openid-connect/logout?id_token_hint=[token]

keysUrl=http://localhost:8081/realms/demo/protocol/openid-connect/certs

scope=openid profile email


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.systemId=sub

Keycloak running on http://localhost:8081

Openmrs running on http://localhost:8080/openmrs

Hi @jonathan ,

Simply copying what I share on slack, the OAuth 2 Login module in OpenMRS does create new users automatically if they successfully authenticate through the SSO provider. However, I have a few clarifying questions and points:

  1. How did you create the users for the OpenMRS client?

  2. The client secret you added to oauth2.properties isn’t used as a traditional secret—instead, it acts as a bearer token for some web requests to the client. This token does expire after a period.

  3. If you’re using Docker, note that using localhost in your URLs won’t work as expected due to container networking.

I recommend checking how this is implemented in the OpenMRS HIS setup and building on that. You should be able to adapt it to your specific needs—you may need docker compose for this (particularly looking at the keycloak service with the accompanying keycloak configs, and if you are able to build the OpenMRS HIS distro, you will find the reference oauth2.properties somewhere around this path target/distro/configs/openmrs/properties/oauth2.properties).

2 Likes

@jonathan it is also likely that there is another module providing conflicting Authentication schemes, such as is the case with the current Ref App Distro that contains an Authentication module.

2 Likes

Thanks @ruhanga, the issue was caused by the Authentication module, which was conflicting and redirecting to the default authentication method instead of using the OAuth2 protocol.

1 Like

Not sure this has had traction so far, but it would be nice to provide a centralisdd auth registry in the core, more like spring’s AuthenticationManagerBuilder but with openmrs-specific hooks for priority ordering, conflict detection, and fallback mechanisms.

2 Likes

@thembo42 it makes sense to consider harvesting the feature into OpenMRS core once the approach has proven effective. A reasonable path might be to first update the OAuth2 Login module to use the proposed library/API. If that integration works well, it can later be brought into the core platform.

IMO, it’s still important to keep OpenMRS core as lightweight and generic as possible, enabling further feature extensions to be added/built through its modular architecture—which remains one of its key strengths, especially if not everyone uses the features.

3 Likes

Well, with 3.0 upgrades like TRUNK-6327, it fits naturally. Though termed as an “authorization plugin,” Spring Security’s broader framework (beyond just ACLs for authz) could provide a plumbing layer with support for multi-scheme delegation without conflicts, allowing runtime registration unlike overrides.

But yes, I really agree with the phased low-risk approach/validation. So it would be potentially important to make a sub-task under TRUNK-6327 to provide evidence for-example, updates to oauth2login module, including success metrics on conflict resolution. This would help build the case for core harvesting while keeping things modular.

3 Likes

This is basically what the authentication module already provides. The issue is that the oauth2login module predates that module and so doesn’t take advantage of it’s more flexible authentication system.

1 Like

By this it makes sense … I think the best approach would be to remove the Authentication module inorder to use the Oath2 module or disable it at startup

Ultimately, the oauth2login module needs to be re-worked to support the authentication module.

2 Likes

I’ve been configuring the OpenMRS OAuth2 Login module to work with Keycloak and using the following mappings successfully:

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.person.gender=gender
openmrs.mapping.user.systemId=sub
openmrs.mapping.user.roles=roles

This works well new users are created automatically in OpenMRS at first login, and their basic details sync correctly.

However, I’m wondering :right_arrow: Is it possible to also create or map a Location for each user during OAuth2 login?

In other words, can the OAuth2 module handle location mapping, or would this require adding a custom listener or module to handle it after user creation?

Any guidance or examples would be appreciated :folded_hands:

cc @ruhanga @ibacher @mksd

1 Like

This isn’t currently supported during the OAuth2 login process. That’s why users are prompted to select a location after logging in, since a user could potentially log in through any of the configured locations.

Adding custom listener logic to handle user-to-location mapping through a custom module could help but, why not use the Data Filter module for this instead?

1 Like