oidc: Remove rpInitiatedLogout again

This commit is contained in:
Johannes Zellner
2023-03-13 19:08:41 +01:00
parent 05a1cc58eb
commit 5e4e292b4d
3 changed files with 59 additions and 70 deletions

View File

@@ -12,6 +12,7 @@ const assert = require('assert'),
paths = require('./paths.js'),
BoxError = require('./boxerror.js'),
HttpError = require('connect-lastmile').HttpError,
HttpSuccess = require('connect-lastmile').HttpSuccess,
users = require('./users.js'),
safe = require('safetydance'),
settings = require('./settings.js');
@@ -273,6 +274,7 @@ function attachInteractionRoutes(routePrefix, app, provider) {
app.get(routePrefix + '/interaction/:uid', setNoCache, async (req, res, next) => {
try {
const { uid, prompt, params, session } = await provider.interactionDetails(req, res);
console.log('details', await provider.interactionDetails(req, res));
debug(`route interaction get uid:${uid} prompt.name:${prompt.name} client_id:${params.client_id} session:${session}`);
@@ -318,38 +320,42 @@ function attachInteractionRoutes(routePrefix, app, provider) {
});
app.post(routePrefix + '/interaction/:uid/login', setNoCache, async (req, res, next) => {
try {
const { uid, prompt: { name } } = await provider.interactionDetails(req, res);
const [detailsError, details] = await safe(provider.interactionDetails(req, res));
if (detailsError) return next(new HttpError(500, detailsError));
debug(`route interaction login post uid:${uid} prompt.name:${name}`, req.body);
const uid = details.uid;
const prompt = details.prompt;
const name = prompt.name;
assert.equal(name, 'login');
debug(`route interaction login post uid:${uid} prompt.name:${name}`, req.body);
if (!req.body.username || typeof req.body.username !== 'string') return next(new HttpError(400, 'A username must be non-empty string'));
if (!req.body.password || typeof req.body.password !== 'string') return next(new HttpError(400, 'A password must be non-empty string'));
if ('totpToken' in req.body && typeof req.body.totpToken !== 'string') return next(new HttpError(400, 'totpToken must be a string' ));
assert.equal(name, 'login');
const { username, password, totpToken } = req.body;
if (!req.body.username || typeof req.body.username !== 'string') return next(new HttpError(400, 'A username must be non-empty string'));
if (!req.body.password || typeof req.body.password !== 'string') return next(new HttpError(400, 'A password must be non-empty string'));
if ('totpToken' in req.body && typeof req.body.totpToken !== 'string') return next(new HttpError(400, 'totpToken must be a string' ));
const verifyFunc = username.indexOf('@') === -1 ? users.verifyWithUsername : users.verifyWithEmail;
const { username, password, totpToken } = req.body;
let [error, user] = await safe(verifyFunc(username, password, users.AP_WEBADMIN, { totpToken }));
if (error && error.reason === BoxError.INVALID_CREDENTIALS) return next(new HttpError(401, error.message));
if (error && error.reason === BoxError.NOT_FOUND) return next(new HttpError(401, 'Unauthorized'));
if (error) return next(new HttpError(500, error));
if (!user) return next(new HttpError(401, 'Unauthorized'));
const verifyFunc = username.indexOf('@') === -1 ? users.verifyWithUsername : users.verifyWithEmail;
// TODO we may have to check what else the Account class provides, in which case we have to map those things
const result = {
login: {
accountId: user.id,
},
};
const [verifyError, user] = await safe(verifyFunc(username, password, users.AP_WEBADMIN, { totpToken }));
if (verifyError && verifyError.reason === BoxError.INVALID_CREDENTIALS) return next(new HttpError(401, verifyError.message));
if (verifyError && verifyError.reason === BoxError.NOT_FOUND) return next(new HttpError(401, 'Unauthorized'));
if (verifyError) return next(new HttpError(500, verifyError));
if (!user) return next(new HttpError(401, 'Unauthorized'));
await provider.interactionFinished(req, res, result, { mergeWithLastSubmission: false });
} catch (err) {
next(err);
}
// TODO we may have to check what else the Account class provides, in which case we have to map those things
const result = {
login: {
accountId: user.id,
},
};
const [interactionFinishError, interaction] = await safe(provider.interactionFinished(req, res, result));
if (interactionFinishError) return next(new HttpError(500, interactionFinishError));
next(new HttpSuccess(200, { redirectTo: interaction.redirectTo }));
});
app.post(routePrefix + '/interaction/:uid/confirm', setNoCache, async (req, res, next) => {
@@ -440,49 +446,7 @@ async function getProvider(routePrefix) {
}
},
features: {
devInteractions: { enabled: false },
// https://github.com/panva/node-oidc-provider/blob/main/docs/README.md#featuresrpinitiatedlogout
rpInitiatedLogout: {
enabled: true,
logoutSource: async function logoutSource(ctx, form) {
// @param ctx - koa request context
// @param form - form source (id="op.logoutForm") to be embedded in the page and submitted by
// the End-User
ctx.body = `<!DOCTYPE html>
<head>
<title>Logout Request</title>
<style>/* css and html classes omitted for brevity, see lib/helpers/defaults.js */</style>
</head>
<body>
<div>
<h1>Do you want to sign-out from ${ctx.host}?</h1>
${form}
<button autofocus type="submit" form="op.logoutForm" value="yes" name="logout">Yes, sign me out</button>
<button type="submit" form="op.logoutForm">No, stay signed in</button>
</div>
</body>
</html>`;
},
postLogoutSuccessSource: async function postLogoutSuccessSource(ctx) {
// @param ctx - koa request context
const {
clientId, clientName, clientUri, initiateLoginUri, logoUri, policyUri, tosUri,
} = ctx.oidc.client || {}; // client is defined if the user chose to stay logged in with the OP
const display = clientName || clientId;
ctx.body = `<!DOCTYPE html>
<head>
<title>Sign-out Success</title>
<style>/* css and html classes omitted for brevity, see lib/helpers/defaults.js */</style>
</head>
<body>
<div>
<h1>Sign-out Success</h1>
<p>Your sign-out ${display ? `with ${display}` : ''} was successful.</p>
</div>
</body>
</html>`;
}
}
devInteractions: { enabled: false }
},
clients: [{
client_id: 'foo',

View File

@@ -30,7 +30,7 @@
</div>
<br/>
<div class="row">
<div class="col-md-12">
<div class="col-md-12 text-center">
<form method="post" action="<%= submitUrl %>">
<button class="btn btn-primary btn-outline" type="submit">Authorize</button>
</form>

View File

@@ -13,6 +13,7 @@
<link type="text/css" rel="stylesheet" href="/3rdparty/fontawesome/css/all.css"/>
<!-- Bootstrap Core JavaScript -->
<script type="text/javascript" src="/3rdparty/js/jquery.min.js"></script>
<script type="text/javascript" src="/3rdparty/js/bootstrap.min.js"></script>
</head>
@@ -31,7 +32,7 @@
<br/>
<div class="row">
<div class="col-md-12">
<form method="post" action="<%= submitUrl %>">
<form id="loginForm">
<div class="form-group">
<label class="control-label" for="inputUsername">Username</label>
<input type="text" class="form-control" id="inputUsername" name="username" autofocus required>
@@ -54,7 +55,31 @@
<script>
console.log('OIDC login');
document.getElementById('loginForm').addEventListener('submit', function (event) {
event.preventDefault();
var apiUrl = '<%= submitUrl %>';
console.log('submit', apiUrl);
var body = {
username: document.getElementById('inputUsername').value,
password: document.getElementById('inputPassword').value,
totpToken: document.getElementById('inputTotpToken').value
};
fetch(apiUrl, {
method: 'POST'
body: JSON.stringify(body),
headers: { 'Content-type': 'application/json; charset=UTF-8' }
}).then(function (response) {
if (response.ok) return response.json();
return Promise.reject(response);
}).then(function (data) {
console.log('login success', data);
}).catch(function (error) {
console.warn('Something went wrong.', error);
});
});
</script>