evcc-io/server/auth/auth.go
2022-01-31 19:42:32 +01:00

85 lines
1.4 KiB
Go

package auth
import (
"crypto/rand"
"fmt"
"io"
"net/http"
"sync"
"github.com/evcc-io/evcc/util"
"github.com/gorilla/mux"
)
var instance *Auth
type Auth struct {
mu sync.Mutex
secret []byte
routes map[string]http.HandlerFunc
}
func generateSecret() ([]byte, error) {
var b [16]byte
_, err := io.ReadFull(rand.Reader, b[:])
return b[:], err
}
func init() {
secret, err := generateSecret()
if err != nil {
panic(err)
}
instance = &Auth{
secret: secret,
routes: make(map[string]http.HandlerFunc),
}
}
func Setup(router *mux.Router) {
router.Methods(http.MethodGet).HandlerFunc(instance.handle)
}
func Register(handler http.HandlerFunc) string {
return instance.register(handler)
}
func (a *Auth) register(handler http.HandlerFunc) string {
a.mu.Lock()
defer a.mu.Unlock()
state := util.NewState()
key := state.Encrypt(a.secret)
a.routes[key] = handler
return key
}
func (a *Auth) handle(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
if error, ok := vars["error"]; ok {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "error: %s: %s\n", error, vars["error_description"])
return
}
state, err := util.DecryptState(vars["state"], a.secret)
if err == nil {
err = state.Validate()
}
a.mu.Lock()
handler := a.routes[vars["state"]]
a.mu.Unlock()
if err != nil || handler == nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "invalid state")
return
}
handler(w, r)
}