Authentication — SSO, LDAP, WebAuthn, Passkeys
Các phương thức xác thực hiện đại: Single Sign-On, LDAP, WebAuthn, Passkeys — so sánh và triển khai.
Xác thực (authentication) đã phát triển xa hơn username + password. Bài này khám phá các phương thức hiện đại.
Password — Tại Sao Không Đủ?#
- 81% data breach dùng password yếu hoặc bị đánh cắp
- User dùng lại password giữa các site
- Mỗi site yêu cầu password khác → user quên
SSO — Single Sign-On#
Đăng nhập 1 lần, dùng được nhiều app:
SAML (Security Assertion Markup Language)#
User → App (Service Provider) → Redirect → IdP (Identity Provider)
↓
Login
↓
SAML Response → App → Login thành côngplaintext<!-- SAML Response (XML) -->
<saml:Assertion>
<saml:Subject>
<saml:NameID>user@company.com</saml:NameID>
</saml:Subject>
<saml:Attribute Name="email">user@company.com</saml:Attribute>
<saml:Attribute Name="role">admin</saml:Attribute>
</saml:Assertion>xmlDùng trong doanh nghiệp — Okta, Azure AD, Google Workspace.
OAuth2 + OIDC#
OAuth2 cho authorization, OIDC (OpenID Connect) thêm authentication layer:
User → App → Redirect → Authorization Server → Login → ID Token (JWT) → Appplaintext// Xác thực xong — nhận ID Token
const idToken = jwt.verify(token, jwksUri);
// {
// "sub": "google-oauth2|123456",
// "email": "alice@example.com",
// "name": "Alice",
// "iss": "https://accounts.google.com"
// }typescriptLDAP — Centralized Directory#
LDAP (Lightweight Directory Access Protocol) lưu user, group, permission tập trung:
dc=example,dc=com
├── ou=people
│ ├── uid=alice
│ │ ├── cn: Alice
│ │ ├── mail: alice@example.com
│ │ └── memberOf: cn=engineers,ou=groups
│ └── uid=bob
└── ou=groups
├── cn=engineers
└── cn=adminstextimport ldap from 'ldapjs';
const client = ldap.createClient({ url: 'ldap://localhost:389' });
// Bind (authenticate)
client.bind('cn=admin,dc=example,dc=com', 'password', (err) => {
if (err) return console.error('Bind failed');
});
// Search
const opts = {
filter: '(&(objectClass=person)(uid=alice))',
scope: 'sub',
attributes: ['cn', 'mail', 'memberOf'],
};
client.search('ou=people,dc=example,dc=com', opts, (err, res) => {
res.on('searchEntry', (entry) => {
console.log(entry.object);
});
});typescriptWebAuthn — Passwordless Authentication#
WebAuthn dùng public-key cryptography — không password, không OTP.
Flow#
1. Register: Browser → Tạo keypair → Gửi public key → Server lưu
2. Login: Server gửi challenge → Browser sign bằng private key → Server verifytextRegister#
// Client
const credential = await navigator.credentials.create({
publicKey: {
challenge: new Uint8Array(challenge),
rp: { name: 'My App', id: 'example.com' },
user: {
id: new Uint8Array(userId),
name: 'alice@example.com',
displayName: 'Alice',
},
pubKeyCredParams: [{ alg: -7, type: 'public-key' }], // ES256
},
});
// Gửi credential lên server
await fetch('/auth/webauthn/register', {
method: 'POST',
body: JSON.stringify({
id: credential.id,
publicKey: arrayBufferToBase64(credential.response.getPublicKey()),
}),
});typescriptLogin#
// Client — nhận challenge từ server
const assertion = await navigator.credentials.get({
publicKey: {
challenge: new Uint8Array(challenge),
allowCredentials: credentials.map(c => ({
id: base64ToArrayBuffer(c.id),
type: 'public-key',
})),
},
});
await fetch('/auth/webauthn/login', {
method: 'POST',
body: JSON.stringify({
id: assertion.id,
signature: arrayBufferToBase64(assertion.response.signature),
authenticatorData: arrayBufferToBase64(assertion.response.authenticatorData),
}),
});typescriptPasskeys — WebAuthn Cho Người Dùng#
Passkeys là WebAuthn + credential sync (iCloud Keychain, Google Password Manager):
User → "Sign in with Face ID / Touch ID"
↓
Device tạo keypair
Public key → Server
Private key → Sync qua iCloud/Google
↓
Đăng nhập ở thiết bị khác — dùng passkey từ cloudplaintextPasskeys đang được Apple, Google, Microsoft đẩy mạnh — thay thế password trong tương lai.
So Sánh#
| Phương thức | Bảo mật | UX | Triển khai |
|---|---|---|---|
| Password | Thấp | Kém | Dễ |
| OAuth2/OIDC | Cao | Tốt (SSO) | Trung bình |
| SAML | Cao | Tốt (SSO) | Phức tạp |
| LDAP | Trung bình | Trung bình | Trung bình |
| WebAuthn | Rất cao | Xuất sắc | Phức tạp |
| Passkeys | Rất cao | Xuất sắc | Trung bình |
Multi-Factor Authentication (MFA)#
Kết hợp nhiều yếu tố:
- Something you know — password
- Something you have — phone, hardware key (YubiKey)
- Something you are — fingerprint, face
// TOTP — Time-based One-Time Password
import { authenticator } from 'otplib';
// Tạo secret
const secret = authenticator.generateSecret();
const otpauth = authenticator.keyuri(user.email, 'MyApp', secret);
// User scan QR code với Google Authenticator
// Verify
const isValid = authenticator.check(token, secret);typescriptImplementation — Auth với NextAuth.js#
// auth.ts
import NextAuth from 'next-auth';
import Google from 'next-auth/providers/google';
import Credentials from 'next-auth/providers/credentials';
export const { handlers, signIn, signOut, auth } = NextAuth({
providers: [
Google,
Credentials({
credentials: { email: {}, password: {} },
async authorize(credentials) {
const user = await db.user.findUnique({
where: { email: credentials.email as string },
});
if (!user || !bcrypt.compareSync(credentials.password as string, user.password)) {
return null;
}
return { id: user.id.toString(), name: user.name, email: user.email };
},
}),
],
callbacks: {
async session({ session, token }) {
session.user.id = token.sub!;
return session;
},
},
});typescriptKết Luận#
Password đang chết dần. SSO + MFA là chuẩn hiện tại. WebAuthn/Passkeys là tương lai.
Cho ứng dụng mới: bắt đầu với OAuth2/OIDC (Google, GitHub login) + optional password. Thêm WebAuthn cho nâng cao. Cho enterprise: SAML + LDAP + MFA.