s_skaidocsTài liệu
LINGO · XÁC THỰC WEBSITE

Object auth

Thông tin người dùng hiện tại đã được server xác thực. Đây là object dữ liệu phẳng, không gọi auth.check() hoặc users.current().

Cách dùng

{% if auth.check %}
  Xin chào {{ auth.user.display_name }}
  <a href="/profile">Hồ sơ</a>
{% else %}
  <a href="/login">Đăng nhập</a>
{% endif %}

Contract hiện tại

{
  "check": true,
  "method": "email_password",
  "provider": "local",
  "user": {
    "id": "account-uuid",
    "display_name": "Nguyễn An",
    "email": "[email protected]",
    "email_verified": false,
    "phone": "0900000000",
    "membership": {
      "id": "membership-uuid",
      "status": "active",
      "joined_at": "timestamp"
    }
  }
}

Ví dụ JSON mô tả cấu trúc; joined_at là time.Time trong template. Guest có check=false, user=null, method=null, provider=null. id là external account thuộc workspace, không phải ID nhân viên Console hay customer.

method là phương thức đăng nhập, provider là nhà cung cấp danh tính; hiện chỉ email_password/local. Không có provider_data, token, hash, quyền Console, bio/avatar hoặc custom profile. Không quảng bá OAuth/OTP khi chưa triển khai.

Trang profile server-rendered

Tạo pages/profile.html. Không gọi API profile GET cũ (đã bỏ) hoặc lấy session.profile để điền input bằng JavaScript nữa.

{% extends "layouts/default.html" %}
{% block content %}
<section data-private-profile>
{% if auth.check %}
{% set user = auth.user %}
<form id="profile-form" method="post" action="/_post/profile/update">
  <input type="hidden" name="_token" value="{{ csrf_token }}">
  <p>Email: {{ user.email }}</p>
  <label>Tên hiển thị
    <input name="display_name" value="{{ user.display_name }}" maxlength="160" required>
  </label>
  <label>Điện thoại
    <input name="phone" value="{{ user.phone }}" maxlength="32">
  </label>
  <button type="submit">Lưu hồ sơ</button>
</form>
{% else %}
  <a href="/login?next=%2Fprofile">Đăng nhập để xem hồ sơ</a>
{% endif %}
</section>
<p id="profile-message" role="status"></p>
{% endblock %}

Lưu hồ sơ — vẫn là POST có CSRF

Form gửi trực tiếp bằng POST, không cần JavaScript hoặc API session cũ (đã bỏ). Thành công chuyển về /profile?saved=1 (303). Nếu muốn lưu không chuyển trang, dùng JavaScript tùy chọn bên dưới. Backend xác thực lại account và chỉ cho cập nhật display_name/phone.

const form = document.querySelector("#profile-form");
const message = document.querySelector("#profile-message");
let busy = false;
form?.addEventListener("submit", async event => {
  event.preventDefault();
  if (busy || !form.reportValidity()) return;
  busy = true;
  const button = form.querySelector('button[type="submit"]');
  button.disabled = true;
  try {
    const response = await fetch("/_post/profile/update", {
      method: "POST", credentials: "same-origin", cache: "no-store",
      headers: {"Content-Type": "application/json", "X-CSRF-Token": form.elements._token.value},
      body: JSON.stringify({
        display_name: form.elements.display_name.value,
        phone: form.elements.phone.value
      })
    });
    const result = await response.json();
    if (!response.ok) throw new Error(result.error?.message || "Không thể lưu.");
    message.textContent = "Đã lưu hồ sơ.";
  } catch (error) {
    message.textContent = error.message;
  } finally {
    busy = false;
    button.disabled = false;
  }
});
window.addEventListener("pagehide", () => {
  document.querySelector("[data-private-profile]")?.replaceChildren();
});
window.addEventListener("pageshow", event => {
  if (event.persisted) location.reload();
});

Chỉ display_name và phone được sửa. display_name bắt buộc, tối đa 160 ký tự; phone tối đa 32 bytes, cho phép rỗng, chỉ số và + ()-. Không gửi id/email/membership/provider. Không có auth.update/login/logout trong template. Logout vẫn POST /_post/auth/logout có CSRF, sau thành công xóa nội dung riêng tư rồi chuyển trang.

Phạm vi và bảo mật

  • Xác thực cookie external account đúng workspace/application. Không chấp nhận auth object tự truyền trong page data, token query string hoặc cookie Console.
  • Phiên hết hạn/thu hồi, account hoặc membership bị chặn không được trả user hợp lệ. Lỗi hạ tầng thật báo lỗi render, không giả thành guest.
  • HTTP chỉ được phép theo policy test; mặc định HTTPS. Không có HTTP request context thì auth là guest, kể cả export/static render.
  • Ảnh chụp auth được tạo trong lần render, dùng chung cho template/layout; không cache user giữa các request. Response private/no-store và Vary Cookie; không bật cache HTML profile ở CDN.
  • Template escape dữ liệu mặc định, không dùng safe/raw cho tên/email/phone. Xóa DOM khi pagehide/logout, reload khi khôi phục bfcache.

Thay cách profile cũ

Website draft đã bỏ bước JavaScript điền profile từ session.profile; dùng auth.user trong HTML. API session cũ (đã bỏ) vẫn phục vụ CSRF/login. POST /_post/profile/update vẫn phục vụ lưu. GET /_post/profile/update còn giữ tương thích cho consumer khác, không còn là hướng dẫn render profile trong theme.

auth.check=false không có nghĩa cấm guest mua hàng. Quyền mua/giỏ guest và quyền tra cứu đơn guest là các chức năng riêng; object auth không tự tạo account cho guest.

Xem orders, API ghi dữ liệu và hướng dẫn website.