Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ontoportal SSO feature #26

Closed
syphax-bouazzouni opened this issue May 16, 2023 · 7 comments
Closed

Ontoportal SSO feature #26

syphax-bouazzouni opened this issue May 16, 2023 · 7 comments
Assignees
Labels
enhancement New feature or request

Comments

@syphax-bouazzouni
Copy link
Contributor

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 ontoportal-lirmm/bioportal_web_ui#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.

@syphax-bouazzouni
Copy link
Contributor Author

Hi @manuelfiorelli, can you please give more details of how it works, and what is needed to make it work?

@manuelfiorelli
Copy link

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:

  • the former uses the standard authentication flow, and it is configured so that the tokens have also the api in their audience
  • the latter is a bearer-only client, since users are only meant to access it through a bearer token

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.

  • in the UI, 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 API
  • in 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:

  • the identity of the user
  • the user email
  • the user first name
  • the user last name
    Instead of contacting the introspection endpoint of the authorization server, we adopted JSON Web Token (JWT), so that all the aforementioned information is stored in the token itself, as a short-lived, signed set of claims.
    To give you an idea, this is an example of a decoded token:
{
  "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": "[email protected]"
}

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.

@syphax-bouazzouni
Copy link
Contributor Author

syphax-bouazzouni commented May 28, 2023

Hi @manuelfiorelli, thanks for this detailed explanation.

So If I understood well,

  1. 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)

  2. 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).

  3. 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)

image

@syphax-bouazzouni
Copy link
Contributor Author

@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?

@jonquet
Copy link
Contributor

jonquet commented May 30, 2023

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
Copy link

@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:

Screen Shot 2023-06-02 at 12 42 48 PM

Please let me know if you'd like any additional details.

@syphax-bouazzouni
Copy link
Contributor Author

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 #37

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants