รู้จักกับ Auth0

/ 11 min read

Share on social media

auth0-basic สามารถดู video ของหัวข้อนี้ก่อนได้ ดู video

Auth0 คืออะไร

Auth0 คือระบบ cloud-based platform ที่ออกแบบมาเพื่อจัดการการยืนยันตัวตน (user authentication) และการอนุญาตสิทธิ์ของผู้ใช้ (authorization) สำหรับ web application, mobile app และ API โดยระบบจะดูแลทุกอย่างที่เกี่ยวกับ

  • ระบบ login and registration processes
  • User profile management
  • Password resets
  • Multi-factor authentication (MFA)
  • การกำหนดว่าผู้ใช้แต่ละคนสามารถทำอะไรได้บ้างภายใน application (rules and permission)

การใช้ Auth0 มีประโยชน์อย่างไรบ้าง

  • ประหยัดเวลาในการพัฒนา การสร้างระบบยืนยันตัวตนที่ปลอดภัยและแข็งแกร่งด้วยตัวเองนั้นซับซ้อนและใช้เวลามาก Auth0 มี solution สำเร็จรูปที่ปรับแต่งให้เราใช้งานได้ทันที
  • ความปลอดภัยที่ดียิ่งขึ้น Auth0 ให้ความสำคัญกับความปลอดภัยด้วย feature ต่างๆที่มี เช่น การเข้ารหัสผ่าน (password hasing), brute force protection, anomaly detection และปฏิบัติตามมาตรฐานความปลอดภัยของ authentication security อยู่แล้ว ดังนั้นเป็นจุดที่ auth0 ช่วยทำให้เราไม่ต้องเสียเวลากับเรื่อง security ด้วยเช่นกัน
  • Scalability Auth0 สามารถขยายได้เมื่อฐานผู้ใช้โตขึ้นเรื่องๆ รองรับทุกระดับตั้งแต่ผู้ใช้ไม่กี่คนไปจนถึงหลักล้านคนได้
  • Customization Auth0 มีวิธีหลากหลายในการปรับแต่งประสบการณ์การเข้าสู่ระบบให้เข้ากับ Brand ของเราเอง (ปรับแต่ง UI login ได้) และยังสามารถรวมกับผู้ให้บริการระบบยืนยันตัวตนอื่นๆ ได้ เช่น Google, Facebook, หรือ enterprise login systems เป็นต้น
  • Simplified User Management มี Dashboard ของ Auth0 ที่ช่วยอำนวยความสะดวกในการจัดการ user สำหรับจัดการข้อมูล users, roles และ permission ต่างๆได้

Key Features ของ Auth0

  • Universal Login ระบบ login แบบครอบคลุมที่สามารถรวมเข้ากับ application ใดก็ได้
  • Single Sign-On (SSO) ผู้ใช้ login เพียงครั้งเดียวก็สามารถเข้าถึงหลาย ๆ application ได้ (โดยไม่ต้องใส่ข้อมูลซ้ำๆกันได้)
  • Social Login login ด้วย social media มีเดียยอดนิยมต่าง ๆ ได้
  • Multi-factor Authentication เพิ่มชั้นความปลอดภัยสำหรับการใช้งานร่วมกับ sensitive action ได้
  • Passwordless Login login โดยไม่ต้องใช้รหัสผ่าน ด้วยตัวเลือกอื่นอย่าง link อีเมลหรือข้อมูล Biometric อย่าง ลายนิ้วมือ, การสแกนใบหน้า ได้
  • Rules and Hooks เพิ่ม code ส่วนขยายของ action ต่างๆใน authentication ได้ เพื่อเพิ่มความยืดหยุ่นในการจัดการการยืนยันตัวตน

ในบทความนี้เราจะมาลองเล่น auth0 ผ่าน Next.js กัน เนื่องจากเป็นตัวอย่างที่เห็นภาพได้ดีทั้งฝั่ง Frontend และ Backend ว่าสามารถใช้งานร่วมกันยังไงได้บ้าง

เพิ่มเติม Auth0 เอง ก็มี library auth0.js เป็น library playground ที่สามารถเล่นได้เลย ทุกคนสามารถไปลองเล่นผ่าน link นี้ได้ จะเป็นตัวอย่าง use case จำเป็นของ auth0 ที่รวมเอาไว้ใน playground แล้วเรียบร้อย

https://github.com/auth0/auth0.js/

ลองเชื่อม Auth0 กับ Next.js

เราจะมาทำ use case ตาม document https://auth0.com/docs/quickstart/webapp/nextjs ของ Auth0 กัน โดยจะเป็นการเชื่อม auth0 ด้วยท่ามาตรฐาน redirect login, register ด้วย email, password หรือ google login (ที่ set ไว้ตาม default ของ auth0)

ก่อนอื่นสิ่งที่ต้องทำคือ

  • เข้า https://auth0.com/ ทำการสร้าง account ของตัวเองออกมา
  • สร้าง project Application 1 ตัวขึ้นมาเป็น โดยการกด Create Application จากหน้า Getting Started auth0-01.webp
  • หลังจากนั้นให้เลือกเป็น Regular Web Application และตั้งชื่อ application ตามที่ตัวเองต้องการ auth0-02.webp
  • หลังจากที่กด Create จะเจอหน้าให้เลือก Technology ที่ใช้งาน ให้เลือกเป็น Next.js และหากทำทุกอย่างเรียบร้อย จะเจอหน้าจอ Quickstart ของ Auth0 + Next.js หน้าตาประมาณนี้ออกมา
auth0-03.webp

ถือเป็นอันเสร็จเรียบร้อยสำหรับการสร้าง application และจากหัวข้อ quickstart ทุกคนสามารถดำเนินการต่อตาม quickstart ได้เลย (สำหรับคนที่มี project next.js อยู่แล้ว)

ส่วนถ้าใครที่ยังพึ่งเริ่ม project เราจะ start project next.js ตามเอกสาร https://nextjs.org/docs/getting-started/installation ด้วย create-next-app กัน

npx create-next-app@latest

หลังจากนั้น ทำการลง library auth0 https://github.com/auth0/nextjs-auth0 เพิ่มเข้าไป

npm install @auth0/nextjs-auth0

ที่เหลือ เราจะไล่ทำตามหัวข้อใน Quickstart กัน (ให้อ่าน Quickstart ควบคู่กันด้วยนะครับ)

  • ทำการเก็บ domain, client Id, client secret เอาไว้ (ตามจุดที่เอกสาร Quickstart บอก)
  • ทำการเพิ่ม Callback URLs และ Logout URLs เพื่อให้ auth0 สามารถ redirect มาถูกได้ (จากในหน้า settings) ** ตัวอย่างนี้คือตัวอย่างที่ทำจาก localhost:3000 ที่เป็นตัวตั้งต้นของ next.js หากใครใช้ domain จริงตัวไหนก็เปลี่ยน domainให้ถูกต้องกันด้วยนะครับ
auth0-04.webp

หลังจากนั้น กลับมาที่ next.js project ให้เพิ่ม .env.local (env สำหรับใช้ใน local) ออกมาตามนี้ (field แต่ละ field ดูความหมายตาม Quickstart ได้ แต่หลักๆคือการ set url, base_url ที่เป็น web ของเรา และ client id, secret ของ application)

AUTH0_SECRET='use [openssl rand -hex 32] to generate a 32 bytes value'
AUTH0_BASE_URL='http://localhost:3000'
AUTH0_ISSUER_BASE_URL='https://dev-gjk0qz2tf82bu2si.us.auth0.com'
AUTH0_CLIENT_ID='qZ25Esc6JCayrrz5MI8LISnDT2zbXVk1'
AUTH0_CLIENT_SECRET='_ajukuZfLkUZc_W2MpVWYrB4NKpoUXytDTTlfODeNatTtvBk8VfLFQsD6NNvxKSw'

โดย AUTH0_SECRET ตามคำแนะนำในเอกสาร Quickstart จะแนะนำให้ใช้คำสั่งด้านล่างนี้สำหรับการ generate secret value สำหรับการเข้ารหัส session cookie ที่มีการ login

openssl rand -hex 32

เมื่อ set env เรียบร้อย ทำการเพิ่ม file app/api/auth/[auth0]/route.js สำหรับ Route Handler ของ Auth0 ขึ้นมา

app/api/auth/[auth0]/route.js
import { handleAuth } from "@auth0/nextjs-auth0";
export const GET = handleAuth();

จากคำสั่งนี้ handleAuth() จะทำการ handle get request ทั้งหมดที่เข้ามาผ่าน path /api/auth/* ด้วย function handleAuth() นั้นจะทำการสร้าง 4 paths นี้ออกมาอัตโนมัติคือ

  • /api/auth/login route สำหรับการเข้าสู่ login ด้วย auth0
  • /api/auth/logout route สำหรับการ logout
  • /api/auth/callback route สำหรับการรับ callback จาก auth0 เมื่อ login สำเร็จเรียบร้อย
  • /api/auth/me route สำหรับดึงข้อมูล user profile

อย่างที่เห็น ด้วย handleAuth() Auth0 ได้ทำการสร้าง path มาตรฐานของการทำ Authentication ให้ทันทีผ่าน SDK ของ auth0 ออกมาโดยไม่ต้องใส่คำสั่งอื่นเพิ่มเติมเข้าไปได้เลย และเพื่อให้สามารถดึงข้อมูล user ออกมาจากการ login ใน React application ได้ ให้ทำการเพิ่ม UserProvider ที่ app/layout.jsx (Root Layout) เพื่อทำการ wrap ข้อมูล user ให้สามารถเข้าถึงจากทุก Component ใน React ได้

app/layout.jsx
import { UserProvider } from "@auth0/nextjs-auth0/client";
export default function RootLayout({ children }) {
return (
<html lang="en">
<UserProvider>
<body>{children}</body>
</UserProvider>
</html>
);
}

เพียงเท่านี้ การติดตั้ง login ก็เป็นอันเสร็จเรียบร้อย ! เราจะลองมาเพิ่มอะไรกันเล็กน้อยเพื่อให้เห็นภาพมากขึ้น นี่คือ Structure ที่เกี่ยวข้องที่เราจะเพิ่มเติมเข้ามา

Terminal window
├── app
├── api
└── auth
└── [auth0]
└── route.js
├── layout.js
├── page.js
└── profile
└── page.js
├── middleware.js

เราทำ 2 หน้าคือ

  • หน้าแรก (app/page.js) สำหรับการเพิ่มทางเข้าสู่ login
  • หน้า profile (app/profile/page.js) เป็นตัวอย่าง สำหรับการดูข้อมูลจาก auth0 (เหมือนดู profile ตัวเอง)

โดย ที่หน้า app/page.js นั้นเราจะไม่เพิ่มอะไรเลยนอกจาก ปุ่มเข้าสู่หน้า Login แบบนี้เลย

app/page.js
<a href="/api/auth/login">Login</a>

โดย ที่หน้า profile app/profile/page.js นั้น ให้เพิ่มตัวอย่างการดึงข้อมูล user ผ่านคำสั่ง getSession เข้ามา (โดย getSession จริงๆมันก็ไปดึงข้อมูล profile ผ่าน /api/auth/me นั่นแหละ)

app/profile/page.js
import { getSession } from "@auth0/nextjs-auth0";
export default async function ProfileServer() {
const { user } = await getSession();
return (
user && (
<>
<div>
<img src={user.picture} alt={user.name} />
<h2>{user.name}</h2>
<p>{user.email}</p>
</div>
<a href="/api/auth/logout">Logout</a>
</>
)
);
}

และเมื่อเราลอง run และเล่นดูผ่าน localhost:3000 ก็จะได้ผลลัพธ์ทรงๆนี้ออกมา (สมมุติว่า login ผ่าน google)

auth0-05.gif

จะเห็นว่าเมื่อเปิดหน้า profile เราก็จะสามารถดึงข้อมูล user คนนั้นออกมาได้หน้าตาประมาณนี้ และหากต้องการเพิ่มการป้องกันว่าหน้า profile จะต้องเข้าเฉพาะคน login ได้เท่านั้น สามารถ เพิ่ม middleware กั้นสิทธิให้เฉพาะที่ login แล้วเข้าหน้า profile ได้เพิ่มเติมโดยใช้ตัวแปร getSession ตัวเดียวกันได้ ผ่านวิธีนี้

middleware.js
import { NextResponse } from "next/server";
import { withMiddlewareAuthRequired, getSession } from "@auth0/nextjs-auth0/edge";
export default withMiddlewareAuthRequired(async (req) => {
const res = NextResponse.next();
const user = await getSession(req, res);
if (!user) {
return NextResponse.redirect("/api/auth/login");
}
return res;
});
export const config = {
matcher: "/profile",
};

โดยสิ่งที่เราเพิ่มไปคือ

  • ทำการเพิ่มตัวเช็คเข้าไปว่าตอนเปิดหน้า profile มา หาก user ยังไม่สามารถดึงค่าจาก getSession ออกมาได้ ให้ทำการส่งกลับไปยังหน้า Login แทน

และเช่นเดียวกันกับการ login เองก็สามารถที่จะ redirect ตรงไปยังหน้า profile ได้ผ่านการ config เพิ่มใน handleLogin เข้ากับ handleAuth เพื่อทำการควบคุมตำแหน่งการ redirect ไปแบบนี้ได้

app/api/auth/[auth0]/route.js
import { handleAuth, handleLogin } from "@auth0/nextjs-auth0";
export const GET = handleAuth({
login: handleLogin({
returnTo: "/profile",
}),
});

เมื่อทำการลอง login ซ้ำอีกที รอบนี้ก็จะเจอว่าหลัง login เสร็จก็จะไปหน้า profile ทันที และเมื่อกด logout ออกมา ก็จะทำการเด้งกลับมายังหน้า login หน้าแรกได้เช่นกัน ก็จะเป็นการผูก flow login และ logout เข้ากันไว้ทั้งหมดได้เรียบร้อย

รู้จักกับ RBAC (Role-Based Access Control)

RBAC ใน Auth0 คือ

  • วิธีการจัดการสิทธิ์ของผู้ใช้ RBAC ช่วยให้คุณควบคุมว่าผู้ใช้แต่ละคนสามารถทำอะไรได้บ้าง หรือทำอะไรไม่ได้บ้าง ภายใน application โดยอ้างอิงจาก role ที่กำหนดให้
  • ทำให้การจัดการสิทธิ์ง่ายขึ้น แทนที่จะต้องกำหนดสิทธิ์ให้ผู้ใช้ทีละคน (ซึ่งจะยุ่งยากมากถ้ามีผู้ใช้จำนวนมาก) RBAC จะให้คุณรวมกลุ่มสิทธิ์ต่างๆ เข้าเป็น role แล้วจึงกำหนด role เหล่านั้นให้กับผู้ใช้แต่ละคนได้
  • โดยปกติ auth0 จะไม่ส่ง role ออกมา แต่สิ่งที่ส่งออกมาคือ Permission ของ role ที่เอาออกมาได้ เราสามารถนำ permission ที่ผูกกับ role ตัวนั้นมาใช้ต่อได้

เพื่อเป็นการใช้ feature นี้สิ่งที่เราจะต้องทำคือ ต้องสร้าง Custom API ขึ้นมาสำหรับการสร้าง Backend Service ที่สามารถป้องกันและควบคุมการ access ได้ รวมถึงสามารถควบคุม scopes (permission) ผ่าน Custom API ได้

Step ต่อไปที่เราจะทำคือ

  • เราจะทำการสร้าง Permission ของเราเองขึ้นมา
  • โดยจะทำการสร้างคู่กับ Role เอาไว้ โดยเดี๋ยวเราจะสร้างออกมาเป็น 2 Roles คือ Admin กับ User
  • และทำการใส่ Permission คู่กับ Admin เอาไว้ เพื่อทดสอบการแสดง Permission ออกมาหลังจากที่ login เรียบร้อย

เริ่มต้น ใน Auth0 นั้นมีหัวข้อชื่อ APIs อยู่ภายใต้ Applications โดยสามารถเข้ามาในหัวข้อนี้ และสามารถกด Create API เพื่อสร้าง API ขึ้นมาได้

auth0-06.webp

ให้ทำการตั้งชื่อ API และระบุ url identifier (จะใช้เป็น url จริงของ Backend service เราหรือเป็น localhost ก็ได้ field นี้เป็นเพียง unique field เพื่อใช้ระบุตำแหน่งของ Custom API ตัวนี้ตอนที่มีการใช้งาน)

auth0-07.webp

หลังจากที่สร้างเรียบร้อย ก็จะเข้ามาเจอหน้านี้ออกมาได้ ก็เป็นการสร้าง Custom API เป็นที่เรียบร้อย

auth0-08.webp

เราจะลองเริ่มต้นสร้าง permission กันก่อน ให้กดไปที่ tab permission และลองเพิ่ม permission ที่อยากกำหนดเพิ่มขึ้นมา อย่างเราจะลองสมมุติเพิ่ม permission ขึ้นมาชื่อ read:test และ read:mike ขึ้นมา (สามารถเพิ่มได้จากการใส่ตรงช่อง Permission และ Description และกดปุ่ม Add ได้เลย)

  • permission นี้คือ permission ที่เราจะทำการสร้าง custom ขึ้นมาเป็นเหมือนกำหนดสิทธิว่าเราจะอนุญาตให้ทำอะไรจากชื่อของ permission นี้บ้าง
auth0-09.webp

หลังจากเพิ่มเรียบร้อย ต่อมาเราจะนำ permission ไปใช้เพื่อกำหนดสิทธิให้กับ user โดยการกดไปยัง tab user และเข้าไปยัง user คนนั้นและเพิ่ม permission ตามที่เพิ่มเข้ามา

auth0-10.webp auth0-11.webp auth0-12.webp

หลังจากที่เพิ่ม permission เรียบร้อย เราจะลองมาดึงข้อมูล permission ผ่าน access token กัน ทีนี้เพื่อให้ permission สามารถส่งข้อมูลผ่าน access token มาได้ ต้องทำการเปิดใช้งาน feature RBAC และ เพิ่ม permission ให้ส่งออกมาผ่าน JWT ให้กลับมาที่ Setting ของ Custom API (ที่ set permission ก่อนหน้านี้) และทำการเปิด feature RBAC และ เพิ่ม permission ใน access token ตามภาพด้านล่างนี้

auth0-13.webp

กลับมาที่ code กันบ้าง เพื่อให้สามารถเรียกใช้งาน Custom API ที่สร้างมาได้ เราจะต้องมาเพิ่มที่ handleAuth โดยเป็นการระบุไปยัง audience ของ Custom API ตัวนั้น (ตามชื่อที่เราสร้างไว้ อย่างเคสตัวอย่างนี้เราสร้างชื่อ localhost:3000 ไว้ เราก็ระบุใส่ audience ตามนี้)

app/api/auth/[auth0]/route.js
import { handleAuth, handleLogin } from "@auth0/nextjs-auth0";
export const GET = handleAuth({
login: handleLogin({
authorizationParams: {
audience: "localhost:3000",
},
returnTo: "/profile",
}),
});

ทีนี้เราจะลองดูสิทธิมาผ่าน middleware กัน ว่าสิทธิเพิ่มไปอย่างถูกต้องหรือไม่ โดยกลับมาที่ middleware.js ทีนี้เพื่อให้สามารถแกะ access token ที่เป็นรูปแบบ jwt ออกมาได้ ให้ทำการลง library jwt-decode เพิ่มเข้ามา

Terminal window
npm install jwt-decode

และที่ middleware.js ให้ลองเพิ่มส่วนสำหรับการแกะข้อมูลผ่าน jwt-decode ออกมา

middleware.js
import { NextResponse } from "next/server";
import { withMiddlewareAuthRequired, getSession } from "@auth0/nextjs-auth0/edge";
import { jwtDecode } from "jwt-decode";
export default withMiddlewareAuthRequired(async (req) => {
const res = NextResponse.next();
const user = await getSession(req, res);
if (!user) {
return NextResponse.redirect("/api/auth/login");
}
console.log("permission", jwtDecode(user.accessToken).permissions);
return res;
});
export const config = {
matcher: "/profile",
};

เมื่อลอง login และเปิดหน้า profile อีกรอบ (เพื่อให้ผ่านการใช้งาน middleware) และลองมาดูที่ log console

auth0-14.webp

เราก็จะเจอ permission ที่ใส่ไปได้โดย permission นี้นั้นเราก็สามารถนำไปใช้ต่อรวมกับ application เราต่อได้นั่นเอง หรือเราลองทำข้อมูลจาก access token (user.accessToken) มาตรวจสอบผ่าน jwt.io ก็จะเจอข้อมูล permission อยู่ใน jwt เช่นกัน

auth0-15.webp

ทีนี้ หลายครั้งเราอาจจะเจอว่า เราไม่ต้องการให้ application นี้ใช้ permission ครบตามที่ access ไว้ สมมุติเราอาจจะมี 2 application อันหนึ่งเราอาจจะอยากใช้แค่ read:mike อีกอันเราอาจจะอยากใช้แค่ read:test เราสามารถกำหนดสิ่งนี้ได้ผ่านสิ่งที่เรียกว่า scope ของ auth โดยเมื่อเราลอง log ตัวแปร user.accessTokenScope ออกมา

auth0-16.webp

เราก็จะเจอว่ามันมี scope ที่ accessToken นี้อนุญาตให้ใช้ได้ โดยปกติการพัฒนา application นั้นนอกจากการตรวจสอบ permission แล้ว เราจะตรวจสอบ scope ด้วย เพื่อตรวจสอบด้วยว่า ได้ “ขออนุญาตจากเจ้าของ” มาแล้วหรือไม่ เพื่อเป็นการตรวจสอบให้แน่ใจว่า เจ้าตัวรับทราบการใช้ scope นี้แล้วหรือไม่

โดยสิ่งนี้สามารถเพิ่มได้ผ่าน handleAuth เช่นกัน โดยการระบุ scope ของสิ่งที่ต้องการขออนุญาตเพิ่มไป เช่น เคสนี้ สมมุตว่า application นี้จะขออนุญาตจาก scope ของ read:mike เพื่อใช้งานใน application ต่อไป

app/api/auth/[auth0]/route.js
import { handleAuth, handleLogin } from "@auth0/nextjs-auth0";
export const GET = handleAuth({
login: handleLogin({
authorizationParams: {
audience: "localhost:3000",
scope: "openid profile email read:mike", // เพิ่ม scope
},
returnTo: "/profile",
}),
});

เมื่อทำการ login ซ้ำอีกทีในหน้า consent เราก็เจอจะส่วนขออนุญาตของการใช้ scope นี้เพิ่มเติมขึ้นมา

auth0-17.webp

และเมื่อ login เสร็จและตรวจสอบค่าตรง accessTokenScope อีกทีก็จะเจอคำว่า read:mike โผล่มาตรง accessTokenScope ออกมาได้ และเมื่อเราใช้คู่กับ permission ก็จะสามารถตรวจสอบได้ว่า ได้ขออนุญาตจาก user มาเรียบร้อยแล้วหรือไม่จาก accessTokenScope ได้นั่นเอง

auth0-18.webp

Flows

ทีนี้นอกเหนือจาก การเพิ่ม permission แล้วใน auth0 เองยังสามารถเพิ่ม action บางอย่างระหว่างกลาง Authentication ออกมาได้ด้วยใน feature ที่ชื่อว่า Action Flows

auth0-19.webp
  • Flow ใน Auth0 เปรียบเสมือนขั้นตอนการทำงานที่ปรับแต่งได้ ซึ่งจะทำงานในจุดต่างๆ ที่กำหนดไว้ระหว่างการยืนยันตัวตนของผู้ใช้ Flow ช่วยให้เราสามารถเพิ่มการทำงานต่างๆ ขึ้นได้อัตโนมัติ ทั้งในขั้นตอน login, register และเหตุการณ์อื่นๆ ที่เกี่ยวข้องกับผู้ใช้
  • Flow ประกอบด้วยส่วนที่เรียกว่า ‘Actions’ ซึ่งเป็น code JavaScript ที่ทำหน้าที่เฉพาะอย่าง โดยภายใน Flow นี้ Auth0 มี Actions สำเร็จรูปให้ใช้งาน และเรายังสามารถสร้าง Actions ของเราเองได้

โดยประเภทของ Flow (ตามภาพด้านบน) ที่สามารถแทรกได้ก็จะมีตั้งแต่

  • Login Flow ทำงานก่อนผู้ใช้ login เข้าสู่ระบบเสร็จ
  • Machine to Machine ทำงานจังหวะก่อน token issued เสร็จ
  • Pre / Post User Registration Flow ทำงานก่อนหรือหลังจากผู้ใช้ใหม่สมัครลงทะเบียนเสร็จ
  • Post Change Password Flow ทำงานหลังจากผู้ใช้เปลี่ยนรหัสผ่านเสร็จ
  • Send Phone Message ทำงานจังหวะก่อนส่ง message SMS ไปหาผู้ใช้เสร็จ
  • Password Reset / Post Challenge เพิ่มการทำงานจังหวะหลังจากที่เริ่มส่ง reset password

โดยโจทย์ที่เราจะทำกันเพิ่มเติมก็คือ เราจะลองเล่น feature ที่ชื่อ Role ใน Auth0 กัน

  • Role ใน Auth0 คือ Group ของสิทธิ์ที่มีชื่อเฉพาะ ซึ่งจะกำหนดว่าผู้ใช้สามารถทำอะไรได้บ้างภายใน application ตัวอย่างเช่น เราอาจจะเพิ่มบทบาทอย่าง “Admin” (ผู้ดูแลระบบ), “Editor”(ผู้แก้ไข), หรือ “Customer” (ลูกค้า)
  • แทนที่เราจะต้องจัดการสิทธิ์ (permission) สำหรับผู้ใช้แต่ละคน (แบบที่เราต้องเพิ่มทีละอันใส่ทีละคนก่อนหน้านี้) เราสามารถสร้าง Roles เหล่านี้แล้วกำหนด Role ให้กับผู้ใช้แทนได้ วิธีนี้จะทำให้การจัดการสิทธิ์ในระบบง่ายขึ้น โดยเฉพาะอย่างยิ่งเวลาที่จำนวนผู้ใช้งาน application เริ่มมีเยอะ จะทำให้การจัดการสิทธิ์สามารถจัดการได้ง่ายขึ้นและเข้าใจได้ง่ายขึ้นด้วยเช่นกัน

สิ่งที่เราจะทำคือ เราจะทำการรวมกลุ่ม Permission ของ read:test, read:mike ที่เราทำกันก่อนหน้านี้มาไว้ใน Role ใหม่ชื่อ Admin และเราจะทำการเพิ่ม Role Admin ให้กับ user ตัวเก่าของเราออกมาแทน

โดยการเข้าไปที่หน้า Role ของ Auth0 และกด Create Role ขึ้นมา

auth0-20.webp

ทำการสร้าง Role ใหม่ขึ้นมาชื่อ Admin และใส่ description เพิ่มเติมเข้าไป

auth0-21.webp

หลังจากนั้นที่ Role Admin ให้ไปที่ tab Permissions (อารมณ์จะเหมือนเพิ่มให้กับ user เมื่อกี้เลย) ให้กด Add Permission และทำการเพิ่ม Permission ของ Custom API ของเราเข้าไป

auth0-22.webp auth0-23.webp

เมื่อเพิ่มเรียบร้อยก็จะได้หน้าตาประมาณนี้ออกมา เท่ากับว่าตอนนี้ Role Admin มี permission ของ read:mike, read:test เป็นที่เรียบร้อย และหาก assign Role นี้ให้ใครไปก็จะได้รับ permission set นี้ไปเช่นเดียวกัน

auth0-24.webp

กลับมาที่หน้าของ user ทำการลบ permission (ใน tab Permissions) ตัวเก่าทิ้งให้หมด และไปยัง tab Roles

auth0-25.webp

ทำการเพิ่ม Role Admin เข้าไปกับ user

auth0-26.webp auth0-27.webp

เมื่อเพิ่มเรียบร้อย และลองกลับมาดูหน้า Pmermission ก็จะเจอว่าได้รับ Permission อันเดียวกันออกมาได้ (และระบุว่ามาจาก Role ของ Admin) เป็นที่เรียบร้อย

auth0-28.webp

ข้อดีอีกอย่างของวิธีนี้คือ

  • หากเราต้องการนำสิทธิ์ Admin ออกจากใครเพียงแค่นำ Role Admin ออก permission เหล่านี้ก็จะออกตามไปด้วยเช่นกัน
  • ส่งผลทำให้จัดการกับสิทธิ์ได้ง่ายขึ้นกว่าเดิมมากเช่นกัน

ทีนี้ ปัญหาของเรื่องนี้มีอยู่ว่า “แล้วถ้าเราอยากได้รับข้อมูล Role มาในระบบเราละ ?” แน่นอน Role เป็นสิ่งที่ระบบไม่ได้ทำการส่งมาผ่านข้อมูล user เอาไว้ ดังนั้น เราจึงต้องใช้ feature Flow นี่แหละ มาช่วยทำการเพิ่ม Role เข้ากับข้อมูล user ผ่าน feature CustomClaim ออกมา

  • Custom Claims ช่วยให้เราสามารถเพิ่มข้อมูลพิเศษลงไปใน token (ทั้ง ID Token และ Access Token) ที่ Auth0 สร้างให้กับผู้ใช้ของเราได้ โดบข้อมูลนี้สามารถใช้เพื่อปรับปรุงวิธีการทำงานของ application และเพิ่มความเฉพาะตัวให้กับประสบการณ์ของผู้ใช้แต่ละคนออกมาได้
  • Claim พิเศษเหล่านี้แตกต่างจากข้อมูลมาตรฐาน (เช่น name, email) ที่เป็นส่วนหนึ่งของมาตรฐาน OpenID Connect (OIDC) โดย Custom Claims นั้นจะสร้างขึ้นตามข้อมูลที่เราอยากเพิ่มเข้าไปได้

โดยสิ่งที่เราจะทำคือ

  • ทำการเพิ่ม Role เข้า idToken และ access token เข้ามา
  • ที่ฝั่งของ application (Next.js) จะสามารถรู้ได้ว่า user นี้มี Role อะไรออกมา
  • โดยเราจะทำการแทรกสิ่งนี้ในขั้นตอนระหว่าง login ก่อนที่จะ redirect กลับมายังฝั่งของ user

ดังนั้น เราจะใช้ feature Login ใน Action Flows ในการเพิ่มสิ่งนี้เข้ามา โดยการกดที่ Login

auth0-29.webp

หลังจากนั้นก็จะเจอหน้า Flow ลักษณะประมาณนี้ออกมา ให้กดที่ tab Custom และกด + เพื่อเพิ่ม Action แบบ custom ขึ้นมา และตั้งชื่อ Action ให้เรียบร้อย

  • จริงๆ Action นั้นมี Marketplace ที่สามารถนำจาก Marketplace ที่คนทำมาแล้วมาใช้งานร่วมกันได้เช่นกัน
  • แต่ในเคสนี้เราจะลองเพิ่มแบบ custom code ขึ้นมา
auth0-31.webp auth0-32.webp

หลังจากนั้นเมื่อเจอหน้านี้เราก็จะเจอส่วนใส่ code ของ Role ขึ้นมา ให้นำ code นี้ไปวางในส่วนของการจัดการ PostLogin

exports.onExecutePostLogin = async (event, api) => {
const namespace = "http://localhost:3000/"; // Your custom namespace
if (event.authorization) {
// Retrieve user roles (modify how you fetch roles as needed)
const roles = event.authorization.roles;
// Add roles to ID token
api.idToken.setCustomClaim(`${namespace}roles`, roles);
api.accessToken.setCustomClaim(`${namespace}/roles`, roles);
}
};

เป้าหมายของ code นี้คือ

  • เราทำการกำหนด namespace ใส่ตัวแปรหนึ่งเอาไว้
  • ทำการตรวจสอบ event ที่ส่งเข้ามาว่า authorization เรียบร้อยแล้วหรือไม่
  • ถ้าเรียบร้อย = ให้ดึง roles จาก event.authorization และนำมาใส่ idToken และ access token ผ่านคำสั่ง api.idToken.setCustomClaim(namespaceroles,roles)และapi.accessToken.setCustomClaim({namespace}roles`, roles)` และ `api.accessToken.setCustomClaim(`{namespace}/roles, roles); โดยการนำค่า roles ส่งเข้าไปผ่าน token เพิ่มเติม

เมื่อเพิ่ม code เรียบร้อยแล้วให้กด Save Draft > Deploy

auth0-36.webp

หลังจากนั้นที่หน้า Login Flow ให้นำ Custom Action ที่สร้างเมื่อกี้ลากมาระหว่างกลางของ Start และ Complete หลังจากนั้นกด Apply เพียงเท่านี้ก็จะเป็นอันเสร็จสิ้นการเพิ่ม Flow เป็นที่เรียบร้อย

auth0-34.webp

กลับมาที่ฝั่งของ next.js ให้เราลอง logout และ login ซ้ำอีกทีและดูผลลัพธ์ผ่าน API me ที่อยู่ใน network

auth0-35.webp

รวมถึงกับฝั่ง middleware ก็จะสามารถดึงข้อมูล Role ออกมาใช้ได้เช่นกัน ก็จะเป็นตัวที่สามารถใช้ช่วยเช็คสิทธิเพิ่มเติมออกมาได้เช่นกัน

Custom Login ผ่าน UI ตัวเอง

ใน Auth0 เองสามารถปรับแต่ง UI สำหรับส่วนของหน้า Login ได้เช่นกัน โดย Auth0 มี universal login สำหรับแก้ code ผ่าน UI โดยตรงใน Auth0 ได้

  • Universal Login มีหน้า login สำเร็จรูปที่สร้างและดูแลโดย Auth0 เวลาผู้ใช้ต้องการ login เข้าสู่ application ระบบจะเปลี่ยนเส้นทางมายังหน้านี้เพื่อทำการ Login (เหมือนที่เราเห็นเวลา redirect login เข้ามา)
  • แทนที่จะต้องสร้างระบบ login เองทั้งหมด Universal Login จะจัดการเรื่องหลักๆ ของการยืนยันตัวตนให้ (เช่น ชื่อผู้ใช้/รหัสผ่าน, การล็อกอินด้วยโซเชียลมีเดีย, ระบบยืนยันตัวตนแบบหลายขั้นตอน (MFA), เป็นต้น) โดยเราสามารถปรับแต่ง UI และรูปแบบการทำงานบางอย่างของหน้า login เพื่อให้ตรงกับเอกลักษณ์ Brand เราเองได้
  • โดย Universal Login มีตัวเลือกระดับการปรับแต่งที่หลากหลาย ตั้งแต่การเปลี่ยนแปลงง่ายๆ ผ่านทาง Auth0 Dashboard ไปจนถึงการปรับแต่งโดยใช้ code (ที่เรากำลังจะทำกัน)

โดยสามารถทำได้ผ่าน Menu Branding > Universal Login เมื่อกดเข้ามาในหน้า Setting จะสามารถปรับแต่ง UI ต่างๆอย่างง่ายได้

ทีนี้เมื่อกดมาที่ tab: Login ตามภาพ เจอจว่าเราสามารถ Customize Login Page แบบ html code ได้เช่นเดียวกัน เมื่อเปิดใช้งาน ก็จะสามารถแก้ส่วน HTML ที่อยู่ด้านล่างได้

auth0-37.webp

และเมื่อลองเลือกเป็นตัวอย่าง Custom Login Form (จากตรง Default Templates) จะเจอว่า มันจะทำการแยกส่วน html form ของ login ออกมา โดยในส่วนนี้เราสามารถปรับแต่ง code ทั้ง html, css, javascript ให้เป็นแบบตัวอย่างได้

auth0-38.webp

และสามารถ preview ดูหน้าตาตัวอย่างออกมาได้เช่นกัน

auth0-39.webp

เมื่อเราทำการ save และลองไป login ดูอีกที ก็จะเจอว่าหน้า Login ก็จะเป็นไปตามที่เรา custom ออกมาได้ และสามารถใช้งาน login ได้เหมือนปกติเลย

auth0-40.webp

** ในหัวข้อนี้จะไม่ได้ลงลึกการปรับแต่ง Universal Login สามารถไปดูเพิ่มเติมได้ผ่านเอกสาร Universal Login ของ Auth0 กันนะครับ

https://auth0.com/docs/customize/universal-login-pages

Auth0 Management API

auth0-management

ภาพจาก: https://community.auth0.com/t/how-can-i-enable-users-to-change-their-email-address-from-a-spa-or-native-app/44064

รวมถึง นอกเหนือจากการ Authentication ที่ระดับ Application แล้ว ตัว Auth0 เองสามารถทำ Authentication แบบ Server to Server ได้ ผ่าน Auth0 Management API

  • Auth0 Management API เปรียบได้กับแผงควบคุมที่ให้เราจัดการทุกแง่มุมของบัญชีใน Auth0 ได้ โดยมีความสามารถที่มากกว่าสิ่งที่เราทำได้จากใน Auth0 Dashboard โดยตรง โดย Auth0 Management API เป็นช่องทางให้ application สามารถเชื่อมต่อกับ Auth0 โดยใช้ RestAPI ได้

โดยสิ่งที่ Auth0 Management API ทำได้นั้นจะมีตั้งแต่

  • จัดการผู้ใช้ create, read, update หรือ delete โปรไฟล์ของผู้ใช้ สามารถทำการเชื่อมต่อบัญชีผู้ใช้จากผู้ให้บริการรับรองตัวตนต่างๆ (identity provider) รวมถึงการจัดการ metadata ต่างๆได้
  • สามารถสร้าง Role ที่มีการจำกัดสิทธิ์ (permission) แบบละเอียดได้ และกำหนด Role ให้แก่ผู้ใช้ เพื่อควบคุมว่าผู้ใช้แต่ละคนสามารถหรือไม่สามารถทำอะไรได้บ้างภายใน application ได้
  • จัดการระบบการทำงานทั่วไปของ Auth0 เช่น สร้าง template email, ส่ง verify email, ตรวจสอบ logs เป็นต้น

ในเคสตัวอย่างนี้เราจะลองมาใช้ Auth0 Management API โดยการเพิ่ม user ผ่าน Rest API กัน โดยสิ่งแรกที่ต้องทำ คือต้องสร้าง application เป็นประเภท machine to machine (เพื่อให้สามารถสร้าง access token เป็น API Credential ได้)

auth0-41.webp

หลังจากนั้นมาที่ APIs ให้ทำการเปิดใช้งาน Auth0 Management API และทำการเปิด permission ที่ต้องการใช้งานใน Management API โดยในที่นี้เราจะเปิดแค่สิทธิ์สำหรับการสร้าง create:users เพื่อให้สามารถสร้าง user ผ่าน application credential ตัวนี้ได้

auth0-42.webp

หลังจากสร้างเรียบร้อยแล้วให้ทำการลองยิง Rest API ดู โดย เราจะทำการยิง API ทั้งหมด 2 เส้นคือ

  1. POST https://<auth-0 domain>/oauth/token = สำหรับการ login ด้วย client id, client secret เพื่อนำ access token มาใช้งาน
  2. POST https://<auth-0 domain>/api/v2/users = สำหรับการสร้าง user ผ่าน API

โดยรายละเอียด API ทั้งหมดสามารถอ่านผ่านเอกสารของ Auth0 Management API ได้ที่นี่ https://auth0.com/docs/api/management/v2

โดย step ที่เราจะทำการยิงคือขั้นแรกให้ทำการยิงเข้าเส้น Login ก่อนด้วยคำสั่งนี้ (หรือด้วย Postman ก็ได้)

Terminal window
curl --location 'https://<auth-0 domain>/oauth/token' \
--header 'Content-Type: application/json' \
--data '{
"grant_type": "client_credentials",
"audience": "https://<auth-0 domain>/api/v2/",
"client_id": "<client id>",
"client_secret": "<client secret>"
}'

หลังจากนั้น นำ access token ที่ได้จาก API ข้างบน มาใส่เป็น header Authorization สำหรับการสร้าง user (access token นั้นเป็นตัวแทน credential ของ machine to machine application ที่เราสร้างขึ้นมา)

Terminal window
curl --location 'https://<auth-0 domain>/api/v2/users' \
--header 'Authorization: Bearer <token จาก client credential>' \
--header 'Content-Type: application/json' \
--data-raw '{
"connection": "Username-Password-Authentication",
"email": "user@example.com",
"username": "username",
"password": "UserPassword1234",
"user_metadata": {},
"email_verified": false,
"verify_email": false,
"app_metadata": {}
}'

เมื่อลองนำมายิงต่อกัน ก็จะได้ผลลัพธ์เป็นตามวิดีโอด้านล่างนี้

auth0-44.gif

** สำหรับ **Auth0 Management API** นั้น สามารถดูรายละเอียดทั้งหมดได้ผ่านเอกสารของ Auth0 เลยเช่นกัน

สรุปทั้งหมด

อย่างที่เราเห็นมา Auth0 นั้นสามารถทำอะไรได้หลายอย่างมากตั้งแต่

  • การทำ Authentication ทั่วไปผ่าน Application ด้วย Auth0 SDK
  • การจัดการสิทธิ์ระหว่าง Permission และ Role
  • การเพิ่ม logic ระหว่างกลางด้วยการใช้งาน Action Flow
  • การจัดการ UI หน้า Login เพิ่มเติมได้ผ่าน Universal Login
  • การจัดการอื่นๆผ่านระบบหลังบ้านด้วย Auth0 Management API

ด้วยคุณสมบัติทั้งหมดนี้นั้น Auth0 ก็ถือได้ว่าเป็น Authentication service ที่ทำงานได้ครอบคลุมเคสของ Authentication ไว้แล้วเรียบร้อย หากใครสนใจอยากเพิ่ม Authentication เข้า application ของเราเอง ก็ลองพิจารณา Auth0 กันดูนะครับ 😁

Related Post

Share on social media