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

[OpenID] Add OpenID Connect support to LORIS #8255

Merged
merged 13 commits into from
Nov 1, 2023
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ core section.***
- ***When possible please provide the number of the pull request(s) containing the
changes in the following format: PR #1234***

## LORIS 26.0 (Release Date: ????-??-??)
### Core
#### Features
- Add OpenID Connect authorization support to LORIS (PR #8255)

## LORIS 25.0 (Release Date: ????-??-??)
### Core
#### Features
Expand All @@ -20,7 +25,6 @@ changes in the following format: PR #1234***
- new postinstall script that automatically installs /project and eeg-browser additional npm dependencies
when `make` or `npm ci` is executed (PR #8244)


#### Updates and Improvements
- Upgrade react to version 18 (PR #8188)
- Rename subproject to Cohort (PR #7817, applied changes in LORIS-MRI PR #882)
Expand Down
19 changes: 19 additions & 0 deletions SQL/New_patches/2022-12-05-openidconnect.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
CREATE TABLE `openid_connect_providers` (
`OpenIDProviderID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`Name` varchar(255) NOT NULL,
`BaseURI` text NOT NULL, -- the provider's base uri that hosts .well-known/openid-configuration
`ClientID` text NOT NULL,
`ClientSecret` text NOT NULL,
`RedirectURI` text NOT NULL, -- our local redirectURI that the provider is configured to authorize
-- should be something like "https://something.loris.ca/oidc/callback"
PRIMARY KEY (`OpenIDProviderID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

CREATE TABLE `openid_connect_csrf` (
`State` varchar(64) NOT NULL UNIQUE,
`OpenIDProviderID` int(10) unsigned NOT NULL AUTO_INCREMENT,
`Nonce` varchar(64) NOT NULL,
`Created` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`State`),
CONSTRAINT `FK_openid_provider` FOREIGN KEY (`OpenIDProviderID`) REFERENCES `openid_connect_providers` (`OpenIDProviderID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
25 changes: 25 additions & 0 deletions modules/login/jsx/loginIndex.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Login extends Component {
},
},
mode: props.defaultmode || 'login',
oidc: null,
component: {
requestAccount: null,
expiredPassword: null,
Expand All @@ -52,6 +53,7 @@ class Login extends Component {
this.fetchData = this.fetchData.bind(this);
this.setForm = this.setForm.bind(this);
this.setMode = this.setMode.bind(this);
this.getOIDCLinks = this.getOIDCLinks.bind(this);
}

/**
Expand Down Expand Up @@ -81,6 +83,7 @@ class Login extends Component {
+ '/' + json.login.logo;
// request account setup.
state.component.requestAccount = json.requestAccount;
state.oidc = json.oidc;
state.isLoaded = true;
this.setState(state);
}).catch((error) => {
Expand Down Expand Up @@ -187,6 +190,7 @@ class Login extends Component {
class={'col-xs-12 col-sm-12 col-md-12 text-danger'}
/>
) : null;
const oidc = this.state.oidc ? this.getOIDCLinks() : '';
const login = (
<div>
<section className={'study-logo'}>
Expand Down Expand Up @@ -234,6 +238,7 @@ class Login extends Component {
<a onClick={() => this.setMode('request')}
style={{cursor: 'pointer'}}>Request Account</a>
</div>
{oidc}
<div className={'help-text'}>
A WebGL-compatible browser is required for full functionality
(Mozilla Firefox, Google Chrome)
Expand Down Expand Up @@ -297,6 +302,26 @@ class Login extends Component {
);
}
}

/**
* Return the OpenID Connect links for this LORIS instance.
*
* @return {JSX}
*/
getOIDCLinks() {
if (!this.state.oidc) {
return null;
}
return (<div className={'oidc-links'}>
{this.state.oidc.map((val) => {
return <div>
<a href={'/oidc/login?loginWith=' + val}>
Login with {val}
</a>
</div>;
})}
</div>);
}
}

Login.propTypes = {
Expand Down
9 changes: 9 additions & 0 deletions modules/login/php/authentication.class.inc
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,15 @@ class Authentication extends \NDB_Page implements ETagCalculator
$requestAccountData['project'] = \Utility::getProjectList();
$values['requestAccount'] = $requestAccountData;

if ($this->loris->hasModule('oidc')) {
$DB = $this->loris->getDatabaseConnection();
$providers = $DB->pselectCol(
"SELECT Name FROM openid_connect_providers",
[],
);
$values['oidc'] = $providers;
}

return $values;
}

Expand Down
61 changes: 61 additions & 0 deletions modules/oidc/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# OIDC

## Purpose

The OIDC (OpenID Connect) module is intended to allow users to login
to LORIS using the OpenID Connect Authorization code flow and log in
with third party identity providers.

If the OIDC provider returns a verified email address that exists in
LORIS and is not pending, the user will be logged in. If the address
does not exist in LORIS, the user will be redirected to the request
account page with the known user info pre-filled.

## Intended Users

The module is used by all users who are trying to access LORIS on
a server that has configured valid OpenID Connect identity providers.

## Scope

This module performs functions relating to the authentication
of users. It does not configure the OIDC workflow.

## Permissions

None, this module is primarily intended for not yet authenticated
users.

## Configurations

The `openid_connect_providers` table must be manually populated. The
exact configuration depends on values that come from the provider.

The columns in the table are:

- Name -- this is a string which is used to identify the provider to
users on the login page behind a "Login with $name" link.
(ie. "Facebook", "Globus", "Google", "Auth0", etc)
- BaseURI -- this is the basis of the third party URLs. It should be
set to the domain which serves the .well-known/openid-configuration
file so that this module can do service discovery.
- ClientID -- This is a value that comes from the third party provider. It
is generally a long string.
- ClientSecret -- This is a value that comes from the third party provider. It
is generally a long string and should not be shared or displayed anywhere.
- RedirectURI -- This is the URI that the provider will redirect to. It generally
*must* be served over HTTPS and must be whitelisted in the configuration,
though some providers allow http://localhost for development purposes.

An example of an insert statement to populate the table is for mylorisserver.example.com
using auth0 as the identity provider is:

```
INSERT INTO `openid_connect_providers` (Name, BaseURI, ClientID, ClientSecret, RedirectURI) VALUES ('auth0', 'https://dev-p8q62jkwrvtaznao.us.auth0.com', '$mypublicid', '$mysecretid', 'https://mylorisserver.example.com/oidc/callback');
```

## Interactions with LORIS

If the email address returned by the OpenID Connect provider does
not yet exist in LORIS, the module will re-direct to the request
account page with the known values pre-filled.
Loading
Loading