passkey: implement passwordless login
This commit is contained in:
+48
-3
@@ -312,6 +312,8 @@ async function renderInteractionPage(req, res) {
|
||||
if (prompt.name === 'login') {
|
||||
const data = {
|
||||
submitUrl: `${ROUTE_PREFIX}/interaction/${uid}/login`,
|
||||
passkeyAuthOptionsUrl: `${ROUTE_PREFIX}/interaction/${uid}/passkey_auth_options`,
|
||||
passkeyLoginUrl: `${ROUTE_PREFIX}/interaction/${uid}/passkey_login`,
|
||||
iconUrl: '/api/v1/cloudron/avatar',
|
||||
name: client.name || await branding.getCloudronName(),
|
||||
footer: marked.parse(await branding.renderFooter()),
|
||||
@@ -449,6 +451,47 @@ async function interactionLogin(req, res, next) {
|
||||
res.status(200).send({ redirectTo });
|
||||
}
|
||||
|
||||
async function interactionPasskeyAuthOptions(req, res, next) {
|
||||
const [detailsError, details] = await safe(gOidcProvider.interactionDetails(req, res));
|
||||
if (detailsError) return next(new HttpError(detailsError.statusCode, detailsError.error_description));
|
||||
|
||||
const { uid } = details;
|
||||
|
||||
const [error, options] = await safe(passkeys.getDiscoverableAuthOptions(uid));
|
||||
if (error) return next(new HttpError(500, error));
|
||||
|
||||
res.status(200).send(options);
|
||||
}
|
||||
|
||||
async function interactionPasskeyLogin(req, res, next) {
|
||||
const [detailsError, details] = await safe(gOidcProvider.interactionDetails(req, res));
|
||||
if (detailsError) return next(new HttpError(detailsError.statusCode, detailsError.error_description));
|
||||
|
||||
if (!req.body.passkeyResponse || typeof req.body.passkeyResponse !== 'object') return next(new HttpError(400, 'passkeyResponse must be an object'));
|
||||
|
||||
const { uid } = details;
|
||||
|
||||
const [verifyError, result] = await safe(passkeys.verifyDiscoverableAuth(uid, req.body.passkeyResponse));
|
||||
if (verifyError) {
|
||||
trace(`interactionPasskeyLogin: passkey verification failed: ${verifyError.message}`);
|
||||
return next(new HttpError(401, 'Passkey verification failed'));
|
||||
}
|
||||
|
||||
const user = await users.get(result.userId);
|
||||
if (!user) return next(new HttpError(401, 'User not found'));
|
||||
|
||||
const interactionResult = {
|
||||
login: {
|
||||
accountId: user.username,
|
||||
},
|
||||
};
|
||||
|
||||
const [interactionFinishError, redirectTo] = await safe(gOidcProvider.interactionResult(req, res, interactionResult));
|
||||
if (interactionFinishError) return next(new HttpError(500, interactionFinishError));
|
||||
|
||||
res.status(200).send({ redirectTo });
|
||||
}
|
||||
|
||||
async function interactionConfirm(req, res, next) {
|
||||
const [detailsError, interactionDetails] = await safe(gOidcProvider.interactionDetails(req, res));
|
||||
if (detailsError) return next(new HttpError(detailsError.statusCode, detailsError.error_description));
|
||||
@@ -772,9 +815,11 @@ async function start() {
|
||||
}
|
||||
|
||||
app.get (`${ROUTE_PREFIX}/interaction/:uid`, setNoCache, renderInteractionPage);
|
||||
app.post(`${ROUTE_PREFIX}/interaction/:uid/login`, setNoCache, json, interactionLogin);
|
||||
app.post(`${ROUTE_PREFIX}/interaction/:uid/confirm`, setNoCache, json, interactionConfirm);
|
||||
app.get (`${ROUTE_PREFIX}/interaction/:uid/abort`, setNoCache, interactionAbort);
|
||||
app.post(`${ROUTE_PREFIX}/interaction/:uid/login`, setNoCache, json, interactionLogin);
|
||||
app.post(`${ROUTE_PREFIX}/interaction/:uid/passkey_auth_options`, setNoCache, json, interactionPasskeyAuthOptions);
|
||||
app.post(`${ROUTE_PREFIX}/interaction/:uid/passkey_login`, setNoCache, json, interactionPasskeyLogin);
|
||||
app.post(`${ROUTE_PREFIX}/interaction/:uid/confirm`, setNoCache, json, interactionConfirm);
|
||||
app.get (`${ROUTE_PREFIX}/interaction/:uid/abort`, setNoCache, interactionAbort);
|
||||
|
||||
// cloudflare access has a bug that it cannot handle OKP key type. https://github.com/sebadob/rauthy/issues/1229#issuecomment-3610993452
|
||||
app.get (`${ROUTE_PREFIX}/jwks_rsaonly`, setNoCache, async function (req, res) {
|
||||
|
||||
Reference in New Issue
Block a user