Skip to content

Commit 837d54e

Browse files
Merge pull request #1490 from ral-facilities/feature/icat-oidc-auth-#494
Add OIDC support #494
2 parents 5288b35 + 2f804cb commit 837d54e

33 files changed

+3464
-1279
lines changed

.husky/common.sh

Lines changed: 0 additions & 8 deletions
This file was deleted.

.husky/pre-commit

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1 @@
1-
#!/bin/sh
2-
. "$(dirname "$0")/_/husky.sh"
3-
. "$(dirname "$0")/common.sh"
4-
51
yarn lint-staged

cypress.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ export default defineConfig({
88
openMode: 0,
99
},
1010
e2e: {
11-
baseUrl: 'http://127.0.0.1:3000',
11+
baseUrl: 'http://localhost:3000',
1212
},
1313
});

cypress/e2e/login.cy.ts

Lines changed: 106 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ describe('Login', () => {
104104
.contains('button', 'Sign in')
105105
.click();
106106

107-
cy.url().should('eq', 'http://127.0.0.1:3000/');
107+
cy.url().should('eq', 'http://localhost:3000/');
108108

109109
cy.window().then(
110110
(window) =>
@@ -125,7 +125,7 @@ describe('Login', () => {
125125
.contains('button', 'Sign in')
126126
.click();
127127

128-
cy.url().should('eq', 'http://127.0.0.1:3000/');
128+
cy.url().should('eq', 'http://localhost:3000/');
129129

130130
cy.window().then(
131131
(window) =>
@@ -146,7 +146,7 @@ describe('Login', () => {
146146
.contains('button', 'Sign in')
147147
.click();
148148

149-
cy.url().should('eq', 'http://127.0.0.1:3000/');
149+
cy.url().should('eq', 'http://localhost:3000/');
150150

151151
let storedToken: string | null;
152152

@@ -177,12 +177,12 @@ describe('Login', () => {
177177
it('should redirect to logout page if logged in and navigating to login page', () => {
178178
cy.login('username', 'password');
179179
cy.visit('/login');
180-
cy.url().should('eq', 'http://127.0.0.1:3000/logout');
180+
cy.url().should('eq', 'http://localhost:3000/logout');
181181
});
182182

183183
it('should redirect to Home page if not logged in and navigating to logout page', () => {
184184
cy.visit('/logout');
185-
cy.url().should('eq', 'http://127.0.0.1:3000/login');
185+
cy.url().should('eq', 'http://localhost:3000/login');
186186
});
187187

188188
it('should redirect to login page when navigating to a plugin then back to the plugin after login', () => {
@@ -199,7 +199,7 @@ describe('Login', () => {
199199
.contains('button', 'Sign in')
200200
.click();
201201

202-
cy.url().should('eq', 'http://127.0.0.1:3000/plugin1');
202+
cy.url().should('eq', 'http://localhost:3000/plugin1');
203203
cy.get('#demo_plugin').contains('Demo Plugin').should('be.visible');
204204
});
205205

@@ -439,6 +439,34 @@ describe('Login', () => {
439439
cy.get('[aria-label="Open user menu"]').should('be.visible');
440440
});
441441

442+
it('should be able to login via SSO after auto login and be displayed as logged in', () => {
443+
cy.visit('/login');
444+
445+
cy.get('#select-mnemonic').click();
446+
cy.contains('Keycloak').click();
447+
448+
cy.url().as('originUrl', { type: 'static' });
449+
450+
cy.contains('button', 'Login with Keycloak').click();
451+
452+
cy.url().should('include', 'http://localhost:8081');
453+
// login to keycloak
454+
cy.origin('http://localhost:8081', () => {
455+
cy.get('#username').type('test');
456+
cy.get('#password').type('password');
457+
cy.get('#kc-login').click();
458+
});
459+
460+
// redirect?
461+
// cy.origin()
462+
cy.get('@originUrl').then((url) => {
463+
cy.url().should('include', url);
464+
});
465+
466+
cy.contains('Sign out').should('exist');
467+
cy.get('[aria-label="Open user menu"]').should('be.visible');
468+
});
469+
442470
it('should autoLogin after logout', () => {
443471
window.localStorage.setItem(
444472
'scigateway:token',
@@ -488,4 +516,76 @@ describe('Login', () => {
488516
cy.contains('Unable to create anonymous session').should('not.exist');
489517
});
490518
});
519+
520+
describe('OIDC', () => {
521+
beforeEach(() => {
522+
cy.intercept('/settings.json', {
523+
plugins: [
524+
{
525+
name: 'demo_plugin',
526+
src: '/plugins/e2e-plugin/main.js',
527+
enable: true,
528+
location: 'main',
529+
},
530+
],
531+
'ui-strings': 'res/default.json',
532+
'auth-provider': 'oidc',
533+
authUrl: 'http://localhost:8000',
534+
autoLogin: true,
535+
'help-tour-steps': [],
536+
});
537+
});
538+
539+
it('should be able to login via SSO PKCE and be displayed as logged in', () => {
540+
cy.visit('/login');
541+
542+
cy.url().as('originUrl', { type: 'static' });
543+
544+
cy.get('#select-mnemonic').click();
545+
cy.contains('Keycloak (PKCE)').click();
546+
547+
cy.contains('button', 'Login with Keycloak (PKCE)').click();
548+
549+
cy.url().should('include', 'http://localhost:8081');
550+
// login to keycloak
551+
cy.origin('http://localhost:8081', () => {
552+
cy.get('#username').type('test');
553+
cy.get('#password').type('password');
554+
cy.get('#kc-login').click();
555+
});
556+
557+
cy.get('@originUrl').then((url) => {
558+
cy.url().should('include', url);
559+
});
560+
561+
cy.contains('Sign in').should('not.exist');
562+
cy.get('[aria-label="Open user menu"]').should('be.visible');
563+
});
564+
565+
it('should be able to login via SSO non-PKCE and be displayed as logged in', () => {
566+
// test that redirect from an authorised route will take you back
567+
cy.visit('/plugin1');
568+
569+
cy.url().as('originUrl', { type: 'static' });
570+
571+
cy.get('#select-mnemonic').click();
572+
cy.contains('Keycloak (Non-PKCE)').click();
573+
574+
cy.contains('button', 'Login with Keycloak (Non-PKCE)').click();
575+
576+
cy.url().should('include', 'http://localhost:8081');
577+
// login to keycloak
578+
cy.origin('http://localhost:8081', () => {
579+
cy.get('#username').type('test');
580+
cy.get('#password').type('password');
581+
cy.get('#kc-login').click();
582+
});
583+
584+
cy.get('@originUrl').then((url) => {
585+
cy.url().should('include', url);
586+
});
587+
588+
cy.get('#demo_plugin').contains('h2', '/plugin1').should('be.visible');
589+
});
590+
});
491591
});

cypress/e2e/scigateway_frontend.cy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ describe('Scigateway', () => {
5959
.contains('button', 'Sign in')
6060
.click();
6161

62-
cy.url().should('eq', 'http://127.0.0.1:3000/');
62+
cy.url().should('eq', 'http://localhost:3000/');
6363

6464
cy.get('button[aria-label="Close navigation menu"]').should('exist');
6565
cy.get('button[aria-label="Help page"]').click();

package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,14 @@
2424
"browserslist": "4.25.0",
2525
"browserslist-to-esbuild": "2.1.1",
2626
"connected-react-router": "6.9.3",
27-
"cookie-parser": "1.4.5",
28-
"express": "4.20.0",
2927
"husky": "9.1.7",
3028
"i18next": "25.4.2",
3129
"i18next-browser-languagedetector": "8.2.0",
3230
"i18next-http-backend": "3.0.2",
3331
"js-cookie": "3.0.1",
34-
"jsonwebtoken": "9.0.0",
3532
"loglevel": "1.9.1",
3633
"prettier": "3.6.0",
3734
"prop-types": "15.8.1",
38-
"query-string": "7.1.1",
3935
"react": "18.3.1",
4036
"react-dom": "18.3.1",
4137
"react-i18next": "14.1.3",
@@ -61,12 +57,12 @@
6157
"lint:cypress": "eslint --ext=tsx --ext=js --ext=jsx --fix ./cypress",
6258
"serve:plugins": "node micro-frontend-tools/serve-plugins.js",
6359
"build:e2e": "cross-env GENERATE_SOURCEMAP=false yarn build",
64-
"e2e:serve": "yarn build:e2e && concurrently \"node server/auth-server.js e2e\" \"node ./server/e2e-test-server.js\"",
65-
"e2e:interactive": "start-server-and-test e2e:serve http://localhost:3000 cy:open",
66-
"e2e": "start-server-and-test e2e:serve http://localhost:3000 cy:run",
60+
"e2e:serve": "yarn build:e2e && concurrently \"node server/auth-server.js e2e\" \"node ./server/e2e-test-server.js\" \"docker compose -f server/keycloak/docker-compose.yml up -d\"",
61+
"e2e:interactive": "start-server-and-test e2e:serve \"http://localhost:3000|http://localhost:8081/realms/testrealm/.well-known/openid-configuration\" cy:open",
62+
"e2e": "start-server-and-test e2e:serve \"http://localhost:3000|http://localhost:8081/realms/testrealm/.well-known/openid-configuration\" cy:run",
6763
"cy:open": "cypress open",
6864
"cy:run": "cypress run",
69-
"postinstall": "husky install; yarn copy-cdn-fallbacks",
65+
"postinstall": "husky; yarn copy-cdn-fallbacks",
7066
"copy-cdn-fallbacks": "node -e \"const fs = require('fs'); fs.copyFile('node_modules/react/umd/react.production.min.js', 'public/react.production.min.js', (err) => { if (err) throw err;}); fs.copyFile('node_modules/react-dom/umd/react-dom.production.min.js', 'public/react-dom.production.min.js', (err) => { if (err) throw err;});\""
7167
},
7268
"lint-staged": {
@@ -96,6 +92,7 @@
9692
"@vitest/coverage-v8": "2.1.9",
9793
"axios-mock-adapter": "1.22.0",
9894
"concurrently": "9.2.0",
95+
"cookie-parser": "1.4.5",
9996
"cors": "2.8.5",
10097
"cross-env": "10.0.0",
10198
"cypress": "14.5.0",
@@ -108,8 +105,11 @@
108105
"eslint-plugin-react": "7.37.5",
109106
"eslint-plugin-react-hooks": "5.2.0",
110107
"eslint-plugin-testing-library": "7.6.3",
108+
"express": "4.20.0",
111109
"globals": "^16.1.0",
112110
"jsdom": "24.1.3",
111+
"jsonwebtoken": "9.0.0",
112+
"jwks-rsa": "3.2.0",
113113
"lint-staged": "16.1.5",
114114
"redux-mock-store": "1.5.4",
115115
"serve": "14.2.0",

0 commit comments

Comments
 (0)