Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions Framework/Backend/config-default.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
"secret": "<secret>",
"id": "<id>",
"redirect_uri": "https://redirect.uri/callback",
"well_known": "http://localhost/.well-known/openid-configuration'"
"well_known": "http://localhost/.well-known/openid-configuration'",
"post_logout_redirect_uri:": "https://google.com",
"end_session_endpoint": "https://localhost/logout"
},
"http": {
"port": 8181,
Expand Down Expand Up @@ -42,6 +44,8 @@
"port": 8080
},
"notification": {
"brokers": ["localhost:9092"]
"brokers": [
"localhost:9092"
]
}
}
}
12 changes: 12 additions & 0 deletions Framework/Backend/http/openid.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ class OpenId {
assert(config.secret, 'Missing config value: secret');
assert(config.well_known, 'Missing config value: well_known');
assert(config.redirect_uri, 'Missing config value: redirect_uri');
assert(config.end_session_endpoint, 'Missing config value: end_session_endpoint');

config.postLogoutRedirectUri = config?.post_logout_redirect_uri ?? 'https://ali-flp.cern.ch/';
config.timeout = config.timeout ?? 5000;

this.config = config;
this.code_verifier = generators.codeVerifier();
custom.setHttpOptionsDefaults({
Expand Down Expand Up @@ -80,6 +84,14 @@ class OpenId {
});
}

/**
* Method that provides a logout redirect URL for the Single-SignIn service
* @returns {string} endSessionUrl - URL redirecting to the logout page
*/
getLogoutUrl() {
return `${this.client.endSessionUrl()}?client_id=${this.client.id}&post_logout_redirect_uri=https://ali-flp.cern.ch/`;
}

/**
* Handles callback
* @param {object} req - callback request object
Expand Down
11 changes: 11 additions & 0 deletions Framework/Backend/http/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,17 @@ class HttpServer {
});
}

/**
* Redirects to the OpenID logout page
* @param {object} _ - HTTP request
* @param {object} res - HTTP response
*/
logout(_, res) {
if (this.openid) {
res.redirect(this.openid.getLogoutUrl());
}
}

/**
* Handles authentication flow (default path of the app: '/')
* - If JWT query.token is valid it grants the access to the application
Expand Down
19 changes: 19 additions & 0 deletions Framework/Backend/test/mocha-http.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
process.env.NODE_ENV = 'test';

const request = require('supertest');
const sinon = require('sinon');
const assert = require('assert');
const path = require('path');
const url = require('url');
Expand Down Expand Up @@ -294,3 +295,21 @@ describe('HTTP constructor checks', () => {
assert.strictEqual(httpServer.limit, '10Mb', 'Provided limit was not set');
});
});

describe('HTTP server with logout functionality', () => {
it('should successfully redirect the user to the logout URL', () => {
const httpServer = new HttpServer(config.http, config.jwt);
const openidMock = {
getLogoutUrl: sinon.stub().returns('http://mock-end-session-url?client_id=mock-client-id&post_logout_redirect_uri=http://info.cern.ch'),
};
const res = {
redirect: sinon.stub(),
};

httpServer.openid = openidMock;
httpServer.logout({}, res);

assert.ok(res.redirect.calledOnce);
assert.ok(res.redirect.calledWith('http://mock-end-session-url?client_id=mock-client-id&post_logout_redirect_uri=http://info.cern.ch'));
});
});
44 changes: 44 additions & 0 deletions Framework/Backend/test/mocha-openid.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
*/

const assert = require('assert');
const { AssertionError } = require('assert');
const sinon = require('sinon');
const config = require('./../config-default.json');
const OpenId = require('./../http/openid.js');

Expand All @@ -21,4 +23,46 @@ describe('OpenID Connect client', () => {
const openid = new OpenId(config.openId);
await assert.rejects(async () => await openid.createIssuer());
}).timeout(5500);

it('should throw assertion error for missing critical configuration value, end_session_endpoint', () => {
assert.throws(() => {
new OpenId({
secret: '<secret>',
id: '<id>',
redirect_uri: 'https://redirect.uri/callback',
well_known: 'http://localhost/.well-known/openid-configuration',
});
}, new AssertionError({ message: 'Missing config value: end_session_endpoint', expected: true, operator: '==' }));
});

it('should successfully add default value for post_logout_redirect_uri', () => {
const openid = new OpenId({
secret: '<secret>',
id: '<id>',
redirect_uri: 'https://redirect.uri/callback',
well_known: 'http://localhost/.well-known/openid-configuration',
end_session_endpoint: 'http://localhost/end-session',
});
assert.strictEqual(openid.config.postLogoutRedirectUri, 'https://ali-flp.cern.ch/');
});
});

describe('Logout', () => {
it('should successfully return the correct logout URL from client of openid', async () => {
const openid = new OpenId(config.openId);

// Mock the client and its endSessionUrl method
const mockClient = {
endSessionUrl: sinon.stub().returns('http://mock-end-session-url'),
id: 'mock-client-id',
};
openid.client = mockClient;

const logoutUrl = openid.getLogoutUrl();

// Assert that getLogoutUrl returns the expected string
const expectedUrl = 'http://mock-end-session-url?client_id=mock-client-id&post_logout_redirect_uri=https://ali-flp.cern.ch/';
assert.strictEqual(logoutUrl, expectedUrl);
assert.ok(mockClient.endSessionUrl.calledOnce);
}).timeout(5500);
});
Loading