package api import ( "database/sql" "log/slog" "net/http" "atlas9.dev/c/core/dbi" "atlas9.dev/c/core/iam" "atlas9.dev/c/demo/lib" ) type IdentityImpl struct { DB *sql.DB Users dbi.Factory[iam.UserStore] Passwords dbi.Factory[iam.PasswordStore] SessionMan *iam.SessionMan Throttle *AccountThrottle } func (s *IdentityImpl) Login(w http.ResponseWriter, r *http.Request) { // Read and validate the request body var req Identity_LoginReq if read(w, r, &req) { return } ctx := r.Context() if !s.Throttle.Check(ctx, req.Email) { write(ctx, w, ErrThrottle, nil) return } ctx = lib.PutSystemAccess(ctx, iam.CapUsersGetByEmail, iam.CapPasswordsGet) token, err := dbi.Write(ctx, s.DB, func(tx dbi.DBI) (string, error) { users := s.Users(tx) passwords := s.Passwords(tx) var user iam.User if err := users.GetByEmail(ctx, req.Email, &user); err != nil { return "", ErrInvalidCredentials } if err := iam.CheckPassword(ctx, passwords, user.ID, req.Password); err != nil { return "", ErrInvalidCredentials } if !user.Verified { return "", ErrEmailNotVerified } return s.SessionMan.CreateSession(ctx, tx, user.ID) }) if err != nil { write(ctx, w, err, nil) return } s.SessionMan.WriteCookie(w, token) write(ctx, w, nil, &Identity_LoginRes{}) } func (s *IdentityImpl) Logout(w http.ResponseWriter, r *http.Request) { ctx := r.Context() cookie, _ := r.Cookie(s.SessionMan.CookieName) if cookie != nil { err := dbi.ReadWrite(ctx, s.DB, func(tx dbi.DBI) error { return s.SessionMan.DeleteSession(ctx, tx, cookie.Value) }) if err != nil { slog.ErrorContext(ctx, "deleting session", "err", err) } } s.SessionMan.ClearCookie(w) write(ctx, w, nil, nil) }