summaryrefslogtreecommitdiff
path: root/templates
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2023-07-31 13:34:44 -0700
committerMike Crute <mike@crute.us>2023-07-31 13:34:44 -0700
commitdb217bbb1f74b7aa955d3095fef62c71946768cf (patch)
tree80ee49b11f137412cf440262950e4a116099eb24 /templates
parent6d867608837f879be2eb934d034f49359f973c84 (diff)
downloadwebsocket_proxy-db217bbb1f74b7aa955d3095fef62c71946768cf.tar.bz2
websocket_proxy-db217bbb1f74b7aa955d3095fef62c71946768cf.tar.xz
websocket_proxy-db217bbb1f74b7aa955d3095fef62c71946768cf.zip
Complete registration flow
Diffstat (limited to 'templates')
-rw-r--r--templates/js/base64.js66
-rw-r--r--templates/login.tpl92
-rw-r--r--templates/register.tpl196
3 files changed, 153 insertions, 201 deletions
diff --git a/templates/js/base64.js b/templates/js/base64.js
new file mode 100644
index 0000000..52df92b
--- /dev/null
+++ b/templates/js/base64.js
@@ -0,0 +1,66 @@
1/*
2 * Base64URL-ArrayBuffer
3 * https://github.com/herrjemand/Base64URL-ArrayBuffer
4 *
5 * Copyright (c) 2017 Yuriy Ackermann <ackermann.yuriy@gmail.com>
6 * Copyright (c) 2012 Niklas von Hertzen
7 * Licensed under the MIT license.
8 *
9 */
10(function(){
11 'use strict';
12
13 let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
14
15 // Use a lookup table to find the index.
16 let lookup = new Uint8Array(256);
17 for (let i = 0; i < chars.length; i++) {
18 lookup[chars.charCodeAt(i)] = i;
19 }
20
21 let encode = function(arraybuffer) {
22 let bytes = new Uint8Array(arraybuffer),
23 i, len = bytes.length, base64url = '';
24
25 for (i = 0; i < len; i+=3) {
26 base64url += chars[bytes[i] >> 2];
27 base64url += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
28 base64url += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
29 base64url += chars[bytes[i + 2] & 63];
30 }
31
32 if ((len % 3) === 2) {
33 base64url = base64url.substring(0, base64url.length - 1);
34 } else if (len % 3 === 1) {
35 base64url = base64url.substring(0, base64url.length - 2);
36 }
37
38 return base64url;
39 };
40
41 let decode = function(base64string) {
42 let bufferLength = base64string.length * 0.75,
43 len = base64string.length, i, p = 0,
44 encoded1, encoded2, encoded3, encoded4;
45
46 let bytes = new Uint8Array(bufferLength);
47
48 for (i = 0; i < len; i+=4) {
49 encoded1 = lookup[base64string.charCodeAt(i)];
50 encoded2 = lookup[base64string.charCodeAt(i+1)];
51 encoded3 = lookup[base64string.charCodeAt(i+2)];
52 encoded4 = lookup[base64string.charCodeAt(i+3)];
53
54 bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
55 bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
56 bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
57 }
58
59 return bytes.buffer
60 };
61
62 window.base64url = {
63 'decode': decode,
64 'encode': encode
65 };
66})();
diff --git a/templates/login.tpl b/templates/login.tpl
index 0323409..7ee357d 100644
--- a/templates/login.tpl
+++ b/templates/login.tpl
@@ -4,76 +4,10 @@
4 <meta charset="utf-8" /> 4 <meta charset="utf-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1"> 5 <meta name="viewport" content="width=device-width, initial-scale=1">
6 <meta name="render-time" content="{{ .RenderTime }}"> 6 <meta name="render-time" content="{{ .RenderTime }}">
7 <meta name="csrf-token" content="{{ .CSRFToken }}" />
7 {{ if .Context.HasKey "title" }}<title>{{ .Context.Get "title" }}</title>{{ else }}<title>SSH Proxy</title>{{ end }} 8 {{ if .Context.HasKey "title" }}<title>{{ .Context.Get "title" }}</title>{{ else }}<title>SSH Proxy</title>{{ end }}
8 9
9 <script type="text/javascript"> 10 <script type="text/javascript" src="/js/base64.js"></script>
10 /*
11 * Base64URL-ArrayBuffer
12 * https://github.com/herrjemand/Base64URL-ArrayBuffer
13 *
14 * Copyright (c) 2017 Yuriy Ackermann <ackermann.yuriy@gmail.com>
15 * Copyright (c) 2012 Niklas von Hertzen
16 * Licensed under the MIT license.
17 *
18 */
19 (function(){
20 'use strict';
21
22 let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
23
24 // Use a lookup table to find the index.
25 let lookup = new Uint8Array(256);
26 for (let i = 0; i < chars.length; i++) {
27 lookup[chars.charCodeAt(i)] = i;
28 }
29
30 let encode = function(arraybuffer) {
31 let bytes = new Uint8Array(arraybuffer),
32 i, len = bytes.length, base64url = '';
33
34 for (i = 0; i < len; i+=3) {
35 base64url += chars[bytes[i] >> 2];
36 base64url += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)];
37 base64url += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
38 base64url += chars[bytes[i + 2] & 63];
39 }
40
41 if ((len % 3) === 2) {
42 base64url = base64url.substring(0, base64url.length - 1);
43 } else if (len % 3 === 1) {
44 base64url = base64url.substring(0, base64url.length - 2);
45 }
46
47 return base64url;
48 };
49
50 let decode = function(base64string) {
51 let bufferLength = base64string.length * 0.75,
52 len = base64string.length, i, p = 0,
53 encoded1, encoded2, encoded3, encoded4;
54
55 let bytes = new Uint8Array(bufferLength);
56
57 for (i = 0; i < len; i+=4) {
58 encoded1 = lookup[base64string.charCodeAt(i)];
59 encoded2 = lookup[base64string.charCodeAt(i+1)];
60 encoded3 = lookup[base64string.charCodeAt(i+2)];
61 encoded4 = lookup[base64string.charCodeAt(i+3)];
62
63 bytes[p++] = (encoded1 << 2) | (encoded2 >> 4);
64 bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
65 bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
66 }
67
68 return bytes.buffer
69 };
70
71 window.base64url = {
72 'decode': decode,
73 'encode': encode
74 };
75 })();
76 </script>
77 11
78 <script type="text/javascript"> 12 <script type="text/javascript">
79 window.addEventListener("load", _ => { 13 window.addEventListener("load", _ => {
@@ -83,24 +17,35 @@
83 document.getElementById("code").value = code; 17 document.getElementById("code").value = code;
84 } 18 }
85 19
20 const usernameCookie = document.cookie
21 .split("; ")
22 .find((row) => row.startsWith("username="))
23 ?.split("=")[1];
24
25 if (usernameCookie != undefined && usernameCookie !== "") {
26 document.getElementById("username").value = usernameCookie;
27 }
28
86 document.getElementById("login").addEventListener("click", evt => { 29 document.getElementById("login").addEventListener("click", evt => {
87 evt.preventDefault(); 30 evt.preventDefault();
88 31
89 var username = document.getElementById("username"); 32 const username = document.getElementById("username").value;
90 fetch("/auth/login/" + username.value) 33 document.cookie = `username=${username}; expires=Fri, 31 Dec 9999 23:59:59 GMT; Secure`;
91 .then((result) => result.json()) 34
35 fetch("/auth/login/" + username)
36 .then((response) => response.json())
92 .then((data) => { 37 .then((data) => {
93 data.publicKey.challenge = base64url.decode(data.publicKey.challenge); 38 data.publicKey.challenge = base64url.decode(data.publicKey.challenge);
94 data.publicKey.allowCredentials.forEach(e => e.id = base64url.decode(e.id)); 39 data.publicKey.allowCredentials.forEach(e => e.id = base64url.decode(e.id));
95 40
96 navigator.credentials.get(data) 41 navigator.credentials.get(data)
97 .then((credential) => { 42 .then((credential) => {
98 fetch("/auth/login/" + username.value, { 43 fetch("/auth/login/" + username, {
99 method: "POST", 44 method: "POST",
100 mode: "same-origin", 45 mode: "same-origin",
101 headers: { 46 headers: {
102 "Content-Type": "application/json", 47 "Content-Type": "application/json",
103 "X-CSRF-Token": "{{ .CSRFToken }}" 48 "X-CSRF-Token": document.querySelector("meta[name=csrf-token]").content,
104 }, 49 },
105 body: JSON.stringify({ 50 body: JSON.stringify({
106 code: document.getElementById("code").value, 51 code: document.getElementById("code").value,
@@ -130,7 +75,6 @@
130 <form> 75 <form>
131 <label for="code">Code: <input type="text" name="code" id="code" /></label><br/> 76 <label for="code">Code: <input type="text" name="code" id="code" /></label><br/>
132 <label for="username">Username: <input type="text" name="username" id="username" autocorrect="off" autocapitalize="none" autocomplete="username" /></label><br/> 77 <label for="username">Username: <input type="text" name="username" id="username" autocorrect="off" autocapitalize="none" autocomplete="username" /></label><br/>
133 <input type="hidden" value="{{ .CSRFToken }}" name="csrf-token" />
134 <input type="submit" id="login" value="Login" /> 78 <input type="submit" id="login" value="Login" />
135 </form> 79 </form>
136 </body> 80 </body>
diff --git a/templates/register.tpl b/templates/register.tpl
index 794ddaa..37252a0 100644
--- a/templates/register.tpl
+++ b/templates/register.tpl
@@ -4,143 +4,85 @@
4 <meta charset="utf-8" /> 4 <meta charset="utf-8" />
5 <meta name="viewport" content="width=device-width, initial-scale=1"> 5 <meta name="viewport" content="width=device-width, initial-scale=1">
6 <meta name="render-time" content="{{ .RenderTime }}"> 6 <meta name="render-time" content="{{ .RenderTime }}">
7 <meta name="csrf-token" content="{{ .CSRFToken }}" />
7 {{ if .Context.HasKey "title" }}<title>{{ .Context.Get "title" }}</title>{{ else }}<title>SSH Proxy</title>{{ end }} 8 {{ if .Context.HasKey "title" }}<title>{{ .Context.Get "title" }}</title>{{ else }}<title>SSH Proxy</title>{{ end }}
8 </head>
9
10 <body>
11 <script type="text/javascript">
12 /*
13 * Base64URL-ArrayBuffer
14 * https://github.com/herrjemand/Base64URL-ArrayBuffer
15 *
16 * Copyright (c) 2017 Yuriy Ackermann <ackermann.yuriy@gmail.com>
17 * Copyright (c) 2012 Niklas von Hertzen
18 * Licensed under the MIT license.
19 *
20 */
21 (function(){
22 'use strict';
23
24 let chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
25
26 // Use a lookup table to find the index.
27 let lookup = new Uint8Array(256);
28 for (let i = 0; i < chars.length; i++) {
29 lookup[chars.charCodeAt(i)] = i;
30 }
31 9
32 let encode = function(arraybuffer) { 10 <script type="text/javascript" src="/js/base64.js"></script>
33 let bytes = new Uint8Array(arraybuffer),
34 i, len = bytes.length, base64url = '';
35 11
36 for (i = 0; i < len; i+=3) { 12 <script type="text/javascript">
37 base64url += chars[bytes[i] >> 2]; 13 function doRegister(evt) {
38 base64url += chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; 14 evt.preventDefault();
39 base64url += chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)];
40 base64url += chars[bytes[i + 2] & 63];
41 }
42
43 if ((len % 3) === 2) {
44 base64url = base64url.substring(0, base64url.length - 1);
45 } else if (len % 3 === 1) {
46 base64url = base64url.substring(0, base64url.length - 2);
47 }
48
49 return base64url;
50 };
51
52 let decode = function(base64string) {
53 let bufferLength = base64string.length * 0.75,
54 len = base64string.length, i, p = 0,
55 encoded1, encoded2, encoded3, encoded4;
56
57 let bytes = new Uint8Array(bufferLength);
58
59 for (i = 0; i < len; i+=4) {
60 encoded1 = lookup[base64string.charCodeAt(i)];
61 encoded2 = lookup[base64string.charCodeAt(i+1)];
62 encoded3 = lookup[base64string.charCodeAt(i+2)];
63 encoded4 = lookup[base64string.charCodeAt(i+3)];
64 15
65 bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); 16 const code = document.getElementById("code").value;
66 bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2);
67 bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63);
68 }
69 17
70 return bytes.buffer 18 const username = document.getElementById("username").value;
71 }; 19 document.cookie = `username=${username}; expires=Fri, 31 Dec 9999 23:59:59 GMT; Secure`;
72 20
73 window.base64url = { 21 fetch(`/auth/register/${username}?code=${code}`)
74 'decode': decode, 22 .then((response) => {
75 'encode': encode 23 if (!response.ok) {
76 }; 24 document.body.innerHTML = "<h1>Error Fetching Registration Request</h1>";
77 })(); 25 throw new Error("Error fetching registration request");
26 }
27 return response.json();
28 })
29 .then((data) => {
30 data.publicKey.challenge = base64url.decode(data.publicKey.challenge);
31 data.publicKey.user.id = base64url.decode(data.publicKey.user.id);
32
33 navigator.credentials.create(data)
34 .then((credential) => {
35 fetch(`/auth/register/${username}`, {
36 method: "POST",
37 mode: "same-origin",
38 headers: {
39 "Content-Type": "application/json",
40 "X-CSRF-Token": document.querySelector("meta[name=csrf-token]").content,
41 },
42 body: JSON.stringify({
43 code: code,
44 type: credential.type,
45 id: credential.id,
46 rawId: base64url.encode(credential.rawId),
47 response: {
48 clientDataJSON: base64url.encode(credential.response.clientDataJSON),
49 attestationObject: base64url.encode(credential.response.attestationObject)
50 }
51 })
52 })
53 .then((response) => {
54 if (response.ok) { document.body.innerHTML = "<h1>Success</h1>"; }
55 else { document.body.innerHTML = "<h1>Failure</h1>"; }
56 });
57 });
58 });
59 }
78 60
79 var request = {{ .Model.WebautnRequest }}; 61 window.addEventListener("load", _ => {
80 request.publicKey.challenge = base64url.decode(request.publicKey.challenge); 62 const urlParams = new URLSearchParams(window.location.search);
81 {{ if .Model.LoginMode }} 63 const code = urlParams.get("code");
82 request.publicKey.allowCredentials.forEach(e => e.id = base64url.decode(e.id)); 64 if (code !== "") {
83 {{ else }} 65 document.getElementById("code").value = code;
84 request.publicKey.user.id = base64url.decode(request.publicKey.user.id); 66 }
85 {{ end }}
86 67
87 {{ if .Model.LoginMode }} 68 const usernameCookie = document.cookie.split("; ")
88 navigator.credentials.get(request) 69 .find((row) => row.startsWith("username="))
89 .then((credential) => { 70 .split("=")[1];
90 console.log(credential);
91 71
92 fetch(document.URL, { 72 if (usernameCookie != undefined && usernameCookie !== "") {
93 method: "POST", 73 document.getElementById("username").value = usernameCookie;
94 mode: "same-origin", 74 }
95 headers: {
96 "Content-Type": "application/json",
97 "X-CSRF-Token": "{{ .CSRFToken }}"
98 },
99 body: JSON.stringify({
100 type: credential.type,
101 id: credential.id,
102 rawId: base64url.encode(credential.rawId),
103 response: {
104 authenticatorData: base64url.encode(credential.response.authenticatorData),
105 clientDataJSON: base64url.encode(credential.response.clientDataJSON),
106 signature: base64url.encode(credential.response.signature),
107 userHandle: base64url.encode(credential.response.userHandle)
108 }
109 })
110 })
111 .then((response) => {
112 if (response.ok) { document.body.innerHTML = "<h1>Success</h1>"; }
113 else { document.body.innerHTML = "<h1>Failure</h1>"; }
114 });
115 });
116 {{ else }}
117 navigator.credentials.create(request)
118 .then((credential) => {
119 console.log(credential);
120 75
121 fetch(document.URL, { 76 document.getElementById("login").addEventListener("click", doRegister);
122 method: "POST", 77 });
123 mode: "same-origin",
124 headers: {
125 "Content-Type": "application/json",
126 "X-CSRF-Token": "{{ .CSRFToken }}"
127 },
128 body: JSON.stringify({
129 type: credential.type,
130 id: credential.id,
131 rawId: base64url.encode(credential.rawId),
132 response: {
133 clientDataJSON: base64url.encode(credential.response.clientDataJSON),
134 attestationObject: base64url.encode(credential.response.attestationObject)
135 }
136 })
137 })
138 .then((response) => {
139 if (response.ok) { document.body.innerHTML = "<h1>Success</h1>"; }
140 else { document.body.innerHTML = "<h1>Failure</h1>"; }
141 });
142 });
143 {{ end }}
144 </script> 78 </script>
79 </head>
80
81 <body>
82 <form>
83 <label for="code">Code: <input type="text" name="code" id="code" /></label><br/>
84 <label for="username">Username: <input type="text" name="username" id="username" autocorrect="off" autocapitalize="none" autocomplete="username" /></label><br/>
85 <input type="submit" id="login" value="Login" />
86 </form>
145 </body> 87 </body>
146</html> 88</html>