-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added additional example of server auth
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
Showing
7 changed files
with
240 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
how-to/integrate-server-authentication/public/app/friendly-error.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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> | ||
|
51 changes: 51 additions & 0 deletions
51
how-to/integrate-server-authentication/public/app/stuck.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> |
28 changes: 28 additions & 0 deletions
28
how-to/integrate-server-authentication/public/preload/auth-preload-check.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'); | ||
} | ||
} | ||
})(); |
45 changes: 45 additions & 0 deletions
45
how-to/integrate-server-authentication/public/second.manifest.fin.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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"; | ||
|
||
/** | ||
|
@@ -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. | ||
*/ | ||
|
@@ -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"); | ||
|
@@ -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"); | ||
|
@@ -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"); | ||
} | ||
|