package handlers import ( "encoding/json" "net/http" "strings" "git.eeqj.de/sneak/neoirc/pkg/irc" ) const minPasswordLength = 8 // HandleLogin authenticates a user with nick and password. func (hdlr *Handlers) HandleLogin() http.HandlerFunc { return func( writer http.ResponseWriter, request *http.Request, ) { request.Body = http.MaxBytesReader( writer, request.Body, hdlr.maxBodySize(), ) hdlr.handleLogin(writer, request) } } func (hdlr *Handlers) handleLogin( writer http.ResponseWriter, request *http.Request, ) { type loginRequest struct { Nick string `json:"nick"` Password string `json:"password"` } var payload loginRequest err := json.NewDecoder(request.Body).Decode(&payload) if err != nil { hdlr.respondError( writer, request, "invalid request body", http.StatusBadRequest, ) return } payload.Nick = strings.TrimSpace(payload.Nick) if payload.Nick == "" || payload.Password == "" { hdlr.respondError( writer, request, "nick and password required", http.StatusBadRequest, ) return } sessionID, clientID, token, err := hdlr.params.Database.LoginUser( request.Context(), payload.Nick, payload.Password, ) if err != nil { hdlr.respondError( writer, request, "invalid credentials", http.StatusUnauthorized, ) return } hdlr.stats.IncrConnections() hdlr.deliverMOTD( request, clientID, sessionID, payload.Nick, ) // Initialize channel state so the new client knows // which channels the session already belongs to. hdlr.initChannelState( request, clientID, sessionID, payload.Nick, ) hdlr.setAuthCookie(writer, request, token) hdlr.respondJSON(writer, request, map[string]any{ "id": sessionID, "nick": payload.Nick, }, http.StatusOK) } // handlePass handles the IRC PASS command to set a // password on the authenticated session, enabling // multi-client login via POST /api/v1/login. func (hdlr *Handlers) handlePass( writer http.ResponseWriter, request *http.Request, sessionID, clientID int64, nick string, bodyLines func() []string, ) { lines := bodyLines() if len(lines) == 0 || lines[0] == "" { hdlr.respondIRCError( writer, request, clientID, sessionID, irc.ErrNeedMoreParams, nick, []string{irc.CmdPass}, "Not enough parameters", ) return } password := lines[0] if len(password) < minPasswordLength { hdlr.respondIRCError( writer, request, clientID, sessionID, irc.ErrNeedMoreParams, nick, []string{irc.CmdPass}, "Password must be at least 8 characters", ) return } err := hdlr.params.Database.SetPassword( request.Context(), sessionID, password, ) if err != nil { hdlr.log.Error( "set password failed", "error", err, ) hdlr.respondError( writer, request, "internal error", http.StatusInternalServerError, ) return } hdlr.respondJSON(writer, request, map[string]string{"status": "ok"}, http.StatusOK) }