Skip to content

Commit

Permalink
Added nicer error pages (#5)
Browse files Browse the repository at this point in the history
* Added nicer error pages

* Fixed error code

* Fixed error code

* Fixed tests
  • Loading branch information
rorylshanks authored Nov 14, 2023
1 parent abe8701 commit 59cf4e4
Show file tree
Hide file tree
Showing 10 changed files with 534 additions and 26 deletions.
1 change: 1 addition & 0 deletions .github/workflows/e2e-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:

- name: Archive E2E Test Output
uses: actions/upload-artifact@v3
if: always()
with:
name: e2e-test-screenshots
path: |
Expand Down
1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ COPY package*.json .
RUN npm i
COPY lib lib
COPY util util
COPY views views
COPY app.js app.js
COPY caddyfile-blank.json caddy.json

Expand Down
41 changes: 26 additions & 15 deletions lib/sso.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import dynamicBackend from './dynamic-backend.js'
import utils from '../util/utils.js'
import { checkAuthHeader } from './token-auth.js'
import crypto from 'crypto'
import errorpages from '../util/errorpage.js'

function addParamsToUrl(baseUrl, params) {
const url = new URL(baseUrl);
Expand All @@ -32,7 +33,8 @@ async function verifyAuth(req, res) {
var route = getRouteForHostname(requestUrl.hostname)
if (!route) {
log.warn({ message: "No route found for request", host: requestUrl })
res.status(400).send(`Failed to find route. Click <a href="${requestUrl.href}">here</a> to try again. If it fails again, please try to clear your cookies.`)
var html = await errorpages.renderErrorPage(404)
res.status(404).send(html)
return
}
// Veriflow supports token-based auth to bypass the SSo process, and this involves checking if the route is valid
Expand Down Expand Up @@ -62,7 +64,8 @@ async function verifyAuth(req, res) {
if (req.session.loggedin) {
let userIsAuthz = await authz.authZRequest(req, res, route)
if (!userIsAuthz) {
res.status(401).send("User is not authorized to access this route.")
var html = await errorpages.renderErrorPage(403)
res.status(403).send(html)
return
}
// If user is AuthN and AuthZ, check dynamic backend config
Expand All @@ -73,7 +76,8 @@ async function verifyAuth(req, res) {
} else {
const backend = await dynamicBackend.checkDynamicBackend(req, res, route, requestUrl)
if (!backend) {
res.status(401).send("Problem with dynamic backend. Please ask the administrator.")
var html = await errorpages.renderErrorPage(500, "ERR_DYNAMIC_BACKEND_FAILED")
res.status(500).send(html)
return
}
}
Expand Down Expand Up @@ -110,8 +114,8 @@ async function verifyAuth(req, res) {
res.redirect(redirectUrl)
} catch (error) {
log.error({ message: "Unknown error occurred in verifyAuth", context: { error: error.message, stack: error.stack } })
res.status(500).send(`Unknown error occoured. Please try to access the original URL again, do not refresh the page. If it fails again, please try to clear your cookies.`)

var html = await errorpages.renderErrorPage(500, "ERR_AUTH_FAILED")
res.status(500).send(html)
}
}

Expand All @@ -134,7 +138,8 @@ async function setSessionCookie(req, res) {
var decoded = await decodeJWT(req.query.token)
if (!decoded) {
log.error({ message: "Failed to decode JWT", context: { jwt: req.query.token } })
res.sendStatus(500)
var html = await errorpages.renderErrorPage(500, "ERR_SET_INVALID_JWT")
res.status(500).send(html)
return
}

Expand All @@ -156,13 +161,14 @@ async function setSessionCookie(req, res) {
res.redirect(redirectUrl);
return
} else {
res.status(400).send("No token included in request")
var html = await errorpages.renderErrorPage(400, "ERR_SET_NO_TOKEN")
res.status(400).send(html)
}

} catch (error) {
log.error({ message: "Unknown error occurred in verifyAuth", context: { error: error.message, stack: error.stack } })
res.status(500).send(`Unknown error occoured. Please try to access the original URL again, do not refresh the page. If it fails again, please try to clear your cookies.`)

log.error({ message: "Unknown error occurred in setSessionCookie", context: { error: error.message, stack: error.stack } })
var html = await errorpages.renderErrorPage(500, "ERR_SET_FAILED")
res.status(500).send(html)
}
}

Expand All @@ -174,14 +180,16 @@ async function setSessionCookie(req, res) {
async function redirectToSsoProvider(req, res) {
try {
if (!req.query.token) {
res.status(400).json({ "error": "Request must include token" })
var html = await errorpages.renderErrorPage(400, "ERR_REDIRECT_NO_TOKEN")
res.status(400).send(html)
return
}
// FIXME: Verify that redirect URL is allowed in config
var redirectToken = await decodeJWT(req.query.token)

if (!redirectToken) {
res.status(500)
var html = await errorpages.renderErrorPage(400, "ERR_REDIRECT_BAD_TOKEN")
res.status(400).send(html)
return
}

Expand Down Expand Up @@ -233,7 +241,8 @@ async function redirectToSsoProvider(req, res) {
res.redirect(redirectUrl)
} catch (error) {
log.error({ message: "Unknown error occoured in redirectToSsoProvider", context: { error: error.message, trace: error.stack } })
res.sendStatus(500)
var html = await errorpages.renderErrorPage(500, "ERR_REDIRECT_FAILED")
res.status(500).send(html)
}

}
Expand Down Expand Up @@ -262,7 +271,8 @@ async function verifySsoCallback(req, res) {

if (!userIdClaim) {
log.warn({ error: "User does not have a userId included in the ID token claims. Check setting idp_provider_user_id_claim", claims: callbackInfo.claims() })
res.status(401).json({ error: "User does not have a userId included in the ID token claims" })
var html = await errorpages.renderErrorPage(500, "ERR_NO_USERID_IN_TOKEN")
res.status(500).send(html)
return
}

Expand Down Expand Up @@ -291,7 +301,8 @@ async function verifySsoCallback(req, res) {

} catch (error) {
log.error({ message: "Unknown error occoured in verifySsoCallback", context: { error: error.message, trace: error.stack } })
res.sendStatus(500)
var html = await errorpages.renderErrorPage(500, "ERR_CALLBACK_FAILED")
res.status(500).send(html)
}
}

Expand Down
136 changes: 130 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"cache": "^3.0.0",
"chokidar": "^3.5.3",
"connect-redis": "^7.1.0",
"ejs": "^3.1.9",
"express": "^4.18.2",
"express-session": "^1.17.3",
"form-data": "^4.0.0",
Expand Down
1 change: 0 additions & 1 deletion test/e2e/steps_file.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ module.exports = function() {
this.fillField('login', '[email protected]');
this.fillField('password', 'password');
this.click('Login');
this.waitForNavigation();
}
});
}
2 changes: 1 addition & 1 deletion test/e2e/tests/basic_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,5 @@ Scenario('Testing Token Auth', async ({ I }) => {
Scenario('Testing Unauthorized Flow', async ({ I }) => {
I.amOnPage('http://test-unauthorized-login.localtest.me:2080/');
I.login()
I.see("User is not authorized to access this route.")
I.see("ERR_NOT_AUTHORIZED")
});
Loading

0 comments on commit 59cf4e4

Please sign in to comment.