From 88e3b77fbea4751f6ea6ee3248e464b9cb96d1b2 Mon Sep 17 00:00:00 2001 From: hackerman <3372410+aeneasr@users.noreply.github.com> Date: Tue, 30 Jan 2024 14:01:02 +0100 Subject: [PATCH] feat: add skip logout consent option (#319) --- src/pkg/index.ts | 8 ++++++ src/pkg/route.ts | 5 ++++ src/routes/logout.ts | 58 ++++++++++++++++++++++++++------------------ 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/src/pkg/index.ts b/src/pkg/index.ts index 17272d34..e9059ecf 100644 --- a/src/pkg/index.ts +++ b/src/pkg/index.ts @@ -5,6 +5,7 @@ import sdk, { apiBaseUrl } from "./sdk" import { UiNode, ErrorAuthenticatorAssuranceLevelNotSatisfied, + OAuth2LogoutRequest, } from "@ory/client" import { ButtonLink, Divider, MenuLink, Typography } from "@ory/elements-markup" import { filterNodesByGroups } from "@ory/integrations/ui" @@ -50,6 +51,13 @@ export const defaultConfig: RouteOptionsCreator = () => { ? true : false }, + shouldSkipLogoutConsent: (challenge) => { + return ( + challenge.client as OAuth2LogoutRequest & { + skip_logout_consent: boolean + } + )?.skip_logout_consent + }, ...sdk, } } diff --git a/src/pkg/route.ts b/src/pkg/route.ts index 3ee75ed0..3f4a587c 100644 --- a/src/pkg/route.ts +++ b/src/pkg/route.ts @@ -5,6 +5,7 @@ import { IdentityApi, OAuth2Api, OAuth2ConsentRequest, + OAuth2LogoutRequest, } from "@ory/client" import { Theme } from "@ory/elements-markup" import { NextFunction, Request, Response, Router } from "express" @@ -20,11 +21,15 @@ export interface RouteOptions { // This is used to determine if the consent route should be registered // We need to check if the required environment variables are set isOAuthConsentRouteEnabled: () => boolean + // Checks if the OAuth2 consent request should be skipped // In some cases Hydra will let us skip the consent request // Setting `TRUSTED_CLIENT_IDS` will skip the consent request for the given client ids shouldSkipConsent: (challenge: OAuth2ConsentRequest) => boolean + // When this returns true, the logout screen will not be shown. + shouldSkipLogoutConsent: (challenge: OAuth2LogoutRequest) => boolean + logoUrl?: string faviconUrl?: string faviconType?: string diff --git a/src/routes/logout.ts b/src/routes/logout.ts index d52c7a9a..262d97df 100644 --- a/src/routes/logout.ts +++ b/src/routes/logout.ts @@ -19,30 +19,42 @@ export const createShowLogoutRoute: RouteCreator = if (typeof logoutChallenge !== "string") { logger.debug("Expected a logout challenge to be set but received none.") - next( - new Error("Expected a logout challenge to be set but received none."), - ) + res.redirect("login") return } - // this should never happen - if (!req.csrfToken) { - logger.warn("Expected CSRF token middleware to be set but received none.") - next( - new Error( - "Expected CSRF token middleware to be set but received none.", - ), - ) - return - } + const { oauth2, shouldSkipLogoutConsent } = createHelpers(req, res) + oauth2 + .getOAuth2LogoutRequest({ logoutChallenge }) + .then(({ data: body }) => { + if (shouldSkipLogoutConsent(body)) { + return oauth2 + .acceptOAuth2LogoutRequest({ logoutChallenge }) + .then(({ data: body }) => res.redirect(body.redirect_to)) + } + + // this should never happen + if (!req.csrfToken) { + logger.warn( + "Expected CSRF token middleware to be set but received none.", + ) + next( + new Error( + "Expected CSRF token middleware to be set but received none.", + ), + ) + return + } - res.render("logout", { - card: UserLogoutCard({ - csrfToken: req.csrfToken(true), - challenge: logoutChallenge, - action: "logout", - }), - }) + res.render("logout", { + card: UserLogoutCard({ + csrfToken: req.csrfToken(true), + challenge: logoutChallenge, + action: "logout", + }), + }) + }) + .catch(() => res.redirect("login")) } export const createSubmitLogoutRoute: RouteCreator = @@ -60,15 +72,15 @@ export const createSubmitLogoutRoute: RouteCreator = // The user rejected to log out, so we'll redirect to /ui/welcome return oauth2 .rejectOAuth2LogoutRequest({ logoutChallenge }) - .then(() => res.redirect("welcome")) - .catch(() => res.redirect("welcome")) + .then(() => res.redirect("login")) + .catch(() => res.redirect("login")) } else { logger.debug("User agreed to log out.") // The user agreed to log out, let's accept the logout request. return oauth2 .acceptOAuth2LogoutRequest({ logoutChallenge }) .then(({ data: body }) => res.redirect(body.redirect_to)) - .catch(() => res.redirect("welcome")) + .catch(() => res.redirect("login")) } }