Improve Gateway authentication and static HTTP fidelity
This commit is contained in:
1 parent
42561bfa34
commit
4ab9411aca
8 files changed
+167
-58
No files matched your search
+2
-33
@@ -7,42 +7,10 @@
|
||||
<title class="_ctxstxt_NetscalerGateway">Citrix Gateway</title>
|
||||
<link rel="icon" href="/logon/LogonPoint/receiver/images/common/icon_vpn.ico" sizes="16x16 32x32 48x48 64x64" type="image/vnd.microsoft.icon">
|
||||
<link rel="shortcut icon" href="/logon/LogonPoint/receiver/images/common/icon_vpn.ico" type="image/vnd.microsoft.icon">
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
html, body { min-height: 100%; margin: 0; }
|
||||
body { background: #302c41 url('/logon/LogonPoint/receiver/images/common/ReceiverFullScreenBackground.jpg') center / cover fixed;
|
||||
font-family: "Helvetica Neue", "Segoe UI", Helvetica, Arial, sans-serif; color: #fff; }
|
||||
main { min-height: 100vh; display: flex; align-items: center; }
|
||||
.band { width: 100%; min-height: 350px; background: rgba(58, 54, 65, .86); display: flex; align-items: center; }
|
||||
.layout { width: min(1086px, calc(100% - 64px)); margin: auto; display: grid; grid-template-columns: 258px 570px; align-items: center; }
|
||||
.logo { width: 160px; height: auto; }
|
||||
h1 { font-size: 18px; font-weight: 400; margin: 0 0 18px; }
|
||||
.row { display: grid; grid-template-columns: 185px 1fr; align-items: center; margin-bottom: 6px; }
|
||||
label { color: #bdbbc2; font-size: 16px; }
|
||||
input { width: 100%; height: 44px; border: 1px solid #ddd; background: white; color: #111; border-radius: 0; padding: 8px 15px; font: inherit; }
|
||||
input:focus-visible { outline: 2px solid #79cce2; outline-offset: 2px; }
|
||||
button { display: block; width: calc(100% - 185px); margin: 26px 0 0 185px; height: 45px; border: 0; border-radius: 0;
|
||||
background: #49a0bb; color: white; font: 600 16px "Helvetica Neue", "Segoe UI", Helvetica, Arial, sans-serif; cursor: pointer; }
|
||||
button:hover { background: #398aa4; }
|
||||
button:focus-visible { outline: 2px solid white; outline-offset: 3px; }
|
||||
button:disabled { cursor: wait; opacity: .75; }
|
||||
#message { margin: 12px 0 0 185px; font-size: 14px; color: #fff; }
|
||||
#message:empty { display: none; }
|
||||
@media (max-width: 950px) {
|
||||
.layout { width: min(570px, calc(100% - 48px)); display: block; padding: 40px 0; }
|
||||
.logo { margin-bottom: 28px; }
|
||||
}
|
||||
@media (max-width: 540px) {
|
||||
.row { display: block; margin-bottom: 16px; }
|
||||
label { display: block; margin-bottom: 7px; }
|
||||
button { width: 100%; margin-left: 0; }
|
||||
#message { margin-left: 0; }
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" href="/vpn/js/rdx/core/css/rdx.css">
|
||||
<script src="/vpn/js/rdx.js"></script>
|
||||
<script src="/vpn/js/gateway_login_view.js"></script>
|
||||
<script src="/logon/LogonPoint/login.js" defer></script>
|
||||
<script>var _ctxstxt_CitrixCopyright = 'Copyright (c) Citrix Systems, Inc.';</script>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
@@ -53,6 +21,7 @@
|
||||
<h1>Melden Sie sich an</h1>
|
||||
<div class="row"><label for="login">Benutzername:</label><input id="login" name="login" required autofocus autocomplete="off" spellcheck="false"></div>
|
||||
<div class="row"><label for="passwd">Kennwort:</label><input id="passwd" name="passwd" type="password" required autocomplete="off"></div>
|
||||
<input type="hidden" name="StateContext" value="">
|
||||
<input type="hidden" name="savecredentials" value="false">
|
||||
<button id="nsg-x1-logon-button" name="nsg-x1-logon-button" value="Log On" type="submit">Anmelden</button>
|
||||
<p id="message" role="status" aria-live="polite"></p>
|
||||
|
||||
+18
-6
@@ -2,19 +2,31 @@
|
||||
const form = document.getElementById('login-form');
|
||||
const button = document.getElementById('nsg-x1-logon-button');
|
||||
const message = document.getElementById('message');
|
||||
async function authenticate(path, body) {
|
||||
const response = await fetch(path, {
|
||||
method: 'POST', body, credentials: 'same-origin', redirect: 'error',
|
||||
headers: { Accept: 'application/vnd.citrix.authenticateresponse-1+xml' },
|
||||
signal: AbortSignal.timeout(10000)
|
||||
});
|
||||
if (!response.ok) throw new Error('Request failed');
|
||||
const result = rdx.utils.authentication(await response.text());
|
||||
form.elements.StateContext.value = result.state;
|
||||
// Only the local authentication endpoint can receive form credentials.
|
||||
if (result.postback === '/nf/auth/doAuthentication.do') form.action = result.postback;
|
||||
return result;
|
||||
}
|
||||
const ready = authenticate('/nf/auth/getAuthenticationRequirements.do', new URLSearchParams())
|
||||
.catch(() => null);
|
||||
form.addEventListener('submit', async event => {
|
||||
event.preventDefault();
|
||||
button.disabled = true;
|
||||
message.textContent = '';
|
||||
try {
|
||||
await ready;
|
||||
const body = new URLSearchParams(new FormData(form));
|
||||
body.set('nsg-x1-logon-button', 'Log On');
|
||||
const response = await fetch('/nf/auth/doAuthentication.do', {
|
||||
method: 'POST', body, credentials: 'same-origin', redirect: 'error',
|
||||
signal: AbortSignal.timeout(10000)
|
||||
});
|
||||
if (!response.ok) throw new Error('Request failed');
|
||||
message.textContent = 'Anmeldung fehlgeschlagen. Überprüfen Sie Ihre Zugangsdaten.';
|
||||
const result = await authenticate('/nf/auth/doAuthentication.do', body);
|
||||
message.textContent = result.errors.join(' ') || 'Anmeldung fehlgeschlagen. Überprüfen Sie Ihre Zugangsdaten.';
|
||||
} catch {
|
||||
message.textContent = 'Die Anmeldung ist derzeit nicht möglich. Versuchen Sie es erneut.';
|
||||
} finally {
|
||||
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
* { box-sizing: border-box; }
|
||||
html, body { min-height: 100%; margin: 0; }
|
||||
body { background: #302c41 url('/logon/LogonPoint/receiver/images/common/ReceiverFullScreenBackground.jpg') center / cover fixed;
|
||||
font-family: "Helvetica Neue", "Segoe UI", Helvetica, Arial, sans-serif; color: #fff; }
|
||||
main { min-height: 100vh; display: flex; align-items: center; }
|
||||
.band { width: 100%; min-height: 350px; background: rgba(58, 54, 65, .86); display: flex; align-items: center; }
|
||||
.layout { width: min(1086px, calc(100% - 64px)); margin: auto; display: grid; grid-template-columns: 258px 570px; align-items: center; }
|
||||
.logo { width: 160px; height: auto; }
|
||||
h1 { font-size: 18px; font-weight: 400; margin: 0 0 18px; }
|
||||
.row { display: grid; grid-template-columns: 185px 1fr; align-items: center; margin-bottom: 6px; }
|
||||
label { color: #bdbbc2; font-size: 16px; }
|
||||
input { width: 100%; height: 44px; border: 1px solid #ddd; background: white; color: #111; border-radius: 0; padding: 8px 15px; font: inherit; }
|
||||
input:focus-visible { outline: 2px solid #79cce2; outline-offset: 2px; }
|
||||
button { display: block; width: calc(100% - 185px); margin: 26px 0 0 185px; height: 45px; border: 0; border-radius: 0;
|
||||
background: #49a0bb; color: white; font: 600 16px "Helvetica Neue", "Segoe UI", Helvetica, Arial, sans-serif; cursor: pointer; }
|
||||
button:hover { background: #398aa4; }
|
||||
button:focus-visible { outline: 2px solid white; outline-offset: 3px; }
|
||||
button:disabled { cursor: wait; opacity: .75; }
|
||||
#message { margin: 12px 0 0 185px; font-size: 14px; color: #fff; }
|
||||
#message:empty { display: none; }
|
||||
@media (max-width: 950px) {
|
||||
.layout { width: min(570px, calc(100% - 48px)); display: block; padding: 40px 0; }
|
||||
.logo { margin-bottom: 28px; }
|
||||
}
|
||||
@media (max-width: 540px) {
|
||||
.row { display: block; margin-bottom: 16px; }
|
||||
label { display: block; margin-bottom: 7px; }
|
||||
button { width: 100%; margin-left: 0; }
|
||||
#message { margin-left: 0; }
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
var _ctxstxt_CitrixCopyright = 'Copyright (c) Citrix Systems, Inc.';
|
||||
window.CTXS = window.CTXS || {};
|
||||
window.rdx = window.rdx || {};
|
||||
rdx.base_view = function (element) { this.element = element; };
|
||||
rdx.base_view.prototype.show = function () { if (this.element) this.element.hidden = false; };
|
||||
rdx.base_view.prototype.hide = function () { if (this.element) this.element.hidden = true; };
|
||||
rdx.utils = {
|
||||
get_view: function (id) { return new rdx.base_view(document.getElementById(id || 'login-form')); },
|
||||
authentication: function (xml) {
|
||||
const document = new DOMParser().parseFromString(xml, 'application/xml');
|
||||
const ns = 'http://citrix.com/authentication/response/1';
|
||||
if (document.getElementsByTagName('parsererror').length ||
|
||||
document.documentElement.localName !== 'AuthenticateResponse' ||
|
||||
document.documentElement.namespaceURI !== ns) throw new Error('Invalid authentication response');
|
||||
const text = name => document.getElementsByTagNameNS(ns, name)[0]?.textContent || '';
|
||||
const errors = Array.from(document.getElementsByTagNameNS(ns, 'Label'))
|
||||
.filter(label => label.getElementsByTagNameNS(ns, 'Type')[0]?.textContent === 'error')
|
||||
.map(label => label.getElementsByTagNameNS(ns, 'Text')[0]?.textContent || '');
|
||||
return { state: text('StateContext'), postback: text('PostBack'), errors };
|
||||
}
|
||||
};
|
||||
CTXS.core = { getView: rdx.utils.get_view };
|
||||
Reference in new issue
Block a user