From c5535fad71803d1df96aa91eb927eaa9ff8233f0 Mon Sep 17 00:00:00 2001 From: Marco <35533749+marcopeocchi@users.noreply.github.com> Date: Wed, 27 Dec 2023 14:32:08 +0100 Subject: [PATCH] jwt in headers+localstorage instead of httpOnly cookie (#117) --- .gitignore | 1 + frontend/src/atoms/rpc.ts | 6 +- frontend/src/components/Logout.tsx | 6 +- frontend/src/lib/httpClient.ts | 8 ++- frontend/src/lib/rpcClient.ts | 9 ++- frontend/src/views/Archive.tsx | 2 +- frontend/src/views/Login.tsx | 12 ++-- go.mod | 11 ++-- go.sum | 98 ++++++------------------------ server/handlers/login.go | 15 +---- server/middleware/jwt.go | 85 ++++++++++++++------------ server/rest/container.go | 5 +- server/rpc/container.go | 17 +++++- server/server.go | 5 +- 14 files changed, 125 insertions(+), 155 deletions(-) diff --git a/.gitignore b/.gitignore index c5c6c6c..ba66166 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ yt-dlp-webui session.dat config.yml cookies.txt +__debug* \ No newline at end of file diff --git a/frontend/src/atoms/rpc.ts b/frontend/src/atoms/rpc.ts index 55a9aa6..e22dd87 100644 --- a/frontend/src/atoms/rpc.ts +++ b/frontend/src/atoms/rpc.ts @@ -5,6 +5,10 @@ import { rpcHTTPEndpoint, rpcWebSocketEndpoint } from './settings' export const rpcClientState = selector({ key: 'rpcClientState', get: ({ get }) => - new RPCClient(get(rpcHTTPEndpoint), get(rpcWebSocketEndpoint)), + new RPCClient( + get(rpcHTTPEndpoint), + get(rpcWebSocketEndpoint), + localStorage.getItem('token') ?? '' + ), dangerouslyAllowMutability: true, }) diff --git a/frontend/src/components/Logout.tsx b/frontend/src/components/Logout.tsx index 2851ccd..3f9c2e5 100644 --- a/frontend/src/components/Logout.tsx +++ b/frontend/src/components/Logout.tsx @@ -10,10 +10,8 @@ export default function Logout() { const url = useRecoilValue(serverURL) const logout = async () => { - const res = await fetch(`${url}/auth/logout`) - if (res.ok) { - navigate('/login') - } + localStorage.removeItem('token') + navigate('/login') } const { i18n } = useI18n() diff --git a/frontend/src/lib/httpClient.ts b/frontend/src/lib/httpClient.ts index e97a52f..b9767a2 100644 --- a/frontend/src/lib/httpClient.ts +++ b/frontend/src/lib/httpClient.ts @@ -7,7 +7,7 @@ export const ffetch = (url: string, opt?: RequestInit) => tryCatch( const fetcher = async (url: string, opt?: RequestInit) => { - const res = await fetch(url, opt) + const jwt = localStorage.getItem('token') if (opt && !opt.headers) { opt.headers = { @@ -15,6 +15,12 @@ const fetcher = async (url: string, opt?: RequestInit) => { } } + if (opt?.headers) { + opt.headers = { ...opt.headers, 'X-Authentication': jwt ?? '' } + } + + const res = await fetch(url, opt) + if (!res.ok) { throw await res.text() } diff --git a/frontend/src/lib/rpcClient.ts b/frontend/src/lib/rpcClient.ts index 5002c6f..fc425d4 100644 --- a/frontend/src/lib/rpcClient.ts +++ b/frontend/src/lib/rpcClient.ts @@ -15,13 +15,15 @@ export class RPCClient { private seq: number private httpEndpoint: string private readonly _socket$: WebSocketSubject + private readonly token?: string - constructor(httpEndpoint: string, webSocketEndpoint: string) { + constructor(httpEndpoint: string, webSocketEndpoint: string, token?: string) { this.seq = 0 this.httpEndpoint = httpEndpoint this._socket$ = webSocket({ - url: webSocketEndpoint + url: token ? `${webSocketEndpoint}?token=${token}` : webSocketEndpoint }) + this.token = token } public get socket$(): Observable> { @@ -49,6 +51,9 @@ export class RPCClient { private async sendHTTP(req: RPCRequest) { const res = await fetch(this.httpEndpoint, { method: 'POST', + headers: { + 'X-Authentication': this.token ?? '' + }, body: JSON.stringify({ ...req, id: this.incrementSeq(), diff --git a/frontend/src/views/Archive.tsx b/frontend/src/views/Archive.tsx index e5895e9..628c2ab 100644 --- a/frontend/src/views/Archive.tsx +++ b/frontend/src/views/Archive.tsx @@ -69,7 +69,7 @@ export default function Downloaded() { pushMessage(e, 'error') navigate('/login') }, - (d) => files$.next(d), + (d) => files$.next(d ?? []), ) )() diff --git a/frontend/src/views/Login.tsx b/frontend/src/views/Login.tsx index 0cc28c6..9884a3c 100644 --- a/frontend/src/views/Login.tsx +++ b/frontend/src/views/Login.tsx @@ -54,7 +54,7 @@ export default function Login() { } const login = async () => { - const task = ffetch(`${url}/auth/login`, { + const task = ffetch(`${url}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' @@ -68,11 +68,15 @@ export default function Login() { pipe( task, matchW( - (l) => { + (error) => { setFormHasError(true) - pushMessage(l, 'error') + pushMessage(error, 'error') }, - () => navigateAndReload() + (token) => { + console.log(token) + localStorage.setItem('token', token) + navigateAndReload() + } ) )() } diff --git a/go.mod b/go.mod index 1f1d9c4..80d681d 100644 --- a/go.mod +++ b/go.mod @@ -3,15 +3,15 @@ module github.com/marcopeocchi/yt-dlp-web-ui go 1.20 require ( - github.com/go-chi/chi/v5 v5.0.10 + github.com/go-chi/chi/v5 v5.0.11 github.com/go-chi/cors v1.2.1 github.com/golang-jwt/jwt/v5 v5.2.0 - github.com/google/uuid v1.4.0 + github.com/google/uuid v1.5.0 github.com/gorilla/websocket v1.5.1 github.com/marcopeocchi/fazzoletti v0.0.0-20230308161120-c545580f79fa golang.org/x/sys v0.15.0 gopkg.in/yaml.v3 v3.0.1 - modernc.org/sqlite v1.27.0 + modernc.org/sqlite v1.28.0 ) require ( @@ -21,12 +21,11 @@ require ( github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.19.0 // indirect - golang.org/x/tools v0.16.0 // indirect - golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + golang.org/x/tools v0.16.1 // indirect lukechampine.com/uint128 v1.3.0 // indirect modernc.org/cc/v3 v3.41.0 // indirect modernc.org/ccgo/v3 v3.16.15 // indirect - modernc.org/libc v1.35.0 // indirect + modernc.org/libc v1.38.0 // indirect modernc.org/mathutil v1.6.0 // indirect modernc.org/memory v1.7.2 // indirect modernc.org/opt v0.1.3 // indirect diff --git a/go.sum b/go.sum index d505685..e9133f2 100644 --- a/go.sum +++ b/go.sum @@ -1,122 +1,62 @@ -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/go-chi/chi/v5 v5.0.10 h1:rLz5avzKpjqxrYwXNfmjkrYYXOyLJd37pz53UFHC6vk= -github.com/go-chi/chi/v5 v5.0.10/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/go-chi/chi/v5 v5.0.11 h1:BnpYbFZ3T3S1WMpD79r7R5ThWX40TaFB7L31Y8xqSwA= +github.com/go-chi/chi/v5 v5.0.11/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4= github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58= -github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= -github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-jwt/jwt/v5 v5.2.0 h1:d/ix8ftRUorsN+5eMIlF4T6J8CAt9rch3My2winC1Jw= github.com/golang-jwt/jwt/v5 v5.2.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= +github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= +github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/marcopeocchi/fazzoletti v0.0.0-20230308161120-c545580f79fa h1:uaAQLGhN4SesB9inOQ1Q6EH+BwTWHQOvwhR0TIJvnYc= github.com/marcopeocchi/fazzoletti v0.0.0-20230308161120-c545580f79fa/go.mod h1:RvfVo/6Sbnfra9kkvIxDW8NYOOaYsHjF0DdtMCs9cdo= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20181106170214-d68db9428509/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 h1:M8tBwCtWD/cZV9DZpFYRUgaymAYAr+aIUTWzDaM3uPs= -golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= -golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= +golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= -lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= lukechampine.com/uint128 v1.3.0 h1:cDdUVfRwDUDovz610ABgFD17nXD4/uDgVHl2sC3+sbo= lukechampine.com/uint128 v1.3.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= -modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= -modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= modernc.org/cc/v3 v3.41.0 h1:QoR1Sn3YWlmA1T4vLaKZfawdVtSiGx8H+cEojbC7v1Q= modernc.org/cc/v3 v3.41.0/go.mod h1:Ni4zjJYJ04CDOhG7dn640WGfwBzfE0ecX8TyMB0Fv0Y= -modernc.org/cc/v4 v4.2.1 h1:xwwaXFwiPaVZpGRMd19NPLsaiNyNBO8oChey4501g1M= -modernc.org/cc/v4 v4.2.1/go.mod h1:0O8vuqhQfwBy+piyfEjzWIUGV4I3TPsXSf0W05+lgN8= -modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= -modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= modernc.org/ccgo/v3 v3.16.15 h1:KbDR3ZAVU+wiLyMESPtbtE/Add4elztFyfsWoNTgxS0= modernc.org/ccgo/v3 v3.16.15/go.mod h1:yT7B+/E2m43tmMOT51GMoM98/MtHIcQQSleGnddkUNI= -modernc.org/ccgo/v4 v4.0.0-20230612200659-63de3e82e68d h1:3yB/pQNL5kVPDifGFqoZjeRxf8m0+Us15rB7ertNASQ= -modernc.org/ccgo/v4 v4.0.0-20230612200659-63de3e82e68d/go.mod h1:austqj6cmEDRfewsUvmGmyIgsI/Nq87oTXlfTgY85Fc= -modernc.org/fileutil v1.0.0/go.mod h1:JHsWpkrk/CnVV1H/eGlFf85BEpfkrp56ro8nojIq9Q8= -modernc.org/gc/v2 v2.1.2-0.20220923113132-f3b5abcf8083 h1:rGoLVwiOxdeVkGYMOF/8Pw7xpDd3OqScJU/tqHgvY1c= -modernc.org/gc/v2 v2.1.2-0.20220923113132-f3b5abcf8083/go.mod h1:Zt5HLUW0j+l02wj99UsPs+1DOFwwsGnqfcw+BGyyP/A= -modernc.org/lex v1.1.0/go.mod h1:+ojes+j0JYCaqwKYCBjcUavscJHmWFKvViUTMU4VjLA= -modernc.org/lexer v1.0.0/go.mod h1:F/Dld0YKYdZCLQ7bD0USbWL4YKCyTDRDHiDTOs0q0vk= -modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM= -modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak= -modernc.org/libc v1.35.0 h1:EQ4szx6Q/QLZuysmAnI4dfRnKbAbNlENp23ruvTJ2nE= -modernc.org/libc v1.35.0/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= -modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= -modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= +modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= +modernc.org/libc v1.38.0 h1:o4Lpk0zNDSdsjfEXnF1FGXWQ9PDi1NOdWcLP5n13FGo= +modernc.org/libc v1.38.0/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE= modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= -modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o= -modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/scannertest v1.0.0/go.mod h1:9qnOCV+wSvq1o9hcOPNwRorND4qpZdtmTvmcdKyN3iE= -modernc.org/sqlite v1.26.0 h1:SocQdLRSYlA8W99V8YH0NES75thx19d9sB/aFc4R8Lw= -modernc.org/sqlite v1.26.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU= -modernc.org/sqlite v1.27.0 h1:MpKAHoyYB7xqcwnUwkuD+npwEa0fojF0B5QRbN+auJ8= -modernc.org/sqlite v1.27.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= -modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= -modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= +modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= -modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= -modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= diff --git a/server/handlers/login.go b/server/handlers/login.go index c750089..256e583 100644 --- a/server/handlers/login.go +++ b/server/handlers/login.go @@ -30,7 +30,7 @@ func Login(w http.ResponseWriter, r *http.Request) { ) if username != req.Username || password != req.Password { - http.Error(w, err.Error(), http.StatusBadRequest) + http.Error(w, "invalid username or password", http.StatusBadRequest) return } @@ -47,18 +47,7 @@ func Login(w http.ResponseWriter, r *http.Request) { return } - cookie := &http.Cookie{ - Name: utils.TOKEN_COOKIE_NAME, - HttpOnly: true, - Secure: false, - Expires: expiresAt, // 30 days - Value: tokenString, - Path: "/", - } - - http.SetCookie(w, cookie) - - if err := json.NewEncoder(w).Encode("ok"); err != nil { + if err := json.NewEncoder(w).Encode(tokenString); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } diff --git a/server/middleware/jwt.go b/server/middleware/jwt.go index 6c46716..6a77ad8 100644 --- a/server/middleware/jwt.go +++ b/server/middleware/jwt.go @@ -1,56 +1,63 @@ package middlewares import ( + "errors" "fmt" "net/http" "os" "time" "github.com/golang-jwt/jwt/v5" - "github.com/marcopeocchi/yt-dlp-web-ui/server/config" - "github.com/marcopeocchi/yt-dlp-web-ui/server/utils" ) +func validateToken(tokenValue string) error { + if tokenValue == "" { + return errors.New("invalid token") + } + + token, _ := jwt.Parse(tokenValue, func(t *jwt.Token) (interface{}, error) { + if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"]) + } + return []byte(os.Getenv("JWT_SECRET")), nil + }) + + if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { + expiresAt, err := time.Parse(time.RFC3339, claims["expiresAt"].(string)) + + if err != nil { + return err + } + + if time.Now().After(expiresAt) { + return errors.New("token expired") + } + } else { + return errors.New("invalid token") + } + + return nil +} + func Authenticated(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if !config.Instance().RequireAuth { - next.ServeHTTP(w, r) - return - } + token := r.Header.Get("X-Authentication") - cookie, err := r.Cookie(utils.TOKEN_COOKIE_NAME) - - if err != nil { - http.Error(w, "invalid token", http.StatusBadRequest) - return - } - - if cookie == nil { - http.Error(w, "invalid token", http.StatusBadRequest) - return - } - - token, _ := jwt.Parse(cookie.Value, func(t *jwt.Token) (interface{}, error) { - if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"]) - } - return []byte(os.Getenv("JWT_SECRET")), nil - }) - - if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { - expiresAt, err := time.Parse(time.RFC3339, claims["expiresAt"].(string)) - - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - if time.Now().After(expiresAt) { - http.Error(w, "token expired", http.StatusBadRequest) - return - } - } else { - http.Error(w, "invalid token", http.StatusBadRequest) + if err := validateToken(token); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + + next.ServeHTTP(w, r) + }) +} + +func WebSocketAuthentication(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + token := r.URL.Query().Get("token") + + if err := validateToken(token); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) return } diff --git a/server/rest/container.go b/server/rest/container.go index 0966d13..a85db8b 100644 --- a/server/rest/container.go +++ b/server/rest/container.go @@ -4,6 +4,7 @@ import ( "database/sql" "github.com/go-chi/chi/v5" + "github.com/marcopeocchi/yt-dlp-web-ui/server/config" "github.com/marcopeocchi/yt-dlp-web-ui/server/internal" middlewares "github.com/marcopeocchi/yt-dlp-web-ui/server/middleware" ) @@ -20,7 +21,9 @@ func ApplyRouter(db *sql.DB, mdb *internal.MemoryDB, mq *internal.MessageQueue) h := Container(db, mdb, mq) return func(r chi.Router) { - r.Use(middlewares.Authenticated) + if config.Instance().RequireAuth { + r.Use(middlewares.Authenticated) + } r.Post("/exec", h.Exec()) r.Get("/running", h.Running()) r.Post("/cookies", h.SetCookies()) diff --git a/server/rpc/container.go b/server/rpc/container.go index c3a5f95..8bd7586 100644 --- a/server/rpc/container.go +++ b/server/rpc/container.go @@ -2,6 +2,7 @@ package rpc import ( "github.com/go-chi/chi/v5" + "github.com/marcopeocchi/yt-dlp-web-ui/server/config" "github.com/marcopeocchi/yt-dlp-web-ui/server/internal" middlewares "github.com/marcopeocchi/yt-dlp-web-ui/server/middleware" ) @@ -17,8 +18,18 @@ func Container(db *internal.MemoryDB, mq *internal.MessageQueue) *Service { // RPC service must be registered before applying this router! func ApplyRouter() func(chi.Router) { return func(r chi.Router) { - r.Use(middlewares.Authenticated) - r.Get("/ws", WebSocket) - r.Post("/http", Post) + r.Route("/ws", func(r chi.Router) { + if config.Instance().RequireAuth { + r.Use(middlewares.WebSocketAuthentication) + } + r.Get("/", WebSocket) + }) + + r.Route("/http", func(r chi.Router) { + if config.Instance().RequireAuth { + r.Use(middlewares.Authenticated) + } + r.Post("/", Post) + }) } } diff --git a/server/server.go b/server/server.go index 0fa43a9..bd1a3b7 100644 --- a/server/server.go +++ b/server/server.go @@ -16,6 +16,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/cors" + "github.com/marcopeocchi/yt-dlp-web-ui/server/config" "github.com/marcopeocchi/yt-dlp-web-ui/server/dbutils" "github.com/marcopeocchi/yt-dlp-web-ui/server/handlers" "github.com/marcopeocchi/yt-dlp-web-ui/server/internal" @@ -96,7 +97,9 @@ func newServer(c serverConfig) *http.Server { // Archive routes r.Route("/archive", func(r chi.Router) { - r.Use(middlewares.Authenticated) + if config.Instance().RequireAuth { + r.Use(middlewares.Authenticated) + } r.Post("/downloaded", handlers.ListDownloaded) r.Post("/delete", handlers.DeleteFile) r.Get("/d/{id}", handlers.SendFile)