user login & middleware complete

This commit is contained in:
Simon Martens
2025-05-22 21:12:29 +02:00
parent 3f57e7a18d
commit 36e34d9e7b
17 changed files with 808 additions and 26 deletions

View File

@@ -503,6 +503,7 @@ const (
ITEMS_IDENTIFIER_FIELD = "identifier"
SESSIONS_TOKEN_FIELD = "token"
SESSIONS_CSRF_FIELD = "csrf"
SESSIONS_USER_FIELD = "user"
SESSIONS_IP_FIELD = "ip"
SESSIONS_USER_AGENT_FIELD = "agent"
@@ -512,8 +513,11 @@ const (
SESSIONS_PERSIST_FIELD = "persist"
USERS_TABLE = "users"
USERS_EMAIL_FIELD = "email"
USERS_SETTINGS_FIELD = "settings"
USERS_NAME_FIELD = "name"
USERS_ROLE_FIELD = "role"
USERS_AVATAR_FIELD = "avatar"
SESSION_COOKIE_NAME = "sid"
)

View File

@@ -0,0 +1,78 @@
package dbmodels
import (
"crypto/rand"
"encoding/base64"
"fmt"
"time"
"github.com/pocketbase/pocketbase/core"
"github.com/pocketbase/pocketbase/tools/types" // For types.NewDateTimeFromTime
)
const (
secureTokenByteLength = 64
)
func generateSecureRandomToken(length int) (string, error) {
if length <= 0 {
length = secureTokenByteLength
}
randomBytes := make([]byte, length)
_, err := rand.Read(randomBytes)
if err != nil {
return "", fmt.Errorf("failed to generate random bytes for token: %w", err)
}
return base64.URLEncoding.EncodeToString(randomBytes), nil
}
func CreateSessionToken(
app core.App,
userID string,
ipAddress string,
userAgent string,
isPersistent bool,
sessionDuration time.Duration,
) (*Session, error) {
collection, err := app.FindCollectionByNameOrId(SESSIONS_TABLE)
if err != nil {
return nil, fmt.Errorf("failed to find '%s' collection: %w", SESSIONS_TABLE, err)
}
sessionTokenClear, err := generateSecureRandomToken(secureTokenByteLength)
if err != nil {
return nil, fmt.Errorf("failed to generate session token: %w", err)
}
csrfTokenClear, err := generateSecureRandomToken(secureTokenByteLength)
if err != nil {
return nil, fmt.Errorf("failed to generate CSRF token: %w", err)
}
record := core.NewRecord(collection)
session := NewSession(record)
// Set required fields with hashed tokens
session.SetToken(sessionTokenClear)
session.SetCSRF(csrfTokenClear)
session.SetUser(userID)
date := types.NowDateTime()
expires := date.Add(sessionDuration)
session.SetExpires(expires)
session.SetPersist(isPersistent)
session.SetLastAccess(types.NowDateTime())
session.SetUserAgent(userAgent)
session.SetIP(ipAddress)
if errSave := app.Save(session); errSave != nil {
app.Logger().Error("Failed to save session token record", "error", errSave, "userID", userID)
return nil, fmt.Errorf("failed to save session token record: %w", errSave)
}
app.Logger().Info("Successfully created session token entry", "recordId", record.Id, "userID", userID)
return session, nil
}

View File

@@ -5,6 +5,24 @@ import (
"github.com/pocketbase/pocketbase/tools/types"
)
type FixedSession struct {
ID string `json:"id"`
Token string `json:"token"`
User string `json:"user"`
Created string `json:"created"`
Updated string `json:"updated"`
Expires types.DateTime `json:"expires"`
IP string `json:"ip"`
UserAgent string `json:"user_agent"`
LastAccess types.DateTime `json:"last_access"`
Persist bool `json:"persist"`
CSRF string `json:"csrf"`
}
func (s *FixedSession) IsExpired() bool {
return s.Expires.IsZero() || s.Expires.Before(types.NowDateTime())
}
var _ core.RecordProxy = (*Place)(nil)
type Session struct {
@@ -18,7 +36,7 @@ func NewSession(record *core.Record) *Session {
}
func (u *Session) TableName() string {
return USERS_TABLE
return SESSIONS_TABLE
}
func (u *Session) Token() string {
@@ -84,3 +102,31 @@ func (u *Session) Persist() bool {
func (u *Session) SetPersist(persist bool) {
u.Set(SESSIONS_PERSIST_FIELD, persist)
}
func (u *Session) CSRF() string {
return u.GetString(SESSIONS_CSRF_FIELD)
}
func (u *Session) SetCSRF(csrf string) {
u.Set(SESSIONS_CSRF_FIELD, csrf)
}
func (u *Session) IsExpired() bool {
return u.Expires().IsZero() || u.Expires().Before(types.NowDateTime())
}
func (u *Session) Fixed() FixedSession {
return FixedSession{
ID: u.Id,
Token: u.Token(),
User: u.User(),
Created: u.Created(),
Updated: u.Updated(),
Expires: u.Expires(),
IP: u.IP(),
UserAgent: u.UserAgent(),
LastAccess: u.LastAccess(),
Persist: u.Persist(),
CSRF: u.CSRF(),
}
}

View File

@@ -6,6 +6,18 @@ import (
"github.com/pocketbase/pocketbase/tools/types"
)
type FixedUser struct {
Id string `json:"id"`
Email string `json:"email"`
Created types.DateTime `json:"created"`
Updated types.DateTime `json:"updated"`
Name string `json:"name"`
Role string `json:"role"`
Avatar string `json:"avatar"`
Verified bool `json:"verified"`
Settings string `json:"settings"`
}
var _ core.RecordProxy = (*Place)(nil)
type User struct {
@@ -22,7 +34,7 @@ func (u *User) TableName() string {
return USERS_TABLE
}
// INFO: Email is already set on the core.Record
// INFO: Email, password functions are already set on the core.Record
// TODO: We need to create a settings struct as soon as we have settings
func (u *User) Name() string {
return u.GetString(USERS_NAME_FIELD)
@@ -59,3 +71,16 @@ func (u *User) Avatar() string {
func (u *User) SetAvatar(avatar *filesystem.File) {
u.Set(USERS_AVATAR_FIELD, avatar)
}
func (u *User) Fixed() FixedUser {
return FixedUser{
Id: u.Id,
Email: u.Email(),
Created: u.Created(),
Updated: u.Updated(),
Name: u.Name(),
Role: u.Role(),
Avatar: u.Avatar(),
Verified: u.Verified(),
}
}