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.