mirror of
https://github.com/1f349/lavender.git
synced 2025-04-14 06:55:55 +01:00
I got to this point before starting again
This commit is contained in:
parent
71a746bd73
commit
80d3298813
26
auth/auth.go
26
auth/auth.go
@ -2,36 +2,14 @@ package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/1f349/lavender/auth/process"
|
||||
"github.com/1f349/lavender/database"
|
||||
)
|
||||
|
||||
// State defines the currently reached authentication state
|
||||
type State byte
|
||||
|
||||
const (
|
||||
// StateUnauthorized defines the "unauthorized" state of a session
|
||||
StateUnauthorized State = iota
|
||||
// StateBase defines the "username" only user state
|
||||
// This state is for providing a username to allow redirecting to oauth clients
|
||||
StateBase
|
||||
// StateBasic defines the "username and password with no OTP" user state
|
||||
// This is skipped if OTP/passkey is optional and not enabled for the user
|
||||
StateBasic
|
||||
// StateExtended defines the "logged in" user state
|
||||
StateExtended
|
||||
// StateSudo defines the "sudo" user state
|
||||
// This state is temporary and has a configurable duration
|
||||
StateSudo
|
||||
)
|
||||
|
||||
func (s State) IsLoggedIn() bool { return s >= StateExtended }
|
||||
|
||||
func (s State) IsSudoAvailable() bool { return s == StateSudo }
|
||||
|
||||
type Provider interface {
|
||||
// AccessState defines the state at which the provider is allowed to show.
|
||||
// Some factors might be unavailable due to user preference.
|
||||
AccessState() State
|
||||
AccessState() process.State
|
||||
|
||||
// Name defines a string value for the provider.
|
||||
Name() string
|
||||
|
@ -1,17 +1,18 @@
|
||||
package authContext
|
||||
|
||||
import (
|
||||
"github.com/1f349/lavender/auth/login-process"
|
||||
"github.com/1f349/lavender/auth/process"
|
||||
"github.com/1f349/lavender/database"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func NewFormContext(req *http.Request, user *database.User) *BaseFormContext {
|
||||
func NewFormContext(req *http.Request, user *database.User, rw http.ResponseWriter) *BaseFormContext {
|
||||
return &BaseFormContext{
|
||||
BaseTemplateContext: BaseTemplateContext{
|
||||
req: req,
|
||||
user: user,
|
||||
},
|
||||
rw: rw,
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,6 +20,7 @@ type FormContext interface {
|
||||
TemplateContext
|
||||
SetUser(user *database.User)
|
||||
UpdateSession(data process.LoginProcessData)
|
||||
ResponseWriter() http.ResponseWriter
|
||||
__formContext()
|
||||
}
|
||||
|
||||
@ -27,6 +29,11 @@ var _ FormContext = &BaseFormContext{}
|
||||
type BaseFormContext struct {
|
||||
BaseTemplateContext
|
||||
loginProcessData process.LoginProcessData
|
||||
rw http.ResponseWriter
|
||||
}
|
||||
|
||||
func (b *BaseFormContext) GetLoginProcessData() process.LoginProcessData {
|
||||
return b.loginProcessData
|
||||
}
|
||||
|
||||
func (b *BaseFormContext) SetUser(user *database.User) {
|
||||
@ -37,4 +44,8 @@ func (b *BaseFormContext) UpdateSession(data process.LoginProcessData) {
|
||||
b.loginProcessData = data
|
||||
}
|
||||
|
||||
func (b *BaseFormContext) ResponseWriter() http.ResponseWriter {
|
||||
return b.rw
|
||||
}
|
||||
|
||||
func (b *BaseFormContext) __formContext() {}
|
||||
|
@ -1,12 +1,14 @@
|
||||
package process
|
||||
|
||||
import "github.com/1f349/mjwt"
|
||||
import (
|
||||
"github.com/1f349/mjwt"
|
||||
)
|
||||
|
||||
var _ mjwt.Claims = (*LoginProcessData)(nil)
|
||||
|
||||
// TODO: add some actual session management
|
||||
type LoginProcessData struct {
|
||||
State byte
|
||||
State State
|
||||
}
|
||||
|
||||
func (d LoginProcessData) Valid() error { return nil }
|
24
auth/process/state.go
Normal file
24
auth/process/state.go
Normal file
@ -0,0 +1,24 @@
|
||||
package process
|
||||
|
||||
// State defines the currently reached authentication state
|
||||
type State byte
|
||||
|
||||
const (
|
||||
// StateUnauthorized defines the "unauthorized" state of a session
|
||||
StateUnauthorized State = iota
|
||||
// StateBase defines the "username" only user state
|
||||
// This state is for providing a username to allow redirecting to oauth clients
|
||||
StateBase
|
||||
// StateBasic defines the "username and password with no OTP" user state
|
||||
// This is skipped if OTP/passkey is optional and not enabled for the user
|
||||
StateBasic
|
||||
// StateExtended defines the "logged in" user state
|
||||
StateExtended
|
||||
// StateSudo defines the "sudo" user state
|
||||
// This state is temporary and has a configurable duration
|
||||
StateSudo
|
||||
)
|
||||
|
||||
func (s State) IsLoggedIn() bool { return s >= StateExtended }
|
||||
|
||||
func (s State) IsSudoAvailable() bool { return s == StateSudo }
|
@ -3,18 +3,25 @@ package providers
|
||||
import (
|
||||
"github.com/1f349/lavender/auth"
|
||||
"github.com/1f349/lavender/auth/authContext"
|
||||
process "github.com/1f349/lavender/auth/process"
|
||||
"github.com/1f349/lavender/database"
|
||||
"github.com/1f349/lavender/logger"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ auth.Provider = (*InitialLogin)(nil)
|
||||
var _ auth.Form = (*InitialLogin)(nil)
|
||||
|
||||
type InitialLogin struct{}
|
||||
type InitialLogin struct {
|
||||
DB *database.Queries
|
||||
}
|
||||
|
||||
func (m *InitialLogin) AccessState() auth.State { return auth.StateUnauthorized }
|
||||
func (m *InitialLogin) AccessState() process.State { return process.StateUnauthorized }
|
||||
|
||||
func (m *InitialLogin) Name() string { return "base" }
|
||||
|
||||
func (m *InitialLogin) RenderTemplate(ctx authContext.FormContext) error {
|
||||
func (m *InitialLogin) RenderTemplate(ctx authContext.TemplateContext) error {
|
||||
type s struct {
|
||||
UserEmail string
|
||||
Redirect string
|
||||
@ -43,5 +50,21 @@ func (m *InitialLogin) AttemptLogin(ctx authContext.FormContext) error {
|
||||
userEmail := req.FormValue("email")
|
||||
rememberMe := req.FormValue("remember-me")
|
||||
logger.Logger.Debug("Hi", "em", userEmail, "rm", rememberMe)
|
||||
|
||||
rw := ctx.ResponseWriter()
|
||||
now := time.Now()
|
||||
future := now.AddDate(1, 0, 0)
|
||||
http.SetCookie(rw, &http.Cookie{
|
||||
Name: "lavender-user-memory",
|
||||
Value: userEmail,
|
||||
Path: "/",
|
||||
Expires: future,
|
||||
MaxAge: int(future.Sub(now).Seconds()),
|
||||
Secure: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
|
||||
ctx.UpdateSession(process.LoginProcessData{State: process.StateBase})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/1f349/cache"
|
||||
"github.com/1f349/lavender/auth"
|
||||
"github.com/1f349/lavender/auth/authContext"
|
||||
"github.com/1f349/lavender/auth/process"
|
||||
"github.com/1f349/lavender/database"
|
||||
"github.com/1f349/lavender/database/types"
|
||||
"github.com/1f349/lavender/issuer"
|
||||
@ -52,7 +53,7 @@ func (o OAuthLogin) authUrlBase(ref string) *url.URL {
|
||||
return o.BaseUrl.Resolve("oauth", o.Name(), ref)
|
||||
}
|
||||
|
||||
func (o OAuthLogin) AccessState() auth.State { return auth.StateUnauthorized }
|
||||
func (o OAuthLogin) AccessState() process.State { return process.StateUnauthorized }
|
||||
|
||||
func (o OAuthLogin) Name() string { return "oauth" }
|
||||
|
||||
@ -154,7 +155,7 @@ func (o OAuthLogin) updateExternalUserInfo(req *http.Request, sso *issuer.WellKn
|
||||
})
|
||||
return auth.UserAuth{
|
||||
Subject: userSubject,
|
||||
Factor: auth.StateExtended,
|
||||
Factor: process.StateExtended,
|
||||
UserInfo: sessionData.UserInfo,
|
||||
}, err
|
||||
case errors.Is(err, sql.ErrNoRows):
|
||||
@ -210,7 +211,7 @@ func (o OAuthLogin) updateExternalUserInfo(req *http.Request, sso *issuer.WellKn
|
||||
// TODO(melon): this feels bad
|
||||
sessionData = auth.UserAuth{
|
||||
Subject: userSubject,
|
||||
Factor: auth.StateExtended,
|
||||
Factor: process.StateExtended,
|
||||
UserInfo: sessionData.UserInfo,
|
||||
}
|
||||
|
||||
@ -275,7 +276,7 @@ func (o OAuthLogin) fetchUserInfo(sso *issuer.WellKnownOIDC, token *oauth2.Token
|
||||
|
||||
return auth.UserAuth{
|
||||
Subject: subject,
|
||||
Factor: auth.StateExtended,
|
||||
Factor: process.StateExtended,
|
||||
UserInfo: userInfoJson,
|
||||
}, nil
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/1f349/lavender/auth"
|
||||
"github.com/1f349/lavender/auth/authContext"
|
||||
"github.com/1f349/lavender/auth/process"
|
||||
"github.com/1f349/lavender/database"
|
||||
"github.com/xlzd/gotp"
|
||||
"net/http"
|
||||
@ -26,7 +27,7 @@ type OtpLogin struct {
|
||||
DB otpLoginDB
|
||||
}
|
||||
|
||||
func (o *OtpLogin) AccessState() auth.State { return auth.StateBasic }
|
||||
func (o *OtpLogin) AccessState() process.State { return process.StateBasic }
|
||||
|
||||
func (o *OtpLogin) Name() string { return "basic" }
|
||||
|
||||
|
@ -3,6 +3,7 @@ package providers
|
||||
import (
|
||||
"github.com/1f349/lavender/auth"
|
||||
"github.com/1f349/lavender/auth/authContext"
|
||||
"github.com/1f349/lavender/auth/process"
|
||||
)
|
||||
|
||||
type passkeyLoginDB interface {
|
||||
@ -18,7 +19,7 @@ type PasskeyLogin struct {
|
||||
DB passkeyLoginDB
|
||||
}
|
||||
|
||||
func (p *PasskeyLogin) AccessState() auth.State { return auth.StateUnauthorized }
|
||||
func (p *PasskeyLogin) AccessState() process.State { return process.StateUnauthorized }
|
||||
|
||||
func (p *PasskeyLogin) Name() string { return "passkey" }
|
||||
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"github.com/1f349/lavender/auth"
|
||||
"github.com/1f349/lavender/auth/authContext"
|
||||
"github.com/1f349/lavender/auth/process"
|
||||
"github.com/1f349/lavender/database"
|
||||
"net/http"
|
||||
)
|
||||
@ -24,7 +25,7 @@ type PasswordLogin struct {
|
||||
DB passwordLoginDB
|
||||
}
|
||||
|
||||
func (b *PasswordLogin) AccessState() auth.State { return auth.StateBase }
|
||||
func (b *PasswordLogin) AccessState() process.State { return process.StateBase }
|
||||
|
||||
func (b *PasswordLogin) Name() string { return "password" }
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"github.com/1f349/lavender/auth/process"
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@ -11,7 +12,7 @@ type UserHandler func(rw http.ResponseWriter, req *http.Request, params httprout
|
||||
|
||||
type UserAuth struct {
|
||||
Subject string
|
||||
Factor State
|
||||
Factor process.State
|
||||
UserInfo UserInfoFields
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ package server
|
||||
import (
|
||||
"context"
|
||||
"github.com/1f349/lavender/auth"
|
||||
"github.com/1f349/lavender/auth/process"
|
||||
"github.com/1f349/mjwt"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/google/uuid"
|
||||
@ -18,7 +19,7 @@ func TestUserAuth_NextFlowUrl(t *testing.T) {
|
||||
assert.Equal(t, url.URL{Path: "/login"}, *u.NextFlowUrl(&url.URL{}))
|
||||
assert.Equal(t, url.URL{Path: "/login", RawQuery: url.Values{"redirect": {"/hello"}}.Encode()}, *u.NextFlowUrl(&url.URL{Path: "/hello"}))
|
||||
assert.Equal(t, url.URL{Path: "/login", RawQuery: url.Values{"redirect": {"/hello?a=A"}}.Encode()}, *u.NextFlowUrl(&url.URL{Path: "/hello", RawQuery: url.Values{"a": {"A"}}.Encode()}))
|
||||
u.Factor = auth.StateExtended
|
||||
u.Factor = process.StateExtended
|
||||
assert.Nil(t, u.NextFlowUrl(&url.URL{}))
|
||||
}
|
||||
|
||||
|
132
server/login.go
132
server/login.go
@ -3,11 +3,11 @@ package server
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/1f349/lavender/auth"
|
||||
"github.com/1f349/lavender/auth/authContext"
|
||||
"github.com/1f349/lavender/auth/process"
|
||||
"github.com/1f349/lavender/auth/providers"
|
||||
"github.com/1f349/lavender/database"
|
||||
"github.com/1f349/lavender/issuer"
|
||||
@ -43,7 +43,7 @@ func getUserLoginName(req *http.Request) string {
|
||||
return originUrl.Query().Get("login_name")
|
||||
}
|
||||
|
||||
func (h *httpServer) getAuthWithState(state auth.State) auth.Provider {
|
||||
func (h *httpServer) getAuthWithState(state process.State) auth.Provider {
|
||||
for _, i := range h.authSources {
|
||||
if i.AccessState() == state {
|
||||
return i
|
||||
@ -75,30 +75,6 @@ func (h *httpServer) loginGet(rw http.ResponseWriter, req *http.Request, _ httpr
|
||||
|
||||
// TODO: some of this should be more like tulip
|
||||
|
||||
cookie, err := req.Cookie("lavender-login-name")
|
||||
if err == nil && cookie.Valid() == nil {
|
||||
loginName := cookie.Value
|
||||
|
||||
_, err := h.db.GetUser(req.Context(), userAuth.Subject)
|
||||
switch {
|
||||
case err == nil:
|
||||
break
|
||||
case errors.Is(err, sql.ErrNoRows):
|
||||
loginName = ""
|
||||
default:
|
||||
http.Error(rw, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
web.RenderPageTemplate(rw, "login-memory", map[string]any{
|
||||
"ServiceName": h.conf.ServiceName,
|
||||
"LoginName": loginName,
|
||||
"Redirect": req.URL.Query().Get("redirect"),
|
||||
"Source": "start",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
buttonCtx := authContext.NewTemplateContext(req, new(database.User))
|
||||
|
||||
buttonTemplates := make([]template.HTML, 0, len(h.authButtons))
|
||||
@ -113,13 +89,17 @@ func (h *httpServer) loginGet(rw http.ResponseWriter, req *http.Request, _ httpr
|
||||
}
|
||||
}
|
||||
|
||||
type loginError struct {
|
||||
Error string `json:"error"`
|
||||
authState := process.StateUnauthorized
|
||||
|
||||
jwtCookie, err := readJwtCookie[process.LoginProcessData](req, "login-process", h.signingKey.KeyStore())
|
||||
if err == nil {
|
||||
authState = jwtCookie.Claims.State
|
||||
return
|
||||
}
|
||||
|
||||
var renderTemplate template.HTML
|
||||
provider := h.getAuthWithState(authState)
|
||||
|
||||
provider := h.getAuthWithState(auth.StateUnauthorized)
|
||||
var renderTemplate template.HTML
|
||||
|
||||
// Maybe the admin has disabled some login providers but does have a button based provider available?
|
||||
form, ok := provider.(auth.Form)
|
||||
@ -127,7 +107,9 @@ func (h *httpServer) loginGet(rw http.ResponseWriter, req *http.Request, _ httpr
|
||||
renderTemplate, err = h.renderAuthTemplate(req, form)
|
||||
if err != nil {
|
||||
logger.Logger.Warn("No provider for login")
|
||||
web.RenderPageTemplate(rw, "login-error", loginError{Error: "No available provider for login"})
|
||||
web.RenderPageTemplate(rw, "login-error", struct {
|
||||
Error string `json:"error"`
|
||||
}{Error: "No available provider for login"})
|
||||
return
|
||||
}
|
||||
}
|
||||
@ -165,6 +147,7 @@ func (h *httpServer) loginPost(rw http.ResponseWriter, req *http.Request, _ http
|
||||
}).String(), http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
loginName := req.PostFormValue("email")
|
||||
|
||||
// append local namespace if @ is missing
|
||||
@ -206,16 +189,16 @@ func (h *httpServer) loginPost(rw http.ResponseWriter, req *http.Request, _ http
|
||||
// TODO(melon): rewrite login system here
|
||||
|
||||
// if the login is the local server
|
||||
if login == issuer.MeWellKnown {
|
||||
// TODO(melon): work on this
|
||||
// TODO: rewrite
|
||||
//err := h.authBasic.AttemptLogin(ctx, req, nil)
|
||||
var err error
|
||||
switch {
|
||||
case errors.As(err, &redirectError):
|
||||
http.Redirect(rw, req, redirectError.Target, redirectError.Code)
|
||||
return
|
||||
}
|
||||
if login != issuer.MeWellKnown {
|
||||
// save state for use later
|
||||
state := login.Config.Namespace + ":" + uuid.NewString()
|
||||
h.flowState.Set(state, flowStateData{loginName, login, req.PostFormValue("redirect")}, time.Now().Add(15*time.Minute))
|
||||
|
||||
// generate oauth2 config and redirect to authorize URL
|
||||
oa2conf := login.OAuth2Config
|
||||
oa2conf.RedirectURL = h.conf.BaseUrl.JoinPath("callback").String()
|
||||
nextUrl := oa2conf.AuthCodeURL(state, oauth2.SetAuthURLParam("login_name", loginUn))
|
||||
http.Redirect(rw, req, nextUrl, http.StatusFound)
|
||||
return
|
||||
}
|
||||
|
||||
@ -238,29 +221,68 @@ func (h *httpServer) loginPost(rw http.ResponseWriter, req *http.Request, _ http
|
||||
}
|
||||
|
||||
// TODO: rewrite
|
||||
formContext := authContext.NewFormContext(req, nil)
|
||||
formContext := authContext.NewFormContext(req, nil, rw)
|
||||
err := authForm.AttemptLogin(formContext)
|
||||
switch {
|
||||
case errors.As(err, &redirectError):
|
||||
http.Redirect(rw, req, redirectError.Target, redirectError.Code)
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: idk why login process data isn't working properly
|
||||
processData := formContext.GetLoginProcessData()
|
||||
if h.setLoginProcessCookie(rw, processData) {
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: figure this out
|
||||
logger.Logger.Debug("POST /login: form render data: ", formContext.Data())
|
||||
http.Redirect(rw, req, h.conf.BaseUrl.JoinPath("login").String(), http.StatusFound)
|
||||
}
|
||||
|
||||
func (h *httpServer) setLoginProcessCookie(rw http.ResponseWriter, data process.LoginProcessData) bool {
|
||||
gen, err := h.signingKey.GenerateJwt("login-process", uuid.NewString(), jwt.ClaimStrings{h.conf.BaseUrl.String()}, time.Hour, data)
|
||||
if err != nil {
|
||||
http.Error(rw, "Failed to generate cookie token", http.StatusInternalServerError)
|
||||
return true
|
||||
}
|
||||
http.SetCookie(rw, &http.Cookie{
|
||||
Name: "lavender-login-process",
|
||||
Value: gen,
|
||||
Path: "/",
|
||||
Secure: true,
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
func (h *httpServer) loginCallback(rw http.ResponseWriter, req *http.Request, _ httprouter.Params, _ auth.UserAuth) {
|
||||
// TODO: rewrite
|
||||
for _, i := range h.authSources {
|
||||
if callback, ok := i.(authContext.CallbackContext); ok {
|
||||
callback.HandleCallback(rw, req)
|
||||
user := callback.User()
|
||||
h.setLoginDataCookie(rw, auth.UserAuth{
|
||||
Subject: user.Subject,
|
||||
Factor: auth.StateExtended,
|
||||
UserInfo: auth.UserInfoFields{},
|
||||
}, "loginName")
|
||||
break
|
||||
}
|
||||
flowState, ok := h.flowState.Get(req.FormValue("state"))
|
||||
if !ok {
|
||||
http.Error(rw, "Invalid flow state", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
token, err := flowState.sso.OAuth2Config.Exchange(context.Background(), req.FormValue("code"), oauth2.SetAuthURLParam("redirect_uri", h.conf.BaseUrl.JoinPath("callback").String()))
|
||||
if err != nil {
|
||||
http.Error(rw, "Failed to exchange code for token", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
userAuth, err := h.updateExternalUserInfo(req, flowState.sso, token)
|
||||
if err != nil {
|
||||
http.Error(rw, "Failed to update external user info", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if h.setLoginDataCookie(rw, userAuth, flowState.loginName) {
|
||||
http.Error(rw, "Failed to save login cookie", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
if flowState.redirect != "" {
|
||||
req.Form.Set("redirect", flowState.redirect)
|
||||
}
|
||||
utils.SafeRedirect(rw, req)
|
||||
}
|
||||
|
||||
const twelveHours = 12 * time.Hour
|
||||
@ -268,7 +290,7 @@ const oneWeek = 7 * 24 * time.Hour
|
||||
|
||||
type lavenderLoginAccess struct {
|
||||
UserInfo auth.UserInfoFields `json:"user_info"`
|
||||
Factor auth.State `json:"factor"`
|
||||
Factor process.State `json:"factor"`
|
||||
mjwtAuth.AccessTokenClaims
|
||||
}
|
||||
|
||||
|
@ -35,10 +35,19 @@ type httpServer struct {
|
||||
// mailLinkCache contains a mapping of verify uuids to user uuids
|
||||
mailLinkCache *cache.Cache[mailLinkKey, string]
|
||||
|
||||
// flowState contains the flow state of 3rd party oauth2
|
||||
flowState *cache.Cache[string, flowStateData]
|
||||
|
||||
authSources []auth.Provider
|
||||
authButtons []auth.Button
|
||||
}
|
||||
|
||||
type flowStateData struct {
|
||||
loginName string
|
||||
sso *issuer.WellKnownOIDC
|
||||
redirect string
|
||||
}
|
||||
|
||||
type mailLink byte
|
||||
|
||||
const (
|
||||
@ -55,14 +64,16 @@ type mailLinkKey struct {
|
||||
func SetupRouter(r *httprouter.Router, config conf.Conf, mailSender *mail.Mail, db *database.Queries, signingKey *mjwt.Issuer) {
|
||||
// TODO: move auth provider init to main function
|
||||
// TODO: allow dynamically changing the providers based on database information
|
||||
authBasic := &providers.PasswordLogin{DB: db}
|
||||
authInitial := &providers.InitialLogin{}
|
||||
authPassword := &providers.PasswordLogin{DB: db}
|
||||
authOtp := &providers.OtpLogin{DB: db}
|
||||
authOAuth := &providers.OAuthLogin{DB: db, BaseUrl: &config.BaseUrl}
|
||||
authOAuth.Init()
|
||||
authPasskey := &providers.PasskeyLogin{DB: db}
|
||||
|
||||
authSources := []auth.Provider{
|
||||
authBasic,
|
||||
authInitial,
|
||||
authPassword,
|
||||
authOtp,
|
||||
authOAuth,
|
||||
authPasskey,
|
||||
|
@ -3,6 +3,7 @@ export const partial = true;
|
||||
---
|
||||
|
||||
<form method="POST" action="/login">
|
||||
<input type="hidden" name="provider" value="base"/>
|
||||
<input type="hidden" name="redirect" value="[[.Redirect]]"/>
|
||||
<div>
|
||||
<label for="field_email">Email:</label>
|
||||
|
@ -3,6 +3,7 @@ export const partial = true;
|
||||
---
|
||||
|
||||
<form method="POST" action="/login">
|
||||
<input type="hidden" name="provider" value="oauth"/>
|
||||
<input type="hidden" name="redirect" value="[[.Redirect]]"/>
|
||||
<div>
|
||||
<label for="field_loginname">Login Name:</label>
|
||||
|
@ -3,6 +3,7 @@ export const partial = true;
|
||||
---
|
||||
|
||||
<form method="POST" action="/login/otp" autocomplete="off">
|
||||
<input type="hidden" name="provider" value="otp"/>
|
||||
<input type="hidden" name="redirect" value="[[.Redirect]]"/>
|
||||
<div>
|
||||
<label for="field_code">OTP Code:</label>
|
||||
|
@ -3,6 +3,7 @@ export const partial = true;
|
||||
---
|
||||
|
||||
<form method="POST" action="/login">
|
||||
<input type="hidden" name="provider" value="password"/>
|
||||
<input type="hidden" name="redirect" value="[[.Redirect]]"/>
|
||||
<input type="hidden" name="email" value="[[.UserEmail]]"/>
|
||||
<div>
|
||||
|
@ -11,6 +11,7 @@ import Layout from "../layouts/Layout.astro";
|
||||
[[ .AuthTemplate ]]
|
||||
<div class="w-full">
|
||||
<form method="POST" action="/login">
|
||||
<input type="hidden" name="provider" value="base"/>
|
||||
<input type="hidden" name="redirect" value="[[ .Redirect ]]"/>
|
||||
<div class="w-full">
|
||||
<label>Email: <input type="email" name="email" value="[[ .UserEmail ]]" required/></label>
|
||||
|
Loading…
x
Reference in New Issue
Block a user