blasphem/pkg/auth/authenticator.go

151 lines
3.3 KiB
Go
Raw Normal View History

2022-09-30 23:54:21 -04:00
package auth
import (
2022-11-11 18:02:52 -05:00
"crypto/rand"
2022-10-26 19:01:45 -04:00
"encoding/hex"
2022-10-25 00:16:29 -04:00
"errors"
2022-09-30 23:54:21 -04:00
"net/http"
2022-10-26 19:01:45 -04:00
"github.com/google/uuid"
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
"dynatron.me/x/blasphem/pkg/frontend"
2022-10-25 00:16:29 -04:00
"dynatron.me/x/blasphem/pkg/storage"
)
var (
ErrInvalidAuth = errors.New("invalid auth")
ErrInvalidHandler = errors.New("no such handler")
2022-09-30 23:54:21 -04:00
)
type Authenticator struct {
2022-11-12 13:34:39 -05:00
store AuthStore
2022-10-27 09:51:11 -04:00
flows FlowStore
sessions SessionStore
providers map[string]AuthProvider
2022-09-30 23:54:21 -04: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-10-26 19:01:45 -04:00
func (a *Authenticator) InstallRoutes(e *echo.Echo) {
authG := e.Group("/auth")
2022-10-29 10:39:06 -04:00
authG.GET("/authorize", frontend.AliasHandler("authorize.html"))
2022-10-26 19:01:45 -04:00
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-10-28 18:12:30 -04:00
func (a *Authenticator) InitAuth(s storage.Store) error {
2022-10-27 09:51:11 -04:00
a.flows = make(FlowStore)
a.sessions.init()
2022-10-25 00:16:29 -04:00
hap, err := NewHAProvider(s)
if err != nil {
return err
}
2022-10-29 10:39:06 -04:00
// XXX: yuck. use init with a registry or something
2022-10-27 09:51:11 -04:00
a.providers = map[string]AuthProvider{
2022-10-25 00:16:29 -04:00
hap.ProviderType(): hap,
}
return nil
2022-09-30 23:54:21 -04:00
}
2022-10-28 18:12:30 -04:00
func (a *Authenticator) Provider(name string) AuthProvider {
p, ok := a.providers[name]
if !ok {
return nil
}
return p
}
2022-10-26 19:43:51 -04:00
type AuthProvider interface { // TODO: this should include stepping
2022-09-30 23:54:21 -04:00
ProviderName() string
ProviderID() *string
ProviderType() string
2022-10-25 00:16:29 -04:00
ProviderBase() AuthProviderBase
FlowSchema() []FlowSchemaItem
2022-11-11 18:02:52 -05:00
ValidateCreds(reqMap map[string]interface{}) (user ProviderUser, success bool)
}
type ProviderUser interface {
ProviderUsername() string
2022-09-30 23:54:21 -04:00
}
type AuthProviderBase struct {
Name string `json:"name"`
ID *string `json:"id"`
Type string `json:"type"`
}
2022-10-25 00:16:29 -04:00
func (bp *AuthProviderBase) ProviderName() string { return bp.Name }
func (bp *AuthProviderBase) ProviderID() *string { return bp.ID }
func (bp *AuthProviderBase) ProviderType() string { return bp.Type }
func (bp *AuthProviderBase) ProviderBase() AuthProviderBase { return *bp }
2022-09-30 23:54:21 -04:00
var HomeAssistant = "homeassistant"
// TODO: make this configurable
2022-10-25 00:16:29 -04:00
func (a *Authenticator) ProvidersHandler(c echo.Context) error {
providers := []AuthProviderBase{
a.Provider(HomeAssistant).ProviderBase(),
2022-09-30 23:54:21 -04:00
}
return c.JSON(http.StatusOK, providers)
}
2022-11-11 18:02:52 -05:00
func (a *Authenticator) Check(f *Flow, rm map[string]interface{}) (ProviderUser, error) {
2022-10-25 00:16:29 -04:00
cID, hasCID := rm["client_id"]
2022-11-11 18:02:52 -05:00
cIDStr, cidIsStr := cID.(string)
if !hasCID || !cidIsStr || cIDStr == "" || cIDStr != string(f.request.ClientID) {
return nil, ErrInvalidAuth
2022-10-25 00:16:29 -04:00
}
for _, h := range f.Handler {
if h == nil {
2022-11-11 18:02:52 -05:00
return nil, ErrInvalidHandler
2022-10-25 00:16:29 -04:00
}
p := a.Provider(*h)
if p == nil {
2022-11-11 18:02:52 -05:00
return nil, ErrInvalidAuth
2022-10-25 00:16:29 -04:00
}
2022-11-11 18:02:52 -05:00
user, success := p.ValidateCreds(rm)
2022-10-25 00:16:29 -04:00
if success {
2022-11-11 18:02:52 -05:00
log.Info().Str("user", user.ProviderUsername()).Msg("Login success")
return user, nil
2022-10-25 00:16:29 -04:00
}
}
2022-11-11 18:02:52 -05:00
return nil, ErrInvalidAuth
2022-10-25 00:16:29 -04:00
}
2022-10-26 19:01:45 -04:00
func genUUID() string {
// must be addressable
u := uuid.New()
return hex.EncodeToString(u[:])
}
2022-11-11 18:02:52 -05:00
func genHex(l int) string {
b := make([]byte, l)
if _, err := rand.Read(b); err != nil {
panic(err)
}
2022-11-12 12:45:25 -05:00
2022-11-11 18:02:52 -05:00
return hex.EncodeToString(b)
}