Tài khoản 놀아
한국어KO EnglishEN Tiếng ViệtVI
Mục lục

Hướng dẫn tích hợp OAuth 2.0

Toàn bộ luồng xử lý: từ yêu cầu ủy quyền (authorization request), đổi token, đến gọi UserInfo.

Tài khoản 놀아 hỗ trợ OAuth 2.0 theo luồng Authorization Code + PKCE. Tài liệu này giải thích toàn bộ luồng xử lý từ đầu đến cuối. Nếu bạn muốn tích hợp ngay, Tích hợp trong 5 phút sẽ nhanh hơn.

Toàn bộ luồng xử lý

[Dịch vụ của bạn]                [Tài khoản 놀아]               [Người dùng]
    │                                │                            │
 1. Tạo verifier/challenge           │                            │
    │──── 2. /oauth/authorize ──────▶│                            │
    │                                │──── 3. Đăng nhập · đồng ý ▶│
    │                                │◀─── 4. Đồng ý ─────────────│
    │◀─── 5. Callback code + state ──│                            │
 6. Kiểm tra state                   │                            │
    │──── 7. /oauth/token ──────────▶│  (máy chủ → máy chủ)       │
    │◀─── 8. access/refresh token ───│                            │
    │──── 9. /oauth/userinfo ───────▶│                            │
    │◀─── 10. Thông tin người dùng ──│                            │

Từ bước 7 trở đi phải thực hiện trên máy chủ, vì có chứa client_secret.

Điểm cuối (endpoint)

Vai tròĐịa chỉ
Yêu cầu ủy quyền (authorization request)https://account.nola.kr/oauth/authorize
Đổi và làm mới tokenhttps://account.nola.kr/oauth/token
Thông tin người dùnghttps://account.nola.kr/oauth/userinfo
Thông tin tên miền dịch vụhttps://account.nola.kr/oauth/domain-info

Tham số và trường phản hồi được tổng hợp thành bảng tại Tra cứu API.

1. Chuẩn bị giá trị PKCE

PKCE là bắt buộc. Nếu yêu cầu không có code_challenge, yêu cầu sẽ bị từ chối với pkce_required, và chỉ chấp nhận phương thức S256.
// Trên máy chủ, trước khi chuyển người dùng đi
$verifier  = rtrim(strtr(base64_encode(random_bytes(32)), '+/', '-_'), '=');
$challenge = rtrim(strtr(base64_encode(hash('sha256', $verifier, true)), '+/', '-_'), '=');
$state     = bin2hex(random_bytes(16));

$_SESSION['nolaa_verifier'] = $verifier;   // dùng ở bước 7
$_SESSION['nolaa_state']    = $state;      // dùng ở bước 6

2. Yêu cầu ủy quyền

Chuyển trình duyệt của người dùng tới địa chỉ sau.

https://account.nola.kr/oauth/authorize
  ?response_type=code
  &client_id=YOUR_CLIENT_ID
  &redirect_uri=YOUR_REDIRECT_URI
  &scope=profile.basic%20profile.avatar
  &state=RANDOM_STATE
  &code_challenge=CODE_CHALLENGE
  &code_challenge_method=S256

client_id, redirect_uri, state, scope và code_challenge đều là bắt buộc. Thiếu bất kỳ giá trị nào, yêu cầu sẽ bị từ chối.

redirect_uri phải khớp chính xác từng ký tự với callback URL đã đăng ký trong Tài khoản của tôi. Nếu khác dấu gạch chéo ở cuối, http/https hoặc cổng (port), kết quả là invalid_redirect_uri.

Chỉ định ngôn ngữ màn hình — ui_locales (tùy chọn)

Thêm tham số này thì màn hình đăng nhập, xác nhận tài khoản và đồng ý sẽ hiển thị bằng ngôn ngữ đó. Mục đích là để người dùng đến từ một dịch vụ tiếng Việt không gặp phải màn hình tiếng Hàn.

  &ui_locales=vi
  • Các giá trị được hỗ trợ là ko, en và vi. Giá trị khác sẽ bị bỏ qua và áp dụng qui tắc mặc định.
  • Có thể ghi nhiều giá trị cách nhau bằng dấu cách, giá trị được hỗ trợ đầu tiên sẽ được dùng. Ví dụ: ui_locales=vi en
  • Nếu người dùng đã tự chọn ngôn ngữ trong Tài khoản 놀아 thì lựa chọn đó được ưu tiên. Tham số này chỉ áp dụng cho người chưa từng chọn — dịch vụ không ghi đè lựa chọn của người dùng.
  • Nếu bỏ qua, hệ thống tự nhận biết theo ngôn ngữ trình duyệt (Accept-Language), không xác định được thì dùng tiếng Hàn.
  • Giá trị này được giữ qua màn hình đăng nhập, và được bỏ sau khi hoàn tất đồng ý.
Chỉ ảnh hưởng đến ngôn ngữ màn hình. Không ảnh hưởng gì đến việc xác thực, phân quyền hay kiểm tra redirect_uri. Người dùng có thể tự đổi ngôn ngữ ở phía trên bất kỳ màn hình nào, và khi đổi thì state, code_challenge cùng mọi tham số khác đều được giữ nguyên.

3. Đăng nhập và đồng ý

Nếu người dùng chưa đăng nhập, màn hình đăng nhập sẽ hiện ra trước. Ngay cả khi đã đăng nhập, màn hình xác nhận tài khoản vẫn hiện một lần để người dùng có cơ hội đổi sang tài khoản khác.

Sau đó, ở màn hình đồng ý, các scope bạn yêu cầu được hiển thị nguyên vẹn cho người dùng.

Scope đã yêu cầuNội dung hiển thị cho người dùng
profile.basicUUID, biệt danh, địa chỉ email
profile.avatarURL ảnh đại diện
service.domain.readThông tin tên miền của dịch vụ đã kết nối

Tổ hợp đã được đồng ý một lần sẽ được ghi nhớ, và từ lần sau màn hình đồng ý sẽ được bỏ qua. Nếu bạn yêu cầu thêm scope mới, hệ thống sẽ xin đồng ý lại lúc đó.

4–5. Callback và kiểm tra state

Khi người dùng đồng ý, họ được đưa về callback URL đã đăng ký.

https://ten-mien-cua-ban.com/oauth/callback?code=abcd1234…&state=RANDOM_STATE
Hãy kiểm tra state trước khi đổi token. Nếu chỉ gửi mà không kiểm tra thì không có tác dụng phòng chống tấn công giả mạo yêu cầu chéo trang (CSRF).
if (!hash_equals($_SESSION['nolaa_state'] ?? '', $_GET['state'] ?? '')) {
    // dừng lại
}

Nếu người dùng từ chối hoặc yêu cầu có vấn đề, ?error=... sẽ được trả về thay thế. Nguyên nhân của từng mã có trong Từ điển mã lỗi.

6. Đổi token

curl -X POST https://account.nola.kr/oauth/token \
  -d "grant_type=authorization_code" \
  -d "code=RECEIVED_CODE" \
  -d "redirect_uri=YOUR_REDIRECT_URI" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET" \
  -d "code_verifier=SAVED_VERIFIER"
{
  "access_token": "…",
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": "…",
  "scope": "profile.avatar profile.basic"
}

authorization code có hiệu lực 10 phút · chỉ dùng một lần. Dù thử lại, cùng một code cũng không dùng được lần thứ hai (invalid_grant).

redirect_uri trong yêu cầu token phải là cùng giá trị với lúc yêu cầu ủy quyền.

7. Thông tin người dùng

curl https://account.nola.kr/oauth/userinfo \
  -H "Authorization: Bearer ACCESS_TOKEN"
{
  "sub": "pws_xxxxxxxxxxxxxxxx",
  "nickname": "놀아유저",
  "scope": "profile.avatar profile.basic service.member_email.read",
  "profile_image_url": "https://…/uploads/….jpg",
  "email": "user@example.com"
}
sub là giá trị khác nhau theo từng dịch vụ (định danh riêng theo dịch vụ, pairwise). Cùng một người nhưng ở dịch vụ khác sẽ nhận sub khác. Hãy dùng nó làm khóa người dùng bên trong dịch vụ của bạn.

Điều kiện để nhận được email có trong Tra cứu scope. Nguyên nhân có thể không phải do scope mà do thiết lập cung cấp email của dịch vụ.

8. Làm mới token

access_token mặc định có hiệu lực 1 giờ, còn refresh_token là 30 ngày.

curl -X POST https://account.nola.kr/oauth/token \
  -d "grant_type=refresh_token" \
  -d "refresh_token=SAVED_REFRESH_TOKEN" \
  -d "client_id=YOUR_CLIENT_ID" \
  -d "client_secret=YOUR_CLIENT_SECRET"
Khi làm mới, refresh_token mới cũng được cấp và token cũ bị thu hồi (xoay vòng, refresh token rotation). Hãy nhớ lưu giá trị mới trong phản hồi. Nếu tiếp tục dùng giá trị cũ, lần làm mới tiếp theo sẽ thất bại.

9. Ngắt kết nối · đăng xuất

Tài khoản 놀아 không có API công khai để thu hồi token (điểm cuối revoke). Hiện không thể để nút «Ngắt kết nối» trong dịch vụ của bạn gọi tới 놀아 nhằm vô hiệu hóa token ở phía 놀아.

Vì vậy dịch vụ cần được xây dựng để khi nhận 401 invalid_token thì lặng lẽ đăng xuất người dùng. Khi người dùng đã dọn dẹp kết nối ở phía Tài khoản 놀아 hoặc token đã hết hạn, đây là tín hiệu duy nhất mà dịch vụ nhận biết được.

Để đăng xuất khỏi chính dịch vụ của bạn, chỉ cần xóa phiên của bạn. Không cần chấm dứt cả phiên Tài khoản 놀아.

Danh mục kiểm tra khi tích hợp

  • Đã đăng ký dịch vụ · xác minh xong tên miền · đăng ký callback URL (phải đủ cả ba thì trạng thái mới thành «Đang hoạt động»)
  • Đã cố định redirect_uri thành hằng số chưa
  • Mỗi yêu cầu có tạo state và code_verifier mới và lưu vào phiên không
  • Tại callback có kiểm tra state không
  • Có đổi token trên máy chủ không
  • Có lưu refresh_token mới không
  • Có xử lý khi nhận 401 không

Trước khi phát hành, hãy xem lại Danh mục kiểm tra bảo mật một lần nữa.