Fake Layout - this file not in layouts directory

Whats OAuth2 Anyway?

Published on: 2025-08-21

Markdown - Whats OAuth2 Anyway?

Example folder structure

Written by: Krix Daniel

The Astro logo on a dark background with a pink glow.

authenticate

authorization

oauth

sso

Reading time: 18 min read

View count:

Reference document: https://www.romaglushko.com/blog/whats-aouth2/

SSO: Phương pháp xác thực (Authentication method)

OAuth: giao thức tiêu chuẩn cấp quyền ứng dụng/dịch vụ đăng nhập vào một ứng dụng/dịch vụ khác

Background OAuth

User Credentials Sharing

Người dùng chia sẽ thông tin đăng nhập (tên, mật khẩu) cho client

Client giữ thông tin đó là request lên Resource Server để lấy dữ liệu về cho người dùng

Nhược điểm:

Flow:

User Credentials Sharing

Personal Access Token (PATs)

Người dùng tự tạo PATs (với phạm vi truy cập) và cung cấp cho Client App để request lấy dữ liệu/thực hiện tác vụ tự động

Ưu điểm:

Nhược điểm:

Flow:

Personal Access Token (PATs)

OAuth

Được thiết kế để Client App yêu cầu quyền truy cập phạm vi của người dùng trong các ứng dụng/dịch vụ (bên thứ 3). Giải quyết việc Client App giữ thông tin đăng nhập của người dùng.

Client App sẽ nhận được ủy quyền của người dùng để truy cập 1 phần thông tin được cung cấp từ ứng dụng/dịch vụ

Ưu điểm:

Nhược điểm:

Roles:

Flow:

OAuth flow

Client Registration (đăng ký app với OAuth2)

Sử dụng workflow OAuth2 bằng cách đăng ký với Authorization Server Thông tin cần cung cấp:

=> Nhận thông tin xác thực cho Client App

OAuth2 client registration

OAuth2 concept

Trust on First use (TOFU)

Authorization Server tự động ghi nhớ việc cấp phép permission cho Client App, nên có thể sẽ bỏ qua việc hỏi người dùng chấp thuận lại. Đang được dùng mặc định cho OAuth2.

=> Authorization Server cấp quyền cho Client App mà không cần hiển thị giao diện chấp thuận

=> Github và Bitbucket đang sử dụng phương thức này, còn Gitlab thì vẫn luôn hỏi lại permission

Tùy vào cách implement Authorization flow mà có thể hiển thị form xác nhận những permisison cần cấp quyền.

Resource Server sẽ validate access token bằng cách request tới Authorization Server

Clients

Hiện có 2 dạng Client App:

Authorization Server (AuthZ)

Nhiệm vụ: https://www.romaglushko.com/blog/whats-aouth2/#authz-servers

Endpoint Discovery: https://www.romaglushko.com/blog/whats-aouth2/#endpoint-discovery

Security: https://www.romaglushko.com/blog/whats-aouth2/#endpoint-discovery

Access Token

Access Token có thể được tạo ra bởi nhiều luồng xác thực với flow OAuth thông qua AuthZ:

OAuth2 implement flows

Access token scopes

Access token được tạo ra theo request scope dựa vào danh sách scopes được đăng ký OAuth trước đó. Trường hợp không cung cấp request scope sẽ nhận full scopes đã được đăng ký.

Token types

Phổ biến nhất: Beaver token (Authorization: Bearer {{ACCESS_TOKEN}}) set cho header request. Có thể encode bằng JWT nếu đăng ký thêm thông tin meta và đảm bảo TLS encryption.

Các token type khác:

Token lifetime

Reference document: https://www.romaglushko.com/blog/whats-aouth2/#token-lifetime

Scopes

Reference document: https://www.romaglushko.com/blog/whats-aouth2/#token-lifetime

Flow can implement with OAuth2

Phân loại theo số lượng đối tượng có trong OAuth2 flow:

Các yếu tố phân loại khác:

Find the way to implement OAuth2 => Khuyến nghị:

⭐ Authorization Code Flow

Đối tượng: Browser, Client App (có thể là BE), Authorization, Resource Server

Các bước trong flow:

Những cách implement:

Authorization Code Flow

Authorization Request

Đây là bước đầu tiên trong OAuth2 flow, Client App request quyền vào những scopes của Resource Owner bằng cách chuyển hướng tới Authorization Server authorize endpoint.

HTTP/1.1 302 Found
 Location: https://auth.example.com/authorize
 ?response_type=code 
 &client_id=Iv23lilfdg920cAzhcxA 
 &redirect_uri=https://www.clientapp.com/callback?isGithub=true
 &scope=read_user%20write_repo%20read_repo
 &state=YOUR_STATE
https://www.clientapp.com/callback?code=1234567890&state=YOUR_STATE

URL parameters:

Grant permission in OAuth2 flow

Code Exchange

Sau khi người dùng đồng ý ủy quyền cho Client App thì sẽ nhận được token từ Authorization Server thông qua URL callback.

HTTP/1.1 302 Found
 Location: https://www.clientapp.com/callback
 ?code=1234567890
 &isGithub=true
 &state=YOUR_STATE

URL parameters:

Request access token

Sử dụng authorization code để request

POST /token HTTP/1.1
Host: auth.example.com
Authorization: Basic {{ base64(client_id:client_secret) }}
Accept: application/json
Content-Type: application/x-www-form-urlencoded 
grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https://www.clientapp.com/callback/

URL params:

Response (tùy vào service mà sẽ có format khác nhau):

{ 
	"token_type":"bearer", 
	"access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a",
	"scope":"read_user write_repo read_repo", 
	"expires_in": 3600, 
	"refresh_token": "ghr_16C7e42F292c6912E7710c838347Ae178"
}

Use access token to access the API

Sử dụng token_typeacccess_token đã nhận từ trước để request lấy resource

GET /user HTTP/1.1
Host: https://api.github.com
Authorization: {{ token_type }} {{ access_token }}
Accept: application/json

Renew access token with refresh token

Vai trò của refresh_token:

POST /token HTTP/1.1
Host: auth.example.com
Authorization: Basic {{ base64(client_id:client_secret) }}
 
grant_type=refresh_token&refresh_token=ghr_16C7e42F292c6912E7710c838347Ae178

URL params:

Note: Response tương tự với authoriaztion code. Đôi khi hệ thống sẽ refresh refesh_token trước đó vì refresh_token cũng có thời gian hết hạn, khi đó những refresh_token trước không thể sử dụng.

OAuth2 refresh token flow

Revoke access token

⭐ Authorization Code Flow with PKCE “pixy” (Proof key for code exchange)

Lý do ra đời:

Vai trò PKCE:

=> PKCE kế thừa lại Authorization Code Flow nhưng không làm ảnh tới flow chính (authorization & token request)

Đối tượng: Browser/Native App/Public app (vai trò Client App), Authorization, Resource Server

Các bước trong flow:

Những cách implement:

Authorization Code Flow With PKCE (Proof key of key exchange)

Implement PKCE

code_verifier: chìa khóa code_challenge: ổ khóa code_challenge_method: kiểu ổ khóa

Trước flow authorization code

=> Ngăn chặn việc kẻ tấn công có Authorization code nhưng không có chìa khóa để request access_token, vì hiện tại chìa khóa đang được giữ ở memory của app (không có quyền truy cập)

PKCE hiện đang hỗ trợ 2 kiểu hash:

Implicit Flow

Đối tượng: Browser/Mobile App/Public app (vai trò Client App), Authorization, Resource Server

Chỉ cần client ID và redirect URL

Implicit flow được thiết kế để hỗ trợ public clients (SPAs, native app) chạy trên nền tảng web browser

access_token sẽ được trả về client thông qua browser URL (redirect_uri)

Tuy nhiên cách này đi kèm với việc bảo mật kém => Hiện tại OAuth2.0 không hỗ trợ implement theo hướng này nữa => Thay vào đó là sử dụng Authorization code flow + PKCE

Tại sao trước đó Implicit flow lại được sử dụng? Trước khi tồn tại server-side proxies hoặc PKCE extension, SPAs đối mặt với một vài thử thách:

Lỗ hổng bảo mật nghiêm trọng Implicit flow?

Cách implement với Js application:

Implicit Flow

HTTP/1.1 302 Found
Location: https://auth.example.com/authorize
	?response_type=token 
	&client_id=Iv23lilfdg920cAzhcxA 
	&redirect_uri=https://www.clientapp.com/callback/ 
	&scope=read_user%20write_repo%20read_repo 
	&state=wSDOBWf0PAC9C7AENIRoCfHnDDSbr

URL params:

HTTP/1.1 302 Found
Location: https://www.clientapp.com/callback/
 #access_token=gho_16C7e42F292c6912E7710c838347Ae178B4a
 &state=wSDOBWf0PAC9C7AENIRoCfHnDDSbr

URL params:

Client Credentials Flow

refer document: https://cheatsheetseries.owasp.org/cheatsheets/OAuth2_Cheat_Sheet.html#client-authentication Server tới server Đối tượng: Backend (vai trò Client App), Authorization, Resource Server

Sử dụng: client_id, client_secret

Flow này không tồn tại Resource Owner và Frontend app, điều duy nhất cần làm là khiến Authorization Server tin tưởng Client app (ở đây là BE), để lấy access_token, không có refresh_token Flow này không thể tương tác vì được thực hiện ở BE server, nên chỉ cần request lấy token trực tiếp chứ không đi qua các bước lấy code như những flow trước đó.

Client Credentials flow

POST /token HTTP/1.1
Host: auth.example.com
Authorization: Basic {{ base64(client_id:client_secret) }}
Accept: application/json
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&scope=read_user%20write_repo%20read_repo

URL params:

Resource Owner Credentials (ROC) Flow

Đối tượng: Resource Owner, Backend/FrontEnd (vai trò Client App), Authorization, Resource Server

Sử dụng: client_id, client_secret, user credentials

Trái ngược với flow Oauth2 thông thường thì ROC flow vẫn được cho là 1 tiêu chuẩn của Oauth nhưng không được sử dụng rộng rãi và không tốt cho bảo mật. So với User Credentials Sharing thì ROC flow sẽ an toàn hơn với việc giới hạn quyền hạn và hạn chế lộ thông tin đăng nhập của người dùng.

Tại sao vẫn tồn tại flow này?

OAuth2 Resource Owner Credentials Flow

POST {tenant}/oauth2/v2.0/token 
Host: login.microsoftonline.com 
Content-Type: application/x-www-form-urlencoded 
client_id=00001111-aaaa-2222-bbbb-3333cccc4444 &scope=user.read%20openid%20profile%20offline_access &username=MyUsername@myTenant.com 
&password=SuperS3cret 
&grant_type=password

URL params:

⭐ Device Code Flow

Phù hợp với Device hoặc Native app không hỗ trợ bật trình duyệt để xác thực => Nhờ 1 browser phía bên ngoài device để thực hiện công việc xác thực Đối tượng: Resource Owner, Device Client (vai trò Client App), Browser, Authorization, Resource Server

Sử dụng: client_id, client_secret, user credentials

Ban đầu OAuth2 flow chỉ nhắm tới việc phục vụ cho những trường hợp sử dụng browser app. Nhưng khi lớn mạnh thì có những thiết bị cũng muốn sử dụng OAuth mà lại không có sẵn browser để thực hiện xác thực. Đó là lý do ra đời Device Code Flow để hỗ trợ việc xác thực cho device/native app thông qua một browser “mượn” thay mặt device xác thực.

Các bước trong flow:

OAuth2 Device Code Flow Device Authorization Request

POST /device_authorization HTTP/1.1
Host: auth.example.com
Accept: application/json
Content-Type: application/x-www-form-urlencoded
 
client_id=Iv23lilfdg920cAzhcxA&scope=read_user%20write_repo%20read_repo

URL params:

{
  "device_code": "GmRhmhcxhwAzkoEqiMEgDnyEysNkuNhszIySk9eS",
  "user_code": "WDJB-MJHT",
  "verification_uri": "https://auth.example.com/login/device",
  "expires_in": 1800,
  "interval": 5
}

Response meaning:

OAuth2 Device Verification Screen

Access Token Polling

Request chờ lấy token từ Device client Hiện tại không có cách để Authorization Server thông báo cho Device client về việc xác thực đã hoàn thành => Device client phải request polling để hỏi Authorization đã có token chưa

POST /token HTTP/1.1
Host: auth.example.com
Accept: application/json
Content-Type: application/x-www-form-urlencoded
 
grant_type=urn:ietf:params:oauth:grant-type:device_code&device_code=device_code&client_id=client_id

URL params:

// pending
{
  "error": "authorization_pending",
  "error_description": "The authorization request is still pending as the end user hasn't yet authorized the device."
}
// request to fast
{
  "error": "slow_down",
  "error_description": "The client should wait before polling the token endpoint again."
}

⭐ Assertion Flow

Choose the suitable flow

The way to choose the right flow

Dựa trên những flow chính được đề cập trong OAuth2, hãy ưu tiên chọn theo thứ tự sau:

References document

FAQ

Gitlab Applications settings: What is Your applications & Authorized applications?

Your applications:

Do Homepage URL & Callback URL have the same domain?

Homepage URL: URL of the client app - ==not affect to OAuth flow==

Callback URL (Redirect URI): When Gitlab refirect after user approves/denied access

No need to be the same domain, Gitlab only validates the callback against the whitelist

Is OAuth need HTTPS?

In production Gitlab required HTTPS (for security)

In local development, Gitlab allow to use http://localhost:<port>