Giới thiệu về OAuth 2.0 và OpenID Connect
OAuth 2.0 là một khung ủy quyền cho phép các ứng dụng của bên thứ ba thay mặt người dùng truy cập các tài nguyên hạn chế mà không làm lộ thông tin đăng nhập của họ. Mặt khác, OpenID Connect là một lớp nhận dạng được xây dựng trên OAuth 2.0, cung cấp khả năng xác thực. Bằng cách kết hợp cả hai, chúng tôi có thể xác thực người dùng một cách an toàn và cho phép truy cập vào các tài nguyên được bảo vệ.
Trong bài viết này, tôi sẽ hướng dẫn bạn quy trình triển khai OAuth 2.0 và OpenID Connect trong ứng dụng Node.js Express. Chúng tôi sẽ sử dụng Passport.js, một phần mềm trung gian phổ biến để xác thực, để hợp lý hóa quy trình.
điều kiện tiên quyết
Trước khi đi sâu vào, hãy đảm bảo rằng bạn đã cài đặt các phần mềm sau trên máy của mình:
- Node.js (v14 trở lên)
- npm (v6 trở lên)
- Trình chỉnh sửa mã, chẳng hạn như Visual Studio Code
Thiết lập ứng dụng Node.js Express
Đầu tiên, tạo một thư mục mới cho dự án của bạn và điều hướng đến nó trong thiết bị đầu cuối của bạn. Sau đó, khởi tạo dự án bằng npm:
1 2 3 4 | <span class="token function">mkdir</span> oauth-openid-nodejs <span class="token builtin class-name">cd</span> oauth-openid-nodejs <span class="token function">npm</span> init -y |
Cài đặt phụ thuộc
Bây giờ, hãy cài đặt các gói cần thiết:
1 2 | <span class="token function">npm</span> <span class="token function">install</span> express passport passport-openidconnect dotenv |
Tạo máy chủ tốc hành
Tạo tệp index.js
trong thư mục gốc của dự án và thêm đoạn mã sau để thiết lập máy chủ Express cơ bản:
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'express'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> app <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> <span class="token string">'/'</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> res</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> res <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token string">'Welcome to the OAuth 2.0 and OpenID Connect demo!'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token constant">PORT</span> <span class="token operator">=</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">PORT</span> <span class="token operator">||</span> <span class="token number">3000</span> <span class="token punctuation">;</span> app <span class="token punctuation">.</span> <span class="token function">listen</span> <span class="token punctuation">(</span> <span class="token constant">PORT</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token template-string"><span class="token template-punctuation string">`</span> <span class="token string">Server running on http://localhost:</span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> <span class="token constant">PORT</span> <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token template-punctuation string">`</span></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Thiết lập Passport.js với OpenID Connect
Định cấu hình Passport.js
Trước tiên, hãy tạo tệp .env
trong thư mục gốc của dự án và lưu trữ ID ứng dụng khách, bí mật ứng dụng khách và URL gọi lại. Những thứ này sẽ được cung cấp bởi nhà cung cấp xác thực bên thứ ba mà bạn chọn hợp tác (ví dụ: Google, Facebook, v.v.):
1 2 3 4 | CLIENT_ID=your_client_id CLIENT_SECRET=your_client_secret CALLBACK_URL=http://localhost:3000/auth/callback |
Tiếp theo, tạo một tệp có tên passport-setup.js
trong thư mục gốc của dự án của bạn và thêm mã sau để định cấu hình Passport.js với chiến lược OpenID Connect:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | <span class="token keyword">const</span> passport <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'passport'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> OidcStrategy <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'passport-openidconnect'</span> <span class="token punctuation">)</span> <span class="token punctuation">.</span> Strategy <span class="token punctuation">;</span> <span class="token keyword">const</span> dotenv <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'dotenv'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> dotenv <span class="token punctuation">.</span> <span class="token function">config</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> passport <span class="token punctuation">.</span> <span class="token function">use</span> <span class="token punctuation">(</span> <span class="token keyword">new</span> <span class="token class-name">OidcStrategy</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> issuer <span class="token operator">:</span> <span class="token string">'https://your-auth-provider.com'</span> <span class="token punctuation">,</span> clientID <span class="token operator">:</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">CLIENT_ID</span> <span class="token punctuation">,</span> clientSecret <span class="token operator">:</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">CLIENT_SECRET</span> <span class="token punctuation">,</span> callbackURL <span class="token operator">:</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">CALLBACK_URL</span> <span class="token punctuation">,</span> scope <span class="token operator">:</span> <span class="token string">'openid profile email'</span> <span class="token punctuation">}</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token parameter">accessToken <span class="token punctuation">,</span> refreshToken <span class="token punctuation">,</span> profile <span class="token punctuation">,</span> done</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token function">done</span> <span class="token punctuation">(</span> <span class="token keyword">null</span> <span class="token punctuation">,</span> profile <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> passport <span class="token punctuation">.</span> <span class="token function">serializeUser</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token parameter">user <span class="token punctuation">,</span> done</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">done</span> <span class="token punctuation">(</span> <span class="token keyword">null</span> <span class="token punctuation">,</span> user <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> passport <span class="token punctuation">.</span> <span class="token function">deserializeUser</span> <span class="token punctuation">(</span> <span class="token punctuation">(</span> <span class="token parameter">user <span class="token punctuation">,</span> done</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token function">done</span> <span class="token punctuation">(</span> <span class="token keyword">null</span> <span class="token punctuation">,</span> user <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> module <span class="token punctuation">.</span> exports <span class="token operator">=</span> passport <span class="token punctuation">;</span> |
Thay thế https://your-auth-provider.com
bằng URL của nhà phát hành thích hợp cho nhà cung cấp xác thực bạn đã chọn.
Cập nhật máy chủ Express
Bây giờ, hãy cập nhật tệp index.js
của bạn để bao gồm Passport.js và cấu hình OpenID Connect:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <span class="token keyword">const</span> express <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'express'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> passport <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'./passport-setup'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> session <span class="token operator">=</span> <span class="token function">require</span> <span class="token punctuation">(</span> <span class="token string">'express-session'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> app <span class="token operator">=</span> <span class="token function">express</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Configure Express to use session middleware</span> app <span class="token punctuation">.</span> <span class="token function">use</span> <span class="token punctuation">(</span> <span class="token function">session</span> <span class="token punctuation">(</span> <span class="token punctuation">{</span> secret <span class="token operator">:</span> <span class="token string">'your-session-secret'</span> <span class="token punctuation">,</span> resave <span class="token operator">:</span> <span class="token boolean">false</span> <span class="token punctuation">,</span> saveUninitialized <span class="token operator">:</span> <span class="token boolean">true</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Initialize Passport.js and session support</span> app <span class="token punctuation">.</span> <span class="token function">use</span> <span class="token punctuation">(</span> passport <span class="token punctuation">.</span> <span class="token function">initialize</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> app <span class="token punctuation">.</span> <span class="token function">use</span> <span class="token punctuation">(</span> passport <span class="token punctuation">.</span> <span class="token function">session</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> app <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> <span class="token string">'/'</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> res</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> res <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token string">'Welcome to the OAuth 2.0 and OpenID Connect demo!'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Add route for OAuth 2.0 authentication</span> app <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> <span class="token string">'/auth'</span> <span class="token punctuation">,</span> passport <span class="token punctuation">.</span> <span class="token function">authenticate</span> <span class="token punctuation">(</span> <span class="token string">'openidconnect'</span> <span class="token punctuation">)</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Add route for OAuth 2.0 callback</span> app <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> <span class="token string">'/auth/callback'</span> <span class="token punctuation">,</span> passport <span class="token punctuation">.</span> <span class="token function">authenticate</span> <span class="token punctuation">(</span> <span class="token string">'openidconnect'</span> <span class="token punctuation">,</span> <span class="token punctuation">{</span> failureRedirect <span class="token operator">:</span> <span class="token string">'/login'</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> res</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> res <span class="token punctuation">.</span> <span class="token function">redirect</span> <span class="token punctuation">(</span> <span class="token string">'/profile'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Add route for user profile</span> app <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> <span class="token string">'/profile'</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> res</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span> <span class="token operator">!</span> req <span class="token punctuation">.</span> user <span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> res <span class="token punctuation">.</span> <span class="token function">redirect</span> <span class="token punctuation">(</span> <span class="token string">'/login'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> res <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token template-string"><span class="token template-punctuation string">`</span> <span class="token string">Hello, </span><span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> req <span class="token punctuation">.</span> user <span class="token punctuation">.</span> displayName <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token string">!</span> <span class="token template-punctuation string">`</span></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token comment">// Add route for login page</span> app <span class="token punctuation">.</span> <span class="token function">get</span> <span class="token punctuation">(</span> <span class="token string">'/login'</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token parameter">req <span class="token punctuation">,</span> res</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> res <span class="token punctuation">.</span> <span class="token function">send</span> <span class="token punctuation">(</span> <span class="token string">'<a href="/auth">Log in with your Identity Providera>'</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token keyword">const</span> <span class="token constant">PORT</span> <span class="token operator">=</span> process <span class="token punctuation">.</span> env <span class="token punctuation">.</span> <span class="token constant">PORT</span> <span class="token operator">||</span> <span class="token number">3000</span> <span class="token punctuation">;</span> app <span class="token punctuation">.</span> <span class="token function">listen</span> <span class="token punctuation">(</span> <span class="token constant">PORT</span> <span class="token punctuation">,</span> <span class="token punctuation">(</span> <span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> console <span class="token punctuation">.</span> <span class="token function">log</span> <span class="token punctuation">(</span> <span class="token template-string"><span class="token template-punctuation string">`</span> <span class="token string">Server running on http://localhost:</span> <span class="token interpolation"><span class="token interpolation-punctuation punctuation">${</span> <span class="token constant">PORT</span> <span class="token interpolation-punctuation punctuation">}</span></span> <span class="token template-punctuation string">`</span></span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">)</span> <span class="token punctuation">;</span> |
Kiểm tra ứng dụng
Bây giờ, bạn đã sẵn sàng thử nghiệm ứng dụng Node.js Express của mình với OAuth 2.0 và OpenID Connect. Khởi động máy chủ bằng cách chạy:
1 2 | node index.js |
Truy cập http://localhost:3000/login
trong trình duyệt của bạn và nhấp vào liên kết “Đăng nhập bằng nhà cung cấp danh tính của bạn”. Bạn sẽ được chuyển hướng đến trang đăng nhập của nhà cung cấp dịch vụ xác thực của mình. Sau khi đăng nhập, bạn sẽ được chuyển hướng trở lại tuyến đường /profile
, nơi bạn sẽ thấy lời chào được cá nhân hóa cùng với tên hiển thị của mình.
Phần kết luận
Trong bài viết này, chúng tôi đã trình bày cách triển khai OAuth 2.0 và OpenID Connect trong ứng dụng Node.js Express bằng Passport.js. Phương pháp xác thực an toàn này cho phép người dùng tự xác thực mà không để lộ thông tin đăng nhập của họ cho ứng dụng của bạn, dẫn đến quy trình xác thực an toàn hơn và đáng tin cậy hơn.
Hãy nhớ thay thế các chi tiết cấu hình mẫu (URL của nhà phát hành, ID ứng dụng khách, bí mật ứng dụng khách, v.v.) bằng thông tin của nhà cung cấp xác thực thực tế của bạn khi triển khai ứng dụng của bạn vào sản xuất.
Và cuối cùng
Như mọi khi, tôi hy vọng bạn thích bài viết này và có một cái gì đó mới. Xin cảm ơn và hẹn gặp lại các bạn trong những bài viết tiếp theo!
Nếu các bạn thích bài viết này thì hãy cho mình 1 like và subscribe để ủng hộ mình nhé. Cảm ơn.