Skip to content

Commit

Permalink
Added additional example of server auth
Browse files Browse the repository at this point in the history
Added a second manifest with an example where the provider is not visible by default and preload scripts are used to determine if the right url is loaded in the hidden window.

Also added an additional scenario where the server auth may use redirects and a redirect may become stuck and the provider.html is never loaded.
  • Loading branch information
johnman committed Nov 6, 2024
1 parent 4c9f3b1 commit bd850c7
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 9 deletions.
1 change: 1 addition & 0 deletions how-to/integrate-server-authentication/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"dos": "node ./scripts/dos.mjs && node ./scripts/kill.mjs",
"kill": "node ./scripts/kill.mjs",
"client": "node ./scripts/launch.mjs",
"secondclient": "node ./scripts/launch.mjs http://localhost:8080/second.manifest.fin.json",
"build-client": "webpack build --config ./client/webpack.config.js --mode=development",
"build-server": "tsc --project ./server",
"build": "npm run build-server && npm run build-client",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>Friendly Error Page</title>
<link rel="icon" type="image/x-icon" href="../favicon.ico" />
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="../common/style/app.css" />
</head>

<body class="col fill gap20">
<header class="row spread middle">
<div class="col">
<h1>Friendly Error Page</h1>
<h1 class="tag">We are on the unhappy path.</h1>
</div>
<div class="row middle gap20">
<image src="../common/images/icon-blue.png" alt="OpenFin" height="40px"></image>
</div>
</header>
<main class="col fill gap10">
<p>
The provider has not been loaded due to an error so the platform cannot perform the necessary
bootstrapping steps to launch the platform.
</p>
<p>If you logout and exit you will be presented with the login screen again.</p>
<p>If you just exit you will be presented with this screen again.</p>
</main>
<footer class="row right gap20">
<button id="btnLogout">Logout and Exit</button>
<button id="btnExit">Exit</button>
</footer>

<script>
const btnLogout = document.querySelector('#btnLogout');
btnLogout.addEventListener('click', () => {
// On logout the session cookies are cleared and close the app
fetch('http://localhost:8080/app/logout', {
method: 'post'
}).then(async (response) => {
await fin.Application.getCurrentSync().quit();
});
});
const btnExit = document.querySelector('#btnExit');
btnExit.addEventListener('click', async () => {
await fin.Application.getCurrentSync().quit();
});
</script>
</body>
</html>
9 changes: 7 additions & 2 deletions how-to/integrate-server-authentication/public/app/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ <h1 class="tag">Demonstrate integrating Server Authentication.</h1>
<main class="col fill gap10 pad20">
<form class="col left gap20">
<p>
This example only has one set of credentials that work
<i>[email protected] / pass1234</i>
This example only has one set of credentials that work:
<i><b>[email protected] / pass1234</b></i>
</p>
<p>
The other set of credentials will authenticate you but then simulate a failure so that you are stuck
in a redirect before the provider page is loaded (you can logout and exit to clear this session):
<i><b>[email protected] / pass1234</b></i>
</p>
<p>Enter your credentials to login:</p>
<fieldset>
Expand Down
51 changes: 51 additions & 0 deletions how-to/integrate-server-authentication/public/app/stuck.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<title>Stuck Redirect</title>
<link rel="icon" type="image/x-icon" href="../favicon.ico" />
<meta name="description" content="" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="../common/style/app.css" />
</head>

<body class="col fill gap20">
<header class="row spread middle">
<div class="col">
<h1>Stuck Redirect</h1>
<h1 class="tag">An example of a window that the platform doesn't control.</h1>
</div>
<div class="row middle gap20">
<image src="../common/images/icon-blue.png" alt="OpenFin" height="40px"></image>
</div>
</header>
<main class="col fill gap10">
<p>
An example assuming you have logged in and are authenticated or you were already authenticated but
something went wrong before you were redirected to the main provider page.
</p>
<p>If you logout and exit you will be presented with the login screen again.</p>
<p>If you just exit you will be presented with this screen again.</p>
</main>
<footer class="row right gap20">
<button id="btnLogout">Logout and Exit</button>
<button id="btnExit">Exit</button>
</footer>
<script>
const btnLogout = document.querySelector('#btnLogout');
btnLogout.addEventListener('click', () => {
// On logout the session cookies are cleared and close the app
fetch('http://localhost:8080/app/logout', {
method: 'post'
}).then(async (response) => {
await fin.Application.getCurrentSync().quit();
});
});
const btnExit = document.querySelector('#btnExit');
btnExit.addEventListener('click', async () => {
await fin.Application.getCurrentSync().quit();
});
</script>
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
console.log('auth-preload-check.js loaded. Performing logic checks.');
// wrap in an async iife to not pollute the global scope and allow the use of await
(async () => {
// preload scripts can be loaded into an iframe so only check the top level window
if (window === window.top && window.fin !== undefined) {
console.log('auth-preload-check.js logic starting.');
// Create a new URL object from the current window location
const url = new URL(window.location.href);

// determine behavior based on the current URL
if (url.pathname === '/app/login') {
console.log('Detected we are on the login page.');
// If we are on the login page ensure the page is visible
await fin.me.show();
} else {
// ensure the page is hidden as we may have shown it if it was the login page and we are now on a redirect page or the provider.
console.log('We are on a page that should not be visible. Ensuring the window is hidden.');
await fin.me.hide();
}

if (url.pathname === '/app/stuck') {
console.log(
'Detected we are authenticated but a redirect has encountered an error and is stuck so the main provider.html page will not be loaded. Showing a friendly error message.'
);
window.open('/app/friendly-error', '_blank');
}
}
})();
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"devtools_port": 9090,
"licenseKey": "openfin-demo-license-key",
"runtime": {
"arguments": "",
"version": "38.126.83.79"
},
"platform": {
"uuid": "integrate-server-authentication-hidden",
"icon": "http://localhost:8080/favicon.ico",
"autoShow": false,
"providerUrl": "http://localhost:8080/platform/provider.html",
"preventQuitOnLastWindowClosed": true,
"frame": false,
"alwaysOnTop": true,
"resizable": false,
"defaultCentered": true,
"defaultHeight": 700,
"defaultWidth": 600,
"saveWindowState": false,
"showTaskbarIcon": false,
"cornerRounding": {
"width": 10,
"height": 10
},
"preloadScripts": [
{
"url": "http://localhost:8080/preload/auth-preload-check.js"
}
]
},
"shortcut": {
"company": "OpenFin",
"description": "A way of showing examples of what OpenFin can do.",
"icon": "http://localhost:8080/favicon.ico",
"name": "Integrate Server Authentication - Hidden Provider - v19.1.0",
"target": ["desktop", "start-menu"]
},
"supportInformation": {
"company": "OpenFin",
"product": "Workspace Starter - Integrate Server Authentication - Client - Hidden Provider",
"email": "[email protected]",
"forwardErrorReports": true
}
}
63 changes: 56 additions & 7 deletions how-to/integrate-server-authentication/server/src/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import path from "path";
const router = express.Router();
export default router;

const sessionIds: { [id: string]: unknown } = {};
const sessionIds: { [id: string]: string } = {};
const stuckUsers: { [id: string]: boolean } = {};
const SESSION_COOKIE_NAME = "app-session-id";

/**
Expand All @@ -19,21 +20,55 @@ function corsMiddleware(req: Request, res: Response, next: NextFunction): void {
next();
}

/**
* Clears the session dictionary of the given session id.
* @param sessionId The session id to clear.
*/
function clearSessionDictionary(sessionId: string): void {
if (sessionId) {
delete stuckUsers[sessionId];
delete sessionIds[sessionId];
}
}

/**
* Get the platform provider.html, if the user has no authentication cookie
* then instead redirect to the login route.
*/
router.get("/platform/provider.html", (req, res, next) => {
console.log("Received request for /platform/provider.html");
if (req.cookies?.[SESSION_COOKIE_NAME] && sessionIds[req.cookies[SESSION_COOKIE_NAME]]) {
console.log("Session cookie available. Navigating to /platform/provider.html");
res.sendFile(path.join(__dirname, "..", "..", "public/platform/provider.html"));
const sessionId = sessionIds[req.cookies[SESSION_COOKIE_NAME]];
const isStuckUser = stuckUsers[sessionId];
if (isStuckUser) {
console.log("Stuck user detected. Navigating to /app/stuck");
res.redirect("/app/stuck");
} else {
console.log("Session cookie available. Navigating to /platform/provider.html");
res.sendFile(path.join(__dirname, "..", "..", "public/platform/provider.html"));
}
} else {
console.log("Session cookie not available. Navigating to /app/login");
res.redirect("/app/login?return=/platform/provider.html");
}
});

/**
* The stuck page represents a page that is intended to be a redirect where encountered an error and stopped.
*/
router.get("/app/stuck", (req, res, next) => {
console.log("Received request for /app/stuck");
res.sendFile(path.join(__dirname, "..", "..", "public/app/stuck.html"));
});

/**
* The friendly error page is something shown to users if something has gone wrong e.g. they are stuck before the provider is loaded.
*/
router.get("/app/friendly-error", (req, res, next) => {
console.log("Received request for /app/friendly-error");
res.sendFile(path.join(__dirname, "..", "..", "public/app/friendly-error.html"));
});

/**
* Get the app.html content, if not authentication redirect to the login page.
*/
Expand Down Expand Up @@ -73,7 +108,8 @@ router.get("/app/login", async (req, res, next) => {
console.log("Received request for /app/login");
if (req.cookies?.[SESSION_COOKIE_NAME]) {
console.log("Session cookies available so clearing them as login has been requested.");
delete sessionIds[req.cookies[SESSION_COOKIE_NAME]];
const sessionId = sessionIds[req.cookies[SESSION_COOKIE_NAME]];
clearSessionDictionary(sessionId);
}
res.clearCookie(SESSION_COOKIE_NAME);
console.log("Navigating to /app/login.html");
Expand All @@ -88,7 +124,8 @@ router.post("/app/logout", (req, res, next) => {
console.log("Received request for /app/logout");
if (req.cookies?.[SESSION_COOKIE_NAME]) {
console.log("Session cookies available so clearing them as login has been requested.");
delete sessionIds[req.cookies[SESSION_COOKIE_NAME]];
const sessionId = sessionIds[req.cookies[SESSION_COOKIE_NAME]];
clearSessionDictionary(sessionId);
}
res.clearCookie(SESSION_COOKIE_NAME);
console.log("Navigating to /app/login.html");
Expand All @@ -101,18 +138,30 @@ router.post("/app/logout", (req, res, next) => {
router.post("/app/login", (req, res, next) => {
console.log("Received post to /app/login.html");
if (req.body?.email === "[email protected]" && req.body?.password === "pass1234") {
const sessionId = Date.now();
const sessionId = `${Date.now()}`;
sessionIds[sessionId] = sessionId;
res.cookie(SESSION_COOKIE_NAME, sessionId);
const returnUrl = req.body?.returnUrl as string;
console.log(
`Login details [email protected] / pass1234 session cookies set. Redirecting to originally requested url: ${returnUrl}`
);
res.redirect(returnUrl);
} else if (req.body?.email === "[email protected]" && req.body?.password === "pass1234") {
const sessionId = `${Date.now()}`;
sessionIds[sessionId] = sessionId;
stuckUsers[sessionId] = true;
res.cookie(SESSION_COOKIE_NAME, sessionId);
const returnUrl = req.body?.returnUrl as string;
console.log(
`Login details [email protected] / pass1234 session cookies set. Redirecting to stuck url: /stuck instead of ${returnUrl}`
);
res.redirect("/app/stuck");
} else {
console.log(
"Demo credentials not provided. You need username/password of [email protected] / pass1234 to login"
"Demo credentials not provided. You need username/password of [email protected] / pass1234 to login or [email protected] / pass1234 to get authenticated and stuck."
);
const sessionId = sessionIds[req.cookies[SESSION_COOKIE_NAME]];
clearSessionDictionary(sessionId);
res.clearCookie(SESSION_COOKIE_NAME);
res.status(401).send("Unauthorized");
}
Expand Down

0 comments on commit bd850c7

Please sign in to comment.