evcc-io/server/providerauth/providerauth.go
Tilo Alexander 4adbf141d6
Provider auth: require authenticated session for OAuth login/logout (#33115)
Co-authored-by: Michael Geers <michael@geers.tv>
2026-08-24 13:47:06 +00:00

66 lines
1.8 KiB
Go

package providerauth
import (
"crypto/rand"
"io"
"net/http"
"github.com/evcc-io/evcc/api"
"github.com/evcc-io/evcc/util"
"github.com/gorilla/mux"
)
var instance *Handler
type AuthProvider struct {
ID string `json:"id"`
Authenticated bool `json:"authenticated"`
}
func init() {
var secret [16]byte
if _, err := io.ReadFull(rand.Reader, secret[:]); err != nil {
panic(err)
}
instance = &Handler{
log: util.NewLogger("providerauth"),
secret: secret[:],
providers: make(map[string]api.AuthProvider),
states: make(map[string]stateEntry),
updateC: make(chan string, 1),
}
}
// Setup connects the redirect handler to the router and registers the callback channel.
// Callback stays open: the cross-site IdP redirect carries no session cookie, the state token gates it.
func Setup(router *mux.Router, paramC chan<- util.Param, authMiddleware mux.MiddlewareFunc) {
gate := func(h http.HandlerFunc) http.Handler { return authMiddleware(h) }
router.Methods(http.MethodGet).Path("/callback").HandlerFunc(instance.handleCallback)
router.Methods(http.MethodGet).Path("/login").Handler(gate(instance.handleLogin))
router.Methods(http.MethodGet).Path("/logout").Handler(gate(instance.handleLogout))
go instance.run(paramC)
}
// Register registers a specific AuthProvider by name
// The returned online channel is used to asynchronously update authorization status
func Register(name string, handler api.AuthProvider) (chan<- bool, error) {
updateC, err := instance.register(name, handler)
if err != nil {
return nil, err
}
// buffered + non-blocking send (see OAuth.setOnline): the value is only a
// signal and the handler re-reads live state, so coalescing is lossless.
onlineC := make(chan bool, 1)
go func() {
for range onlineC {
updateC <- name
}
}()
return onlineC, nil
}