I'm a bit late to the party here, but here's how you can do it:
import (
"fmt"
"os"
"golang.org/x/term"
)
type Tty struct {
state *term.State
line string
}
func readPassword(tty chan Tty, fd int) {
state, _ := term.MakeRaw(fd)
t := term.NewTerminal(os.Stdin, "")
f := func(s string, i int, r rune) (string, int, bool) {
fmt.Print("*")
return s, i, false
}
t.AutoCompleteCallback = f
line, _ := t.ReadPassword(">")
tty <- Tty{state: state, line: line}
}
func enterPassword() string {
tty := make(chan Tty)
fd := int(os.Stdin.Fd())
go readPassword(tty, fd)
t := <-tty
close(tty)
term.Restore(fd, t.state)
password := t.line
return password
}
Sadly, the AutoCompleteCallback function isn't triggered when a backspace or null character key is pressed. However, if you are ok with substituting another key for the backspace, you can clear out the existing line and replace it. For example, on my keyboard the delete key maps to 55302, so I can add a conditional as follows:
f := func(s string, i int, r rune) (string, int, bool) {
if r == 55302 {
fmt.Printf("\r>%s", strings.Repeat(" ", i))
fmt.Printf("\r>%s", strings.Repeat("*", i-1))
return s[:i-1], i - 1, true
}
fmt.Print("*")
return s, i, false
}
This mimics the backspace functionality.