user and password as authentication requirements (#87)

* user and password as authentication requirements

* updated README.md
This commit is contained in:
Marco
2023-09-23 11:41:01 +02:00
committed by GitHub
parent 19062c9f41
commit 8bbc8aa35e
5 changed files with 70 additions and 25 deletions

View File

@@ -107,6 +107,12 @@ Or with docker but building the container manually.
```sh
docker build -t yt-dlp-webui .
docker run -d -p 3033:3033 -v <your dir>:/downloads yt-dlp-webui
docker run -d -p 3033:3033 \
-v <your dir>:/downloads \
-v <your dir>:/config \ # optional
yt-dlp-webui
```
If you opt to add RPC authentication...
@@ -115,9 +121,11 @@ docker run -d \
-p 3033:3033 \
-e JWT_SECRET randomsecret
-v /path/to/downloads:/downloads \
-v /path/for/config:/config \ # optional
marcobaobao/yt-dlp-webui \
--auth \
--secret your_rpc_secret
--user your_username \
--pass your_pass
```
If you wish for limiting the download queue size...
@@ -163,8 +171,10 @@ Usage yt-dlp-webui:
Port where server will listen at (default 3033)
-qs int
Download queue size (default 8)
-secret string
Secret required for auth
-user string
Username required for auth
-pass string
Password required for auth
```
### Config file
@@ -181,7 +191,9 @@ downloaderPath: /usr/local/bin/yt-dlp
# Optional settings
require_auth: true
rpc_secret: my_random_secret
username: my_username
password: my_random_secret
queue_size: 4
```

View File

@@ -33,22 +33,32 @@ const Title = styled(Typography)({
})
export default function Login() {
const [secret, setSecret] = useState('')
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [formHasError, setFormHasError] = useState(false)
const url = useRecoilValue(serverURL)
const navigate = useNavigate()
const navigateAndReload = () => {
navigate('/')
window.location.reload()
}
const login = async () => {
const res = await fetch(`${url}/auth/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ secret })
body: JSON.stringify({
username,
password,
})
res.ok ? navigate('/') : setFormHasError(true)
})
res.ok ? navigateAndReload() : setFormHasError(true)
}
return (
@@ -62,17 +72,23 @@ export default function Login() {
Authentication token will expire after 30 days.
</Title>
<Title fontWeight={'500'} fontSize={16} color={'gray'}>
In order to enable RPC authentication append the --auth
In order to enable RPC authentication append the --auth,
<br />
and --secret [secret] flags.
--user [username] and --pass [password] flags.
</Title>
<TextField
id="outlined-password-input"
label="RPC secret"
type="password"
autoComplete="current-password"
label="Username"
type="text"
autoComplete="yt-dlp-webui-username"
error={formHasError}
onChange={e => setSecret(e.currentTarget.value)}
onChange={e => setUsername(e.currentTarget.value)}
/>
<TextField
label="Password"
type="password"
autoComplete="yt-dlp-webui-password"
error={formHasError}
onChange={e => setPassword(e.currentTarget.value)}
/>
<Button variant="contained" size="large" onClick={() => login()}>
Submit

16
main.go
View File

@@ -5,6 +5,7 @@ import (
"flag"
"io/fs"
"log"
"os"
"runtime"
"github.com/marcopeocchi/yt-dlp-web-ui/server"
@@ -20,7 +21,11 @@ var (
downloaderPath string
requireAuth bool
rpcSecret string
username string
password string
userFromEnv = os.Getenv("USERNAME")
passFromEnv = os.Getenv("PASSWORD")
//go:embed frontend/dist/index.html
//go:embed frontend/dist/assets/*
@@ -28,6 +33,7 @@ var (
)
func init() {
flag.IntVar(&port, "port", 3033, "Port where server will listen at")
flag.IntVar(&queueSize, "qs", runtime.NumCPU(), "Download queue size")
@@ -36,7 +42,8 @@ func init() {
flag.StringVar(&downloaderPath, "driver", "yt-dlp", "yt-dlp executable path")
flag.BoolVar(&requireAuth, "auth", false, "Enable RPC authentication")
flag.StringVar(&rpcSecret, "secret", "", "Secret required for auth")
flag.StringVar(&username, "user", userFromEnv, "Username required for auth")
flag.StringVar(&password, "pass", passFromEnv, "Password required for auth")
flag.Parse()
}
@@ -56,10 +63,11 @@ func main() {
c.DownloaderPath(downloaderPath)
c.RequireAuth(requireAuth)
c.RPCSecret(rpcSecret)
c.Username(username)
c.Password(password)
// if config file is found it will be merged with the current config struct
if _, err := c.TryLoadFromFile(configFile); err != nil {
if _, err := c.LoadFromFile(configFile); err != nil {
log.Println(cli.BgRed, "config", cli.Reset, "no config file found")
}

View File

@@ -14,7 +14,8 @@ type serverConfig struct {
DownloadPath string `yaml:"downloadPath"`
DownloaderPath string `yaml:"downloaderPath"`
RequireAuth bool `yaml:"require_auth"`
RPCSecret string `yaml:"rpc_secret"`
Username string `yaml:"username"`
Password string `yaml:"password"`
QueueSize int `yaml:"queue_size"`
}
@@ -22,7 +23,7 @@ type config struct {
cfg serverConfig
}
func (c *config) TryLoadFromFile(filename string) (serverConfig, error) {
func (c *config) LoadFromFile(filename string) (serverConfig, error) {
fd, err := os.Open(filename)
if err != nil {
return serverConfig{}, err
@@ -55,8 +56,12 @@ func (c *config) RequireAuth(value bool) {
c.cfg.RequireAuth = value
}
func (c *config) RPCSecret(secret string) {
c.cfg.RPCSecret = secret
func (c *config) Username(username string) {
c.cfg.Username = username
}
func (c *config) Password(password string) {
c.cfg.Password = password
}
func (c *config) QueueSize(size int) {

View File

@@ -11,18 +11,21 @@ import (
)
type LoginRequest struct {
Secret string `json:"secret"`
Username string `json:"username"`
Password string `json:"password"`
}
func Login(w http.ResponseWriter, r *http.Request) {
req := new(LoginRequest)
err := json.NewDecoder(r.Body).Decode(&req)
err := json.NewDecoder(r.Body).Decode(req)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
if config.Instance().GetConfig().RPCSecret != req.Secret {
cfg := config.Instance().GetConfig()
if cfg.Username != req.Username || cfg.Password != req.Password {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
@@ -31,6 +34,7 @@ func Login(w http.ResponseWriter, r *http.Request) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"expiresAt": expiresAt,
"username": req.Username,
})
tokenString, err := token.SignedString([]byte(os.Getenv("JWT_SECRET")))