ข้ามไปยังเนื้อหาหลัก

เพิ่มการยืนยันตัวตนให้กับแอป Angular ของคุณ (Add authentication to your Angular application)

คู่มือนี้จะแสดงวิธีการผสานรวม Logto Angular SDK v2 เข้ากับแอปพลิเคชันของคุณ

เคล็ดลับ:
  • คู่มือนี้ใช้ SDK @logto/angular v2 ของ Logto โดยตรง ซึ่งรองรับ Angular 20 และมีระบบ dependency injection และ Signals
  • โปรเจกต์ตัวอย่างสามารถดูได้ที่ SDK repository ของเรา

ข้อกำหนดเบื้องต้น​

การติดตั้ง​

ติดตั้ง Logto SDK ผ่านตัวจัดการแพ็กเกจที่คุณชื่นชอบ:

npm i @logto/angular

การผสานรวม​

เริ่มต้น Logto provider​

ในโปรเจกต์ Angular ของคุณ ให้ลงทะเบียน provideLogto และเส้นทางของแอปพลิเคชันใน app.config.ts:

app/app.config.ts
import { type ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideLogto } from '@logto/angular';

import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
providers: [
provideLogto({
endpoint: '<your-logto-endpoint>',
appId: '<your-app-id>',
}),
provideRouter(routes),
// ...other providers
],
};

provideLogto จะกู้คืนสถานะการยืนยันตัวตนโดยอัตโนมัติหลังจากเรนเดอร์เบราว์เซอร์ครั้งแรก คุณไม่จำเป็นต้องเรียกเมธอดเริ่มต้นในคอมโพเนนต์ของคุณ

บันทึก:

เมื่อใช้ server-side rendering (SSR) สถานะการยืนยันตัวตนและโทเค็นจะมีเฉพาะในเบราว์เซอร์เท่านั้น ใช้ isLoading() เพื่อแสดงสถานะกำลังโหลดจนกว่าการเริ่มต้นจะเสร็จสิ้น หากคุณต้องการข้อมูลที่ผ่านการยืนยันตัวตนระหว่างการเรนเดอร์ฝั่งเซิร์ฟเวอร์ ให้ใช้ SDK สำหรับฝั่งเซิร์ฟเวอร์หรือ BFF

กำหนดค่า redirect URIs​

ก่อนที่เราจะลงลึกในรายละเอียด นี่คือภาพรวมประสบการณ์ของผู้ใช้ปลายทาง กระบวนการลงชื่อเข้าใช้สามารถสรุปได้ดังนี้:

  1. แอปของคุณเรียกใช้งานเมธอดลงชื่อเข้าใช้
  2. ผู้ใช้จะถูกเปลี่ยนเส้นทางไปยังหน้าลงชื่อเข้าใช้ของ Logto สำหรับแอปเนทีฟ ระบบจะเปิดเบราว์เซอร์ของระบบ
  3. ผู้ใช้ลงชื่อเข้าใช้และถูกเปลี่ยนเส้นทางกลับไปยังแอปของคุณ (ตามที่กำหนดไว้ใน redirect URI)

เกี่ยวกับการลงชื่อเข้าใช้แบบเปลี่ยนเส้นทาง (redirect-based sign-in)​

  1. กระบวนการยืนยันตัวตนนี้เป็นไปตามโปรโตคอล OpenID Connect (OIDC) และ Logto บังคับใช้มาตรการรักษาความปลอดภัยอย่างเข้มงวดเพื่อปกป้องการลงชื่อเข้าใช้ของผู้ใช้
  2. หากคุณมีหลายแอป คุณสามารถใช้ผู้ให้บริการข้อมูลระบุตัวตน (Logto) เดียวกันได้ เมื่อผู้ใช้ลงชื่อเข้าใช้แอปหนึ่งแล้ว Logto จะดำเนินการลงชื่อเข้าใช้โดยอัตโนมัติเมื่อผู้ใช้เข้าถึงแอปอื่น

หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับเหตุผลและประโยชน์ของการลงชื่อเข้าใช้แบบเปลี่ยนเส้นทาง โปรดดูที่ อธิบายประสบการณ์การลงชื่อเข้าใช้ของ Logto


บันทึก:

ในตัวอย่างโค้ดต่อไปนี้ เราถือว่าแอปของคุณกำลังทำงานอยู่ที่ http://localhost:3000/

กำหนดค่า Redirect URI​

ไปที่หน้ารายละเอียดแอปพลิเคชันใน Logto Console เพิ่ม redirect URI http://localhost:3000/callback

Redirect URI in Logto Console

เช่นเดียวกับการลงชื่อเข้าใช้ ผู้ใช้ควรถูกเปลี่ยนเส้นทางไปที่ Logto เพื่อออกจากเซสชันที่ใช้ร่วมกัน เมื่อเสร็จสิ้นแล้ว ควรเปลี่ยนเส้นทางผู้ใช้กลับไปยังเว็บไซต์ของคุณ ตัวอย่างเช่น เพิ่ม http://localhost:3000/ ในส่วน post sign-out redirect URI

จากนั้นคลิก "Save" เพื่อบันทึกการเปลี่ยนแปลง

จัดการ redirect​

สร้างคอมโพเนนต์ callback เพื่อดำเนินการลงชื่อเข้าใช้ให้สมบูรณ์หลังจาก Logto เปลี่ยนเส้นทางผู้ใช้กลับมายังแอปของคุณ ใช้ afterNextRender เพื่อให้การจัดการ callback ทำงานเฉพาะในเบราว์เซอร์:

app/callback.component.ts
import { afterNextRender, Component, inject } from '@angular/core';
import { LogtoService } from '@logto/angular';

@Component({
selector: 'app-callback',
standalone: true,
template: `
@if (logto.error(); as error) {
<p role="alert">{{ error.message }}</p>
} @else {
<p>กำลังดำเนินการลงชื่อเข้าใช้...</p>
}
`,
})
export class CallbackComponent {
readonly logto = inject(LogtoService);

constructor() {
afterNextRender(() => {
void (async () => {
const callbackUri = window.location.href;

if (!(await this.logto.isSignInRedirected(callbackUri))) {
window.location.replace(window.location.origin);
return;
}

await this.logto.handleSignInCallback(callbackUri);
})().catch(() => {
// SDK จะส่งข้อผิดพลาด callback ผ่าน logto.error() สำหรับใช้ใน template
});
});
}
}

isSignInRedirected() จะตรวจสอบว่า URL ตรงกับเซสชันการลงชื่อเข้าใช้ที่กำลังดำเนินการอยู่หรือไม่ หากมีผู้เข้าชมเส้นทาง callback โดยไม่มีเซสชันนี้ ตัวอย่างนี้จะนำผู้ใช้งานกลับไปยังหน้าแรกของแอปพลิเคชันแทนที่จะพยายามดำเนินการลงชื่อเข้าใช้ให้สมบูรณ์

ลงทะเบียนเส้นทาง callback ใน app.routes.ts โดยต้องตรงกับ path ของ redirect URI และต้องไม่ต้องการการยืนยันตัวตน ตัวอย่างเช่น ใช้ callback สำหรับ redirect URI ที่ลงท้ายด้วย /callback:

app/app.routes.ts
import { type Routes } from '@angular/router';

import { CallbackComponent } from './callback.component';

export const routes: Routes = [
{ path: 'callback', component: CallbackComponent },
// ...other routes
];

คอมโพเนนต์ root ต้องมี <router-outlet /> เพื่อเรนเดอร์เส้นทางนี้ ดังที่แสดงในขั้นตอนถัดไป

สร้างฟังก์ชันลงชื่อเข้าใช้และออกจากระบบ​

inject LogtoService เพื่อเริ่มต้นการลงชื่อเข้าใช้และออกจากระบบ ส่ง redirect URIs ที่ลงทะเบียนไว้ไปยังเมธอดเหล่านี้ postRedirectUri จะบอก SDK ว่าควรนำทางไปที่ใดหลังจากจัดการ callback การลงชื่อเข้าใช้สำเร็จ:

บันทึก:

ก่อนเรียก signIn() โปรดตรวจสอบให้แน่ใจว่าคุณได้กำหนดค่า Redirect URI ใน Admin Console อย่างถูกต้องแล้ว

app/app.component.ts
import { Component, inject } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { LogtoService } from '@logto/angular';

@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet],
templateUrl: './app.component.html',
})
export class AppComponent {
readonly logto = inject(LogtoService);

async signIn() {
await this.logto.signIn({
redirectUri: 'http://localhost:3000/callback',
postRedirectUri: window.location.origin,
});
}

async signOut() {
await this.logto.signOut('http://localhost:3000/');
}
}

อ่านค่า isLoading(), isAuthenticated(), และ error() Signals ได้โดยตรงใน template:

app/app.component.html
@if (logto.error(); as error) {
<p role="alert">{{ error.message }}</p>
} @if (logto.isLoading()) {
<p>กำลังโหลด...</p>
} @else if (logto.isAuthenticated()) {
<button type="button" (click)="signOut()">ออกจากระบบ</button>
} @else {
<button type="button" (click)="signIn()">ลงชื่อเข้าใช้</button>
}

<router-outlet />

ให้คง <router-outlet /> ไว้นอกเงื่อนไขการยืนยันตัวตน เพื่อให้ callback สามารถเรนเดอร์ได้ก่อนที่ผู้ใช้จะลงชื่อเข้าใช้

การเรียกใช้ .signOut() จะล้างข้อมูล Logto ทั้งหมดในหน่วยความจำและ localStorage หากมีอยู่

จุดตรวจสอบ: ทดสอบแอปพลิเคชันของคุณ​

ตอนนี้คุณสามารถทดสอบแอปพลิเคชันของคุณได้แล้ว:

  1. รันแอปพลิเคชันของคุณ คุณจะเห็นปุ่มลงชื่อเข้าใช้
  2. คลิกปุ่มลงชื่อเข้าใช้ SDK จะเริ่มกระบวนการลงชื่อเข้าใช้และเปลี่ยนเส้นทางคุณไปยังหน้าลงชื่อเข้าใช้ของ Logto
  3. หลังจากที่คุณลงชื่อเข้าใช้แล้ว คุณจะถูกเปลี่ยนเส้นทางกลับไปยังแอปพลิเคชันของคุณและเห็นปุ่มลงชื่อออก
  4. คลิกปุ่มลงชื่อออกเพื่อเคลียร์ที่เก็บโทเค็นและออกจากระบบ

รับข้อมูลผู้ใช้​

แสดงข้อมูลผู้ใช้​

เพื่อแสดงข้อมูลของผู้ใช้ ให้ใช้ getIdTokenClaims() เพื่ออ่าน การอ้างสิทธิ์ (claims) จาก โทเค็น ID (ID token) โดยไม่ต้องมีการร้องขอเครือข่ายเพิ่มเติม เพิ่ม effect ใน AppComponent ของคุณเพื่อโหลด การอ้างสิทธิ์ (claims) เมื่อ isAuthenticated() เป็น true รวมถึงเมื่อมีการกู้คืนเซสชันที่มีอยู่แล้ว นำเข้า JsonPipe เพื่อแสดงผลลัพธ์:

app/app.component.ts
import { JsonPipe } from '@angular/common';
import { Component, effect, inject, signal } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { LogtoService, type IdTokenClaims } from '@logto/angular';

@Component({
selector: 'app-root',
standalone: true,
imports: [JsonPipe, RouterOutlet],
templateUrl: './app.component.html',
})
export class AppComponent {
readonly logto = inject(LogtoService);
readonly user = signal<IdTokenClaims | undefined>(undefined);

constructor() {
effect(() => {
if (!this.logto.isAuthenticated()) {
this.user.set(undefined);
return;
}

void this.logto
.getIdTokenClaims()
.then((claims) => {
this.user.set(claims);
})
.catch(() => {
// The SDK exposes the error through logto.error() for the template.
});
});
}

// ...keep the signIn() and signOut() methods from the previous step
}

เพิ่มโค้ดต่อไปนี้ภายในเงื่อนไข logto.isAuthenticated() ของเทมเพลตของคุณ:

app/app.component.html
@if (user(); as claims) {
<pre>{{ claims | json }}</pre>
}

ขอ การอ้างสิทธิ์ (claims) เพิ่มเติม​

คุณอาจพบว่าข้อมูลผู้ใช้บางอย่างหายไปในอ็อบเจกต์ที่ส่งคืนจาก getIdTokenClaims() สาเหตุเนื่องจาก OAuth 2.0 และ OpenID Connect (OIDC) ถูกออกแบบมาให้สอดคล้องกับหลักการสิทธิ์น้อยที่สุด (principle of least privilege; PoLP) และ Logto ถูกสร้างขึ้นบนมาตรฐานเหล่านี้

โดยปกติแล้ว จะมีการส่งคืนการอ้างสิทธิ์ (claim) แบบจำกัด หากคุณต้องการข้อมูลเพิ่มเติม คุณสามารถร้องขอขอบเขต (scope) เพิ่มเติมเพื่อเข้าถึงการอ้างสิทธิ์ (claim) ที่มากขึ้นได้

ข้อมูล:

"การอ้างสิทธิ์ (Claim)" คือการยืนยันข้อมูลบางอย่างเกี่ยวกับผู้ถูกอ้างถึง (subject); "ขอบเขต (Scope)" คือกลุ่มของการอ้างสิทธิ์ (claim) ในกรณีนี้ การอ้างสิทธิ์ (claim) คือข้อมูลบางอย่างเกี่ยวกับผู้ใช้

ตัวอย่างที่ไม่เป็นทางการของความสัมพันธ์ระหว่างขอบเขต (scope) กับการอ้างสิทธิ์ (claim) มีดังนี้:

เคล็ดลับ:

การอ้างสิทธิ์ (claim) "sub" หมายถึง "ผู้ถูกอ้างถึง (subject)" ซึ่งคือตัวระบุที่ไม่ซ้ำของผู้ใช้ (เช่น user ID)

Logto SDK จะร้องขอขอบเขต (scope) สามรายการเสมอ ได้แก่ openid, profile และ offline_access

เพิ่ม scopes ลงในการตั้งค่า provideLogto ของคุณ:

app/app.config.ts
import { type ApplicationConfig } from '@angular/core';
import { provideLogto, UserScope } from '@logto/angular';

export const appConfig: ApplicationConfig = {
providers: [
provideLogto({
endpoint: '<your-logto-endpoint>',
appId: '<your-app-id>',
scopes: [
UserScope.Email,
UserScope.Phone,
UserScope.CustomData,
UserScope.Identities,
UserScope.Organizations,
],
}),
// ...other providers
],
};

ลงชื่อเข้าใช้อีกครั้งหลังจากเปลี่ยน scopes การอ้างสิทธิ์ (claims) เพิ่มเติมใน โทเค็น ID (ID token) เช่น email และ phone_number จะสามารถเข้าถึงได้จาก getIdTokenClaims() และจะแสดงผลตามตัวอย่างข้างต้น

การอ้างสิทธิ์ (Claims) ที่ต้องใช้การร้องขอผ่านเครือข่าย​

เพื่อป้องกันไม่ให้โทเค็น ID (ID token) มีขนาดใหญ่เกินไป การอ้างสิทธิ์บางรายการจำเป็นต้องร้องขอผ่านเครือข่ายเพื่อดึงข้อมูล ตัวอย่างเช่น การอ้างสิทธิ์ custom_data จะไม่ถูกรวมอยู่ในอ็อบเจกต์ผู้ใช้ แม้ว่าจะร้องขอไว้ในขอบเขต (scopes) ก็ตาม หากต้องการเข้าถึงการอ้างสิทธิ์เหล่านี้ คุณสามารถใช้เมธอด fetchUserInfo():

app/app.component.ts
// เพิ่มเมธอดนี้ใน AppComponent และเรียกหลังจากลงชื่อเข้าใช้
async loadUserInfo() {
const userInfo = await this.logto.fetchUserInfo();
// ตอนนี้คุณสามารถเข้าถึง userInfo.custom_data, userInfo.identities ฯลฯ ได้แล้ว
return userInfo;
}
เมธอดนี้จะดึงข้อมูลผู้ใช้โดยการร้องขอไปยัง จุดปลาย userinfo หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับขอบเขต (scopes) และการอ้างสิทธิ์ (claims) ที่มีอยู่ ดูที่หัวข้อ ขอบเขตและการอ้างสิทธิ์

fetchUserInfo() สามารถใช้ร่วมกับ โทเค็นการเข้าถึง (Access token) สำหรับทรัพยากร API ได้ การตั้งค่า resources จะไม่ป้องกัน SDK จากการร้องขอข้อมูลผู้ใช้

ขอบเขต (Scopes) และ การอ้างสิทธิ์ (Claims)​

Logto ใช้แนวทาง ขอบเขต (Scope) และ การอ้างสิทธิ์ (Claim) ของ OIDC เพื่อกำหนดขอบเขตและการอ้างสิทธิ์สำหรับดึงข้อมูลผู้ใช้จากโทเค็น ID (ID token) และ OIDC จุดปลาย userinfo ทั้ง "ขอบเขต (Scope)" และ "การอ้างสิทธิ์ (Claim)" เป็นคำศัพท์จากข้อกำหนดของ OAuth 2.0 และ OpenID Connect (OIDC)

สำหรับการอ้างสิทธิ์ OIDC มาตรฐาน การรวมอยู่ในโทเค็น ID จะถูกกำหนดอย่างเคร่งครัดโดยขอบเขตที่ร้องขอ การอ้างสิทธิ์เพิ่มเติม (เช่น custom_data และ organizations) สามารถตั้งค่าเพิ่มเติมให้ปรากฏในโทเค็น ID ได้ผ่าน การตั้งค่า Custom ID token

ต่อไปนี้คือรายการขอบเขต (Scopes) ที่รองรับและการอ้างสิทธิ์ (Claims) ที่เกี่ยวข้อง:

ขอบเขต OIDC มาตรฐาน​

openid (ค่าเริ่มต้น)

ชื่อการอ้างสิทธิ์ประเภทคำอธิบาย
substringตัวระบุที่ไม่ซ้ำของผู้ใช้

profile (ค่าเริ่มต้น)

ชื่อการอ้างสิทธิ์ประเภทคำอธิบาย
namestringชื่อเต็มของผู้ใช้
usernamestringชื่อผู้ใช้ของผู้ใช้
picturestringURL ของรูปโปรไฟล์ผู้ใช้ปลายทาง (End-User) URL นี้ต้องอ้างถึงไฟล์รูปภาพ (เช่น PNG, JPEG, หรือ GIF) ไม่ใช่หน้าเว็บที่มีรูปภาพ โปรดทราบว่า URL นี้ควรอ้างอิงถึงรูปโปรไฟล์ของผู้ใช้ปลายทางโดยเฉพาะ เหมาะสำหรับแสดงเมื่ออธิบายผู้ใช้ปลายทาง ไม่ใช่รูปภาพใด ๆ ที่ผู้ใช้ถ่ายมาโดยพลการ
created_atnumberเวลาที่สร้างผู้ใช้ปลายทาง เวลานี้แสดงเป็นจำนวนมิลลิวินาทีตั้งแต่ Unix epoch (1970-01-01T00:00:00Z)
updated_atnumberเวลาที่ข้อมูลของผู้ใช้ปลายทางถูกอัปเดตล่าสุด เวลานี้แสดงเป็นจำนวนมิลลิวินาทีตั้งแต่ Unix epoch (1970-01-01T00:00:00Z)

การอ้างสิทธิ์มาตรฐาน อื่น ๆ เช่น family_name, given_name, middle_name, nickname, preferred_username, profile, website, gender, birthdate, zoneinfo, และ locale จะถูกรวมอยู่ในขอบเขต profile ด้วยโดยไม่จำเป็นต้องร้องขอ endpoint userinfo ความแตกต่างเมื่อเทียบกับการอ้างสิทธิ์ข้างต้นคือ การอ้างสิทธิ์เหล่านี้จะถูกส่งกลับเมื่อค่าของมันไม่ว่างเปล่าเท่านั้น ในขณะที่การอ้างสิทธิ์ข้างต้นจะคืนค่า null หากค่าว่างเปล่า

บันทึก:

ต่างจากการอ้างสิทธิ์มาตรฐาน การอ้างสิทธิ์ created_at และ updated_at ใช้หน่วยเป็นมิลลิวินาทีแทนที่จะเป็นวินาที

email

ชื่อการอ้างสิทธิ์ประเภทคำอธิบาย
emailstringอีเมลของผู้ใช้
email_verifiedbooleanอีเมลได้รับการยืนยันแล้วหรือไม่

phone

ชื่อการอ้างสิทธิ์ประเภทคำอธิบาย
phone_numberstringเบอร์โทรศัพท์ของผู้ใช้
phone_number_verifiedbooleanเบอร์โทรศัพท์ได้รับการยืนยันแล้วหรือไม่

address

โปรดดูรายละเอียดของการอ้างสิทธิ์ที่อยู่ได้ที่ OpenID Connect Core 1.0

ข้อมูล:

ขอบเขตที่มีเครื่องหมาย (ค่าเริ่มต้น) จะถูกร้องขอเสมอโดย Logto SDK การอ้างสิทธิ์ภายใต้ขอบเขต OIDC มาตรฐานจะถูกรวมอยู่ในโทเค็น ID เสมอเมื่อมีการร้องขอขอบเขตที่เกี่ยวข้อง — ไม่สามารถปิดได้

ขอบเขตเพิ่มเติม​

ขอบเขตต่อไปนี้เป็นขอบเขตที่ Logto ขยายขึ้นและจะคืนค่าการอ้างสิทธิ์ผ่าน userinfo endpoint การอ้างสิทธิ์เหล่านี้ยังสามารถตั้งค่าให้ถูกรวมอยู่ในโทเค็น ID ได้โดยตรงผ่าน Console > Custom JWT ดู Custom ID token สำหรับรายละเอียดเพิ่มเติม

custom_data

ชื่อการอ้างสิทธิ์ประเภทคำอธิบายรวมในโทเค็น ID โดยค่าเริ่มต้น
custom_dataobjectข้อมูลกำหนดเองของผู้ใช้

identities

ชื่อการอ้างสิทธิ์ประเภทคำอธิบายรวมในโทเค็น ID โดยค่าเริ่มต้น
identitiesobjectข้อมูลตัวตนที่เชื่อมโยงของผู้ใช้
sso_identitiesarrayข้อมูล SSO ที่เชื่อมโยงของผู้ใช้

roles

ชื่อการอ้างสิทธิ์ประเภทคำอธิบายรวมในโทเค็น ID โดยค่าเริ่มต้น
rolesstring[]บทบาท (Role) ของผู้ใช้✅

urn:logto:scope:organizations

ชื่อการอ้างสิทธิ์ประเภทคำอธิบายรวมในโทเค็น ID โดยค่าเริ่มต้น
organizationsstring[]รหัสองค์กรที่ผู้ใช้สังกัด✅
organization_dataobject[]ข้อมูลองค์กรที่ผู้ใช้สังกัด
บันทึก:

การอ้างสิทธิ์ขององค์กรเหล่านี้สามารถดึงได้ผ่าน userinfo endpoint เมื่อใช้ โทเค็นทึบ (Opaque token) อย่างไรก็ตาม โทเค็นทึบไม่สามารถใช้เป็นโทเค็นองค์กรสำหรับเข้าถึงทรัพยากรเฉพาะองค์กร ดู โทเค็นทึบและองค์กร สำหรับรายละเอียดเพิ่มเติม

urn:logto:scope:organization_roles

ชื่อการอ้างสิทธิ์ประเภทคำอธิบายรวมในโทเค็น ID โดยค่าเริ่มต้น
organization_rolesstring[]บทบาทขององค์กรที่ผู้ใช้สังกัดในรูปแบบ <organization_id>:<role_name>✅

ทรัพยากร API (API resources)​

เราแนะนำให้อ่าน 🔐 การควบคุมการเข้าถึงตามบทบาท (RBAC) ก่อน เพื่อทำความเข้าใจแนวคิดพื้นฐานของ RBAC ใน Logto และวิธีตั้งค่าทรัพยากร API อย่างถูกต้อง

กำหนดค่า Logto client​

เมื่อคุณตั้งค่า ทรัพยากร API (API resources) เรียบร้อยแล้ว คุณสามารถเพิ่มทรัพยากรเหล่านี้ขณะกำหนดค่า Logto ในแอปของคุณได้:

app/app.config.ts
import { type ApplicationConfig } from '@angular/core';
import { provideLogto } from '@logto/angular';

export const appConfig: ApplicationConfig = {
providers: [
provideLogto({
endpoint: '<your-logto-endpoint>',
appId: '<your-app-id>',
resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'],
}),
// ...other providers
],
};

แต่ละ ทรัพยากร API (API resource) จะมี สิทธิ์ (scopes) ของตัวเอง

ตัวอย่างเช่น ทรัพยากร https://shopping.your-app.com/api มีสิทธิ์ shopping:read และ shopping:write และทรัพยากร https://store.your-app.com/api มีสิทธิ์ store:read และ store:write

หากต้องการร้องขอสิทธิ์เหล่านี้ คุณสามารถเพิ่มขณะกำหนดค่า Logto ในแอปของคุณได้:

app/app.config.ts
import { type ApplicationConfig } from '@angular/core';
import { provideLogto } from '@logto/angular';

export const appConfig: ApplicationConfig = {
providers: [
provideLogto({
endpoint: '<your-logto-endpoint>',
appId: '<your-app-id>',
scopes: ['shopping:read', 'shopping:write', 'store:read', 'store:write'],
resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'],
}),
// ...other providers
],
};

คุณอาจสังเกตได้ว่า ขอบเขต (scopes) ถูกกำหนดแยกจาก ทรัพยากร API (API resources) นี่เป็นเพราะ Resource Indicators for OAuth 2.0 ระบุว่า ขอบเขตสุดท้ายสำหรับคำขอจะเป็นผลคูณคาร์ทีเซียนของขอบเขตทั้งหมดในบริการเป้าหมายทั้งหมด

ดังนั้น ในกรณีข้างต้น ขอบเขต (scopes) สามารถทำให้เรียบง่ายขึ้นจากการกำหนดใน Logto โดยทั้งสอง ทรัพยากร API (API resources) สามารถมีขอบเขต read และ write ได้โดยไม่ต้องมีคำนำหน้า จากนั้น ในการตั้งค่า Logto:

app/app.config.ts
import { type ApplicationConfig } from '@angular/core';
import { provideLogto } from '@logto/angular';

export const appConfig: ApplicationConfig = {
providers: [
provideLogto({
endpoint: '<your-logto-endpoint>',
appId: '<your-app-id>',
scopes: ['read', 'write'], // ขอบเขต (scopes) ที่ต้องการ
resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], // ทรัพยากร API (API resources) ที่ต้องการ
}),
// ...other providers
],
};

สำหรับแต่ละ ทรัพยากร API (API resource) จะร้องขอทั้งขอบเขต read และ write

บันทึก:

คุณสามารถร้องขอขอบเขต (scopes) ที่ไม่ได้กำหนดไว้ใน ทรัพยากร API (API resources) ได้ เช่น คุณสามารถร้องขอขอบเขต email ได้ แม้ว่า ทรัพยากร API (API resources) จะไม่มีขอบเขต email ให้ ขอบเขตที่ไม่มีจะถูกละเว้นอย่างปลอดภัย

หลังจากลงชื่อเข้าใช้สำเร็จ Logto จะออกขอบเขตที่เหมาะสมให้กับ ทรัพยากร API (API resources) ตามบทบาทของผู้ใช้

ลงชื่อเข้าใช้อีกครั้งหลังจากเปลี่ยนแปลงทรัพยากรหรือขอบเขต (scopes) เพื่อให้ผู้ใช้สามารถอนุญาตการกำหนดค่าที่อัปเดตแล้ว

ดึงโทเค็นการเข้าถึงสำหรับทรัพยากร API​

เพื่อดึงโทเค็นการเข้าถึง (Access token) สำหรับทรัพยากร API เฉพาะ คุณสามารถใช้เมธอด getAccessToken() ได้ดังนี้:

app/api-resource.component.ts
import { Component, inject, signal } from '@angular/core';
import { LogtoService } from '@logto/angular';

@Component({
selector: 'app-api-resource',
standalone: true,
template: `
@if (logto.error(); as error) {
<p role="alert">{{ error.message }}</p>
}
@if (logto.isAuthenticated()) {
<button type="button" [disabled]="logto.isLoading()" (click)="loadAccessToken()">
รับโทเค็นการเข้าถึง API (Get API access token)
</button>
<pre>{{ accessToken() }}</pre>
}
`,
})
export class ApiResourceComponent {
readonly logto = inject(LogtoService);
readonly accessToken = signal('');

async loadAccessToken() {
this.accessToken.set(await this.logto.getAccessToken('https://shopping.your-app.com/api'));
}
}

เมธอดนี้จะส่งคืนโทเค็นการเข้าถึง (JWT access token) ที่สามารถใช้เข้าถึงทรัพยากร API ได้เมื่อผู้ใช้มีสิทธิ์ที่เกี่ยวข้อง หากโทเค็นการเข้าถึงที่แคชไว้หมดอายุแล้ว เมธอดนี้จะพยายามใช้โทเค็นรีเฟรช (Refresh token) เพื่อขอโทเค็นการเข้าถึงใหม่โดยอัตโนมัติ

ใช้ตัวระบุทรัพยากร (resource identifier) ที่ตรงกับการกำหนดค่าของคุณ เรียก getAccessToken(resource) ทุกครั้งที่คุณส่งคำขอ API เพื่อให้ SDK ส่งคืนโทเค็นที่ถูกต้อง แทนที่จะเก็บโทเค็นไว้ใน component ของคุณตลอดไป

ดึงโทเค็นองค์กร​

หากคุณยังไม่คุ้นเคยกับ องค์กร (Organization) โปรดอ่าน 🏢 องค์กร (หลายผู้เช่า; Multi-tenancy) เพื่อเริ่มต้น

คุณต้องเพิ่ม UserScope.Organizations ขอบเขต (scope) ขณะตั้งค่า Logto client:

app/app.config.ts
import { type ApplicationConfig } from '@angular/core';
import { provideLogto, UserScope } from '@logto/angular';

export const appConfig: ApplicationConfig = {
providers: [
provideLogto({
endpoint: '<your-logto-endpoint>',
appId: '<your-app-id>',
scopes: [UserScope.Organizations],
}),
// ...other providers
],
};

เมื่อผู้ใช้ลงชื่อเข้าใช้แล้ว คุณสามารถดึงโทเค็นองค์กร (organization token) สำหรับผู้ใช้ได้:

app/organizations.component.ts
import { Component, effect, inject, signal } from '@angular/core';
import { LogtoService } from '@logto/angular';

@Component({
selector: 'app-organizations',
standalone: true,
template: `
@if (logto.error(); as error) {
<p role="alert">{{ error.message }}</p>
}
@if (logto.isAuthenticated()) {
<ul>
@for (organizationId of organizationIds(); track organizationId) {
<li>
<span>{{ organizationId }}</span>
<button
type="button"
[disabled]="logto.isLoading()"
(click)="loadOrganizationToken(organizationId)"
>
รับโทเค็นองค์กร (Get organization token)
</button>
</li>
}
</ul>
<pre>{{ organizationToken() }}</pre>
}
`,
})
export class OrganizationsComponent {
readonly logto = inject(LogtoService);
readonly organizationIds = signal<string[]>([]);
readonly organizationToken = signal('');

constructor() {
effect(() => {
if (!this.logto.isAuthenticated()) {
this.organizationIds.set([]);
this.organizationToken.set('');
return;
}

void this.logto
.getIdTokenClaims()
.then((claims) => {
this.organizationIds.set(claims.organizations ?? []);
})
.catch(() => {
// SDK จะเปิดเผยข้อผิดพลาดผ่าน logto.error() สำหรับใช้ใน template
});
});
}

async loadOrganizationToken(organizationId: string) {
this.organizationToken.set(await this.logto.getOrganizationToken(organizationId));
}
}

ผสาน UserScope.Organizations เข้ากับขอบเขตที่มีอยู่ และลงชื่อเข้าใช้อีกครั้งหลังจากอัปเดตการกำหนดค่า getOrganizationToken(organizationId) จะส่งคืนโทเค็นสำหรับ Logto องค์กรที่เลือก ใช้ getAccessToken(resource) สำหรับโทเค็นทรัพยากร API

แนบโทเค็นการเข้าถึงกับ request headers​

ใส่โทเค็นไว้ใน HTTP header Authorization โดยใช้รูปแบบ Bearer (Bearer YOUR_TOKEN) ตัวอย่างเช่น เพิ่มเมธอดนี้ใน component ที่ผ่านการยืนยันตัวตนซึ่ง inject LogtoService:

async fetchProducts() {
const accessToken = await this.logto.getAccessToken('https://shopping.your-app.com/api');
const response = await fetch('https://shopping.your-app.com/api/products', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});

if (!response.ok) {
throw new Error(`Request failed: ${response.status}`);
}

return response.json();
}
บันทึก:

ตัวอย่างนี้ใช้ fetch หากคุณใช้ Angular HttpClient ให้ตั้งค่า header Authorization เดียวกันใน options ของคำขอ

อ่านเพิ่มเติม​

กระบวนการสำหรับผู้ใช้ปลายทาง: กระบวนการยืนยันตัวตน, กระบวนการบัญชี, และกระบวนการองค์กร ตั้งค่าตัวเชื่อมต่อ การอนุญาต (Authorization)