Closed syphax-bouazzouni closed 10 months ago
Hi @manuelfiorelli, can you please give more details of how it works, and what is needed to make it work?
The implementation of single sign-on (SSO) in EcoPortal relies on the OAuth 2.0 and OpenID Connect standards. The current deployment in a development environment uses Keycloak as the actual identity and access management software that implements both standards, playing the role of the authorization server.
Both the web UI and and the API were modeled in the authorization server as client, each with its own set of client credentials:
In EcoPortal, we externalized authentication and login management to the authorization server, while keeping the internal user management and api key to avoid disrupting the architecture. After the login phase, most of EcoPortal runs unmodified.
By first, SSO must be enabled, by setting the global variable $SSO_ENABLED
to true
and providing the necessary configurations at the UI and API levels.
config/initializers/oauth2.rb
takes most configuration from metadata returned by authorization server in a standard format and saved in the file config/oauth2/idp-configuration.json
. Additionally, this initializer takes the aforementioned client credentials stored using the Rails Credentials APIin the API, the configuration is stored in the LinkedData Settings:
begin
LinkedData.config do |config|
# ...
# OAUTH2
config.oauth2_enabled = true
config.oauth2_authorization_server_signature_cert = 'certificate of the the authorization server'
config.oauth2_authorization_server_signature_alg = 'RS256'
config.oauth2_username_claim = 'preferred_username'
config.oauth2_given_name_claim = 'given_name'
config.oauth2_family_name_claim = 'family_name'
config.oauth2_email_claim = 'email'
config.oauth2_audience = 'the api service id'
config.oauth2_issuer = 'the authorization server endpoint'
If SSO is enabled, the login procedure changes slightly. If you look at config/routes.rb
, you can see that login and logout are served by a different controller dedicated to OAuth 2.0.
Upon login, the Oauth2Controller
initiates the interaction with the authorization server, by asking an authorization code: the controller redirects the user to the authorization server, allowing the user to login if not already logged in (hence, SSO), and then redirecting the user to a callback URL in the web UI together with the requested authorization code.
After performing some security checks (including validating some nonces to avoid some common attacks), the callback implementation uses the authorization code to request (in a backend channell) an access token for the web UI client. This access token is then used to invoke the users/authenticate
API, replacing the usual user credentials.
If SSO is enabled, the implementation of the endpoint users/authenticate
is changed accordingly. The idea is that the API validates the supplied access token, obtaining:
{
"exp": 1676239549,
"iat": 1676239249,
"auth_time": 1676239238,
"jti": "4e8158ab-b869-4138-8366-c0ce5e2ace62",
"iss": "the authorization server endpoint",
"aud": [
"ecoportaldev-api",
"ecoportal-api",
"account"
],
"sub": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"typ": "Bearer",
"azp": "ecoportaldev-web-ui",
"nonce": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"session_state": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"acr": "1",
"allowed-origins": [
"https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
],
"realm_access": {
"roles": [
"default-roles-ecoportal",
"offline_access",
"uma_authorization"
]
},
"resource_access": {
"ecoportaldev-api": {
"roles": [
"Logged_in"
]
},
"ecoportal-api": {
"roles": [
"Logged_in"
]
},
"account": {
"roles": [
"manage-account",
"manage-account-links",
"view-profile"
]
}
},
"scope": "openid profile email",
"sid": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"email_verified": true,
"name": "A B",
"preferred_username": "ab",
"given_name": "A",
"family_name": "B",
"email": "ab@example.org"
}
After checking that this token was signed by the EcoPortal authorization server, the API can do a few checks on its own, including that the token is temporally valid and intended to be consumed by the API (in addition to the user interface). If all these checks pass, the the API knows that the authorization server authenticated the users and authorized the user to access the API. Indeed, without checking the audience, the user could have requested a token for a different service, and then have used to access the API.
Beyond login, the 'Oauth2Controller` also implements RP-initialed logout, so that one a users logs out from EcoPortal it closes also the session in the authorization server.
Hi @manuelfiorelli, thanks for this detailed explanation.
So If I understood well,
You have added for Lifewatch an external authentification server (to share the same authentication system with all the applications of the organization including Ecoportal), using this tool Keycloak (Lifewatch-Ecoportal specific)
If the global variable $SSO_ENABLED
is set to true, now the UI login link redirects users to your Keycloak server that returns a callback with the token of the authenticated user (and other information about him).
That token
received in the UI is then sent to the API which tries to decode it. If successful the user is connected (and created if first time connection)
On the UI side, you used this package https://github.com/nov/openid_connect (to contact the Authorization server) On the backend, you used this package https://github.com/oauth-xx/oauth2 (to decode the Token)
I think the implementation is good, and that we can integrate to Agroprotal (@jonquet). But in the condition that it is generalized to work with multiple Authorization servers (identity providers), Self-hosted organizational authorization servers, Github, and ORCID.
So do you think @manuelfiorelli, that you can update it (easily) to make it work with the multiple authorization servers (in priority for Github)
@jvendetti if we make it work for Github, do you have any requirements and/or remarks to give us; regarding your parallel work on GitHub request changes?
I think the implementation is good, and that we can integrate to Agroprotal (@jonquet). But in the condition that it is generalized to work with multiple Authorization servers (identity providers), Self-hosted organizational authorization servers, Github, and ORCID.
This will be my opinion too. @manuelfiorelli we know it's a bit more time and work to generalize to "other" possible situation in the Alliance... but that's really what makes the difference. If you have a chance to do it, would be very nice.
@jvendetti if we make it work for Github, do you have any requirements and/or remarks to give us; regarding your parallel work on GitHub request changes?
When I generate a GitHub issue from the Rails application, I'm interested in some way to know if the logged in user has a GitHub account name. The idea is that the body of the GitHub issue could incorporate their GitHub account name with GitHub's mention syntax, e.g., something like:
This request was issued from OnotPortal by @syphax-bouazzouni
We're interested in using the mention syntax so that we can take advantage of GitHub's built-in notifications. Currently we output the BioPortal account name in issue bodies, but the GitHub account name would be preferable:
Please let me know if you'd like any additional details.
Close as done in https://github.com/ontoportal-lirmm/bioportal_web_ui/releases/tag/v2.7.0, discussion about merging it to the main Ontoportal branch will be discussed here https://github.com/ontoportal/ontoportal-project/issues/37
Context
We are working currently at Agroportal with Ecoportal to align our two codebases; which means resting extracting reusable features from both codebases and sharing them with Ontoportal.
For example, EcoPortal developed an administration panel for groups and categories (to add, edit and delete them) that we extracted from Ecoportal and integrated into Agroprtal (and soonly shared with Ontoportal, see https://github.com/ontoportal-lirmm/bioportal_web_ui/pull/237)
In that context, we also discovered that EcoPortal implemented an SSO implementation, which we think can be a great contribution to Ontoportal.
This implementation includes those changes:
The developer of this is @manuelfiorelli from the Ecoportal team.