blasphem/pkg/auth/authenticator.go

125 lines
2.8 KiB
Go
Raw Permalink Normal View History

2022-09-30 23:54:21 -04:00
package auth
import (
2022-10-25 00:16:29 -04:00
"errors"
2022-09-30 23:54:21 -04:00
"net/http"
2022-12-19 13:09:01 -05:00
"sync"
2022-09-30 23:54:21 -04:00
"github.com/labstack/echo/v4"
2022-10-26 18:27:24 -04:00
"github.com/rs/zerolog/log"
2022-09-30 23:54:21 -04:00
2022-11-12 15:56:17 -05:00
"dynatron.me/x/blasphem/pkg/auth/provider"
2022-10-25 00:16:29 -04:00
"dynatron.me/x/blasphem/pkg/storage"
2022-11-12 16:31:26 -05:00
// providers
_ "dynatron.me/x/blasphem/pkg/auth/provider/hass"
_ "dynatron.me/x/blasphem/pkg/auth/provider/trustednets"
2022-10-25 00:16:29 -04:00
)
var (
2022-11-12 17:58:24 -05:00
ErrDisabled = errors.New("user disabled")
2022-10-25 00:16:29 -04:00
ErrInvalidAuth = errors.New("invalid auth")
ErrInvalidHandler = errors.New("no such handler")
2022-12-19 02:42:01 -05:00
ErrInvalidIP = errors.New("invalid IP")
ErrUserAuthRemote = errors.New("user cannot authenticate remotely")
2022-09-30 23:54:21 -04:00
)
2022-12-19 19:24:01 -05:00
type authenticator struct {
2022-12-19 13:09:01 -05:00
sync.Mutex
2022-11-12 13:34:39 -05:00
store AuthStore
2022-11-20 08:49:24 -05:00
flows *AuthFlowManager
2022-12-18 21:26:34 -05:00
authCodes authCodeStore
2022-11-12 15:56:17 -05:00
providers map[string]provider.AuthProvider
2022-09-30 23:54:21 -04:00
}
2022-12-19 19:24:01 -05:00
type Authenticator interface {
ValidateAccessToken(token AccessToken) *RefreshToken
2022-12-20 19:05:45 -05:00
InstallRoutes(e *echo.Echo)
2022-12-19 19:24:01 -05:00
}
2022-11-11 18:02:52 -05:00
type AuthError struct {
2022-11-12 12:45:25 -05:00
Error string `json:"error"`
2022-11-11 18:02:52 -05:00
Description string `json:"error_description"`
}
2022-12-20 19:05:45 -05:00
func (a *authenticator) InstallRoutes(e *echo.Echo) {
2022-10-26 19:01:45 -04:00
authG := e.Group("/auth")
authG.GET("/providers", a.ProvidersHandler)
authG.POST("/token", a.TokenHandler)
authG.POST("/login_flow", a.BeginLoginFlowHandler)
loginFlow := authG.Group("/login_flow") // TODO: add IP address affinity middleware
loginFlow.POST("/:flow_id", a.LoginFlowHandler)
loginFlow.DELETE("/:flow_id", a.LoginFlowDeleteHandler)
}
2022-12-20 13:16:30 -05:00
func New(s storage.Store) (Authenticator, error) {
2022-12-19 19:24:01 -05:00
a := &authenticator{
providers: make(map[string]provider.AuthProvider),
}
2022-11-12 16:31:26 -05:00
for _, pI := range provider.Providers {
nProv, err := pI(s)
if err != nil {
2022-12-19 19:24:01 -05:00
return nil, err
2022-11-12 16:31:26 -05:00
}
a.providers[nProv.ProviderType()] = nProv
2022-10-25 00:16:29 -04:00
}
2022-11-20 08:49:24 -05:00
a.flows = NewAuthFlowManager()
2022-11-12 16:31:26 -05:00
2022-12-18 09:55:08 -05:00
a.authCodes.init()
2022-10-25 00:16:29 -04:00
2022-11-12 16:31:26 -05:00
var err error
2022-11-12 15:56:17 -05:00
a.store, err = a.newAuthStore(s)
if err != nil {
2022-12-19 19:24:01 -05:00
return nil, err
2022-11-12 15:56:17 -05:00
}
2022-12-19 19:24:01 -05:00
return a, nil
2022-09-30 23:54:21 -04:00
}
2022-12-19 19:24:01 -05:00
func (a *authenticator) Provider(name string) provider.AuthProvider {
2022-10-28 18:12:30 -04:00
p, ok := a.providers[name]
if !ok {
return nil
}
return p
}
2022-09-30 23:54:21 -04:00
var HomeAssistant = "homeassistant"
// TODO: make this configurable
2022-12-19 19:24:01 -05:00
func (a *authenticator) ProvidersHandler(c echo.Context) error {
2022-11-12 15:56:17 -05:00
providers := []provider.AuthProviderBase{
2022-10-25 00:16:29 -04:00
a.Provider(HomeAssistant).ProviderBase(),
2022-09-30 23:54:21 -04:00
}
return c.JSON(http.StatusOK, providers)
}
2022-12-19 19:24:01 -05:00
func (a *authenticator) Check(f *LoginFlow, req *http.Request, rm map[string]interface{}) (user provider.ProviderUser, clientID string, err error) {
2022-10-25 00:16:29 -04:00
cID, hasCID := rm["client_id"]
2022-12-18 09:55:08 -05:00
clientID, cidIsStr := cID.(string)
if !hasCID || !cidIsStr || clientID == "" || clientID != string(f.ClientID) {
return nil, clientID, ErrInvalidAuth
2022-10-25 00:16:29 -04:00
}
2022-11-20 08:49:24 -05:00
p := a.Provider(f.Handler.String())
if p == nil {
2022-12-18 09:55:08 -05:00
return nil, clientID, ErrInvalidAuth
2022-11-20 08:49:24 -05:00
}
2022-10-25 00:16:29 -04:00
2022-11-20 08:49:24 -05:00
user, success := p.ValidateCreds(req, rm)
2022-10-25 00:16:29 -04:00
2022-11-20 08:49:24 -05:00
if success {
log.Info().Interface("user", user.UserData()).Msg("Login success")
2022-12-18 09:55:08 -05:00
return user, clientID, nil
2022-10-25 00:16:29 -04:00
}
2022-12-18 09:55:08 -05:00
return nil, clientID, ErrInvalidAuth
2022-10-25 00:16:29 -04:00
}