basic auth / session stuff working

This commit is contained in:
Samuel Lorch 2023-03-11 00:18:16 +01:00
parent b7daba6040
commit b80bc4d32f
6 changed files with 144 additions and 23 deletions

View file

@ -15,6 +15,7 @@
"@open-rpc/client-js": "^1.8.1",
"@vueuse/core": "^9.13.0",
"@vueuse/head": "^1.1.15",
"axios": "^1.3.4",
"events": "^3.3.0",
"focus-trap": "^7.3.1",
"focus-trap-vue": "^4.0.2",

63
client/pnpm-lock.yaml generated
View file

@ -12,6 +12,7 @@ specifiers:
'@vue-macros/volar': ^0.8.4
'@vueuse/core': ^9.13.0
'@vueuse/head': ^1.1.15
axios: ^1.3.4
eslint: ^8.35.0
eslint-plugin-vue: ^9.9.0
events: ^3.3.0
@ -37,6 +38,7 @@ dependencies:
'@open-rpc/client-js': 1.8.1
'@vueuse/core': 9.13.0_vue@3.2.47
'@vueuse/head': 1.1.15_vue@3.2.47
axios: 1.3.4
events: 3.3.0
focus-trap: 7.3.1
focus-trap-vue: 4.0.2_oggptlzwchqpaguemspe4ract4
@ -1507,11 +1509,25 @@ packages:
'@babel/types': 7.21.2
dev: true
/asynckit/0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
dev: false
/available-typed-arrays/1.0.5:
resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
engines: {node: '>= 0.4'}
dev: true
/axios/1.3.4:
resolution: {integrity: sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==}
dependencies:
follow-redirects: 1.15.2
form-data: 4.0.0
proxy-from-env: 1.1.0
transitivePeerDependencies:
- debug
dev: false
/balanced-match/1.0.2:
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
dev: true
@ -1646,6 +1662,13 @@ packages:
resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
dev: true
/combined-stream/1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
engines: {node: '>= 0.8'}
dependencies:
delayed-stream: 1.0.0
dev: false
/concat-map/0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
dev: true
@ -1741,6 +1764,11 @@ packages:
resolution: {integrity: sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ==}
dev: true
/delayed-stream/1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
dev: false
/destr/1.2.2:
resolution: {integrity: sha512-lrbCJwD9saUQrqUfXvl6qoM+QN3W7tLV5pAOs+OqOmopCCz/JkE05MHedJR1xfk4IAnZuJXPVuN5+7jNA2ZCiA==}
dev: true
@ -2140,12 +2168,31 @@ packages:
tabbable: 6.1.1
dev: false
/follow-redirects/1.15.2:
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
engines: {node: '>=4.0'}
peerDependencies:
debug: '*'
peerDependenciesMeta:
debug:
optional: true
dev: false
/for-each/0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
dependencies:
is-callable: 1.2.7
dev: true
/form-data/4.0.0:
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
engines: {node: '>= 6'}
dependencies:
asynckit: 0.4.0
combined-stream: 1.0.8
mime-types: 2.1.35
dev: false
/fs-minipass/2.1.0:
resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
engines: {node: '>= 8'}
@ -2800,6 +2847,18 @@ packages:
braces: 3.0.2
picomatch: 2.3.1
/mime-db/1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
dev: false
/mime-types/2.1.35:
resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
engines: {node: '>= 0.6'}
dependencies:
mime-db: 1.52.0
dev: false
/mimic-fn/2.1.0:
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
engines: {node: '>=6'}
@ -3086,6 +3145,10 @@ packages:
resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==}
dev: true
/proxy-from-env/1.1.0:
resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
dev: false
/prr/1.0.1:
resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
dev: true

View file

@ -3,7 +3,7 @@ import IDashboard from '~icons/ri/dashboard-2-line';
import IRule from '~icons/material-symbols/rule-folder-outline-sharp';
import IAddress from '~icons/eos-icons/ip';
import { authenticate, checkAuthentication, setup } from "./api";
import { authenticate, logout, checkAuthentication, setup } from "./api";
enum NavState { Open, Reduced, Collapsed };
const NavStateCount = 3;
@ -31,17 +31,18 @@ async function tryLogin() {
console.info("authentication error");
} else {
// TODO Check for MFA here
authState = 1;
authState = AuthState.Authenticated;
}
}
async function tryLogout() {
authState = 0;
logout();
authState = AuthState.Unauthenticated;
}
function deAuthenticatedCallback() {
console.info("Unauthenticated");
authState = 0;
authState = AuthState.Unauthenticated;
}
onMounted(async() => {
@ -49,7 +50,7 @@ onMounted(async() => {
let res = await checkAuthentication();
authState = res.auth;
loginDisabled = false;
if (authState > 0) {
if (authState === AuthState.Authenticated) {
console.info("Already Authenticated ", authState);
}
else console.info("Check Authentication error",res.error);

View file

@ -1,7 +1,8 @@
import { RequestManager, HTTPTransport, WebSocketTransport, Client } from "@open-rpc/client-js";
import axios from "axios";
const httpTransport = new HTTPTransport("http://"+ window.location.host +"/api");
const socktransport = new WebSocketTransport("ws://"+ window.location.host + "/ws/api");
const manager = new RequestManager([socktransport, httpTransport], () => crypto.randomUUID());
// const socktransport = new WebSocketTransport("ws://"+ window.location.host + "/ws/api");
const manager = new RequestManager([httpTransport], () => crypto.randomUUID());
const client = new Client(manager);
let deAuthenticatedCallback;
@ -30,23 +31,31 @@ export async function authenticate(username: string, password: string): Promise<
}
}
export async function logout(): Promise<any> {
const pResponse = axios.post("/logout", null, {timeout: 10100});
try {
const response = await pResponse;
return { data: response.data, error: null};
} catch (error) {
return { data: null, error: error};
}
}
export async function checkAuthentication() {
const res = await apiCall("session-check", {});
if (res.error == "HTTP: Your Session cookie is invalid") return {auth: 0, error: null};
if (res.error == "HTTP: Your Session Requires TFA") return {auth: 1, error: null};
else if (res.error) return {auth: 0, error: res.error};
else {
/* TODO add commit_hash storing
const pResponse = axios.post("/session", null, {timeout: 10100});
try {
const response = await pResponse;
const last_hash = window.localStorage.getItem("commit_hash");
if (last_hash) {
if (last_hash !== res.data.commit_hash) {
if (last_hash !== response.data.commit_hash) {
console.log("Detected New Backend Version, Reloading...");
window.localStorage.removeItem("commit_hash");
window.location.reload(true);
window.location.reload();
}
} else window.localStorage.setItem("commit_hash", res.data.commit_hash);
*/
} else window.localStorage.setItem("commit_hash", response.data.commit_hash);
return {auth: 2, error: null};
} catch (error) {
return {auth: 0, error: error};
}
}

View file

@ -18,6 +18,9 @@ export default defineConfig({
server: {
"proxy": {
"/api": "http://localhost:8080",
"/login": "http://localhost:8080",
"/logout": "http://localhost:8080",
"/session": "http://localhost:8080",
"/ws": {
target: "ws://localhost:8080",
ws: true,

View file

@ -1,11 +1,15 @@
package server
import (
"encoding/json"
"io"
"net/http"
"runtime/debug"
"sync"
"time"
"github.com/google/uuid"
"golang.org/x/exp/slog"
)
type SessionKeyType string
@ -19,9 +23,29 @@ type Session struct {
// TODO Add []websocket.Conn pointer to close all active websockets, alternativly do this via context cancelation
}
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
type SessionResponse struct {
CommitHash string `json:"commit_hash"`
}
var sessionsSync sync.Mutex
var sessions map[string]*Session = map[string]*Session{}
var CommitHash = func() string {
if info, ok := debug.ReadBuildInfo(); ok {
for _, setting := range info.Settings {
if setting.Key == "vcs.revision" {
return setting.Value
}
}
}
return "asd"
}()
func GetSession(r *http.Request) (string, *Session) {
c, err := r.Cookie("session")
if err != nil {
@ -69,18 +93,28 @@ func CleanupSessions(stop chan struct{}) {
}
func HandleLogin(w http.ResponseWriter, r *http.Request) {
username := r.PostFormValue("username")
password := r.PostFormValue("password")
if username == "admin" && password == "12345" {
GenerateSession(w, username)
buf, err := io.ReadAll(r.Body)
if err != nil {
slog.Error("Reading Body", err)
return
}
var req LoginRequest
err = json.Unmarshal(buf, &req)
if err != nil {
slog.Error("Unmarshal", err)
return
}
if req.Username == "admin" && req.Password == "12345" {
slog.Info("User Login Successfull")
GenerateSession(w, req.Username)
w.WriteHeader(http.StatusOK)
http.Redirect(w, r, "/", http.StatusFound)
return
}
w.WriteHeader(http.StatusUnauthorized)
}
func HandleLogout(w http.ResponseWriter, r *http.Request) {
http.SetCookie(w, &http.Cookie{Name: SessionCookieName, Value: "", Expires: time.Now()})
http.SetCookie(w, &http.Cookie{Name: SessionCookieName, HttpOnly: true, SameSite: http.SameSiteStrictMode, Value: "", Expires: time.Now()})
w.WriteHeader(http.StatusOK)
}
@ -97,4 +131,14 @@ func HandleSession(w http.ResponseWriter, r *http.Request) {
}
http.SetCookie(w, &http.Cookie{Name: SessionCookieName, HttpOnly: true, SameSite: http.SameSiteStrictMode, Value: id, Expires: s.Expires})
w.WriteHeader(http.StatusOK)
resp := SessionResponse{
CommitHash: CommitHash,
}
res, err := json.Marshal(resp)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Write(res)
}