OAuth2 là một giao thức ủy quyền cho phép các ứng dụng có quyền truy cập hạn chế vào tài khoản người dùng trên dịch vụ HTTP. Nó cung cấp một cách an toàn để người dùng cấp quyền truy cập vào tài nguyên của họ cho các ứng dụng bên thứ ba mà không cần chia sẻ thông tin đăng nhập.
Một trong những bài toán được ứng dụng là Single Sign On cho user.
Một người dùng có tài khoản trên Google bao gồm các thông tin như email, số điện thoại, profile,…
Người dùng đó muốn sử dụng những dữ liệu này để tạo tài khoản và đăng nhập được vào ứng dụng Tiktok. Do đó, Tiktok cần truy cập được vào dữ liệu của người dùng trên Google.
Hướng giải quyết đầu tiên là người dùng nhập thông tin đăng nhập của mình trên Google vào ứng dụng Tiktok. Sau đó, Tiktok gửi thông tin này cho Google để xác thực và lấy dữ liệu người dùng.
Nhược điểm của cách làm này là Tiktok có được thông tin đăng nhập của người dùng trên Google. Do đó, tính rủi rõ rất cao đối với thông tin và tài khoản của người dùng Google.
Cải tiến: sử dụng OAuth2 protocol để cấp phép truy cập dữ liệu của người dùng từ bên thứ 3.
OAuth2 là một giao thức cho phép RO cấp phát quyền truy cập tài nguyên cho CA từ RS.
Có 3 điều chúng ta cần nhớ:
Khi CA muốn RO cấp phát quyền truy cập, CA khởi tạo luồng OAuth2
Để hiểu rõ luồng hoạt động của OAuth2, chúng ta sẽ phân chia thành 3 tiến trình con:
Khi CA muốn có quyền truy cập dữ liệu của RO, CA sẽ điều hướng RO tới AS để RO yêu cầu AS cấp phát. Mọi thông tin đăng nhập của RO sẽ được an toàn khi thực hiện ở AS. Điều này hoàn toàn giúp cho RO giữ được thông tin cá nhân mà CA không được phép truy cập
Sau khi AS xác thực được RO, AS cần xác thực CA để đảm bảo CA đã được đăng kí và được phép hoạt động trên hệ thống của AS. Sau đó, AS cấp cho CA một code exchange. Code này có mục đích là đảm bảo rằng RO đã đồng ý cho phép CA được truy cập vào dữ liệu của RO.
Sau khi được xác thực bởi AS và được RO đồng ý cấp quyền, CA có thể dùng code exchange để yêu cầu AS cấp access token
AS xác thực RO và CA
CA redirect RO tới AS với những thông tin (qua query string) bao gồm những thông tin sau:
CA nhận code exchange
Code exchange được AS gửi tới CA thông qua query string của response redirect. CA dùng code exchange này để yêu cầu AS cấp access token.
Code exchange đại diện cho 2 điều:
AS tạo access token
Khi CA yêu cầu access token, AS cần xác thực code exchange. Nếu hợp lệ sẽ tạo access token.
Access token đảm bảo:
Có nhiều phương thức để tạo access token, một trong những phương thức là sử dụng JWT và mã hóa bất đối xứng (Asymmetric)
Tóm lược thì JWT là một chữ kí số, đoạn mã JWT bao gồm 3 phần:
Cấu trúc của JWT token như sau: [header].[payload].[signature]
Mã hóa đối xứng mà trong đó người gửi và người nhận cùng chia sẻ một khóa bí mật (private key)
Người gửi dùng private key để mã hóa và người nhận dùng nó để giải mã
Một số đặc điểm của mã hóa đối xứng:
Mã hóa đối xứng thường được sử dụng để mã hóa dữ liệu trong các giao thức SSL/TLS, lưu trữ database,…
Một số loại mã hóa đối xứng như AES, DES, 3DES, RC4
Mã hóa bất đối xứng gồm khóa công khai dùng để mã hóa (public key) và khóa bí mật dùng để giải mã (private key). Public key có thể được công khai với tất cả các bên trong khi private key không được chia sẻ. Chúng ta cũng có thể đảo ngược lại quá trình trên.
Một số đặc điểm của mã hóa bất đối xứng:
Mã hóa bất đối xứng được dùng để mã hóa thông tin trao đổi khóa của mã hóa đối xứng: quá trình handshake của SSL, chữ kí số, …
Trong trường hợp tạo tạo access token, AS sử dụng mã hóa bất đối xứng để tạo một chữ kí số lên access token ở phần signature, nhằm đảm bảo access token đó được tạo ra bởi 1 nguồn đáng tin cậy, có thể xác thực ở bất kì client nào.
Đầu tiên, chúng ta tạo ra cặp private key/public key
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
)
public_key = private_key.public_key()
# Serialize private key
private_key_pem = private_key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.TraditionalOpenSSL,
encryption_algorithm=serialization.NoEncryption(),
).decode("utf-8")
# Serialize public key
public_key_pem = public_key.public_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
).decode("utf-8")
import jwt
claim = {
"sub": ...,
"email": ...,
"scopes": ...,
"exp": datetime.utcnow() + timedelta(minutes=15),
"iss": ...,
...
}
access_token = jwt.encode(
claim,
key=private_key_pem,
algorithm="RS256"
)
Để xác thực được access token, chúng ta cần phải có public key tương ứng với access token. Public key này được lưu trữ trên server và đây là 1 best practice để lấy được public key
Bản implementation đơn giản của OAuth2 bằng Python và Flask: