From f2998ce7910f6dbc96647a74d45813c56080e44d Mon Sep 17 00:00:00 2001 From: charIeszhao <12833674+charIeszhao@users.noreply.github.com> Date: Thu, 10 Sep 2026 07:44:40 +0000 Subject: [PATCH] chore: update translations and generated content --- .../quick-starts/framework/angular/README.mdx | 11 +- .../framework/angular/_api-resources.mdx | 59 ++++++- .../angular/_get-user-information.mdx | 158 ++++++++--------- .../framework/angular/_guide-tip.md | 4 +- .../framework/angular/_installation.mdx | 31 +--- .../framework/angular/_integration.mdx | 159 ++++++++++++----- .../angular/code/_config-organization-code.md | 17 ++ .../angular/code/_config-resources-code.md | 19 +- .../_config-resources-with-scopes-code.md | 19 +- ...onfig-resources-with-shared-scopes-code.md | 18 ++ .../angular/code/_get-access-token-code.md | 28 +++ .../_get-organization-access-token-code.md | 59 +++++++ .../quick-starts/framework/angular/README.mdx | 9 +- .../framework/angular/_api-resources.mdx | 59 ++++++- .../angular/_get-user-information.mdx | 154 +++++++---------- .../framework/angular/_guide-tip.md | 4 +- .../framework/angular/_installation.mdx | 31 +--- .../framework/angular/_integration.mdx | 153 ++++++++++++----- .../angular/code/_config-organization-code.md | 17 ++ .../angular/code/_config-resources-code.md | 21 +-- .../_config-resources-with-scopes-code.md | 21 +-- ...onfig-resources-with-shared-scopes-code.md | 18 ++ .../angular/code/_get-access-token-code.md | 28 +++ .../_get-organization-access-token-code.md | 59 +++++++ .../quick-starts/framework/angular/README.mdx | 11 +- .../framework/angular/_api-resources.mdx | 59 ++++++- .../angular/_get-user-information.mdx | 156 +++++++---------- .../framework/angular/_guide-tip.md | 4 +- .../framework/angular/_installation.mdx | 31 +--- .../framework/angular/_integration.mdx | 156 ++++++++++++----- .../angular/code/_config-organization-code.md | 17 ++ .../angular/code/_config-resources-code.md | 19 +- .../_config-resources-with-scopes-code.md | 19 +- ...onfig-resources-with-shared-scopes-code.md | 18 ++ .../angular/code/_get-access-token-code.md | 28 +++ .../_get-organization-access-token-code.md | 59 +++++++ .../quick-starts/framework/angular/README.mdx | 11 +- .../framework/angular/_api-resources.mdx | 59 ++++++- .../angular/_get-user-information.mdx | 158 ++++++++--------- .../framework/angular/_guide-tip.md | 4 +- .../framework/angular/_installation.mdx | 31 +--- .../framework/angular/_integration.mdx | 154 ++++++++++++----- .../angular/code/_config-organization-code.md | 17 ++ .../angular/code/_config-resources-code.md | 19 +- .../_config-resources-with-scopes-code.md | 19 +- ...onfig-resources-with-shared-scopes-code.md | 18 ++ .../angular/code/_get-access-token-code.md | 28 +++ .../_get-organization-access-token-code.md | 59 +++++++ .../quick-starts/framework/angular/README.mdx | 7 +- .../framework/angular/_api-resources.mdx | 59 ++++++- .../angular/_get-user-information.mdx | 162 ++++++++---------- .../framework/angular/_guide-tip.md | 4 +- .../framework/angular/_installation.mdx | 31 +--- .../framework/angular/_integration.mdx | 153 ++++++++++++----- .../angular/code/_config-organization-code.md | 17 ++ .../angular/code/_config-resources-code.md | 19 +- .../_config-resources-with-scopes-code.md | 19 +- ...onfig-resources-with-shared-scopes-code.md | 18 ++ .../angular/code/_get-access-token-code.md | 28 +++ .../_get-organization-access-token-code.md | 59 +++++++ .../quick-starts/framework/angular/README.mdx | 7 +- .../framework/angular/_api-resources.mdx | 59 ++++++- .../angular/_get-user-information.mdx | 156 +++++++---------- .../framework/angular/_guide-tip.md | 4 +- .../framework/angular/_installation.mdx | 31 +--- .../framework/angular/_integration.mdx | 160 ++++++++++++----- .../angular/code/_config-organization-code.md | 17 ++ .../angular/code/_config-resources-code.md | 19 +- .../_config-resources-with-scopes-code.md | 19 +- ...onfig-resources-with-shared-scopes-code.md | 18 ++ .../angular/code/_get-access-token-code.md | 28 +++ .../_get-organization-access-token-code.md | 59 +++++++ .../quick-starts/framework/angular/README.mdx | 13 +- .../framework/angular/_api-resources.mdx | 59 ++++++- .../angular/_get-user-information.mdx | 161 ++++++++--------- .../framework/angular/_guide-tip.md | 4 +- .../framework/angular/_installation.mdx | 31 +--- .../framework/angular/_integration.mdx | 154 ++++++++++++----- .../angular/code/_config-organization-code.md | 17 ++ .../angular/code/_config-resources-code.md | 19 +- .../_config-resources-with-scopes-code.md | 19 +- ...onfig-resources-with-shared-scopes-code.md | 18 ++ .../angular/code/_get-access-token-code.md | 28 +++ .../_get-organization-access-token-code.md | 59 +++++++ .../quick-starts/framework/angular/README.mdx | 11 +- .../framework/angular/_api-resources.mdx | 59 ++++++- .../angular/_get-user-information.mdx | 160 ++++++++--------- .../framework/angular/_guide-tip.md | 4 +- .../framework/angular/_installation.mdx | 31 +--- .../framework/angular/_integration.mdx | 152 +++++++++++----- .../angular/code/_config-organization-code.md | 17 ++ .../angular/code/_config-resources-code.md | 19 +- .../_config-resources-with-scopes-code.md | 19 +- ...onfig-resources-with-shared-scopes-code.md | 18 ++ .../angular/code/_get-access-token-code.md | 28 +++ .../_get-organization-access-token-code.md | 59 +++++++ .../quick-starts/framework/angular/README.mdx | 13 +- .../framework/angular/_api-resources.mdx | 59 ++++++- .../angular/_get-user-information.mdx | 160 ++++++++--------- .../framework/angular/_guide-tip.md | 4 +- .../framework/angular/_installation.mdx | 31 +--- .../framework/angular/_integration.mdx | 158 ++++++++++++----- .../angular/code/_config-organization-code.md | 17 ++ .../angular/code/_config-resources-code.md | 19 +- .../_config-resources-with-scopes-code.md | 19 +- ...onfig-resources-with-shared-scopes-code.md | 18 ++ .../angular/code/_get-access-token-code.md | 28 +++ .../_get-organization-access-token-code.md | 59 +++++++ 108 files changed, 3440 insertions(+), 1767 deletions(-) create mode 100644 i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md create mode 100644 i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md create mode 100644 i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md create mode 100644 i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md create mode 100644 i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md create mode 100644 i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md create mode 100644 i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md create mode 100644 i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md create mode 100644 i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md create mode 100644 i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md create mode 100644 i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md create mode 100644 i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md create mode 100644 i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md create mode 100644 i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md create mode 100644 i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md create mode 100644 i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md create mode 100644 i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md create mode 100644 i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md create mode 100644 i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md create mode 100644 i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md create mode 100644 i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md create mode 100644 i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md create mode 100644 i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md create mode 100644 i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md create mode 100644 i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md diff --git a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx index abf628be3be..4f78f39f8ce 100644 --- a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx +++ b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx @@ -2,7 +2,7 @@ slug: /quick-starts/angular sidebar_label: Angular sidebar_custom_props: - description: Angular ist eine JavaScript-Bibliothek zum Erstellen von Benutzeroberflächen. + description: Angular ist ein Framework zum Erstellen von Webanwendungen. (Angular is a framework for building web applications.) --- import FurtherReadings from '../../fragments/_further-readings.md'; @@ -13,14 +13,17 @@ import GuideTip from './_guide-tip.md'; import Installation from './_installation.mdx'; import Integration from './_integration.mdx'; -# Authentifizierung zu deiner Angular-Anwendung hinzufügen +# Authentifizierung zu deiner Angular-Anwendung hinzufügen (Add authentication to your Angular application) + +Diese Anleitung zeigt dir, wie du das Logto Angular SDK v2 in deine Anwendung integrierst. ## Voraussetzungen \{#prerequisites} -- Ein [Logto Cloud](https://cloud.logto.io) Konto oder ein [selbstgehostetes Logto](/introduction/set-up-logto-oss). -- Eine Logto Single-Page-Anwendung erstellt. +- Ein [Logto Cloud](https://cloud.logto.io) Konto oder ein [selbst gehostetes Logto](/introduction/set-up-logto-oss). +- Eine Single-Page-Anwendung (SPA), die in der Logto Console erstellt wurde. +- Ein Angular 20 Projekt. ## Installation \{#installation} diff --git a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx index 5f463c34cbc..e7412e7dc16 100644 --- a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx +++ b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx @@ -1,22 +1,67 @@ import ApiResourcesDescription from '../../fragments/_api-resources-description.md'; import ConfigApiResources from '../../fragments/_config-api-resources.mdx'; +import FetchAccessTokenForApiResources from '../../fragments/_fetch-access-token-for-api-resources.mdx'; +import FetchOrganizationTokenForUser from '../../fragments/_fetch-organization-token-for-user.mdx'; +import ConfigOrganizationCode from './code/_config-organization-code.md'; import ConfigResourcesCode from './code/_config-resources-code.md'; import ConfigResourcesWithScopesCode from './code/_config-resources-with-scopes-code.md'; - -### Konfiguration von `angular-auth-oidc-client` für API-Ressource \{#configure-angular-auth-oidc-client-for-api-resource} +import ConfigResourcesWithSharedScopesCode from './code/_config-resources-with-shared-scopes-code.md'; +import GetAccessTokenCode from './code/_get-access-token-code.md'; +import GetOrganizationAccessTokenCode from './code/_get-organization-access-token-code.md'; +### Logto-Client konfigurieren \{#configure-logto-client} + } configResourcesWithScopesCode={} + configResourcesWithSharedScopesCode={} /> -Jetzt wird das Zugangstoken im JSON Web Token (JWT) Format anstelle eines zufälligen Strings (opakes Token) vorliegen. +Melde dich nach Änderungen an den Ressourcen oder Berechtigungen erneut an, damit der Benutzer die aktualisierte Konfiguration autorisieren kann. -:::warning -Sowohl `autoUserInfo` als auch `renewUserInfoAfterTokenRenew` werden deaktiviert, wenn `resource` gesetzt ist. Dies liegt daran, dass das Zugangstoken für die spezifische API-Ressource und nicht für den User-Info-Endpunkt angefordert wird. -::: +### Zugangstoken für die API-Ressource abrufen \{#fetch-access-token-for-the-api-resource} + +} +/> + +Verwende den exakten Ressourcenbezeichner aus deiner Konfiguration. Rufe `getAccessToken(resource)` immer dann auf, wenn du eine API-Anfrage stellst, damit das SDK ein gültiges Token zurückgeben kann, anstatt ein Token unbegrenzt in deiner Komponente zu behalten. + +### Organisationstokens abrufen \{#fetch-organization-tokens} + +} + getOrganizationAccessTokenCode={} +/> + +Füge `UserScope.Organizations` zu allen bestehenden Berechtigungen hinzu und melde dich nach der Aktualisierung der Konfiguration erneut an. `getOrganizationToken(organizationId)` gibt ein Token für die ausgewählte Logto-Organisation zurück; verwende `getAccessToken(resource)` für ein API-Ressourcen-Token. + +### Zugangstoken an Anfrage-Header anhängen \{#attach-access-token-to-request-headers} + +Platziere das Token im `Authorization` HTTP-Header im Bearer-Format (`Bearer YOUR_TOKEN`). Füge zum Beispiel diese Methode zu einer authentifizierten Komponente hinzu, die `LogtoService` injiziert: -Derzeit unterstützen nur die offiziellen Logto SDKs die Möglichkeit, sowohl Benutzerinformationen als auch Zugangstokens für API-Ressourcen anzufordern. Wenn du beides anfordern musst, zögere bitte nicht, uns zu kontaktieren. +```ts +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(); +} +``` + +:::note +Das Beispiel verwendet `fetch`. Wenn du Angular `HttpClient` verwendest, setze denselben `Authorization`-Header in dessen Anfrageoptionen. +::: diff --git a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx index 667d8e0fde9..aadfe915eab 100644 --- a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx +++ b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx @@ -5,130 +5,106 @@ import FindUserInfoMissing from '../../fragments/_find-user-info-missing.mdx'; import ScopesAndClaims from '../../fragments/_scopes-and-claims.mdx'; import ScopesAndClaimsIntroduction from '../../fragments/_scopes-claims-introduction.md'; -Sobald sich der Benutzer erfolgreich angemeldet hat, wird Logto ein [ID-Token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) ausstellen, das die Benutzerinformationsansprüche enthält. Das ID-Token ist ein JSON Web Token (JWT). - -Es ist wichtig zu beachten, dass die Benutzerinformationsansprüche, die abgerufen werden können, von den -Berechtigungen abhängen, die der Benutzer während der Anmeldung verwendet hat. In Anbetracht der Leistung und der Datengröße kann das ID-Token möglicherweise nicht alle Benutzeransprüche enthalten; einige Benutzeransprüche sind nur im [userinfo endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) verfügbar (siehe die untenstehende Liste). - -Das `buildAngularAuthConfig()`-Utility aktiviert `autoUserInfo` und `renewUserInfoAfterTokenRenew`, wenn keine `resource` in der Konfiguration angegeben ist. Das bedeutet, dass Logto automatisch die Benutzerinformationen abruft, nachdem sich der Benutzer angemeldet hat, und die Benutzerinformationen erneuert, nachdem das Token erneuert wurde. - -:::info -Um mehr über die Konfiguration der `angular-auth-oidc-client`-Bibliothek zu erfahren, siehe die [offizielle Dokumentation](https://angular-auth-oidc-client.com/). -::: - ### Benutzerinformationen anzeigen \{#display-user-information} -Der `OidcSecurityService` bietet eine bequeme Möglichkeit, den Authentifizierungsstatus sowie die Benutzerinformationen zu abonnieren: +Um die Informationen des Benutzers anzuzeigen, verwende `getIdTokenClaims()`, um Ansprüche (Claims) aus dem ID-Token ohne zusätzliche Netzwerkabfrage auszulesen. Füge deiner `AppComponent` einen `effect` hinzu, um die Ansprüche zu laden, sobald `isAuthenticated()` wahr wird, auch wenn eine bestehende Sitzung wiederhergestellt wird. Importiere `JsonPipe`, um das Ergebnis anzuzeigen: ```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; -import { decodeIdToken, type IdTokenClaims } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - idTokenClaims?: IdTokenClaims; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService.checkAuth().subscribe(({ isAuthenticated, idToken, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - this.idTokenClaims = decodeIdToken(idToken); - this.accessToken = accessToken; +// Importiere JsonPipe, um das Ergebnis als JSON anzuzeigen +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(undefined); + + constructor() { + effect(() => { + if (!this.logto.isAuthenticated()) { + this.user.set(undefined); + return; + } + + void this.logto + .getIdTokenClaims() + .then((claims) => { + this.user.set(claims); + }) + .catch(() => { + // Das SDK gibt den Fehler über logto.error() für das Template aus. + }); }); } - // ...andere Methoden + // ...behalte die Methoden signIn() und signOut() aus dem vorherigen Schritt bei } ``` -Und im Template verwenden: +Füge Folgendes innerhalb des `logto.isAuthenticated()`-Zweigs deines Templates hinzu: ```html title="app/app.component.html" - - -
{{ idTokenClaims | json }}
-

Zugangstoken: {{ accessToken }}

- - -
+@if (user(); as claims) { +
{{ claims | json }}
+} ``` ### Zusätzliche Ansprüche anfordern \{#request-additional-claims} - + -Um zusätzliche Berechtigungen anzufordern, kannst du die Auth-Provider-Konfigurationen anpassen: +Füge die Berechtigungen (Scopes) zu deiner `provideLogto`-Konfiguration hinzu: -```tsx title="app/app.config.ts" -import { UserScope, buildAngularAuthConfig } from '@logto/js'; +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...andere Konfigurationen - // highlight-start - scopes: [ - UserScope.Email, - UserScope.Phone, - UserScope.CustomData, - UserScope.Identities, - UserScope.Organizations, - ], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + scopes: [ + UserScope.Email, + UserScope.Phone, + UserScope.CustomData, + UserScope.Identities, + UserScope.Organizations, + ], }), - // ...andere Provider + // ...weitere Provider ], }; ``` -Dann kannst du auf die zusätzlichen Ansprüche im Rückgabewert von `idToken` zugreifen. +Melde dich nach der Änderung der Berechtigungen erneut an. Die zusätzlichen ID-Token-Ansprüche wie `email` und `phone_number` sind dann über `getIdTokenClaims()` verfügbar und werden wie oben im Beispiel angezeigt. -{/* eslint-disable prettier/prettier */} {`import { OidcSecurityService } from 'angular-auth-oidc-client'; -// highlight-next-line -import { type UserInfoResponse } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - // highlight-next-line - userData?: UserInfoResponse; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService - .checkAuth() - // highlight-next-line - .subscribe(({ isAuthenticated, userData, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - // highlight-next-line - this.userData = userData; - this.accessToken = accessToken; - }); + {`// Füge diese Methode zur AppComponent hinzu und rufe sie nach der Anmeldung auf. +async loadUserInfo() { + const userInfo = await this.logto.fetchUserInfo(); + // Jetzt kannst du auf userInfo.custom_data, userInfo.identities usw. zugreifen. + return userInfo; +}`} } - - // ...andere Methoden -} - -// Jetzt kannst du auf den Anspruch \`userData.custom_data\` zugreifen`} -} /> -{/* eslint-enable prettier/prettier */} + +`fetchUserInfo()` kann zusammen mit API-Ressourcen-Zugangstokens verwendet werden. Die Konfiguration von `resources` verhindert nicht, dass das SDK Benutzerinformationen anfordert. ### Berechtigungen und Ansprüche \{#scopes-and-claims} diff --git a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md index d5ffb98c619..5c47e0fa679 100644 --- a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md +++ b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md @@ -1,6 +1,6 @@ :::tip -- Die folgende Demonstration basiert auf Angular 18.0.0 und [angular-auth-oidc-client](https://github.com/damienbod/angular-auth-oidc-client). -- Das Beispielprojekt ist im [GitHub-Repository](https://github.com/logto-io/js/tree/master/packages/angular-sample) verfügbar. +- Diese Anleitung verwendet das offizielle `@logto/angular` v2 SDK, das Angular 20 unterstützt und Dependency Injection sowie Signals bereitstellt. +- Das Beispielprojekt ist in unserem [SDK-Repository](https://github.com/logto-io/js/tree/master/packages/angular-sample) verfügbar. ::: diff --git a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx index d2b765bb75c..41aabde1f14 100644 --- a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx +++ b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx @@ -1,30 +1,3 @@ -import TabItem from '@theme/TabItem'; -import Tabs from '@theme/Tabs'; +import NpmLikeInstallation from '../../fragments/_npm-like-installation.mdx'; -Installiere Logto JS Core SDK und die Angular OIDC Client-Bibliothek: - - - - - -```bash -npm i @logto/js angular-auth-oidc-client -``` - - - - -```bash -pnpm add @logto/js angular-auth-oidc-client -``` - - - - -```bash -yarn add @logto/js angular-auth-oidc-client -``` - - - - + diff --git a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx index 057f79b2091..32cf66f5520 100644 --- a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx +++ b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx @@ -1,33 +1,41 @@ import Checkpoint from '../../fragments/_checkpoint-test-your-application.md'; +import SignInNote from '../../fragments/_sign-in-note.mdx'; import AssumingUrl from '../../fragments/_web-assuming-url.mdx'; import WebConfigureRedirectUris from '../../fragments/_web-configure-redirect-uris.mdx'; import SignInFlowSummary from '../../fragments/_web-sign-in-flow-summary.mdx'; import SignOutNote from '../../fragments/_web-sign-out-note.md'; -### Anwendung konfigurieren \{#configure-application} +### Logto-Provider initialisieren \{#init-logto-provider} -Füge in deinem Angular-Projekt den Auth-Provider in deiner `app.config.ts` hinzu: +Registriere in deinem Angular-Projekt `provideLogto` und deine Anwendungsrouten in `app.config.ts`: ```ts title="app/app.config.ts" -import { buildAngularAuthConfig } from '@logto/js'; -import { provideAuth } from 'angular-auth-oidc-client'; +// Registriere Logto und die Routen in der Angular-Konfiguration +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: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - endpoint: '', - appId: '', - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - }), + provideLogto({ + endpoint: '', + appId: '', }), - // ...andere Provider + provideRouter(routes), + // ...weitere Provider ], }; ``` +`provideLogto` stellt den Authentifizierungsstatus nach dem ersten Rendern im Browser automatisch wieder her. Du musst in deinen Komponenten keine Initialisierungsmethode aufrufen. + +:::note +Bei der Verwendung von Server-Side Rendering (SSR) sind Authentifizierungsstatus und Tokens nur im +Browser verfügbar. Verwende `isLoading()`, um einen Ladezustand anzuzeigen, bis die Initialisierung abgeschlossen ist. Wenn du authentifizierte Daten während des Server-Renderings benötigst, verwende ein Server- oder BFF-SDK. +::: + ### Redirect-URIs konfigurieren \{#configure-redirect-uris} @@ -38,55 +46,116 @@ export const appConfig: ApplicationConfig = { ### Redirect behandeln \{#handle-redirect} -Da wir `http://localhost:3000/callback` als Redirect-URI verwenden, müssen wir sie nun ordnungsgemäß behandeln. Die Bibliothek `angular-auth-oidc-client` bietet integrierte Unterstützung für die Behandlung des Redirects. Du kannst einfach die Auth-Provider-Konfigurationen richtig einstellen und die Bibliothek übernimmt den Rest. +Erstelle eine Callback-Komponente, um die Anmeldung abzuschließen, nachdem Logto den Benutzer zurück zu deiner Anwendung umgeleitet hat. Verwende `afterNextRender`, damit die Callback-Verarbeitung nur im Browser ausgeführt wird: + +```ts title="app/callback.component.ts" +// Callback-Komponente zur Verarbeitung der Rückleitung nach der Anmeldung +import { afterNextRender, Component, inject } from '@angular/core'; +import { LogtoService } from '@logto/angular'; + +@Component({ + selector: 'app-callback', + standalone: true, + template: ` + @if (logto.error(); as error) { +

{{ error.message }}

+ } @else { +

Anmeldung wird abgeschlossen...

+ } + `, +}) +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(() => { + // Das SDK gibt Callback-Fehler über logto.error() für das Template aus. + }); + }); + } +} +``` -```ts title="app/app.config.ts" -export const appConfig: ApplicationConfig = { - providers: [ - provideAuth({ - config: buildAngularAuthConfig({ - // ...andere Konfigurationen - // highlight-start - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - // highlight-end - }), - }), - // ...andere Provider - ], -}; +`isSignInRedirected()` prüft, ob die URL zu einer aktiven Anmeldesitzung passt. Wenn jemand die Callback-Route ohne eine solche Sitzung aufruft, leitet dieses Beispiel stattdessen auf die Startseite der Anwendung zurück, anstatt die Anmeldung abzuschließen. + +Registriere die Callback-Route in `app.routes.ts`. Sie muss mit dem Pfad deiner Redirect-URI übereinstimmen und darf keine Authentifizierung erfordern. Verwende zum Beispiel `callback` für eine Redirect-URI, die mit `/callback` endet: + +```ts title="app/app.routes.ts" +// Registrierung der Callback-Route +import { type Routes } from '@angular/router'; + +import { CallbackComponent } from './callback.component'; + +export const routes: Routes = [ + { path: 'callback', component: CallbackComponent }, + // ...weitere Routen +]; ``` -### Anmeldung und Abmeldung implementieren \{#implement-sign-in-and-sign-out} +Die Root-Komponente benötigt ein ``, um diese Route zu rendern, wie im nächsten Schritt gezeigt. -In der Komponente, in der du die Anmeldung und Abmeldung implementieren möchtest (zum Beispiel `app.component.ts`), injiziere den `OidcSecurityService` und verwende ihn, um sich anzumelden und abzumelden. +### Anmeldung und Abmeldung implementieren \{#implement-sign-in-and-sign-out} -```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; +Injiziere `LogtoService`, um die Anmeldung und Abmeldung zu starten. Übergebe die registrierten Redirect-URIs an diese Methoden. Die `postRedirectUri` gibt dem SDK an, wohin nach erfolgreicher Verarbeitung des Anmelde-Callbacks navigiert werden soll: -export class AppComponent implements OnInit { - constructor(public oidcSecurityService: OidcSecurityService) {} + - signIn() { - this.oidcSecurityService.authorize(); +```ts title="app/app.component.ts" +// Anmeldung und Abmeldung in der Hauptkomponente implementieren +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, + }); } - signOut() { - this.oidcSecurityService.logoff().subscribe((result) => { - console.log('app sign-out', result); - }); + async signOut() { + await this.logto.signOut('http://localhost:3000/'); } } ``` -Füge dann im Template Buttons zum Anmelden und Abmelden hinzu: +Lese die `isLoading()`, `isAuthenticated()` und `error()` Signale direkt im Template aus: ```html title="app/app.component.html" - -
- +@if (logto.error(); as error) { +

{{ error.message }}

+} @if (logto.isLoading()) { +

Lädt...

+} @else if (logto.isAuthenticated()) { + +} @else { + +} + + ``` +Halte `` außerhalb der Authentifizierungsbedingungen, damit der Callback gerendert werden kann, bevor der Benutzer angemeldet ist. + diff --git a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md new file mode 100644 index 00000000000..63c1a120189 --- /dev/null +++ b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md @@ -0,0 +1,17 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: [UserScope.Organizations], + // highlight-end + }), + // ...other providers + ], +}; +``` diff --git a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md index d680e1b878a..7d6a1a15342 100644 --- a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md +++ b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md @@ -1,14 +1,15 @@ -```ts title="/app/app.config.ts" +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), // ...other providers ], diff --git a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md index 135f1616058..d2c0c7bba17 100644 --- a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md +++ b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md @@ -1,15 +1,16 @@ ```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - scopes: ['openid', 'profile', 'offline_access', 'read', 'write'], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['shopping:read', 'shopping:write', 'store:read', 'store:write'], + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), // ...other providers ], diff --git a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md new file mode 100644 index 00000000000..1c169d358b4 --- /dev/null +++ b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md @@ -0,0 +1,18 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['read', 'write'], // Berechtigungen + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], // API-Ressourcen + // highlight-end + }), + // ...other providers + ], +}; +``` diff --git a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md new file mode 100644 index 00000000000..f5197b7f4a0 --- /dev/null +++ b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md @@ -0,0 +1,28 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { + +
{{ accessToken() }}
+ } + `, +}) +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')); + } +} +``` diff --git a/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md new file mode 100644 index 00000000000..68db1b56cbb --- /dev/null +++ b/i18n/de/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md @@ -0,0 +1,59 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { +
    + @for (organizationId of organizationIds(); track organizationId) { +
  • + {{ organizationId }} + +
  • + } +
+
{{ organizationToken() }}
+ } + `, +}) +export class OrganizationsComponent { + readonly logto = inject(LogtoService); + readonly organizationIds = signal([]); + 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(() => { + // Das SDK gibt den Fehler über logto.error() für das Template aus. + }); + }); + } + + async loadOrganizationToken(organizationId: string) { + this.organizationToken.set(await this.logto.getOrganizationToken(organizationId)); + } +} +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx index 8ea63ad5086..0efb30a5fe3 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx +++ b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx @@ -2,7 +2,7 @@ slug: /quick-starts/angular sidebar_label: Angular sidebar_custom_props: - description: Angular es una biblioteca de JavaScript para construir interfaces de usuario. + description: Angular es un framework para construir aplicaciones web. --- import FurtherReadings from '../../fragments/_further-readings.md'; @@ -15,12 +15,15 @@ import Integration from './_integration.mdx'; # Añade autenticación a tu aplicación Angular +Esta guía te mostrará cómo integrar Logto Angular SDK v2 en tu aplicación. + -## Prerrequisitos \{#prerequisites} +## Requisitos previos \{#prerequisites} - Una cuenta de [Logto Cloud](https://cloud.logto.io) o un [Logto autoalojado](/introduction/set-up-logto-oss). -- Una aplicación de una sola página de Logto creada. +- Una aplicación de una sola página (SPA) creada en Logto Console. +- Un proyecto Angular 20. ## Instalación \{#installation} diff --git a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx index 4233a3af0ff..61743439bc0 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx +++ b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx @@ -1,22 +1,67 @@ import ApiResourcesDescription from '../../fragments/_api-resources-description.md'; import ConfigApiResources from '../../fragments/_config-api-resources.mdx'; +import FetchAccessTokenForApiResources from '../../fragments/_fetch-access-token-for-api-resources.mdx'; +import FetchOrganizationTokenForUser from '../../fragments/_fetch-organization-token-for-user.mdx'; +import ConfigOrganizationCode from './code/_config-organization-code.md'; import ConfigResourcesCode from './code/_config-resources-code.md'; import ConfigResourcesWithScopesCode from './code/_config-resources-with-scopes-code.md'; - -### Configurar `angular-auth-oidc-client` para el recurso de API \{#configure-angular-auth-oidc-client-for-api-resource} +import ConfigResourcesWithSharedScopesCode from './code/_config-resources-with-shared-scopes-code.md'; +import GetAccessTokenCode from './code/_get-access-token-code.md'; +import GetOrganizationAccessTokenCode from './code/_get-organization-access-token-code.md'; +### Configura el cliente de Logto \{#configure-logto-client} + } configResourcesWithScopesCode={} + configResourcesWithSharedScopesCode={} /> -Ahora, el token de acceso estará en el formato JSON Web Token (JWT) en lugar de una cadena aleatoria (token opaco). +Vuelve a iniciar sesión después de cambiar los recursos o alcances para que el usuario pueda autorizar la configuración actualizada. -:::warning -Tanto `autoUserInfo` como `renewUserInfoAfterTokenRenew` estarán deshabilitados cuando se establezca `resource`. Esto se debe a que el token de acceso se solicitará para el recurso de API específico y no para el endpoint de información del usuario. -::: +### Obtén el token de acceso para el recurso de API \{#fetch-access-token-for-the-api-resource} + +} +/> + +Utiliza el identificador exacto del recurso de tu configuración. Llama a `getAccessToken(resource)` cada vez que realices una solicitud a la API para que el SDK pueda devolver un token válido, en lugar de mantener un token indefinidamente en tu componente. + +### Obtén tokens de organización \{#fetch-organization-tokens} + +} + getOrganizationAccessTokenCode={} +/> + +Combina `UserScope.Organizations` con cualquier alcance existente y vuelve a iniciar sesión después de actualizar la configuración. `getOrganizationToken(organizationId)` devuelve un token para la organización de Logto seleccionada; utiliza `getAccessToken(resource)` para un token de recurso de API. + +### Adjunta el token de acceso a las cabeceras de la solicitud \{#attach-access-token-to-request-headers} + +Coloca el token en la cabecera HTTP `Authorization` usando el formato Bearer (`Bearer YOUR_TOKEN`). Por ejemplo, añade este método a un componente autenticado que inyecta `LogtoService`: -Actualmente, solo los SDKs oficiales de Logto admiten la capacidad de solicitar tanto información del usuario como tokens de acceso a recursos de API. Si necesitas solicitar ambos, no dudes en contactarnos. +```ts +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(); +} +``` + +:::note +El ejemplo utiliza `fetch`. Si usas `HttpClient` de Angular, establece la misma cabecera `Authorization` en las opciones de la solicitud. +::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx index c4a30faf2d8..a63719e452c 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx +++ b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx @@ -5,129 +5,105 @@ import FindUserInfoMissing from '../../fragments/_find-user-info-missing.mdx'; import ScopesAndClaims from '../../fragments/_scopes-and-claims.mdx'; import ScopesAndClaimsIntroduction from '../../fragments/_scopes-claims-introduction.md'; -Una vez que el usuario ha iniciado sesión con éxito, Logto emitirá un [Token de ID](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) que contiene los reclamos de información del usuario. El Token de ID es un JSON Web Token (JWT). - -Es importante tener en cuenta que los reclamos de información del usuario que se pueden recuperar dependen de los alcances utilizados por el usuario durante el inicio de sesión, y considerando el rendimiento y el tamaño de los datos, el Token de ID puede no contener todos los reclamos del usuario, algunos reclamos del usuario solo están disponibles en el [endpoint de userinfo](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) (ver la lista relacionada a continuación). - -La utilidad `buildAngularAuthConfig()` habilitará `autoUserInfo` y `renewUserInfoAfterTokenRenew` si no se proporciona un `resource` en la configuración. Esto significa que Logto obtendrá automáticamente la información del usuario después de que el usuario inicie sesión y renovará la información del usuario después de que se renueve el token. - -:::info -Para obtener más información sobre cómo configurar la biblioteca `angular-auth-oidc-client`, consulta la [documentación oficial](https://angular-auth-oidc-client.com/). -::: - ### Mostrar información del usuario \{#display-user-information} -El `OidcSecurityService` proporciona una manera conveniente de suscribirse al estado de autenticación así como a la información del usuario: +Para mostrar la información del usuario, utiliza `getIdTokenClaims()` para leer los reclamos (claims) del token de ID sin una solicitud de red adicional. Añade un `effect` a tu `AppComponent` para cargar los reclamos cuando `isAuthenticated()` sea verdadero, incluso cuando se restaure una sesión existente. Importa `JsonPipe` para mostrar el resultado: ```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; -import { decodeIdToken, type IdTokenClaims } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - idTokenClaims?: IdTokenClaims; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService.checkAuth().subscribe(({ isAuthenticated, idToken, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - this.idTokenClaims = decodeIdToken(idToken); - this.accessToken = accessToken; +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(undefined); + + constructor() { + effect(() => { + if (!this.logto.isAuthenticated()) { + this.user.set(undefined); + return; + } + + void this.logto + .getIdTokenClaims() + .then((claims) => { + this.user.set(claims); + }) + .catch(() => { + // El SDK expone el error a través de logto.error() para la plantilla. + }); }); } - // ...otros métodos + // ...mantén los métodos signIn() y signOut() del paso anterior } ``` -Y úsalo en la plantilla: +Agrega lo siguiente dentro de la rama `logto.isAuthenticated()` de tu plantilla: ```html title="app/app.component.html" - - -
{{ idTokenClaims | json }}
-

Access token: {{ accessToken }}

- - -
+@if (user(); as claims) { +
{{ claims | json }}
+} ``` ### Solicitar reclamos adicionales \{#request-additional-claims} - + -Para solicitar alcances adicionales, puedes configurar las configuraciones del proveedor de autenticación: +Agrega los alcances (scopes) a tu configuración de `provideLogto`: -```tsx title="app/app.config.ts" -import { UserScope, buildAngularAuthConfig } from '@logto/js'; +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...otras configuraciones - // highlight-start - scopes: [ - UserScope.Email, - UserScope.Phone, - UserScope.CustomData, - UserScope.Identities, - UserScope.Organizations, - ], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + scopes: [ + UserScope.Email, + UserScope.Phone, + UserScope.CustomData, + UserScope.Identities, + UserScope.Organizations, + ], }), // ...otros proveedores ], }; ``` -Luego puedes acceder a los reclamos adicionales en el valor de retorno de `idToken`. +Vuelve a iniciar sesión después de cambiar los alcances. Los reclamos adicionales del token de ID, como `email` y `phone_number`, estarán disponibles desde `getIdTokenClaims()` y se mostrarán con el ejemplo anterior. -{/* eslint-disable prettier/prettier */} {`import { OidcSecurityService } from 'angular-auth-oidc-client'; -// highlight-next-line -import { type UserInfoResponse } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - // highlight-next-line - userData?: UserInfoResponse; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService - .checkAuth() - // highlight-next-line - .subscribe(({ isAuthenticated, userData, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - // highlight-next-line - this.userData = userData; - this.accessToken = accessToken; - }); + {`// Agrega este método a AppComponent y llámalo después de iniciar sesión. +async loadUserInfo() { + const userInfo = await this.logto.fetchUserInfo(); + // Ahora puedes acceder a userInfo.custom_data, userInfo.identities, etc. + return userInfo; +}`} } - - // ...otros métodos -} - -// Ahora puedes acceder al reclamo \`userData.custom_data\``} -} /> -{/* eslint-enable prettier/prettier */} + +`fetchUserInfo()` se puede usar junto con los tokens de acceso a recursos de API. Configurar `resources` no impide que el SDK solicite información del usuario. ### Alcances y reclamos \{#scopes-and-claims} diff --git a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md index 7b8077011c0..999760787d7 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md +++ b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md @@ -1,6 +1,6 @@ :::tip -- La siguiente demostración está construida sobre Angular 18.0.0 y [angular-auth-oidc-client](https://github.com/damienbod/angular-auth-oidc-client). -- El proyecto de ejemplo está disponible en el [repositorio de GitHub](https://github.com/logto-io/js/tree/master/packages/angular-sample). +- Esta guía utiliza el SDK oficial `@logto/angular` v2, que es compatible con Angular 20 y proporciona inyección de dependencias y Signals. +- El proyecto de ejemplo está disponible en nuestro [repositorio de SDK](https://github.com/logto-io/js/tree/master/packages/angular-sample). ::: diff --git a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx index ece1292e896..41aabde1f14 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx +++ b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx @@ -1,30 +1,3 @@ -import TabItem from '@theme/TabItem'; -import Tabs from '@theme/Tabs'; +import NpmLikeInstallation from '../../fragments/_npm-like-installation.mdx'; -Instala Logto JS core SDK y la biblioteca cliente Angular OIDC: - - - - - -```bash -npm i @logto/js angular-auth-oidc-client -``` - - - - -```bash -pnpm add @logto/js angular-auth-oidc-client -``` - - - - -```bash -yarn add @logto/js angular-auth-oidc-client -``` - - - - + diff --git a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx index 7087ef0a452..a6ca5ddd497 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx +++ b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx @@ -1,33 +1,40 @@ import Checkpoint from '../../fragments/_checkpoint-test-your-application.md'; +import SignInNote from '../../fragments/_sign-in-note.mdx'; import AssumingUrl from '../../fragments/_web-assuming-url.mdx'; import WebConfigureRedirectUris from '../../fragments/_web-configure-redirect-uris.mdx'; import SignInFlowSummary from '../../fragments/_web-sign-in-flow-summary.mdx'; import SignOutNote from '../../fragments/_web-sign-out-note.md'; -### Configurar la aplicación \{#configure-application} +### Inicializar el proveedor de Logto \{#init-logto-provider} -En tu proyecto de Angular, añade el proveedor de autenticación en tu `app.config.ts`: +En tu proyecto Angular, registra `provideLogto` y las rutas de tu aplicación en `app.config.ts`: ```ts title="app/app.config.ts" -import { buildAngularAuthConfig } from '@logto/js'; -import { provideAuth } from 'angular-auth-oidc-client'; +// Importaciones necesarias para la configuración de la aplicación Angular +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: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - endpoint: '', - appId: '', - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - }), + provideLogto({ + endpoint: '', + appId: '', }), + provideRouter(routes), // ...otros proveedores ], }; ``` +`provideLogto` restaura el estado de autenticación automáticamente después del primer renderizado en el navegador. No necesitas llamar a un método de inicialización en tus componentes. + +:::note +Cuando uses renderizado del lado del servidor (SSR), el estado de autenticación y los tokens solo están disponibles en el navegador. Usa `isLoading()` para mostrar un estado de carga hasta que finalice la inicialización. Si necesitas datos autenticados durante el renderizado en el servidor, utiliza un SDK de servidor o BFF. +::: + ### Configurar URIs de redirección \{#configure-redirect-uris} @@ -38,55 +45,113 @@ export const appConfig: ApplicationConfig = { ### Manejar la redirección \{#handle-redirect} -Dado que usamos `http://localhost:3000/callback` como el URI de redirección, ahora necesitamos manejarlo adecuadamente. La biblioteca `angular-auth-oidc-client` proporciona soporte integrado para manejar la redirección. Solo necesitas configurar correctamente las configuraciones del proveedor de autenticación y la biblioteca se encargará del resto. +Crea un componente de callback para completar el inicio de sesión después de que Logto redirija al usuario de vuelta a tu aplicación. Usa `afterNextRender` para que el manejo del callback se ejecute solo en el navegador: + +```ts title="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) { +

{{ error.message }}

+ } @else { +

Completando el inicio de sesión...

+ } + `, +}) +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(() => { + // El SDK expone los errores del callback a través de logto.error() para la plantilla. + }); + }); + } +} +``` -```ts title="app/app.config.ts" -export const appConfig: ApplicationConfig = { - providers: [ - provideAuth({ - config: buildAngularAuthConfig({ - // ...otras configuraciones - // highlight-start - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - // highlight-end - }), - }), - // ...otros proveedores - ], -}; +`isSignInRedirected()` verifica si la URL coincide con una sesión de inicio de sesión activa. Si alguien visita la ruta de callback sin una sesión activa, este ejemplo lo devuelve a la página principal de la aplicación en lugar de intentar completar el inicio de sesión. + +Registra la ruta de callback en `app.routes.ts`. Debe coincidir con la ruta de tu URI de redirección y no debe requerir autenticación. Por ejemplo, usa `callback` para una URI de redirección que termine en `/callback`: + +```ts title="app/app.routes.ts" +import { type Routes } from '@angular/router'; + +import { CallbackComponent } from './callback.component'; + +export const routes: Routes = [ + { path: 'callback', component: CallbackComponent }, + // ...otras rutas +]; ``` -### Implementar inicio y cierre de sesión \{#implement-sign-in-and-sign-out} +El componente raíz necesita un `` para renderizar esta ruta, como se muestra en el siguiente paso. -En el componente donde deseas implementar el inicio y cierre de sesión (por ejemplo, `app.component.ts`), inyecta el `OidcSecurityService` y úsalo para iniciar y cerrar sesión. +### Implementar inicio y cierre de sesión \{#implement-sign-in-and-sign-out} -```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; +Inyecta `LogtoService` para iniciar el inicio y cierre de sesión. Pasa los URIs de redirección registrados a estos métodos. El `postRedirectUri` le indica al SDK a dónde navegar después de manejar correctamente el callback de inicio de sesión: -export class AppComponent implements OnInit { - constructor(public oidcSecurityService: OidcSecurityService) {} + - signIn() { - this.oidcSecurityService.authorize(); +```ts title="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, + }); } - signOut() { - this.oidcSecurityService.logoff().subscribe((result) => { - console.log('app sign-out', result); - }); + async signOut() { + await this.logto.signOut('http://localhost:3000/'); } } ``` -Luego, en la plantilla, añade botones para iniciar y cerrar sesión: +Lee las señales `isLoading()`, `isAuthenticated()` y `error()` directamente en la plantilla: ```html title="app/app.component.html" - -
- +@if (logto.error(); as error) { +

{{ error.message }}

+} @if (logto.isLoading()) { +

Cargando...

+} @else if (logto.isAuthenticated()) { + +} @else { + +} + + ``` +Mantén `` fuera de las condiciones de autenticación para que el callback pueda renderizarse antes de que el usuario inicie sesión. + diff --git a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md new file mode 100644 index 00000000000..63c1a120189 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md @@ -0,0 +1,17 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: [UserScope.Organizations], + // highlight-end + }), + // ...other providers + ], +}; +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md index d680e1b878a..cbc76209487 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md +++ b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md @@ -1,16 +1,17 @@ -```ts title="/app/app.config.ts" +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), - // ...other providers + // ...otros proveedores ], }; ``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md index 135f1616058..b280de267e7 100644 --- a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md +++ b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md @@ -1,17 +1,18 @@ ```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - scopes: ['openid', 'profile', 'offline_access', 'read', 'write'], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['shopping:read', 'shopping:write', 'store:read', 'store:write'], + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), - // ...other providers + // ...otros proveedores ], }; ``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md new file mode 100644 index 00000000000..49a85369613 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md @@ -0,0 +1,18 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['read', 'write'], // Alcances solicitados + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], // Recursos de API solicitados + // highlight-end + }), + // ...otros proveedores + ], +}; +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md new file mode 100644 index 00000000000..9ba41854e04 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md @@ -0,0 +1,28 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { + +
{{ accessToken() }}
+ } + `, +}) +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')); + } +} +``` diff --git a/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md new file mode 100644 index 00000000000..ec67cf42537 --- /dev/null +++ b/i18n/es/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md @@ -0,0 +1,59 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { +
    + @for (organizationId of organizationIds(); track organizationId) { +
  • + {{ organizationId }} + +
  • + } +
+
{{ organizationToken() }}
+ } + `, +}) +export class OrganizationsComponent { + readonly logto = inject(LogtoService); + readonly organizationIds = signal([]); + 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(() => { + // El SDK expone el error a través de logto.error() para la plantilla. + }); + }); + } + + async loadOrganizationToken(organizationId: string) { + this.organizationToken.set(await this.logto.getOrganizationToken(organizationId)); + } +} +``` diff --git a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx index c10bf845c5d..6f49deb0c87 100644 --- a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx +++ b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx @@ -2,7 +2,7 @@ slug: /quick-starts/angular sidebar_label: Angular sidebar_custom_props: - description: Angular est une bibliothèque JavaScript pour construire des interfaces utilisateur. (Angular is a JavaScript library for building user interfaces.) + description: Angular est un framework pour créer des applications web. --- import FurtherReadings from '../../fragments/_further-readings.md'; @@ -15,12 +15,15 @@ import Integration from './_integration.mdx'; # Ajoutez l’authentification à votre application Angular (Add authentication to your Angular application) +Ce guide vous montrera comment intégrer le SDK Logto Angular v2 dans votre application. + ## Prérequis \{#prerequisites} - Un compte [Logto Cloud](https://cloud.logto.io) ou un [Logto auto-hébergé](/introduction/set-up-logto-oss). -- Une application monopage Logto créée. +- Une application monopage (SPA) créée dans la Console Logto. +- Un projet Angular 20. ## Installation \{#installation} @@ -30,7 +33,7 @@ import Integration from './_integration.mdx'; -## Obtenir les informations utilisateur \{#get-user-information} +## Obtenir les informations de l’utilisateur \{#get-user-information} @@ -38,6 +41,6 @@ import Integration from './_integration.mdx'; -## Lectures complémentaires \{#further-readings} +## Pour aller plus loin \{#further-readings} diff --git a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx index 5251aadc6e2..7e3ca0f573e 100644 --- a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx +++ b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx @@ -1,22 +1,67 @@ import ApiResourcesDescription from '../../fragments/_api-resources-description.md'; import ConfigApiResources from '../../fragments/_config-api-resources.mdx'; +import FetchAccessTokenForApiResources from '../../fragments/_fetch-access-token-for-api-resources.mdx'; +import FetchOrganizationTokenForUser from '../../fragments/_fetch-organization-token-for-user.mdx'; +import ConfigOrganizationCode from './code/_config-organization-code.md'; import ConfigResourcesCode from './code/_config-resources-code.md'; import ConfigResourcesWithScopesCode from './code/_config-resources-with-scopes-code.md'; - -### Configurer `angular-auth-oidc-client` pour la ressource API \{#configure-angular-auth-oidc-client-for-api-resource} +import ConfigResourcesWithSharedScopesCode from './code/_config-resources-with-shared-scopes-code.md'; +import GetAccessTokenCode from './code/_get-access-token-code.md'; +import GetOrganizationAccessTokenCode from './code/_get-organization-access-token-code.md'; +### Configurer le client Logto \{#configure-logto-client} + } configResourcesWithScopesCode={} + configResourcesWithSharedScopesCode={} /> -Maintenant, le jeton d’accès (Access token) sera au format JSON Web Token (JWT) au lieu d'une chaîne aléatoire (jeton opaque). +Reconnectez-vous après avoir modifié les ressources ou les portées afin que l'utilisateur puisse autoriser la configuration mise à jour. -:::warning -Les deux `autoUserInfo` et `renewUserInfoAfterTokenRenew` seront désactivés lorsque `resource` est défini. Cela est dû au fait que le jeton d’accès (Access token) sera demandé pour la ressource API spécifique et non pour le point de terminaison des informations utilisateur. -::: +### Récupérer un jeton d’accès pour la ressource API \{#fetch-access-token-for-the-api-resource} + +} +/> + +Utilisez l'identifiant exact de la ressource de votre configuration. Appelez `getAccessToken(resource)` chaque fois que vous effectuez une requête API afin que le SDK puisse retourner un jeton valide, plutôt que de conserver indéfiniment un jeton dans votre composant. + +### Récupérer des jetons d’organisation \{#fetch-organization-tokens} + +} + getOrganizationAccessTokenCode={} +/> + +Fusionnez `UserScope.Organizations` avec toute portée existante, puis reconnectez-vous après avoir mis à jour la configuration. `getOrganizationToken(organizationId)` retourne un jeton pour l’organisation Logto sélectionnée ; utilisez `getAccessToken(resource)` pour un jeton de ressource API. + +### Attacher le jeton d’accès aux en-têtes de requête \{#attach-access-token-to-request-headers} + +Placez le jeton dans l’en-tête HTTP `Authorization` en utilisant le format Bearer (`Bearer YOUR_TOKEN`). Par exemple, ajoutez cette méthode à un composant authentifié qui injecte `LogtoService` : -Actuellement, seuls les SDK officiels de Logto prennent en charge la capacité de demander à la fois des informations utilisateur et des jetons d’accès (Access tokens) pour les ressources API. Si vous avez besoin de demander les deux, n'hésitez pas à nous contacter. +```ts +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(); +} +``` + +:::note +L’exemple utilise `fetch`. Si vous utilisez Angular `HttpClient`, définissez le même en-tête `Authorization` dans les options de la requête. +::: diff --git a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx index d157cba6967..b19a01a7ea3 100644 --- a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx +++ b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx @@ -5,129 +5,105 @@ import FindUserInfoMissing from '../../fragments/_find-user-info-missing.mdx'; import ScopesAndClaims from '../../fragments/_scopes-and-claims.mdx'; import ScopesAndClaimsIntroduction from '../../fragments/_scopes-claims-introduction.md'; -Une fois que l'utilisateur s'est connecté avec succès, Logto émettra un [jeton d’identifiant (ID token)](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) qui contient les revendications d'informations utilisateur. Le jeton d’identifiant est un JSON Web Token (JWT). - -Il est important de noter que les revendications d'informations utilisateur qui peuvent être récupérées dépendent des portées utilisées par l'utilisateur lors de la connexion, et en considérant les performances et la taille des données, le jeton d’identifiant peut ne pas contenir toutes les revendications utilisateur, certaines revendications utilisateur ne sont disponibles que dans le [point de terminaison userinfo](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) (voir la liste ci-dessous). - -L'utilitaire `buildAngularAuthConfig()` activera `autoUserInfo` et `renewUserInfoAfterTokenRenew` s'il n'y a pas de `resource` fourni dans la configuration. Cela signifie que Logto récupérera automatiquement les informations utilisateur après la connexion de l'utilisateur et renouvellera les informations utilisateur après le renouvellement du jeton. - -:::info -Pour en savoir plus sur la configuration de la bibliothèque `angular-auth-oidc-client`, consultez la [documentation officielle](https://angular-auth-oidc-client.com/). -::: - ### Afficher les informations utilisateur \{#display-user-information} -Le `OidcSecurityService` fournit un moyen pratique de s'abonner à l'état d'authentification ainsi qu'aux informations utilisateur : +Pour afficher les informations de l'utilisateur, utilisez `getIdTokenClaims()` pour lire les revendications (Claims) du jeton d’identifiant (ID token) sans requête réseau supplémentaire. Ajoutez un `effect` à votre `AppComponent` pour charger les revendications lorsque `isAuthenticated()` devient vrai, y compris lors de la restauration d'une session existante. Importez `JsonPipe` pour afficher le résultat : ```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; -import { decodeIdToken, type IdTokenClaims } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - idTokenClaims?: IdTokenClaims; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService.checkAuth().subscribe(({ isAuthenticated, idToken, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - this.idTokenClaims = decodeIdToken(idToken); - this.accessToken = accessToken; +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(undefined); + + constructor() { + effect(() => { + if (!this.logto.isAuthenticated()) { + this.user.set(undefined); + return; + } + + void this.logto + .getIdTokenClaims() + .then((claims) => { + this.user.set(claims); + }) + .catch(() => { + // Le SDK expose l’erreur via logto.error() pour le template. + }); }); } - // ...other methods + // ...conservez les méthodes signIn() et signOut() de l’étape précédente } ``` -Et utilisez-le dans le modèle : +Ajoutez ce qui suit à l'intérieur de la branche `logto.isAuthenticated()` de votre template : ```html title="app/app.component.html" - - -
{{ idTokenClaims | json }}
-

Access token: {{ accessToken }}

- - -
+@if (user(); as claims) { +
{{ claims | json }}
+} ``` ### Demander des revendications supplémentaires \{#request-additional-claims} - + -Pour demander des portées supplémentaires, vous pouvez configurer les configurations du fournisseur d'authentification : +Ajoutez les portées (scopes) à votre configuration `provideLogto` : -```tsx title="app/app.config.ts" -import { UserScope, buildAngularAuthConfig } from '@logto/js'; +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other configs - // highlight-start - scopes: [ - UserScope.Email, - UserScope.Phone, - UserScope.CustomData, - UserScope.Identities, - UserScope.Organizations, - ], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + scopes: [ + UserScope.Email, + UserScope.Phone, + UserScope.CustomData, + UserScope.Identities, + UserScope.Organizations, + ], }), - // ...other providers + // ...autres providers ], }; ``` -Ensuite, vous pouvez accéder aux revendications supplémentaires dans la valeur de retour de `idToken`. +Reconnectez-vous après avoir modifié les portées. Les revendications supplémentaires du jeton d’identifiant (ID token), telles que `email` et `phone_number`, seront disponibles via `getIdTokenClaims()` et affichées par l’exemple ci-dessus. -{/* eslint-disable prettier/prettier */} {`import { OidcSecurityService } from 'angular-auth-oidc-client'; -// highlight-next-line -import { type UserInfoResponse } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - // highlight-next-line - userData?: UserInfoResponse; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService - .checkAuth() - // highlight-next-line - .subscribe(({ isAuthenticated, userData, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - // highlight-next-line - this.userData = userData; - this.accessToken = accessToken; - }); + {`// Ajoutez cette méthode à AppComponent et appelez-la après la connexion. +async loadUserInfo() { + const userInfo = await this.logto.fetchUserInfo(); + // Vous pouvez maintenant accéder à userInfo.custom_data, userInfo.identities, etc. + return userInfo; +}`} } - - // ...other methods -} - -// Now you can access the claim \`userData.custom_data\``} -} /> -{/* eslint-enable prettier/prettier */} + +`fetchUserInfo()` peut être utilisé en parallèle des jetons d’accès (Access tokens) de ressource API. La configuration de `resources` n'empêche pas le SDK de demander les informations utilisateur. ### Portées et revendications \{#scopes-and-claims} diff --git a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md index e34a62bb6b1..2d63a21dbb1 100644 --- a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md +++ b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md @@ -1,6 +1,6 @@ :::tip -- La démonstration suivante est construite sur Angular 18.0.0 et [angular-auth-oidc-client](https://github.com/damienbod/angular-auth-oidc-client). -- Le projet d'exemple est disponible dans le [répertoire GitHub](https://github.com/logto-io/js/tree/master/packages/angular-sample). +- Ce guide utilise le SDK officiel `@logto/angular` v2, qui prend en charge Angular 20 et fournit l'injection de dépendances ainsi que les Signals. +- Le projet d'exemple est disponible dans notre [dépôt SDK](https://github.com/logto-io/js/tree/master/packages/angular-sample). ::: diff --git a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx index a6d84659f96..41aabde1f14 100644 --- a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx +++ b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx @@ -1,30 +1,3 @@ -import TabItem from '@theme/TabItem'; -import Tabs from '@theme/Tabs'; +import NpmLikeInstallation from '../../fragments/_npm-like-installation.mdx'; -Installez Logto JS core SDK et la bibliothèque cliente Angular OIDC : - - - - - -```bash -npm i @logto/js angular-auth-oidc-client -``` - - - - -```bash -pnpm add @logto/js angular-auth-oidc-client -``` - - - - -```bash -yarn add @logto/js angular-auth-oidc-client -``` - - - - + diff --git a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx index 03e697c84be..4857d8bd632 100644 --- a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx +++ b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx @@ -1,34 +1,40 @@ import Checkpoint from '../../fragments/_checkpoint-test-your-application.md'; +import SignInNote from '../../fragments/_sign-in-note.mdx'; import AssumingUrl from '../../fragments/_web-assuming-url.mdx'; import WebConfigureRedirectUris from '../../fragments/_web-configure-redirect-uris.mdx'; import SignInFlowSummary from '../../fragments/_web-sign-in-flow-summary.mdx'; import SignOutNote from '../../fragments/_web-sign-out-note.md'; -### Configurer l'application \{#configure-application} +### Initialiser le fournisseur Logto \{#init-logto-provider} -Dans votre projet Angular, ajoutez le fournisseur d'authentification dans votre `app.config.ts` : +Dans votre projet Angular, enregistrez `provideLogto` et vos routes d'application dans `app.config.ts` : ```ts title="app/app.config.ts" -import { buildAngularAuthConfig } from '@logto/js'; -import { provideAuth } from 'angular-auth-oidc-client'; +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: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - endpoint: '', - appId: '', - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - }), + provideLogto({ + endpoint: '', + appId: '', }), - // ...other providers + provideRouter(routes), + // ...autres providers ], }; ``` -### Configurer les URIs de redirection \{#configure-redirect-uris} +`provideLogto` restaure automatiquement l'état d'authentification après le premier rendu du navigateur. Vous n'avez pas besoin d'appeler une méthode d'initialisation dans vos composants. + +:::note +Lorsque vous utilisez le rendu côté serveur (SSR), l'état d'authentification et les jetons sont disponibles uniquement dans le navigateur. Utilisez `isLoading()` pour afficher un état de chargement jusqu'à la fin de l'initialisation. Si vous avez besoin de données authentifiées lors du rendu serveur, utilisez un SDK serveur ou BFF. +::: + +### Configurer les URI de redirection \{#configure-redirect-uris} @@ -38,55 +44,113 @@ export const appConfig: ApplicationConfig = { ### Gérer la redirection \{#handle-redirect} -Puisque nous utilisons `http://localhost:3000/callback` comme URI de redirection, nous devons maintenant la gérer correctement. La bibliothèque `angular-auth-oidc-client` fournit un support intégré pour gérer la redirection. Vous pouvez simplement configurer correctement les configurations du fournisseur d'authentification et la bibliothèque s'occupera du reste. +Créez un composant de rappel pour terminer la connexion après que Logto ait redirigé l'utilisateur vers votre application. Utilisez `afterNextRender` pour que le traitement du callback ne s'exécute que dans le navigateur : + +```ts title="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) { +

{{ error.message }}

+ } @else { +

Finalisation de la connexion...

+ } + `, +}) +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(() => { + // Le SDK expose les erreurs de callback via logto.error() pour le template. + }); + }); + } +} +``` -```ts title="app/app.config.ts" -export const appConfig: ApplicationConfig = { - providers: [ - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - // highlight-end - }), - }), - // ...other providers - ], -}; +`isSignInRedirected()` vérifie si l'URL correspond à une session de connexion active. Si quelqu'un visite la route de callback sans session, cet exemple le renvoie à la page d'accueil de l'application au lieu de tenter de finaliser la connexion. + +Enregistrez la route de callback dans `app.routes.ts`. Elle doit correspondre au chemin de votre URI de redirection et ne doit pas nécessiter d'authentification. Par exemple, utilisez `callback` pour une URI de redirection se terminant par `/callback` : + +```ts title="app/app.routes.ts" +import { type Routes } from '@angular/router'; + +import { CallbackComponent } from './callback.component'; + +export const routes: Routes = [ + { path: 'callback', component: CallbackComponent }, + // ...autres routes +]; ``` -### Implémenter la connexion et la déconnexion \{#implement-sign-in-and-sign-out} +Le composant racine doit contenir un `` pour afficher cette route, comme montré à l'étape suivante. -Dans le composant où vous souhaitez implémenter la connexion et la déconnexion (par exemple, `app.component.ts`), injectez le `OidcSecurityService` et utilisez-le pour vous connecter et vous déconnecter. +### Implémenter la connexion et la déconnexion \{#implement-sign-in-and-sign-out} -```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; +Injectez `LogtoService` pour démarrer la connexion et la déconnexion. Passez les URI de redirection enregistrées à ces méthodes. Le `postRedirectUri` indique au SDK où naviguer après avoir traité avec succès le callback de connexion : -export class AppComponent implements OnInit { - constructor(public oidcSecurityService: OidcSecurityService) {} + - signIn() { - this.oidcSecurityService.authorize(); +```ts title="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, + }); } - signOut() { - this.oidcSecurityService.logoff().subscribe((result) => { - console.log('app sign-out', result); - }); + async signOut() { + await this.logto.signOut('http://localhost:3000/'); } } ``` -Ensuite, dans le modèle, ajoutez des boutons pour se connecter et se déconnecter : +Lisez les signaux `isLoading()`, `isAuthenticated()` et `error()` directement dans le template : ```html title="app/app.component.html" - -
- +@if (logto.error(); as error) { +

{{ error.message }}

+} @if (logto.isLoading()) { +

Chargement…

+} @else if (logto.isAuthenticated()) { + +} @else { + +} + + ``` +Gardez `` en dehors des conditions d'authentification afin que le callback puisse s'afficher avant que l'utilisateur ne soit connecté. + diff --git a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md new file mode 100644 index 00000000000..63c1a120189 --- /dev/null +++ b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md @@ -0,0 +1,17 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: [UserScope.Organizations], + // highlight-end + }), + // ...other providers + ], +}; +``` diff --git a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md index d680e1b878a..7d6a1a15342 100644 --- a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md +++ b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md @@ -1,14 +1,15 @@ -```ts title="/app/app.config.ts" +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), // ...other providers ], diff --git a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md index 135f1616058..d2c0c7bba17 100644 --- a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md +++ b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md @@ -1,15 +1,16 @@ ```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - scopes: ['openid', 'profile', 'offline_access', 'read', 'write'], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['shopping:read', 'shopping:write', 'store:read', 'store:write'], + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), // ...other providers ], diff --git a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md new file mode 100644 index 00000000000..18f488dfd41 --- /dev/null +++ b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md @@ -0,0 +1,18 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['read', 'write'], + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end + }), + // ...other providers + ], +}; +``` diff --git a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md new file mode 100644 index 00000000000..58bd6a338fb --- /dev/null +++ b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md @@ -0,0 +1,28 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { + +
{{ accessToken() }}
+ } + `, +}) +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')); + } +} +``` diff --git a/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md new file mode 100644 index 00000000000..ee020b79d56 --- /dev/null +++ b/i18n/fr/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md @@ -0,0 +1,59 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { +
    + @for (organizationId of organizationIds(); track organizationId) { +
  • + {{ organizationId }} + +
  • + } +
+
{{ organizationToken() }}
+ } + `, +}) +export class OrganizationsComponent { + readonly logto = inject(LogtoService); + readonly organizationIds = signal([]); + 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(() => { + // Le SDK expose l’erreur via logto.error() pour le template. + }); + }); + } + + async loadOrganizationToken(organizationId: string) { + this.organizationToken.set(await this.logto.getOrganizationToken(organizationId)); + } +} +``` diff --git a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx index 8e353739e3b..3d01f6e7729 100644 --- a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx +++ b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx @@ -2,7 +2,7 @@ slug: /quick-starts/angular sidebar_label: Angular sidebar_custom_props: - description: Angular はユーザーインターフェースを構築するための JavaScript ライブラリです。 + description: Angular は Web アプリケーションを構築するためのフレームワークです。 --- import FurtherReadings from '../../fragments/_further-readings.md'; @@ -15,12 +15,15 @@ import Integration from './_integration.mdx'; # Angular アプリケーションへ認証機能の追加 +このガイドでは、Logto Angular SDK v2 をアプリケーションに統合する方法を紹介します。 + ## 前提条件 \{#prerequisites} -- [Logto Cloud](https://cloud.logto.io) アカウントまたは [セルフホスト Logto](/introduction/set-up-logto-oss)。 -- Logto シングルページアプリケーションが作成されていること。 +- [Logto Cloud](https://cloud.logto.io) アカウント、または [セルフホスト型 Logto](/introduction/set-up-logto-oss)。 +- Logto コンソールで作成したシングルページアプリケーション (SPA)。 +- Angular 20 プロジェクト。 ## インストール \{#installation} @@ -38,6 +41,6 @@ import Integration from './_integration.mdx'; -## さらなる読み物 \{#further-readings} +## さらに読む \{#further-readings} diff --git a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx index 833755b48be..bfba116e92e 100644 --- a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx +++ b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx @@ -1,22 +1,67 @@ import ApiResourcesDescription from '../../fragments/_api-resources-description.md'; import ConfigApiResources from '../../fragments/_config-api-resources.mdx'; +import FetchAccessTokenForApiResources from '../../fragments/_fetch-access-token-for-api-resources.mdx'; +import FetchOrganizationTokenForUser from '../../fragments/_fetch-organization-token-for-user.mdx'; +import ConfigOrganizationCode from './code/_config-organization-code.md'; import ConfigResourcesCode from './code/_config-resources-code.md'; import ConfigResourcesWithScopesCode from './code/_config-resources-with-scopes-code.md'; - -### API リソース用に `angular-auth-oidc-client` を設定する \{#configure-angular-auth-oidc-client-for-api-resource} +import ConfigResourcesWithSharedScopesCode from './code/_config-resources-with-shared-scopes-code.md'; +import GetAccessTokenCode from './code/_get-access-token-code.md'; +import GetOrganizationAccessTokenCode from './code/_get-organization-access-token-code.md'; +### Logto クライアントの設定 \{#configure-logto-client} + } configResourcesWithScopesCode={} + configResourcesWithSharedScopesCode={} /> -これで、アクセス トークンはランダムな文字列(不透明トークン)ではなく、JSON Web Token (JWT) 形式になります。 +リソースやスコープを変更した場合は、再度サインインしてユーザーが更新された設定を認可 (Authorization) できるようにしてください。 -:::warning -`resource` が設定されると、`autoUserInfo` と `renewUserInfoAfterTokenRenew` の両方が無効になります。これは、アクセス トークンがユーザー情報エンドポイントではなく、特定の API リソースのために要求されるためです。 -::: +### API リソース用のアクセス トークン (Access token) を取得する \{#fetch-access-token-for-the-api-resource} + +} +/> + +設定で指定したリソース識別子を正確に使用してください。API リクエストを行うたびに `getAccessToken(resource)` を呼び出すことで、SDK が有効なトークンを返します。トークンをコンポーネント内で無期限に保持しないようにしましょう。 + +### 組織トークン (Organization token) を取得する \{#fetch-organization-tokens} + +} + getOrganizationAccessTokenCode={} +/> + +`UserScope.Organizations` を既存のスコープとマージし、設定を更新した後は再度サインインしてください。`getOrganizationToken(organizationId)` は選択した Logto 組織のトークンを返します。API リソースのトークンが必要な場合は `getAccessToken(resource)` を使用してください。 + +### アクセス トークン (Access token) をリクエストヘッダーに付与する \{#attach-access-token-to-request-headers} + +トークンは Bearer 形式(`Bearer YOUR_TOKEN`)で `Authorization` HTTP ヘッダーに設定します。例えば、`LogtoService` を注入した認証済みコンポーネントに次のメソッドを追加します: -現在、Logto の公式 SDK のみがユーザー情報と API リソース アクセス トークンの両方を要求する機能をサポートしています。両方を要求する必要がある場合は、お気軽にお問い合わせください。 +```ts +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(); +} +``` + +:::note +この例では `fetch` を使用しています。Angular の `HttpClient` を利用する場合も、同じくリクエストオプションで `Authorization` ヘッダーを設定してください。 +::: diff --git a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx index c49913e1b9d..cd3d69d6f49 100644 --- a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx +++ b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx @@ -5,130 +5,106 @@ import FindUserInfoMissing from '../../fragments/_find-user-info-missing.mdx'; import ScopesAndClaims from '../../fragments/_scopes-and-claims.mdx'; import ScopesAndClaimsIntroduction from '../../fragments/_scopes-claims-introduction.md'; -ユーザーが正常にサインインすると、Logto はユーザー情報クレームを含む [ID トークン](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) を発行します。ID トークンは JSON Web Token (JWT) です。 - -ユーザー情報クレームは、サインイン時にユーザーが使用したスコープに依存して取得できることに注意が必要です。パフォーマンスとデータサイズを考慮して、ID トークンにはすべてのユーザークレームが含まれていない場合があります。一部のユーザークレームは [userinfo エンドポイント](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) でのみ利用可能です(以下の関連リストを参照)。 - -`buildAngularAuthConfig()` ユーティリティは、設定に `resource` が提供されていない場合、`autoUserInfo` と `renewUserInfoAfterTokenRenew` を有効にします。これは、ユーザーがサインインした後に Logto が自動的にユーザー情報を取得し、トークンが更新された後にユーザー情報を更新することを意味します。 - -:::info -`angular-auth-oidc-client` ライブラリの設定について詳しく知るには、[公式ドキュメント](https://angular-auth-oidc-client.com/) を参照してください。 -::: - ### ユーザー情報の表示 \{#display-user-information} -`OidcSecurityService` は、認証状態およびユーザー情報を購読するための便利な方法を提供します: +ユーザー情報を表示するには、追加のネットワークリクエストなしで ID トークンからクレーム (Claims) を読み取るために `getIdTokenClaims()` を使用します。`isAuthenticated()` が true になったとき(既存セッションが復元された場合も含む)にクレーム (Claims) を読み込むため、`AppComponent` に `effect` を追加します。結果を表示するために `JsonPipe` をインポートします: ```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; -import { decodeIdToken, type IdTokenClaims } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - idTokenClaims?: IdTokenClaims; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService.checkAuth().subscribe(({ isAuthenticated, idToken, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - this.idTokenClaims = decodeIdToken(idToken); - this.accessToken = accessToken; +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(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. + }); }); } - // ...other methods + // ...keep the signIn() and signOut() methods from the previous step } ``` -テンプレートで使用します: +テンプレートの `logto.isAuthenticated()` ブランチ内に次を追加します: ```html title="app/app.component.html" - - -
{{ idTokenClaims | json }}
-

Access token: {{ accessToken }}

- - -
+@if (user(); as claims) { +
{{ claims | json }}
+} ``` -### 追加のクレームをリクエストする \{#request-additional-claims} +### 追加のクレーム (Claims) をリクエストする \{#request-additional-claims} - + -追加のスコープをリクエストするには、認証プロバイダーの設定を構成します: +`provideLogto` 設定にスコープ (Scopes) を追加します: -```tsx title="app/app.config.ts" -import { UserScope, buildAngularAuthConfig } from '@logto/js'; +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other configs - // highlight-start - scopes: [ - UserScope.Email, - UserScope.Phone, - UserScope.CustomData, - UserScope.Identities, - UserScope.Organizations, - ], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + scopes: [ + UserScope.Email, + UserScope.Phone, + UserScope.CustomData, + UserScope.Identities, + UserScope.Organizations, + ], }), // ...other providers ], }; ``` -その後、`idToken` の戻り値で追加のクレームにアクセスできます。 +スコープ (Scopes) を変更した後は再度サインインしてください。`email` や `phone_number` などの追加の ID トークン クレーム (Claims) が `getIdTokenClaims()` から取得でき、上記の例で表示されます。 -{/* eslint-disable prettier/prettier */} {`import { OidcSecurityService } from 'angular-auth-oidc-client'; -// highlight-next-line -import { type UserInfoResponse } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - // highlight-next-line - userData?: UserInfoResponse; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService - .checkAuth() - // highlight-next-line - .subscribe(({ isAuthenticated, userData, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - // highlight-next-line - this.userData = userData; - this.accessToken = accessToken; - }); + {`// このメソッドを AppComponent に追加し、サインイン後に呼び出してください。 +async loadUserInfo() { + const userInfo = await this.logto.fetchUserInfo(); + // これで userInfo.custom_data, userInfo.identities などにアクセスできます。 + return userInfo; +}`} } - - // ...other methods -} - -// Now you can access the claim \`userData.custom_data\``} -} /> -{/* eslint-enable prettier/prettier */} -### スコープとクレーム \{#scopes-and-claims} +`fetchUserInfo()` は API リソース アクセストークン (Access token) と併用できます。`resources` を設定しても、SDK がユーザー情報をリクエストすることを妨げません。 + +### スコープ (Scopes) とクレーム (Claims) \{#scopes-and-claims} diff --git a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md index 9153bcddb20..6e5291bbb9e 100644 --- a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md +++ b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md @@ -1,6 +1,6 @@ :::tip -- 以下のデモは Angular 18.0.0 と [angular-auth-oidc-client](https://github.com/damienbod/angular-auth-oidc-client) に基づいて構築されています。 -- サンプルプロジェクトは [GitHub リポジトリ](https://github.com/logto-io/js/tree/master/packages/angular-sample) で利用可能です。 +- このガイドでは、Angular 20 をサポートし、依存性注入と Signals を提供するファーストパーティ製の `@logto/angular` v2 SDK を使用しています。 +- サンプルプロジェクトは [SDK リポジトリ](https://github.com/logto-io/js/tree/master/packages/angular-sample) で利用できます。 ::: diff --git a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx index 9d513e9b0b8..41aabde1f14 100644 --- a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx +++ b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx @@ -1,30 +1,3 @@ -import TabItem from '@theme/TabItem'; -import Tabs from '@theme/Tabs'; +import NpmLikeInstallation from '../../fragments/_npm-like-installation.mdx'; -Logto JS コア SDK と Angular OIDC クライアントライブラリをインストールします: - - - - - -```bash -npm i @logto/js angular-auth-oidc-client -``` - - - - -```bash -pnpm add @logto/js angular-auth-oidc-client -``` - - - - -```bash -yarn add @logto/js angular-auth-oidc-client -``` - - - - + diff --git a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx index 80d76c6d9d9..a3d88d35307 100644 --- a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx +++ b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx @@ -1,33 +1,39 @@ import Checkpoint from '../../fragments/_checkpoint-test-your-application.md'; +import SignInNote from '../../fragments/_sign-in-note.mdx'; import AssumingUrl from '../../fragments/_web-assuming-url.mdx'; import WebConfigureRedirectUris from '../../fragments/_web-configure-redirect-uris.mdx'; import SignInFlowSummary from '../../fragments/_web-sign-in-flow-summary.mdx'; import SignOutNote from '../../fragments/_web-sign-out-note.md'; -### アプリケーションの設定 \{#configure-application} +### Logto プロバイダーの初期化 \{#init-logto-provider} -Angular プロジェクトで、`app.config.ts` に認証プロバイダーを追加します: +Angular プロジェクトで、`app.config.ts` に `provideLogto` とアプリケーションのルートを登録します: ```ts title="app/app.config.ts" -import { buildAngularAuthConfig } from '@logto/js'; -import { provideAuth } from 'angular-auth-oidc-client'; +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: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - endpoint: '', - appId: '', - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - }), + provideLogto({ + endpoint: '', + appId: '', }), - // ...他のプロバイダー + provideRouter(routes), + // ...other providers ], }; ``` +`provideLogto` は、最初のブラウザレンダリング後に認証 (Authentication) 状態を自動的に復元します。コンポーネントで初期化メソッドを呼び出す必要はありません。 + +:::note +サーバーサイドレンダリング (SSR) を使用する場合、認証 (Authentication) 状態とトークンはブラウザでのみ利用可能です。初期化が完了するまで `isLoading()` を使ってローディング状態を表示してください。サーバーレンダリング中に認証済みデータが必要な場合は、サーバーまたは BFF SDK を利用してください。 +::: + ### リダイレクト URI の設定 \{#configure-redirect-uris} @@ -38,55 +44,113 @@ export const appConfig: ApplicationConfig = { ### リダイレクトの処理 \{#handle-redirect} -`http://localhost:3000/callback` をリダイレクト URI として使用するため、適切に処理する必要があります。`angular-auth-oidc-client` ライブラリはリダイレクトの処理をサポートしています。認証プロバイダーの設定を適切に行うだけで、ライブラリが残りを処理します。 +Logto がユーザーをアプリケーションにリダイレクトした後、サインインを完了するためのコールバックコンポーネントを作成します。`afterNextRender` を使用して、コールバック処理がブラウザでのみ実行されるようにします: + +```ts title="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) { +

{{ error.message }}

+ } @else { +

サインインを完了しています...

+ } + `, +}) +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 はコールバックエラーを logto.error() 経由でテンプレートに公開します。 + }); + }); + } +} +``` -```ts title="app/app.config.ts" -export const appConfig: ApplicationConfig = { - providers: [ - provideAuth({ - config: buildAngularAuthConfig({ - // ...他の設定 - // highlight-start - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - // highlight-end - }), - }), - // ...他のプロバイダー - ], -}; +`isSignInRedirected()` は、URL がアクティブなサインインセッションと一致するかどうかを確認します。誰かがセッションなしでコールバックルートにアクセスした場合、この例ではサインインを試みる代わりにアプリケーションのホームページへ戻します。 + +`app.routes.ts` でコールバックルートを登録します。これはリダイレクト URI のパスと一致し、認証 (Authentication) を必要としない必要があります。たとえば、リダイレクト URI が `/callback` で終わる場合は `callback` を使用します: + +```ts title="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 +]; ``` -### サインインとサインアウトの実装 \{#implement-sign-in-and-sign-out} +ルートコンポーネントには、このルートをレンダリングするために `` が必要です(次のステップで説明します)。 -サインインとサインアウトを実装したいコンポーネント(例えば、`app.component.ts`)で、`OidcSecurityService` をインジェクトし、それを使用してサインインとサインアウトを行います。 +### サインインとサインアウトの実装 \{#implement-sign-in-and-sign-out} -```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; +`LogtoService` をインジェクトしてサインインとサインアウトを開始します。これらのメソッドには登録済みのリダイレクト URI を渡します。`postRedirectUri` は、サインインコールバックの処理が正常に完了した後に SDK が遷移する場所を指定します: -export class AppComponent implements OnInit { - constructor(public oidcSecurityService: OidcSecurityService) {} + - signIn() { - this.oidcSecurityService.authorize(); +```ts title="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, + }); } - signOut() { - this.oidcSecurityService.logoff().subscribe((result) => { - console.log('app sign-out', result); - }); + async signOut() { + await this.logto.signOut('http://localhost:3000/'); } } ``` -次に、テンプレートにサインインとサインアウトのボタンを追加します: +テンプレート内で `isLoading()`、`isAuthenticated()`、`error()` シグナルを直接参照できます: ```html title="app/app.component.html" - -
- +@if (logto.error(); as error) { +

{{ error.message }}

+} @if (logto.isLoading()) { +

読み込み中...

+} @else if (logto.isAuthenticated()) { + +} @else { + +} + + ``` +コールバックがサインイン前にレンダリングできるよう、`` は認証 (Authentication) 条件の外側に配置してください。 + diff --git a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md new file mode 100644 index 00000000000..63c1a120189 --- /dev/null +++ b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md @@ -0,0 +1,17 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: [UserScope.Organizations], + // highlight-end + }), + // ...other providers + ], +}; +``` diff --git a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md index d680e1b878a..7d6a1a15342 100644 --- a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md +++ b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md @@ -1,14 +1,15 @@ -```ts title="/app/app.config.ts" +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), // ...other providers ], diff --git a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md index 135f1616058..d2c0c7bba17 100644 --- a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md +++ b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md @@ -1,15 +1,16 @@ ```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - scopes: ['openid', 'profile', 'offline_access', 'read', 'write'], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['shopping:read', 'shopping:write', 'store:read', 'store:write'], + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), // ...other providers ], diff --git a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md new file mode 100644 index 00000000000..18f488dfd41 --- /dev/null +++ b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md @@ -0,0 +1,18 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['read', 'write'], + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end + }), + // ...other providers + ], +}; +``` diff --git a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md new file mode 100644 index 00000000000..51cc44231ae --- /dev/null +++ b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md @@ -0,0 +1,28 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { + +
{{ accessToken() }}
+ } + `, +}) +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')); + } +} +``` diff --git a/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md new file mode 100644 index 00000000000..8971e7f66b3 --- /dev/null +++ b/i18n/ja/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md @@ -0,0 +1,59 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { +
    + @for (organizationId of organizationIds(); track organizationId) { +
  • + {{ organizationId }} + +
  • + } +
+
{{ organizationToken() }}
+ } + `, +}) +export class OrganizationsComponent { + readonly logto = inject(LogtoService); + readonly organizationIds = signal([]); + 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() を通じてエラーを公開します。 + }); + }); + } + + async loadOrganizationToken(organizationId: string) { + this.organizationToken.set(await this.logto.getOrganizationToken(organizationId)); + } +} +``` diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx index 868585568e0..6a49aac1b95 100644 --- a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx +++ b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx @@ -2,7 +2,7 @@ slug: /quick-starts/angular sidebar_label: Angular sidebar_custom_props: - description: Angular는 사용자 인터페이스를 구축하기 위한 JavaScript 라이브러리입니다. + description: Angular는 웹 애플리케이션을 구축하기 위한 프레임워크입니다. --- import FurtherReadings from '../../fragments/_further-readings.md'; @@ -15,12 +15,15 @@ import Integration from './_integration.mdx'; # Angular 애플리케이션에 인증 (Authentication)을 추가하세요 +이 가이드에서는 Logto Angular SDK v2를 애플리케이션에 통합하는 방법을 안내합니다. + ## 사전 준비 사항 \{#prerequisites} - [Logto Cloud](https://cloud.logto.io) 계정 또는 [셀프 호스팅 Logto](/introduction/set-up-logto-oss). -- Logto 단일 페이지 애플리케이션이 생성되어 있어야 합니다. +- Logto Console에서 생성된 싱글 페이지 애플리케이션 (SPA). +- Angular 20 프로젝트. ## 설치 \{#installation} diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx index feb26609420..36f41befa5d 100644 --- a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx +++ b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx @@ -1,22 +1,67 @@ import ApiResourcesDescription from '../../fragments/_api-resources-description.md'; import ConfigApiResources from '../../fragments/_config-api-resources.mdx'; +import FetchAccessTokenForApiResources from '../../fragments/_fetch-access-token-for-api-resources.mdx'; +import FetchOrganizationTokenForUser from '../../fragments/_fetch-organization-token-for-user.mdx'; +import ConfigOrganizationCode from './code/_config-organization-code.md'; import ConfigResourcesCode from './code/_config-resources-code.md'; import ConfigResourcesWithScopesCode from './code/_config-resources-with-scopes-code.md'; - -### API 리소스에 대해 `angular-auth-oidc-client` 구성하기 \{#configure-angular-auth-oidc-client-for-api-resource} +import ConfigResourcesWithSharedScopesCode from './code/_config-resources-with-shared-scopes-code.md'; +import GetAccessTokenCode from './code/_get-access-token-code.md'; +import GetOrganizationAccessTokenCode from './code/_get-organization-access-token-code.md'; +### Logto 클라이언트 구성하기 \{#configure-logto-client} + } configResourcesWithScopesCode={} + configResourcesWithSharedScopesCode={} /> -이제 액세스 토큰은 불투명 토큰 대신 JSON Web Token (JWT) 형식으로 제공됩니다. +리소스 또는 스코프를 변경한 후에는 사용자가 업데이트된 구성을 인가 (Authorization)할 수 있도록 다시 로그인하세요. -:::warning -`resource`가 설정되면 `autoUserInfo`와 `renewUserInfoAfterTokenRenew`는 비활성화됩니다. 이는 액세스 토큰이 사용자 정보 엔드포인트가 아닌 특정 API 리소스를 위해 요청되기 때문입니다. -::: +### API 리소스에 대한 액세스 토큰 가져오기 \{#fetch-access-token-for-the-api-resource} + +} +/> + +구성에서 사용한 정확한 리소스 식별자를 사용하세요. API 요청을 할 때마다 `getAccessToken(resource)`를 호출하면 SDK가 유효한 토큰을 반환하므로, 컴포넌트에 토큰을 무기한 보관하지 않아도 됩니다. + +### 조직 토큰 가져오기 \{#fetch-organization-tokens} + +} + getOrganizationAccessTokenCode={} +/> + +`UserScope.Organizations`를 기존 스코프와 병합하고, 구성을 업데이트한 후 다시 로그인하세요. `getOrganizationToken(organizationId)`는 선택한 Logto 조직에 대한 토큰을 반환하며, API 리소스 토큰이 필요할 때는 `getAccessToken(resource)`를 사용하세요. + +### 액세스 토큰을 요청 헤더에 첨부하기 \{#attach-access-token-to-request-headers} + +토큰을 Bearer 형식(`Bearer YOUR_TOKEN`)으로 `Authorization` HTTP 헤더에 넣으세요. 예를 들어, `LogtoService`를 주입받는 인증된 컴포넌트에 아래 메서드를 추가할 수 있습니다: -현재, Logto 공식 SDK만이 사용자 정보와 API 리소스 액세스 토큰을 모두 요청할 수 있는 기능을 지원합니다. 둘 다 요청해야 하는 경우, 주저하지 말고 저희에게 연락해 주세요. +```ts +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(); +} +``` + +:::note +예제에서는 `fetch`를 사용했습니다. Angular `HttpClient`를 사용하는 경우에도 동일하게 요청 옵션에 `Authorization` 헤더를 설정하세요. +::: diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx index 9d1ac8d9f7b..d9fd307851b 100644 --- a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx +++ b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx @@ -5,130 +5,106 @@ import FindUserInfoMissing from '../../fragments/_find-user-info-missing.mdx'; import ScopesAndClaims from '../../fragments/_scopes-and-claims.mdx'; import ScopesAndClaimsIntroduction from '../../fragments/_scopes-claims-introduction.md'; -사용자가 성공적으로 로그인하면, Logto는 사용자 정보 클레임을 포함하는 [ID 토큰](https://openid.net/specs/openid-connect-core-1_0.html#IDToken)을 발급합니다. ID 토큰은 JSON Web Token (JWT)입니다. +### 사용자 정보 표시하기 \{#display-user-information} -사용자 정보 클레임은 사용자가 로그인할 때 사용한 스코프에 따라 검색할 수 있으며, 성능과 데이터 크기를 고려하여 ID 토큰에 모든 사용자 클레임이 포함되지 않을 수 있습니다. 일부 사용자 클레임은 [userinfo 엔드포인트](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo)에서만 사용할 수 있습니다 (아래 관련 목록 참조). - -`buildAngularAuthConfig()` 유틸리티는 구성에 `resource`가 제공되지 않은 경우 `autoUserInfo` 및 `renewUserInfoAfterTokenRenew`를 활성화합니다. 이는 Logto가 사용자가 로그인한 후 자동으로 사용자 정보를 가져오고 토큰이 갱신된 후 사용자 정보를 갱신한다는 것을 의미합니다. - -:::info -`angular-auth-oidc-client` 라이브러리 구성에 대해 더 알아보려면 [공식 문서](https://angular-auth-oidc-client.com/)를 참조하세요. -::: - -### 사용자 정보 표시 \{#display-user-information} - -`OidcSecurityService`는 인증 상태와 사용자 정보에 구독할 수 있는 편리한 방법을 제공합니다: +사용자 정보를 표시하려면, 추가 네트워크 요청 없이 ID 토큰에서 클레임 (Claim)을 읽기 위해 `getIdTokenClaims()`를 사용하세요. 기존 세션이 복원될 때를 포함하여 `isAuthenticated()`가 true가 될 때 클레임을 로드하도록 `AppComponent`에 `effect`를 추가하세요. 결과를 표시하려면 `JsonPipe`를 import 하세요: ```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; -import { decodeIdToken, type IdTokenClaims } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - idTokenClaims?: IdTokenClaims; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService.checkAuth().subscribe(({ isAuthenticated, idToken, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - this.idTokenClaims = decodeIdToken(idToken); - this.accessToken = accessToken; +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(undefined); + + constructor() { + effect(() => { + if (!this.logto.isAuthenticated()) { + this.user.set(undefined); + return; + } + + void this.logto + .getIdTokenClaims() + .then((claims) => { + this.user.set(claims); + }) + .catch(() => { + // SDK는 템플릿에서 logto.error()를 통해 에러를 노출합니다. + }); }); } - // ...other methods + // ...이전 단계의 signIn() 및 signOut() 메서드를 유지하세요 } ``` -그리고 템플릿에서 사용하세요: +템플릿의 `logto.isAuthenticated()` 분기 내에 다음을 추가하세요: ```html title="app/app.component.html" - - -
{{ idTokenClaims | json }}
-

Access token: {{ accessToken }}

- - -
+@if (user(); as claims) { +
{{ claims | json }}
+} ``` -### 추가 클레임 요청 \{#request-additional-claims} +### 추가 클레임 (Claim) 요청하기 \{#request-additional-claims} - + -추가 스코프를 요청하려면 인증 제공자 구성을 설정할 수 있습니다: +스코프 (Scope)를 `provideLogto` 설정에 추가하세요: -```tsx title="app/app.config.ts" -import { UserScope, buildAngularAuthConfig } from '@logto/js'; +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other configs - // highlight-start - scopes: [ - UserScope.Email, - UserScope.Phone, - UserScope.CustomData, - UserScope.Identities, - UserScope.Organizations, - ], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + scopes: [ + UserScope.Email, + UserScope.Phone, + UserScope.CustomData, + UserScope.Identities, + UserScope.Organizations, + ], }), - // ...other providers + // ...다른 providers ], }; ``` -그런 다음 `idToken`의 반환 값에서 추가 클레임에 접근할 수 있습니다. +스코프를 변경한 후 다시 로그인하세요. `email` 및 `phone_number`와 같은 추가 ID 토큰 클레임 (Claim)은 `getIdTokenClaims()`에서 사용할 수 있으며, 위 예시에서 표시됩니다. -{/* eslint-disable prettier/prettier */} {`import { OidcSecurityService } from 'angular-auth-oidc-client'; -// highlight-next-line -import { type UserInfoResponse } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - // highlight-next-line - userData?: UserInfoResponse; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService - .checkAuth() - // highlight-next-line - .subscribe(({ isAuthenticated, userData, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - // highlight-next-line - this.userData = userData; - this.accessToken = accessToken; - }); + {`// 이 메서드를 AppComponent에 추가하고 로그인 후 호출하세요. +async loadUserInfo() { + const userInfo = await this.logto.fetchUserInfo(); + // 이제 userInfo.custom_data, userInfo.identities 등에도 접근할 수 있습니다. + return userInfo; +}`} } - - // ...other methods -} - -// 이제 \`userData.custom_data\` 클레임에 접근할 수 있습니다.`} -} /> -{/* eslint-enable prettier/prettier */} -### 스코프와 클레임 \{#scopes-and-claims} +`fetchUserInfo()`는 API 리소스 액세스 토큰과 함께 사용할 수 있습니다. `resources`를 설정해도 SDK가 사용자 정보를 요청하는 것을 막지 않습니다. + +### 스코프 (Scope)와 클레임 (Claim) \{#scopes-and-claims} diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md index 75430cb384d..df91dca6226 100644 --- a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md +++ b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md @@ -1,6 +1,6 @@ :::tip -- 다음 데모는 Angular 18.0.0과 [angular-auth-oidc-client](https://github.com/damienbod/angular-auth-oidc-client)를 기반으로 구축되었습니다. -- 샘플 프로젝트는 [GitHub 저장소](https://github.com/logto-io/js/tree/master/packages/angular-sample)에서 확인할 수 있습니다. +- 이 가이드는 Angular 20을 지원하고 의존성 주입 및 Signals를 제공하는 1st-party `@logto/angular` v2 SDK를 사용합니다. +- 샘플 프로젝트는 [SDK 저장소](https://github.com/logto-io/js/tree/master/packages/angular-sample)에서 확인할 수 있습니다. ::: diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx index b920ce5ed68..41aabde1f14 100644 --- a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx +++ b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx @@ -1,30 +1,3 @@ -import TabItem from '@theme/TabItem'; -import Tabs from '@theme/Tabs'; +import NpmLikeInstallation from '../../fragments/_npm-like-installation.mdx'; -Logto JS core SDK 및 Angular OIDC 클라이언트 라이브러리를 설치하세요: - - - - - -```bash -npm i @logto/js angular-auth-oidc-client -``` - - - - -```bash -pnpm add @logto/js angular-auth-oidc-client -``` - - - - -```bash -yarn add @logto/js angular-auth-oidc-client -``` - - - - + diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx index 8b89a2245f5..5300abd2259 100644 --- a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx +++ b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx @@ -1,33 +1,40 @@ import Checkpoint from '../../fragments/_checkpoint-test-your-application.md'; +import SignInNote from '../../fragments/_sign-in-note.mdx'; import AssumingUrl from '../../fragments/_web-assuming-url.mdx'; import WebConfigureRedirectUris from '../../fragments/_web-configure-redirect-uris.mdx'; import SignInFlowSummary from '../../fragments/_web-sign-in-flow-summary.mdx'; import SignOutNote from '../../fragments/_web-sign-out-note.md'; -### 애플리케이션 구성 \{#configure-application} +### Logto 프로바이더 초기화 \{#init-logto-provider} -Angular 프로젝트에서 `app.config.ts`에 인증 제공자를 추가하세요: +Angular 프로젝트에서 `app.config.ts`에 `provideLogto`와 애플리케이션 라우트를 등록하세요: ```ts title="app/app.config.ts" -import { buildAngularAuthConfig } from '@logto/js'; -import { provideAuth } from 'angular-auth-oidc-client'; +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: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - endpoint: '', - appId: '', - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - }), + provideLogto({ + endpoint: '', + appId: '', }), + provideRouter(routes), // ...other providers ], }; ``` +`provideLogto`는 브라우저가 처음 렌더링된 후 인증 (Authentication) 상태를 자동으로 복원합니다. 컴포넌트에서 초기화 메서드를 호출할 필요가 없습니다. + +:::note +서버 사이드 렌더링 (SSR)을 사용할 때, 인증 상태와 토큰은 브라우저에서만 사용할 수 있습니다. +초기화가 완료될 때까지 `isLoading()`을 사용하여 로딩 상태를 표시하세요. 서버 렌더링 중 인증된 데이터가 필요하다면 서버 또는 BFF SDK를 사용하세요. +::: + ### 리디렉션 URI 구성 \{#configure-redirect-uris} @@ -38,55 +45,113 @@ export const appConfig: ApplicationConfig = { ### 리디렉션 처리 \{#handle-redirect} -`http://localhost:3000/callback`을 리디렉션 URI로 사용하므로, 이를 적절히 처리해야 합니다. `angular-auth-oidc-client` 라이브러리는 리디렉션 처리를 위한 내장 지원을 제공합니다. 인증 제공자 구성을 적절히 설정하면 라이브러리가 나머지를 처리합니다. +Logto가 사용자를 애플리케이션으로 다시 리디렉션한 후 로그인을 완료하기 위해 콜백 컴포넌트를 만드세요. 콜백 처리가 브라우저에서만 실행되도록 `afterNextRender`를 사용합니다: + +```ts title="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) { +

{{ error.message }}

+ } @else { +

로그인 완료 중...

+ } + `, +}) +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는 콜백 오류를 템플릿에서 logto.error()로 노출합니다. + }); + }); + } +} +``` -```ts title="app/app.config.ts" -export const appConfig: ApplicationConfig = { - providers: [ - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - // highlight-end - }), - }), - // ...other providers - ], -}; +`isSignInRedirected()`는 URL이 활성 로그인 세션과 일치하는지 확인합니다. 누군가 세션 없이 콜백 라우트를 방문하면, 이 예제는 로그인 완료를 시도하는 대신 애플리케이션 홈 페이지로 돌려보냅니다. + +`app.routes.ts`에 콜백 라우트를 등록하세요. 이 경로는 리디렉션 URI의 경로와 일치해야 하며, 인증이 필요하지 않아야 합니다. 예를 들어, 리디렉션 URI가 `/callback`으로 끝난다면 `callback`을 사용하세요: + +```ts title="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 +]; ``` -### 로그인 및 로그아웃 구현 \{#implement-sign-in-and-sign-out} +루트 컴포넌트에는 다음 단계에서 보여주는 것처럼 이 라우트를 렌더링하기 위해 ``이 필요합니다. -로그인 및 로그아웃을 구현하려는 컴포넌트 (예: `app.component.ts`)에서 `OidcSecurityService`를 주입하고 이를 사용하여 로그인 및 로그아웃을 수행하세요. +### 로그인 및 로그아웃 구현 \{#implement-sign-in-and-sign-out} -```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; +로그인 및 로그아웃을 시작하려면 `LogtoService`를 주입하세요. 등록한 리디렉션 URI를 이 메서드에 전달합니다. `postRedirectUri`는 로그인 콜백 처리 후 SDK가 이동할 위치를 지정합니다: -export class AppComponent implements OnInit { - constructor(public oidcSecurityService: OidcSecurityService) {} + - signIn() { - this.oidcSecurityService.authorize(); +```ts title="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, + }); } - signOut() { - this.oidcSecurityService.logoff().subscribe((result) => { - console.log('app sign-out', result); - }); + async signOut() { + await this.logto.signOut('http://localhost:3000/'); } } ``` -그런 다음, 템플릿에 로그인 및 로그아웃 버튼을 추가하세요: +템플릿에서 `isLoading()`, `isAuthenticated()`, `error()` 시그널을 직접 읽을 수 있습니다: ```html title="app/app.component.html" - -
- +@if (logto.error(); as error) { +

{{ error.message }}

+} @if (logto.isLoading()) { +

로딩 중...

+} @else if (logto.isAuthenticated()) { + +} @else { + +} + + ``` +콜백이 사용자가 로그인되기 전에 렌더링될 수 있도록 ``은 인증 조건문 바깥에 두세요. + diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md new file mode 100644 index 00000000000..1f1a143d595 --- /dev/null +++ b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md @@ -0,0 +1,17 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: [UserScope.Organizations], // 조직 (Organizations)에 대한 스코프를 요청합니다. + // highlight-end + }), + // ...other providers + ], +}; +``` diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md index d680e1b878a..7d6a1a15342 100644 --- a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md +++ b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md @@ -1,14 +1,15 @@ -```ts title="/app/app.config.ts" +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), // ...other providers ], diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md index 135f1616058..d2c0c7bba17 100644 --- a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md +++ b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md @@ -1,15 +1,16 @@ ```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - scopes: ['openid', 'profile', 'offline_access', 'read', 'write'], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['shopping:read', 'shopping:write', 'store:read', 'store:write'], + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), // ...other providers ], diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md new file mode 100644 index 00000000000..b1daaa80d0a --- /dev/null +++ b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md @@ -0,0 +1,18 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['read', 'write'], // 스코프 (Scope) 예시 + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], // API 리소스 예시 + // highlight-end + }), + // ...other providers + ], +}; +``` diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md new file mode 100644 index 00000000000..d19f4c15d43 --- /dev/null +++ b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md @@ -0,0 +1,28 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { + +
{{ accessToken() }}
+ } + `, +}) +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')); + } +} +``` diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md new file mode 100644 index 00000000000..61a79ea86de --- /dev/null +++ b/i18n/ko/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md @@ -0,0 +1,59 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { +
    + @for (organizationId of organizationIds(); track organizationId) { +
  • + {{ organizationId }} + +
  • + } +
+
{{ organizationToken() }}
+ } + `, +}) +export class OrganizationsComponent { + readonly logto = inject(LogtoService); + readonly organizationIds = signal([]); + 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()를 통해 오류를 노출합니다. + }); + }); + } + + async loadOrganizationToken(organizationId: string) { + this.organizationToken.set(await this.logto.getOrganizationToken(organizationId)); + } +} +``` diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx index 3cfcb6ad5c2..711c211bb38 100644 --- a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx @@ -2,7 +2,7 @@ slug: /quick-starts/angular sidebar_label: Angular sidebar_custom_props: - description: Angular é uma biblioteca JavaScript para construir interfaces de usuário. + description: Angular é um framework para construção de aplicações web. --- import FurtherReadings from '../../fragments/_further-readings.md'; @@ -15,12 +15,15 @@ import Integration from './_integration.mdx'; # Adicionar autenticação ao seu aplicativo Angular +Este guia mostrará como integrar o Logto Angular SDK v2 ao seu aplicativo. + ## Pré-requisitos \{#prerequisites} - Uma conta no [Logto Cloud](https://cloud.logto.io) ou um [Logto auto-hospedado](/introduction/set-up-logto-oss). -- Um aplicativo de página única Logto criado. +- Um aplicativo de página única (SPA) criado no Logto Console. +- Um projeto Angular 20. ## Instalação \{#installation} diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx index 7f7d952b332..ec40a831408 100644 --- a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx @@ -1,22 +1,67 @@ import ApiResourcesDescription from '../../fragments/_api-resources-description.md'; import ConfigApiResources from '../../fragments/_config-api-resources.mdx'; +import FetchAccessTokenForApiResources from '../../fragments/_fetch-access-token-for-api-resources.mdx'; +import FetchOrganizationTokenForUser from '../../fragments/_fetch-organization-token-for-user.mdx'; +import ConfigOrganizationCode from './code/_config-organization-code.md'; import ConfigResourcesCode from './code/_config-resources-code.md'; import ConfigResourcesWithScopesCode from './code/_config-resources-with-scopes-code.md'; - -### Configurar `angular-auth-oidc-client` para recurso de API \{#configure-angular-auth-oidc-client-for-api-resource} +import ConfigResourcesWithSharedScopesCode from './code/_config-resources-with-shared-scopes-code.md'; +import GetAccessTokenCode from './code/_get-access-token-code.md'; +import GetOrganizationAccessTokenCode from './code/_get-organization-access-token-code.md'; +### Configurar o cliente Logto \{#configure-logto-client} + } configResourcesWithScopesCode={} + configResourcesWithSharedScopesCode={} /> -Agora, o token de acesso estará no formato JSON Web Token (JWT) em vez de uma string aleatória (token opaco). +Faça login novamente após alterar os recursos ou escopos para que o usuário possa autorizar a configuração atualizada. -:::warning -Tanto `autoUserInfo` quanto `renewUserInfoAfterTokenRenew` serão desativados quando `resource` estiver configurado. Isso ocorre porque o token de acesso será solicitado para o recurso de API específico e não para o endpoint de informações do usuário. -::: +### Buscar token de acesso para o recurso de API \{#fetch-access-token-for-the-api-resource} + +} +/> + +Use o identificador exato do recurso da sua configuração. Chame `getAccessToken(resource)` sempre que fizer uma requisição à API para que o SDK possa retornar um token válido, em vez de manter um token indefinidamente em seu componente. + +### Buscar tokens de organização \{#fetch-organization-tokens} + +} + getOrganizationAccessTokenCode={} +/> + +Una `UserScope.Organizations` a quaisquer escopos existentes e faça login novamente após atualizar a configuração. `getOrganizationToken(organizationId)` retorna um token para a organização Logto selecionada; use `getAccessToken(resource)` para um token de recurso de API. + +### Anexar token de acesso aos cabeçalhos da requisição \{#attach-access-token-to-request-headers} + +Coloque o token no cabeçalho HTTP `Authorization` usando o formato Bearer (`Bearer SEU_TOKEN`). Por exemplo, adicione este método a um componente autenticado que injeta `LogtoService`: -Atualmente, apenas os SDKs oficiais do Logto suportam a capacidade de solicitar tanto informações do usuário quanto tokens de acesso a recursos de API. Se você precisar solicitar ambos, por favor, não hesite em nos contatar. +```ts +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(); +} +``` + +:::note +O exemplo usa `fetch`. Se você usar o `HttpClient` do Angular, defina o mesmo cabeçalho `Authorization` nas opções da requisição. +::: diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx index 60845933adf..25279b51fae 100644 --- a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx @@ -5,129 +5,105 @@ import FindUserInfoMissing from '../../fragments/_find-user-info-missing.mdx'; import ScopesAndClaims from '../../fragments/_scopes-and-claims.mdx'; import ScopesAndClaimsIntroduction from '../../fragments/_scopes-claims-introduction.md'; -Uma vez que o usuário tenha feito login com sucesso, o Logto emitirá um [Token de ID (ID token)](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) que contém as reivindicações de informações do usuário. O Token de ID é um JSON Web Token (JWT). - -É importante notar que as reivindicações de informações do usuário que podem ser recuperadas dependem dos escopos usados pelo usuário durante o login, e considerando o desempenho e o tamanho dos dados, o Token de ID pode não conter todas as reivindicações do usuário; algumas reivindicações do usuário estão disponíveis apenas no [endpoint userinfo](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) (veja a lista relacionada abaixo). - -A utilidade `buildAngularAuthConfig()` habilitará `autoUserInfo` e `renewUserInfoAfterTokenRenew` se não houver `resource` fornecido na configuração. Isso significa que o Logto buscará automaticamente as informações do usuário após o login e renovará as informações do usuário após a renovação do token. - -:::info -Para saber mais sobre como configurar a biblioteca `angular-auth-oidc-client`, veja a [documentação oficial](https://angular-auth-oidc-client.com/). -::: - ### Exibir informações do usuário \{#display-user-information} -O `OidcSecurityService` fornece uma maneira conveniente de se inscrever no estado de autenticação, bem como nas informações do usuário: +Para exibir as informações do usuário, use `getIdTokenClaims()` para ler as reivindicações (Claims) do token de ID sem uma solicitação de rede adicional. Adicione um `effect` ao seu `AppComponent` para carregar as reivindicações quando `isAuthenticated()` se tornar verdadeiro, inclusive quando uma sessão existente for restaurada. Importe `JsonPipe` para exibir o resultado: ```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; -import { decodeIdToken, type IdTokenClaims } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - idTokenClaims?: IdTokenClaims; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService.checkAuth().subscribe(({ isAuthenticated, idToken, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - this.idTokenClaims = decodeIdToken(idToken); - this.accessToken = accessToken; +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(undefined); + + constructor() { + effect(() => { + if (!this.logto.isAuthenticated()) { + this.user.set(undefined); + return; + } + + void this.logto + .getIdTokenClaims() + .then((claims) => { + this.user.set(claims); + }) + .catch(() => { + // O SDK expõe o erro através de logto.error() para o template. + }); }); } - // ...outros métodos + // ...mantenha os métodos signIn() e signOut() do passo anterior } ``` -E use-o no template: +Adicione o seguinte dentro do ramo `logto.isAuthenticated()` do seu template: ```html title="app/app.component.html" - - -
{{ idTokenClaims | json }}
-

Access token: {{ accessToken }}

- - -
+@if (user(); as claims) { +
{{ claims | json }}
+} ``` ### Solicitar reivindicações adicionais \{#request-additional-claims} - + -Para solicitar escopos adicionais, você pode configurar as configurações do provedor de autenticação: +Adicione os escopos (scopes) à sua configuração `provideLogto`: -```tsx title="app/app.config.ts" -import { UserScope, buildAngularAuthConfig } from '@logto/js'; +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...outras configurações - // highlight-start - scopes: [ - UserScope.Email, - UserScope.Phone, - UserScope.CustomData, - UserScope.Identities, - UserScope.Organizations, - ], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + scopes: [ + UserScope.Email, + UserScope.Phone, + UserScope.CustomData, + UserScope.Identities, + UserScope.Organizations, + ], }), - // ...outros provedores + // ...outros providers ], }; ``` -Então você pode acessar as reivindicações adicionais no valor de retorno de `idToken`. +Faça login novamente após alterar os escopos. As reivindicações adicionais do token de ID, como `email` e `phone_number`, estarão disponíveis a partir de `getIdTokenClaims()` e serão exibidas pelo exemplo acima. -{/* eslint-disable prettier/prettier */} {`import { OidcSecurityService } from 'angular-auth-oidc-client'; -// highlight-next-line -import { type UserInfoResponse } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - // highlight-next-line - userData?: UserInfoResponse; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService - .checkAuth() - // highlight-next-line - .subscribe(({ isAuthenticated, userData, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - // highlight-next-line - this.userData = userData; - this.accessToken = accessToken; - }); + {`// Adicione este método ao AppComponent e chame-o após o login. +async loadUserInfo() { + const userInfo = await this.logto.fetchUserInfo(); + // Agora você pode acessar userInfo.custom_data, userInfo.identities, etc. + return userInfo; +}`} } - - // ...outros métodos -} - -// Agora você pode acessar a reivindicação \`userData.custom_data\``} -} /> -{/* eslint-enable prettier/prettier */} + +`fetchUserInfo()` pode ser usado juntamente com tokens de acesso de recursos de API. Configurar `resources` não impede que o SDK solicite informações do usuário. ### Escopos e reivindicações \{#scopes-and-claims} diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md index 7ad8229c5b1..ae9bc6f11d3 100644 --- a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md @@ -1,6 +1,6 @@ :::tip -- A demonstração a seguir é construída em Angular 18.0.0 e [angular-auth-oidc-client](https://github.com/damienbod/angular-auth-oidc-client). -- O projeto de exemplo está disponível no [repositório GitHub](https://github.com/logto-io/js/tree/master/packages/angular-sample). +- Este guia utiliza o SDK oficial `@logto/angular` v2, que oferece suporte ao Angular 20 e fornece injeção de dependência e Signals. +- O projeto de exemplo está disponível em nosso [repositório do SDK](https://github.com/logto-io/js/tree/master/packages/angular-sample). ::: diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx index 2df466d3d71..41aabde1f14 100644 --- a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx @@ -1,30 +1,3 @@ -import TabItem from '@theme/TabItem'; -import Tabs from '@theme/Tabs'; +import NpmLikeInstallation from '../../fragments/_npm-like-installation.mdx'; -Instale o Logto JS core SDK e a biblioteca cliente Angular OIDC: - - - - - -```bash -npm i @logto/js angular-auth-oidc-client -``` - - - - -```bash -pnpm add @logto/js angular-auth-oidc-client -``` - - - - -```bash -yarn add @logto/js angular-auth-oidc-client -``` - - - - + diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx index 5738e93258a..a84f155e274 100644 --- a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx @@ -1,33 +1,40 @@ import Checkpoint from '../../fragments/_checkpoint-test-your-application.md'; +import SignInNote from '../../fragments/_sign-in-note.mdx'; import AssumingUrl from '../../fragments/_web-assuming-url.mdx'; import WebConfigureRedirectUris from '../../fragments/_web-configure-redirect-uris.mdx'; import SignInFlowSummary from '../../fragments/_web-sign-in-flow-summary.mdx'; import SignOutNote from '../../fragments/_web-sign-out-note.md'; -### Configurar aplicativo \{#configure-application} +### Inicializar o provedor Logto \{#init-logto-provider} -No seu projeto Angular, adicione o provedor de autenticação no seu `app.config.ts`: +No seu projeto Angular, registre `provideLogto` e as rotas do seu aplicativo em `app.config.ts`: ```ts title="app/app.config.ts" -import { buildAngularAuthConfig } from '@logto/js'; -import { provideAuth } from 'angular-auth-oidc-client'; +// Exemplo de configuração do Logto no Angular +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: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - endpoint: '', - appId: '', - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - }), + provideLogto({ + endpoint: '', + appId: '', }), - // ...outros provedores + provideRouter(routes), + // ...outros providers ], }; ``` +O `provideLogto` restaura automaticamente o estado de autenticação após o primeiro render do navegador. Você não precisa chamar um método de inicialização em seus componentes. + +:::note +Ao usar renderização do lado do servidor (SSR), o estado de autenticação e os tokens estão disponíveis apenas no navegador. Use `isLoading()` para exibir um estado de carregamento até que a inicialização termine. Se você precisar de dados autenticados durante a renderização no servidor, utilize um SDK de servidor ou BFF. +::: + ### Configurar URIs de redirecionamento \{#configure-redirect-uris} @@ -36,57 +43,118 @@ export const appConfig: ApplicationConfig = { -### Lidar com redirecionamento \{#handle-redirect} +### Lidar com o redirecionamento \{#handle-redirect} + +Crie um componente de callback para completar o login após o Logto redirecionar o usuário de volta para seu aplicativo. Use `afterNextRender` para que o tratamento do callback ocorra apenas no navegador: + +```ts title="app/callback.component.ts" +// Exemplo de componente de callback para autenticação Logto +import { afterNextRender, Component, inject } from '@angular/core'; +import { LogtoService } from '@logto/angular'; + +@Component({ + selector: 'app-callback', + standalone: true, + template: ` + @if (logto.error(); as error) { +

{{ error.message }}

+ } @else { +

Completando o login...

+ } + `, +}) +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(() => { + // O SDK expõe erros de callback através de logto.error() para o template. + }); + }); + } +} +``` -Como usamos `http://localhost:3000/callback` como o URI de redirecionamento, agora precisamos lidar com isso adequadamente. A biblioteca `angular-auth-oidc-client` fornece suporte embutido para lidar com o redirecionamento. Você pode simplesmente configurar os configs do provedor de autenticação corretamente e a biblioteca cuidará do resto. +`isSignInRedirected()` verifica se a URL corresponde a uma sessão de login ativa. Se alguém acessar a rota de callback sem uma sessão, este exemplo o retorna para a página inicial do aplicativo em vez de tentar completar o login. -```ts title="app/app.config.ts" -export const appConfig: ApplicationConfig = { - providers: [ - provideAuth({ - config: buildAngularAuthConfig({ - // ...outras configurações - // highlight-start - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - // highlight-end - }), - }), - // ...outros provedores - ], -}; +Registre a rota de callback em `app.routes.ts`. Ela deve corresponder ao caminho do seu URI de redirecionamento e não deve exigir autenticação. Por exemplo, use `callback` para um URI de redirecionamento terminando em `/callback`: + +```ts title="app/app.routes.ts" +// Exemplo de rota de callback para autenticação Logto +import { type Routes } from '@angular/router'; + +import { CallbackComponent } from './callback.component'; + +export const routes: Routes = [ + { path: 'callback', component: CallbackComponent }, + // ...outras rotas +]; ``` -### Implementar login e logout \{#implement-sign-in-and-sign-out} +O componente raiz precisa de um `` para renderizar essa rota, como mostrado no próximo passo. -No componente onde você deseja implementar login e logout (por exemplo, `app.component.ts`), injete o `OidcSecurityService` e use-o para fazer login e logout. +### Implementar login e logout \{#implement-sign-in-and-sign-out} -```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; +Injete o `LogtoService` para iniciar o login e o logout. Passe os URIs de redirecionamento registrados para esses métodos. O `postRedirectUri` informa ao SDK para onde navegar após o processamento bem-sucedido do callback de login: -export class AppComponent implements OnInit { - constructor(public oidcSecurityService: OidcSecurityService) {} + - signIn() { - this.oidcSecurityService.authorize(); +```ts title="app/app.component.ts" +// Exemplo de uso do LogtoService para login e logout +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, + }); } - signOut() { - this.oidcSecurityService.logoff().subscribe((result) => { - console.log('app sign-out', result); - }); + async signOut() { + await this.logto.signOut('http://localhost:3000/'); } } ``` -Em seguida, no template, adicione botões para login e logout: +Leia os sinais `isLoading()`, `isAuthenticated()` e `error()` diretamente no template: ```html title="app/app.component.html" - -
- +@if (logto.error(); as error) { +

{{ error.message }}

+} @if (logto.isLoading()) { +

Carregando...

+} @else if (logto.isAuthenticated()) { + +} @else { + +} + + ``` +Mantenha o `` fora das condições de autenticação para que o callback possa ser renderizado antes do usuário estar autenticado. + diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md new file mode 100644 index 00000000000..7a7b7b0a8a7 --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md @@ -0,0 +1,17 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // destaque-início + scopes: [UserScope.Organizations], + // destaque-fim + }), + // ...outros providers + ], +}; +``` diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md index d680e1b878a..7d6a1a15342 100644 --- a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md @@ -1,14 +1,15 @@ -```ts title="/app/app.config.ts" +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), // ...other providers ], diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md index 135f1616058..d2c0c7bba17 100644 --- a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md @@ -1,15 +1,16 @@ ```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - scopes: ['openid', 'profile', 'offline_access', 'read', 'write'], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['shopping:read', 'shopping:write', 'store:read', 'store:write'], + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), // ...other providers ], diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md new file mode 100644 index 00000000000..5a8e342c65d --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md @@ -0,0 +1,18 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['read', 'write'], // escopos de permissão + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], // recursos de API + // highlight-end + }), + // ...outros providers + ], +}; +``` diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md new file mode 100644 index 00000000000..714d2ba7c07 --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md @@ -0,0 +1,28 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { + +
{{ accessToken() }}
+ } + `, +}) +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')); + } +} +``` diff --git a/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md new file mode 100644 index 00000000000..e76af2e8f82 --- /dev/null +++ b/i18n/pt-BR/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md @@ -0,0 +1,59 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { +
    + @for (organizationId of organizationIds(); track organizationId) { +
  • + {{ organizationId }} + +
  • + } +
+
{{ organizationToken() }}
+ } + `, +}) +export class OrganizationsComponent { + readonly logto = inject(LogtoService); + readonly organizationIds = signal([]); + 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(() => { + // O SDK expõe o erro através de logto.error() para o template. + }); + }); + } + + async loadOrganizationToken(organizationId: string) { + this.organizationToken.set(await this.logto.getOrganizationToken(organizationId)); + } +} +``` diff --git a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx index 3b4fe551e2c..58e1652a4ab 100644 --- a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx +++ b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx @@ -2,7 +2,7 @@ slug: /quick-starts/angular sidebar_label: Angular sidebar_custom_props: - description: Angular คือไลบรารี JavaScript สำหรับสร้างส่วนติดต่อผู้ใช้ (user interfaces) + description: Angular คือเฟรมเวิร์กสำหรับสร้างเว็บแอปพลิเคชัน --- import FurtherReadings from '../../fragments/_further-readings.md'; @@ -15,18 +15,21 @@ import Integration from './_integration.mdx'; # เพิ่มการยืนยันตัวตนให้กับแอป Angular ของคุณ (Add authentication to your Angular application) +คู่มือนี้จะแสดงวิธีการผสานรวม Logto Angular SDK v2 เข้ากับแอปพลิเคชันของคุณ + ## ข้อกำหนดเบื้องต้น \{#prerequisites} -- บัญชี [Logto Cloud](https://cloud.logto.io) หรือ [Logto ที่โฮสต์เอง](/introduction/set-up-logto-oss) -- มีการสร้างแอปพลิเคชันแบบหน้าเดียวของ Logto แล้ว +- บัญชี [Logto Cloud](https://cloud.logto.io) หรือ [Logto แบบโฮสต์เอง](/introduction/set-up-logto-oss) +- แอปพลิเคชันหน้าเดียว (SPA) ที่สร้างใน Logto Console +- โปรเจกต์ Angular 20 ## การติดตั้ง \{#installation} -## การเชื่อมต่อระบบ \{#integration} +## การผสานรวม \{#integration} @@ -34,7 +37,7 @@ import Integration from './_integration.mdx'; -## ทรัพยากร API \{#api-resources} +## ทรัพยากร API (API resources) \{#api-resources} diff --git a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx index 9d18b8c8180..0b3c58f586f 100644 --- a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx +++ b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx @@ -1,22 +1,67 @@ import ApiResourcesDescription from '../../fragments/_api-resources-description.md'; import ConfigApiResources from '../../fragments/_config-api-resources.mdx'; +import FetchAccessTokenForApiResources from '../../fragments/_fetch-access-token-for-api-resources.mdx'; +import FetchOrganizationTokenForUser from '../../fragments/_fetch-organization-token-for-user.mdx'; +import ConfigOrganizationCode from './code/_config-organization-code.md'; import ConfigResourcesCode from './code/_config-resources-code.md'; import ConfigResourcesWithScopesCode from './code/_config-resources-with-scopes-code.md'; - -### กำหนดค่า `angular-auth-oidc-client` สำหรับทรัพยากร API \{#configure-angular-auth-oidc-client-for-api-resource} +import ConfigResourcesWithSharedScopesCode from './code/_config-resources-with-shared-scopes-code.md'; +import GetAccessTokenCode from './code/_get-access-token-code.md'; +import GetOrganizationAccessTokenCode from './code/_get-organization-access-token-code.md'; +### กำหนดค่า Logto client \{#configure-logto-client} + } configResourcesWithScopesCode={} + configResourcesWithSharedScopesCode={} /> -ตอนนี้ โทเค็นการเข้าถึง (Access token) จะอยู่ในรูปแบบ JSON Web Token (JWT) แทนที่จะเป็นสตริงสุ่ม (โทเค็นทึบ (Opaque token)) +ลงชื่อเข้าใช้อีกครั้งหลังจากเปลี่ยนแปลงทรัพยากรหรือขอบเขต (scopes) เพื่อให้ผู้ใช้สามารถอนุญาตการกำหนดค่าที่อัปเดตแล้ว -:::warning -ทั้ง `autoUserInfo` และ `renewUserInfoAfterTokenRenew` จะถูกปิดใช้งานเมื่อมีการตั้งค่า `resource` เนื่องจากโทเค็นการเข้าถึง (Access token) จะถูกขอสำหรับทรัพยากร API เฉพาะ ไม่ใช่สำหรับ user info endpoint -::: +### ดึงโทเค็นการเข้าถึงสำหรับทรัพยากร API \{#fetch-access-token-for-the-api-resource} + +} +/> + +ใช้ตัวระบุทรัพยากร (resource identifier) ที่ตรงกับการกำหนดค่าของคุณ เรียก `getAccessToken(resource)` ทุกครั้งที่คุณส่งคำขอ API เพื่อให้ SDK ส่งคืนโทเค็นที่ถูกต้อง แทนที่จะเก็บโทเค็นไว้ใน component ของคุณตลอดไป + +### ดึงโทเค็นองค์กร \{#fetch-organization-tokens} + +} + getOrganizationAccessTokenCode={} +/> + +ผสาน `UserScope.Organizations` เข้ากับขอบเขตที่มีอยู่ และลงชื่อเข้าใช้อีกครั้งหลังจากอัปเดตการกำหนดค่า `getOrganizationToken(organizationId)` จะส่งคืนโทเค็นสำหรับ Logto องค์กรที่เลือก ใช้ `getAccessToken(resource)` สำหรับโทเค็นทรัพยากร API + +### แนบโทเค็นการเข้าถึงกับ request headers \{#attach-access-token-to-request-headers} + +ใส่โทเค็นไว้ใน HTTP header `Authorization` โดยใช้รูปแบบ Bearer (`Bearer YOUR_TOKEN`) ตัวอย่างเช่น เพิ่มเมธอดนี้ใน component ที่ผ่านการยืนยันตัวตนซึ่ง inject `LogtoService`: -ปัจจุบัน มีเพียง SDK อย่างเป็นทางการของ Logto เท่านั้นที่รองรับความสามารถในการขอข้อมูลผู้ใช้และโทเค็นการเข้าถึงทรัพยากร API พร้อมกัน หากคุณต้องการขอทั้งสองอย่างนี้ โปรดติดต่อเราได้เลย +```ts +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(); +} +``` + +:::note +ตัวอย่างนี้ใช้ `fetch` หากคุณใช้ Angular `HttpClient` ให้ตั้งค่า header `Authorization` เดียวกันใน options ของคำขอ +::: diff --git a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx index ccc105ae0f1..a52c979ee97 100644 --- a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx +++ b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx @@ -5,133 +5,106 @@ import FindUserInfoMissing from '../../fragments/_find-user-info-missing.mdx'; import ScopesAndClaims from '../../fragments/_scopes-and-claims.mdx'; import ScopesAndClaimsIntroduction from '../../fragments/_scopes-claims-introduction.md'; -เมื่อผู้ใช้ลงชื่อเข้าใช้สำเร็จ Logto จะออก [โทเค็น ID (ID token)](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) ซึ่งมีการอ้างสิทธิ์ข้อมูลผู้ใช้ (user information claims) โทเค็น ID เป็น JSON Web Token (JWT) - -สิ่งสำคัญที่ควรทราบคือ การอ้างสิทธิ์ข้อมูลผู้ใช้ที่สามารถดึงมาได้ขึ้นอยู่กับ -ขอบเขต (scopes) ที่ผู้ใช้เลือกขณะลงชื่อเข้าใช้ และเพื่อประสิทธิภาพและขนาดข้อมูล โทเค็น ID -อาจไม่มีการอ้างสิทธิ์ของผู้ใช้ทั้งหมด โดยบางการอ้างสิทธิ์จะมีเฉพาะใน [userinfo endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) เท่านั้น (ดู -รายการที่เกี่ยวข้องด้านล่าง) - -ยูทิลิตี้ `buildAngularAuthConfig()` จะเปิดใช้งาน `autoUserInfo` และ `renewUserInfoAfterTokenRenew` หากไม่มี `resource` ถูกกำหนดใน config ซึ่งหมายความว่า Logto จะดึงข้อมูลผู้ใช้โดยอัตโนติหลังจากผู้ใช้ลงชื่อเข้าใช้ และจะรีเฟรชข้อมูลผู้ใช้หลังจากโทเค็นถูกรีเฟรช - -:::info -หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับการตั้งค่าไลบรารี `angular-auth-oidc-client` ดู [เอกสารทางการ](https://angular-auth-oidc-client.com/) -::: - ### แสดงข้อมูลผู้ใช้ \{#display-user-information} -`OidcSecurityService` มีวิธีที่สะดวกในการ subscribe สถานะการยืนยันตัวตนรวมถึงข้อมูลผู้ใช้: +เพื่อแสดงข้อมูลของผู้ใช้ ให้ใช้ `getIdTokenClaims()` เพื่ออ่าน การอ้างสิทธิ์ (claims) จาก โทเค็น ID (ID token) โดยไม่ต้องมีการร้องขอเครือข่ายเพิ่มเติม เพิ่ม `effect` ใน `AppComponent` ของคุณเพื่อโหลด การอ้างสิทธิ์ (claims) เมื่อ `isAuthenticated()` เป็น true รวมถึงเมื่อมีการกู้คืนเซสชันที่มีอยู่แล้ว นำเข้า `JsonPipe` เพื่อแสดงผลลัพธ์: ```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; -import { decodeIdToken, type IdTokenClaims } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - idTokenClaims?: IdTokenClaims; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService.checkAuth().subscribe(({ isAuthenticated, idToken, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - this.idTokenClaims = decodeIdToken(idToken); - this.accessToken = accessToken; +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(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. + }); }); } - // ...other methods + // ...keep the signIn() and signOut() methods from the previous step } ``` -และนำไปใช้ในเทมเพลต: +เพิ่มโค้ดต่อไปนี้ภายในเงื่อนไข `logto.isAuthenticated()` ของเทมเพลตของคุณ: ```html title="app/app.component.html" - - -
{{ idTokenClaims | json }}
-

Access token: {{ accessToken }}

- - -
+@if (user(); as claims) { +
{{ claims | json }}
+} ``` -### ขอการอ้างสิทธิ์เพิ่มเติม \{#request-additional-claims} +### ขอ การอ้างสิทธิ์ (claims) เพิ่มเติม \{#request-additional-claims} - + -หากต้องการขอขอบเขต (scopes) เพิ่มเติม คุณสามารถตั้งค่าใน auth provider configs ได้ดังนี้: +เพิ่ม scopes ลงในการตั้งค่า `provideLogto` ของคุณ: -```tsx title="app/app.config.ts" -import { UserScope, buildAngularAuthConfig } from '@logto/js'; +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other configs - // highlight-start - scopes: [ - UserScope.Email, - UserScope.Phone, - UserScope.CustomData, - UserScope.Identities, - UserScope.Organizations, - ], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + scopes: [ + UserScope.Email, + UserScope.Phone, + UserScope.CustomData, + UserScope.Identities, + UserScope.Organizations, + ], }), // ...other providers ], }; ``` -จากนั้นคุณสามารถเข้าถึงการอ้างสิทธิ์เพิ่มเติมในค่าที่คืนมาของ `idToken` +ลงชื่อเข้าใช้อีกครั้งหลังจากเปลี่ยน scopes การอ้างสิทธิ์ (claims) เพิ่มเติมใน โทเค็น ID (ID token) เช่น `email` และ `phone_number` จะสามารถเข้าถึงได้จาก `getIdTokenClaims()` และจะแสดงผลตามตัวอย่างข้างต้น -{/* eslint-disable prettier/prettier */} {`import { OidcSecurityService } from 'angular-auth-oidc-client'; -// highlight-next-line -import { type UserInfoResponse } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - // highlight-next-line - userData?: UserInfoResponse; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService - .checkAuth() - // highlight-next-line - .subscribe(({ isAuthenticated, userData, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - // highlight-next-line - this.userData = userData; - this.accessToken = accessToken; - }); + {`// เพิ่มเมธอดนี้ใน AppComponent และเรียกหลังจากลงชื่อเข้าใช้ +async loadUserInfo() { + const userInfo = await this.logto.fetchUserInfo(); + // ตอนนี้คุณสามารถเข้าถึง userInfo.custom_data, userInfo.identities ฯลฯ ได้แล้ว + return userInfo; +}`} } - - // ...other methods -} - -// ตอนนี้คุณสามารถเข้าถึง claim \`userData.custom_data\` ได้แล้ว`} -} /> -{/* eslint-enable prettier/prettier */} -### ขอบเขตและการอ้างสิทธิ์ \{#scopes-and-claims} +`fetchUserInfo()` สามารถใช้ร่วมกับ โทเค็นการเข้าถึง (Access token) สำหรับทรัพยากร API ได้ การตั้งค่า `resources` จะไม่ป้องกัน SDK จากการร้องขอข้อมูลผู้ใช้ + +### ขอบเขต (Scopes) และ การอ้างสิทธิ์ (Claims) \{#scopes-and-claims} diff --git a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md index 4a8d1a6b115..99b1fa522ef 100644 --- a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md +++ b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md @@ -1,6 +1,6 @@ :::tip -- ตัวอย่างสาธิตต่อไปนี้สร้างขึ้นบน Angular 18.0.0 และ [angular-auth-oidc-client](https://github.com/damienbod/angular-auth-oidc-client) -- โปรเจกต์ตัวอย่างสามารถดูได้ใน [GitHub repository](https://github.com/logto-io/js/tree/master/packages/angular-sample) +- คู่มือนี้ใช้ SDK `@logto/angular` v2 ของ Logto โดยตรง ซึ่งรองรับ Angular 20 และมีระบบ dependency injection และ Signals +- โปรเจกต์ตัวอย่างสามารถดูได้ที่ [SDK repository](https://github.com/logto-io/js/tree/master/packages/angular-sample) ของเรา ::: diff --git a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx index 41b74f0900e..41aabde1f14 100644 --- a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx +++ b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx @@ -1,30 +1,3 @@ -import TabItem from '@theme/TabItem'; -import Tabs from '@theme/Tabs'; +import NpmLikeInstallation from '../../fragments/_npm-like-installation.mdx'; -ติดตั้ง Logto JS core SDK และไลบรารี Angular OIDC client: - - - - - -```bash -npm i @logto/js angular-auth-oidc-client -``` - - - - -```bash -pnpm add @logto/js angular-auth-oidc-client -``` - - - - -```bash -yarn add @logto/js angular-auth-oidc-client -``` - - - - + diff --git a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx index e0921305805..df5ff4785ef 100644 --- a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx +++ b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx @@ -1,34 +1,40 @@ import Checkpoint from '../../fragments/_checkpoint-test-your-application.md'; +import SignInNote from '../../fragments/_sign-in-note.mdx'; import AssumingUrl from '../../fragments/_web-assuming-url.mdx'; import WebConfigureRedirectUris from '../../fragments/_web-configure-redirect-uris.mdx'; import SignInFlowSummary from '../../fragments/_web-sign-in-flow-summary.mdx'; import SignOutNote from '../../fragments/_web-sign-out-note.md'; -### กำหนดค่าแอปพลิเคชัน \{#configure-application} +### เริ่มต้น Logto provider \{#init-logto-provider} -ในโปรเจกต์ Angular ของคุณ ให้เพิ่ม auth provider ใน `app.config.ts`: +ในโปรเจกต์ Angular ของคุณ ให้ลงทะเบียน `provideLogto` และเส้นทางของแอปพลิเคชันใน `app.config.ts`: ```ts title="app/app.config.ts" -import { buildAngularAuthConfig } from '@logto/js'; -import { provideAuth } from 'angular-auth-oidc-client'; +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: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - endpoint: '', - appId: '', - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - }), + provideLogto({ + endpoint: '', + appId: '', }), + provideRouter(routes), // ...other providers ], }; ``` -### กำหนดค่า redirect URI \{#configure-redirect-uris} +`provideLogto` จะกู้คืนสถานะการยืนยันตัวตนโดยอัตโนมัติหลังจากเรนเดอร์เบราว์เซอร์ครั้งแรก คุณไม่จำเป็นต้องเรียกเมธอดเริ่มต้นในคอมโพเนนต์ของคุณ + +:::note +เมื่อใช้ server-side rendering (SSR) สถานะการยืนยันตัวตนและโทเค็นจะมีเฉพาะในเบราว์เซอร์เท่านั้น ใช้ `isLoading()` เพื่อแสดงสถานะกำลังโหลดจนกว่าการเริ่มต้นจะเสร็จสิ้น หากคุณต้องการข้อมูลที่ผ่านการยืนยันตัวตนระหว่างการเรนเดอร์ฝั่งเซิร์ฟเวอร์ ให้ใช้ SDK สำหรับฝั่งเซิร์ฟเวอร์หรือ BFF +::: + +### กำหนดค่า redirect URIs \{#configure-redirect-uris} @@ -38,55 +44,113 @@ export const appConfig: ApplicationConfig = { ### จัดการ redirect \{#handle-redirect} -เนื่องจากเราใช้ `http://localhost:3000/callback` เป็น redirect URI ตอนนี้เราต้องจัดการให้ถูกต้อง ไลบรารี `angular-auth-oidc-client` มีฟีเจอร์ในตัวสำหรับจัดการ redirect นี้ คุณเพียงแค่กำหนดค่า config ของ auth provider ให้ถูกต้อง ไลบรารีจะจัดการส่วนที่เหลือให้เอง +สร้างคอมโพเนนต์ callback เพื่อดำเนินการลงชื่อเข้าใช้ให้สมบูรณ์หลังจาก Logto เปลี่ยนเส้นทางผู้ใช้กลับมายังแอปของคุณ ใช้ `afterNextRender` เพื่อให้การจัดการ callback ทำงานเฉพาะในเบราว์เซอร์: + +```ts title="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) { +

{{ error.message }}

+ } @else { +

กำลังดำเนินการลงชื่อเข้าใช้...

+ } + `, +}) +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 + }); + }); + } +} +``` -```ts title="app/app.config.ts" -export const appConfig: ApplicationConfig = { - providers: [ - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - // highlight-end - }), - }), - // ...other providers - ], -}; +`isSignInRedirected()` จะตรวจสอบว่า URL ตรงกับเซสชันการลงชื่อเข้าใช้ที่กำลังดำเนินการอยู่หรือไม่ หากมีผู้เข้าชมเส้นทาง callback โดยไม่มีเซสชันนี้ ตัวอย่างนี้จะนำผู้ใช้งานกลับไปยังหน้าแรกของแอปพลิเคชันแทนที่จะพยายามดำเนินการลงชื่อเข้าใช้ให้สมบูรณ์ + +ลงทะเบียนเส้นทาง callback ใน `app.routes.ts` โดยต้องตรงกับ path ของ redirect URI และต้องไม่ต้องการการยืนยันตัวตน ตัวอย่างเช่น ใช้ `callback` สำหรับ redirect URI ที่ลงท้ายด้วย `/callback`: + +```ts title="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 +]; ``` -### สร้างฟังก์ชัน sign-in และ sign-out \{#implement-sign-in-and-sign-out} +คอมโพเนนต์ root ต้องมี `` เพื่อเรนเดอร์เส้นทางนี้ ดังที่แสดงในขั้นตอนถัดไป -ในคอมโพเนนต์ที่คุณต้องการเพิ่มฟังก์ชัน sign-in และ sign-out (เช่น `app.component.ts`) ให้ inject `OidcSecurityService` และใช้สำหรับ sign in และ sign out +### สร้างฟังก์ชันลงชื่อเข้าใช้และออกจากระบบ \{#implement-sign-in-and-sign-out} -```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; +inject `LogtoService` เพื่อเริ่มต้นการลงชื่อเข้าใช้และออกจากระบบ ส่ง redirect URIs ที่ลงทะเบียนไว้ไปยังเมธอดเหล่านี้ `postRedirectUri` จะบอก SDK ว่าควรนำทางไปที่ใดหลังจากจัดการ callback การลงชื่อเข้าใช้สำเร็จ: -export class AppComponent implements OnInit { - constructor(public oidcSecurityService: OidcSecurityService) {} + - signIn() { - this.oidcSecurityService.authorize(); +```ts title="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, + }); } - signOut() { - this.oidcSecurityService.logoff().subscribe((result) => { - console.log('app sign-out', result); - }); + async signOut() { + await this.logto.signOut('http://localhost:3000/'); } } ``` -จากนั้นใน template ให้เพิ่มปุ่มสำหรับ sign in และ sign out: +อ่านค่า `isLoading()`, `isAuthenticated()`, และ `error()` Signals ได้โดยตรงใน template: ```html title="app/app.component.html" - -
- +@if (logto.error(); as error) { +

{{ error.message }}

+} @if (logto.isLoading()) { +

กำลังโหลด...

+} @else if (logto.isAuthenticated()) { + +} @else { + +} + + ``` +ให้คง `` ไว้นอกเงื่อนไขการยืนยันตัวตน เพื่อให้ callback สามารถเรนเดอร์ได้ก่อนที่ผู้ใช้จะลงชื่อเข้าใช้ + diff --git a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md new file mode 100644 index 00000000000..63c1a120189 --- /dev/null +++ b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md @@ -0,0 +1,17 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: [UserScope.Organizations], + // highlight-end + }), + // ...other providers + ], +}; +``` diff --git a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md index d680e1b878a..7d6a1a15342 100644 --- a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md +++ b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md @@ -1,14 +1,15 @@ -```ts title="/app/app.config.ts" +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), // ...other providers ], diff --git a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md index 135f1616058..d2c0c7bba17 100644 --- a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md +++ b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md @@ -1,15 +1,16 @@ ```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - scopes: ['openid', 'profile', 'offline_access', 'read', 'write'], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['shopping:read', 'shopping:write', 'store:read', 'store:write'], + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), // ...other providers ], diff --git a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md new file mode 100644 index 00000000000..79bb4a92c13 --- /dev/null +++ b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md @@ -0,0 +1,18 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['read', 'write'], // ขอบเขต (scopes) ที่ต้องการ + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], // ทรัพยากร API (API resources) ที่ต้องการ + // highlight-end + }), + // ...other providers + ], +}; +``` diff --git a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md new file mode 100644 index 00000000000..c440c668970 --- /dev/null +++ b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md @@ -0,0 +1,28 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { + +
{{ accessToken() }}
+ } + `, +}) +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')); + } +} +``` diff --git a/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md new file mode 100644 index 00000000000..641730314fa --- /dev/null +++ b/i18n/th/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md @@ -0,0 +1,59 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { +
    + @for (organizationId of organizationIds(); track organizationId) { +
  • + {{ organizationId }} + +
  • + } +
+
{{ organizationToken() }}
+ } + `, +}) +export class OrganizationsComponent { + readonly logto = inject(LogtoService); + readonly organizationIds = signal([]); + 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)); + } +} +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx index 358fb6959bf..e0bba819bae 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx @@ -2,7 +2,7 @@ slug: /quick-starts/angular sidebar_label: Angular sidebar_custom_props: - description: Angular 是一个用于构建用户界面的 JavaScript 库。 + description: Angular 是一个用于构建 Web 应用程序的框架。 --- import FurtherReadings from '../../fragments/_further-readings.md'; @@ -15,12 +15,15 @@ import Integration from './_integration.mdx'; # 为你的 Angular 应用添加认证 (Authentication) +本指南将向你展示如何将 Logto Angular SDK v2 集成到你的应用中。 + -## 前提条件 \{#prerequisites} +## 前置条件 \{#prerequisites} -- 一个 [Logto Cloud](https://cloud.logto.io) 账户或一个 [自托管 Logto](/introduction/set-up-logto-oss)。 -- 一个已创建的 Logto 单页应用程序。 +- 一个 [Logto Cloud](https://cloud.logto.io) 账户或 [自托管 Logto](/introduction/set-up-logto-oss)。 +- 在 Logto 控制台中创建的单页应用程序(SPA)。 +- 一个 Angular 20 项目。 ## 安装 \{#installation} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx index e9c1e91195b..911058a4478 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx @@ -1,22 +1,67 @@ import ApiResourcesDescription from '../../fragments/_api-resources-description.md'; import ConfigApiResources from '../../fragments/_config-api-resources.mdx'; +import FetchAccessTokenForApiResources from '../../fragments/_fetch-access-token-for-api-resources.mdx'; +import FetchOrganizationTokenForUser from '../../fragments/_fetch-organization-token-for-user.mdx'; +import ConfigOrganizationCode from './code/_config-organization-code.md'; import ConfigResourcesCode from './code/_config-resources-code.md'; import ConfigResourcesWithScopesCode from './code/_config-resources-with-scopes-code.md'; - -### 为 API 资源配置 `angular-auth-oidc-client` \{#configure-angular-auth-oidc-client-for-api-resource} +import ConfigResourcesWithSharedScopesCode from './code/_config-resources-with-shared-scopes-code.md'; +import GetAccessTokenCode from './code/_get-access-token-code.md'; +import GetOrganizationAccessTokenCode from './code/_get-organization-access-token-code.md'; +### 配置 Logto 客户端 \{#configure-logto-client} + } configResourcesWithScopesCode={} + configResourcesWithSharedScopesCode={} /> -现在,访问令牌 (access token) 将采用 JSON Web Token (JWT) 格式,而不是随机字符串(不透明令牌 (opaque token))。 +更改资源或权限 (Scopes) 后请重新登录,以便用户可以授权更新后的配置。 -:::warning -当设置了 `resource` 时,`autoUserInfo` 和 `renewUserInfoAfterTokenRenew` 都将被禁用。这是因为访问令牌 (access token) 将为特定的 API 资源请求,而不是为用户信息端点请求。 -::: +### 获取 API 资源的访问令牌 (Access token) \{#fetch-access-token-for-the-api-resource} + +} +/> + +请使用你配置中的精确资源标识符。每次发起 API 请求时调用 `getAccessToken(resource)`,这样 SDK 能返回有效的令牌,而不是在组件中无限期保存令牌。 + +### 获取组织令牌 (Organization tokens) \{#fetch-organization-tokens} + +} + getOrganizationAccessTokenCode={} +/> + +将 `UserScope.Organizations` 与已有的权限 (Scopes) 合并,并在更新配置后重新登录。`getOrganizationToken(organizationId)` 会返回所选 Logto 组织 (Organization) 的令牌;如需 API 资源的令牌,请使用 `getAccessToken(resource)`。 + +### 将访问令牌 (Access token) 附加到请求头 \{#attach-access-token-to-request-headers} + +将令牌放入 `Authorization` HTTP 头中,使用 Bearer 格式(`Bearer YOUR_TOKEN`)。例如,可以将此方法添加到注入了 `LogtoService` 的认证组件中: -目前,只有 Logto 官方 SDK 支持同时请求用户信息和 API 资源访问令牌 (access token) 的功能。如果你需要同时请求这两者,请随时联系我们。 +```ts +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(); +} +``` + +:::note +示例中使用了 `fetch`。如果你使用 Angular 的 `HttpClient`,请在请求选项中设置同样的 `Authorization` 头。 +::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx index 7a2dca8d2b5..ca43236e49e 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx @@ -5,130 +5,106 @@ import FindUserInfoMissing from '../../fragments/_find-user-info-missing.mdx'; import ScopesAndClaims from '../../fragments/_scopes-and-claims.mdx'; import ScopesAndClaimsIntroduction from '../../fragments/_scopes-claims-introduction.md'; -一旦用户成功登录,Logto 将签发一个包含用户信息声明的 [ID 令牌](https://openid.net/specs/openid-connect-core-1_0.html#IDToken)。ID 令牌是一个 JSON Web Token (JWT)。 - -需要注意的是,可以检索的用户信息声明取决于用户在登录时使用的权限,并且考虑到性能和数据大小,ID 令牌可能不包含所有用户声明,某些用户声明仅在 [userinfo endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) 中可用(请参阅下面的相关列表)。 - -如果配置中没有提供 `resource`,`buildAngularAuthConfig()` 工具将启用 `autoUserInfo` 和 `renewUserInfoAfterTokenRenew`。这意味着 Logto 将在用户登录后自动获取用户信息,并在令牌更新后更新用户信息。 - -:::info -要了解有关配置 `angular-auth-oidc-client` 库的更多信息,请参阅 [官方文档](https://angular-auth-oidc-client.com/)。 -::: - ### 显示用户信息 \{#display-user-information} -`OidcSecurityService` 提供了一种方便的方法来订阅认证状态以及用户信息: +要显示用户的信息,可以使用 `getIdTokenClaims()` 从 ID 令牌 (ID token) 中读取声明 (Claims),无需额外的网络请求。在你的 `AppComponent` 中添加一个 `effect`,当 `isAuthenticated()` 变为 true 时加载声明 (Claims),包括恢复现有会话时。导入 `JsonPipe` 以显示结果: ```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; -import { decodeIdToken, type IdTokenClaims } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - idTokenClaims?: IdTokenClaims; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService.checkAuth().subscribe(({ isAuthenticated, idToken, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - this.idTokenClaims = decodeIdToken(idToken); - this.accessToken = accessToken; +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(undefined); + + constructor() { + effect(() => { + if (!this.logto.isAuthenticated()) { + this.user.set(undefined); + return; + } + + void this.logto + .getIdTokenClaims() + .then((claims) => { + this.user.set(claims); + }) + .catch(() => { + // SDK 会通过 logto.error() 向模板暴露错误。 + }); }); } - // ...other methods + // ...保留上一步的 signIn() 和 signOut() 方法 } ``` -并在模板中使用它: +在你的模板的 `logto.isAuthenticated()` 分支内添加以下内容: ```html title="app/app.component.html" - - -
{{ idTokenClaims | json }}
-

Access token: {{ accessToken }}

- - -
+@if (user(); as claims) { +
{{ claims | json }}
+} ``` -### 请求额外的声明 \{#request-additional-claims} +### 请求额外声明 (Claims) \{#request-additional-claims} - + -要请求额外的权限,你可以配置认证提供者配置: +在你的 `provideLogto` 配置中添加 scopes: -```tsx title="app/app.config.ts" -import { UserScope, buildAngularAuthConfig } from '@logto/js'; +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other configs - // highlight-start - scopes: [ - UserScope.Email, - UserScope.Phone, - UserScope.CustomData, - UserScope.Identities, - UserScope.Organizations, - ], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + scopes: [ + UserScope.Email, + UserScope.Phone, + UserScope.CustomData, + UserScope.Identities, + UserScope.Organizations, + ], }), - // ...other providers + // ...其他 providers ], }; ``` -然后你可以在 `idToken` 的返回值中访问额外的声明。 +更改 scopes 后请重新登录。额外的 ID 令牌 (ID token) 声明 (Claims),如 `email` 和 `phone_number`,将可通过 `getIdTokenClaims()` 获取,并由上面的示例显示。 -{/* eslint-disable prettier/prettier */} {`import { OidcSecurityService } from 'angular-auth-oidc-client'; -// highlight-next-line -import { type UserInfoResponse } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - // highlight-next-line - userData?: UserInfoResponse; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService - .checkAuth() - // highlight-next-line - .subscribe(({ isAuthenticated, userData, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - // highlight-next-line - this.userData = userData; - this.accessToken = accessToken; - }); + {`// 将此方法添加到 AppComponent 并在登录后调用。 +async loadUserInfo() { + const userInfo = await this.logto.fetchUserInfo(); + // 现在你可以访问 userInfo.custom_data、userInfo.identities 等。 + return userInfo; +}`} } - - // ...other methods -} - -// Now you can access the claim \`userData.custom_data\``} -} /> -{/* eslint-enable prettier/prettier */} -### 权限和声明 \{#scopes-and-claims} +`fetchUserInfo()` 可以与 API 资源 (API resource) 访问令牌 (Access token) 一起使用。配置 `resources` 并不会阻止 SDK 请求用户信息。 + +### 权限 (Scopes) 与声明 (Claims) \{#scopes-and-claims} diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md index 7895ba7af45..6993874e03d 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md @@ -1,6 +1,6 @@ :::tip -- 以下演示基于 Angular 18.0.0 和 [angular-auth-oidc-client](https://github.com/damienbod/angular-auth-oidc-client)。 -- 示例项目可在 [GitHub 仓库](https://github.com/logto-io/js/tree/master/packages/angular-sample) 中找到。 +- 本指南使用官方的 `@logto/angular` v2 SDK,该 SDK 支持 Angular 20,并提供依赖注入和 Signals。 +- 示例项目可在我们的 [SDK 仓库](https://github.com/logto-io/js/tree/master/packages/angular-sample) 中获取。 ::: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx index 92160b59c25..41aabde1f14 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx @@ -1,30 +1,3 @@ -import TabItem from '@theme/TabItem'; -import Tabs from '@theme/Tabs'; +import NpmLikeInstallation from '../../fragments/_npm-like-installation.mdx'; -安装 Logto JS core SDK 和 Angular OIDC 客户端库: - - - - - -```bash -npm i @logto/js angular-auth-oidc-client -``` - - - - -```bash -pnpm add @logto/js angular-auth-oidc-client -``` - - - - -```bash -yarn add @logto/js angular-auth-oidc-client -``` - - - - + diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx index d8c2f06a2f2..39f174ba7ac 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx @@ -1,33 +1,39 @@ import Checkpoint from '../../fragments/_checkpoint-test-your-application.md'; +import SignInNote from '../../fragments/_sign-in-note.mdx'; import AssumingUrl from '../../fragments/_web-assuming-url.mdx'; import WebConfigureRedirectUris from '../../fragments/_web-configure-redirect-uris.mdx'; import SignInFlowSummary from '../../fragments/_web-sign-in-flow-summary.mdx'; import SignOutNote from '../../fragments/_web-sign-out-note.md'; -### 配置应用程序 \{#configure-application} +### 初始化 Logto provider \{#init-logto-provider} -在你的 Angular 项目中,在 `app.config.ts` 中添加认证 (Authentication) 提供者: +在你的 Angular 项目中,在 `app.config.ts` 文件中注册 `provideLogto` 和你的应用路由: ```ts title="app/app.config.ts" -import { buildAngularAuthConfig } from '@logto/js'; -import { provideAuth } from 'angular-auth-oidc-client'; +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: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - endpoint: '', - appId: '', - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - }), + provideLogto({ + endpoint: '', + appId: '', }), + provideRouter(routes), // ...other providers ], }; ``` +`provideLogto` 会在浏览器首次渲染后自动恢复认证 (Authentication) 状态。你无需在组件中调用初始化方法。 + +:::note +当使用服务端渲染(SSR)时,认证 (Authentication) 状态和令牌仅在浏览器中可用。使用 `isLoading()` 在初始化完成前显示加载状态。如果你需要在服务端渲染期间获取已认证的数据,请使用服务端或 BFF SDK。 +::: + ### 配置重定向 URI \{#configure-redirect-uris} @@ -38,55 +44,113 @@ export const appConfig: ApplicationConfig = { ### 处理重定向 \{#handle-redirect} -由于我们使用 `http://localhost:3000/callback` 作为重定向 URI,现在我们需要正确处理它。`angular-auth-oidc-client` 库提供了处理重定向的内置支持。你只需正确配置认证 (Authentication) 提供者配置,库将处理其余部分。 +创建一个回调组件,在 Logto 将用户重定向回你的应用后完成登录。使用 `afterNextRender`,确保回调处理仅在浏览器中运行: + +```ts title="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) { +

{{ error.message }}

+ } @else { +

正在完成登录...

+ } + `, +}) +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 会通过 logto.error() 向模板暴露回调错误。 + }); + }); + } +} +``` -```ts title="app/app.config.ts" -export const appConfig: ApplicationConfig = { - providers: [ - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - // highlight-end - }), - }), - // ...other providers - ], -}; +`isSignInRedirected()` 用于检查当前 URL 是否匹配一个活跃的登录会话。如果有人在没有登录会话的情况下访问回调路由,本示例会将其重定向回应用首页,而不是尝试完成登录。 + +在 `app.routes.ts` 中注册回调路由。它必须与重定向 URI 的路径一致,并且不能要求认证 (Authentication)。例如,重定向 URI 以 `/callback` 结尾时,使用 `callback`: + +```ts title="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 +]; ``` -### 实现登录和登出 \{#implement-sign-in-and-sign-out} +根组件需要一个 `` 来渲染该路由,具体见下一步。 -在你想要实现登录和登出的组件中(例如,`app.component.ts`),注入 `OidcSecurityService` 并使用它进行登录和登出。 +### 实现登录与登出 \{#implement-sign-in-and-sign-out} -```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; +注入 `LogtoService` 以启动登录和登出。将已注册的重定向 URI 传递给这些方法。`postRedirectUri` 告诉 SDK 在成功处理登录回调后跳转到哪里: -export class AppComponent implements OnInit { - constructor(public oidcSecurityService: OidcSecurityService) {} + - signIn() { - this.oidcSecurityService.authorize(); +```ts title="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, + }); } - signOut() { - this.oidcSecurityService.logoff().subscribe((result) => { - console.log('app sign-out', result); - }); + async signOut() { + await this.logto.signOut('http://localhost:3000/'); } } ``` -然后,在模板中添加登录和登出的按钮: +在模板中直接读取 `isLoading()`、`isAuthenticated()` 和 `error()` 信号: ```html title="app/app.component.html" - -
- +@if (logto.error(); as error) { +

{{ error.message }}

+} @if (logto.isLoading()) { +

加载中...

+} @else if (logto.isAuthenticated()) { + +} @else { + +} + + ``` +请将 `` 放在认证 (Authentication) 条件之外,这样回调可以在用户登录前渲染。 + diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md new file mode 100644 index 00000000000..63c1a120189 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md @@ -0,0 +1,17 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: [UserScope.Organizations], + // highlight-end + }), + // ...other providers + ], +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md index d680e1b878a..7d6a1a15342 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md @@ -1,14 +1,15 @@ -```ts title="/app/app.config.ts" +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), // ...other providers ], diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md index 135f1616058..090311fd534 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md @@ -1,15 +1,16 @@ ```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - scopes: ['openid', 'profile', 'offline_access', 'read', 'write'], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['shopping:read', 'shopping:write', 'store:read', 'store:write'], // 权限 (Scopes) + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], // API 资源 + // highlight-end }), // ...other providers ], diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md new file mode 100644 index 00000000000..bdc55917082 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md @@ -0,0 +1,18 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['read', 'write'], // 权限 (Scopes) + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], // API 资源 + // highlight-end + }), + // ...other providers + ], +}; +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md new file mode 100644 index 00000000000..0472bb992e6 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md @@ -0,0 +1,28 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { + +
{{ accessToken() }}
+ } + `, +}) +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')); + } +} +``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md new file mode 100644 index 00000000000..d1e7274b86d --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md @@ -0,0 +1,59 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { +
    + @for (organizationId of organizationIds(); track organizationId) { +
  • + {{ organizationId }} + +
  • + } +
+
{{ organizationToken() }}
+ } + `, +}) +export class OrganizationsComponent { + readonly logto = inject(LogtoService); + readonly organizationIds = signal([]); + 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() 向模板暴露错误信息。 + }); + }); + } + + async loadOrganizationToken(organizationId: string) { + this.organizationToken.set(await this.logto.getOrganizationToken(organizationId)); + } +} +``` diff --git a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx index 99944a0f1bc..9d42cfcb69d 100644 --- a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx +++ b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/README.mdx @@ -2,7 +2,7 @@ slug: /quick-starts/angular sidebar_label: Angular sidebar_custom_props: - description: Angular 是一個用於構建使用者介面的 JavaScript 函式庫。 + description: Angular 是一個用於構建網頁應用程式的框架。 --- import FurtherReadings from '../../fragments/_further-readings.md'; @@ -15,12 +15,15 @@ import Integration from './_integration.mdx'; # 為你的 Angular 應用程式新增驗證 (Authentication) +本指南將帶你整合 Logto Angular SDK v2 到你的應用程式中。 + ## 先決條件 \{#prerequisites} -- 一個 [Logto Cloud](https://cloud.logto.io) 帳戶或 [自託管 Logto](/introduction/set-up-logto-oss)。 -- 一個已建立的 Logto 單頁應用程式。 +- 一個 [Logto Cloud](https://cloud.logto.io) 帳號或 [自行架設 Logto](/introduction/set-up-logto-oss)。 +- 已在 Logto Console 建立的單頁應用程式(SPA)。 +- 一個 Angular 20 專案。 ## 安裝 \{#installation} @@ -30,11 +33,11 @@ import Integration from './_integration.mdx'; -## 獲取使用者資訊 \{#get-user-information} +## 取得使用者資訊 \{#get-user-information} -## API 資源 \{#api-resources} +## API 資源 (API resources) \{#api-resources} diff --git a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx index 67019c95c61..e7a2aed7109 100644 --- a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx +++ b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_api-resources.mdx @@ -1,22 +1,67 @@ import ApiResourcesDescription from '../../fragments/_api-resources-description.md'; import ConfigApiResources from '../../fragments/_config-api-resources.mdx'; +import FetchAccessTokenForApiResources from '../../fragments/_fetch-access-token-for-api-resources.mdx'; +import FetchOrganizationTokenForUser from '../../fragments/_fetch-organization-token-for-user.mdx'; +import ConfigOrganizationCode from './code/_config-organization-code.md'; import ConfigResourcesCode from './code/_config-resources-code.md'; import ConfigResourcesWithScopesCode from './code/_config-resources-with-scopes-code.md'; - -### 為 API 資源 (API resource) 配置 `angular-auth-oidc-client` \{#configure-angular-auth-oidc-client-for-api-resource} +import ConfigResourcesWithSharedScopesCode from './code/_config-resources-with-shared-scopes-code.md'; +import GetAccessTokenCode from './code/_get-access-token-code.md'; +import GetOrganizationAccessTokenCode from './code/_get-organization-access-token-code.md'; +### 設定 Logto 用戶端 \{#configure-logto-client} + } configResourcesWithScopesCode={} + configResourcesWithSharedScopesCode={} /> -現在,存取權杖 (Access token) 將以 JSON Web Token (JWT) 格式呈現,而非隨機字串(不透明權杖 (Opaque token))。 +變更資源或權限範圍(Scopes)後,請重新登入,讓使用者能授權更新後的設定。 -:::warning -當設定 `resource` 時,`autoUserInfo` 和 `renewUserInfoAfterTokenRenew` 皆會被停用。這是因為存取權杖 (Access token) 將針對特定 API 資源 (API resource) 請求,而非使用者資訊端點。 -::: +### 取得 API 資源的存取權杖 (Access token) \{#fetch-access-token-for-the-api-resource} + +} +/> + +請使用你設定中的精確資源標示符(resource identifier)。每次發送 API 請求時呼叫 `getAccessToken(resource)`,讓 SDK 回傳有效的權杖,而不是在元件中無限期保存權杖。 + +### 取得組織權杖 (Organization tokens) \{#fetch-organization-tokens} + +} + getOrganizationAccessTokenCode={} +/> + +將 `UserScope.Organizations` 與現有權限範圍合併,並在更新設定後重新登入。`getOrganizationToken(organizationId)` 會回傳指定 Logto 組織的權杖;如需 API 資源權杖,請使用 `getAccessToken(resource)`。 + +### 將存取權杖 (Access token) 加入請求標頭 \{#attach-access-token-to-request-headers} + +將權杖放入 `Authorization` HTTP 標頭,格式為 Bearer(`Bearer YOUR_TOKEN`)。例如,將下列方法加入注入 `LogtoService` 的驗證元件: -目前,僅 Logto 官方 SDK 支援同時請求使用者資訊與 API 資源存取權杖 (Access tokens) 的功能。如需同時請求,請隨時與我們聯繫。 +```ts +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(); +} +``` + +:::note +範例使用 `fetch`。如果你使用 Angular 的 `HttpClient`,請在其請求選項中設定相同的 `Authorization` 標頭。 +::: diff --git a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx index 16ca403d383..0d93b999fba 100644 --- a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx +++ b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_get-user-information.mdx @@ -5,130 +5,106 @@ import FindUserInfoMissing from '../../fragments/_find-user-info-missing.mdx'; import ScopesAndClaims from '../../fragments/_scopes-and-claims.mdx'; import ScopesAndClaimsIntroduction from '../../fragments/_scopes-claims-introduction.md'; -當使用者成功登入後,Logto 會簽發一個包含使用者資訊宣告的 [ID 權杖 (ID token)](https://openid.net/specs/openid-connect-core-1_0.html#IDToken)。ID 權杖是一個 JSON Web Token (JWT)。 - -需要注意的是,根據使用者在登入時使用的權限範圍 (Scopes),可檢索的使用者資訊宣告可能不同。考慮到效能和資料大小,ID 權杖可能不包含所有使用者宣告,部分使用者宣告僅在 [userinfo endpoint](https://openid.net/specs/openid-connect-core-1_0.html#UserInfo) 中可用(請參閱下方相關列表)。 - -如果配置中沒有提供 `resource`,`buildAngularAuthConfig()` 工具將啟用 `autoUserInfo` 和 `renewUserInfoAfterTokenRenew`。這意味著 Logto 會在使用者登入後自動獲取使用者資訊,並在權杖更新後更新使用者資訊。 - -:::info -欲了解更多關於配置 `angular-auth-oidc-client` 函式庫的資訊,請參閱 [官方文件](https://angular-auth-oidc-client.com/)。 -::: - ### 顯示使用者資訊 \{#display-user-information} -`OidcSecurityService` 提供了一種方便的方法來訂閱驗證狀態以及使用者資訊: +若要顯示使用者資訊,請使用 `getIdTokenClaims()` 從 ID 權杖 (ID token) 讀取宣告 (claims),無需額外網路請求。於你的 `AppComponent` 中新增 `effect`,當 `isAuthenticated()` 變為 true(包含恢復現有會話時)時載入宣告。匯入 `JsonPipe` 以顯示結果: ```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; -import { decodeIdToken, type IdTokenClaims } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - idTokenClaims?: IdTokenClaims; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService.checkAuth().subscribe(({ isAuthenticated, idToken, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - this.idTokenClaims = decodeIdToken(idToken); - this.accessToken = accessToken; +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(undefined); + + constructor() { + effect(() => { + if (!this.logto.isAuthenticated()) { + this.user.set(undefined); + return; + } + + void this.logto + .getIdTokenClaims() + .then((claims) => { + this.user.set(claims); + }) + .catch(() => { + // SDK 會透過 logto.error() 將錯誤暴露給模板。 + }); }); } - // ...其他方法 + // ...保留前一步驟的 signIn() 與 signOut() 方法 } ``` -並在模板中使用: +在模板的 `logto.isAuthenticated()` 分支內新增以下內容: ```html title="app/app.component.html" - - -
{{ idTokenClaims | json }}
-

Access token: {{ accessToken }}

- - -
+@if (user(); as claims) { +
{{ claims | json }}
+} ``` -### 請求額外的宣告 \{#request-additional-claims} +### 請求額外宣告 (claims) \{#request-additional-claims} - + -要請求額外的權限範圍 (Scopes),可以配置驗證提供者的配置: +於你的 `provideLogto` 設定中新增權限範圍 (scopes): -```tsx title="app/app.config.ts" -import { UserScope, buildAngularAuthConfig } from '@logto/js'; +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...其他配置 - // highlight-start - scopes: [ - UserScope.Email, - UserScope.Phone, - UserScope.CustomData, - UserScope.Identities, - UserScope.Organizations, - ], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + scopes: [ + UserScope.Email, + UserScope.Phone, + UserScope.CustomData, + UserScope.Identities, + UserScope.Organizations, + ], }), - // ...其他提供者 + // ...其他 providers ], }; ``` -然後你可以在 `idToken` 的返回值中訪問額外的宣告。 +變更權限範圍 (scopes) 後請重新登入。額外的 ID 權杖 (ID token) 宣告 (claims),如 `email` 與 `phone_number`,將可透過 `getIdTokenClaims()` 取得,並由上述範例顯示。 -{/* eslint-disable prettier/prettier */} {`import { OidcSecurityService } from 'angular-auth-oidc-client'; -// highlight-next-line -import { type UserInfoResponse } from '@logto/js'; - -export class AppComponent implements OnInit { - isAuthenticated = false; - // highlight-next-line - userData?: UserInfoResponse; - accessToken?: string; - - constructor(public oidcSecurityService: OidcSecurityService) {} - - ngOnInit() { - this.oidcSecurityService - .checkAuth() - // highlight-next-line - .subscribe(({ isAuthenticated, userData, accessToken }) => { - console.log('app authenticated', isAuthenticated, idToken); - this.isAuthenticated = isAuthenticated; - // highlight-next-line - this.userData = userData; - this.accessToken = accessToken; - }); + {`// 將此方法加入 AppComponent,並於登入後呼叫。 +async loadUserInfo() { + const userInfo = await this.logto.fetchUserInfo(); + // 現在你可以存取 userInfo.custom_data、userInfo.identities 等。 + return userInfo; +}`} } - - // ...其他方法 -} - -// 現在你可以訪問宣告 \`userData.custom_data\``} -} /> -{/* eslint-enable prettier/prettier */} -### 權限範圍 (Scopes) 和宣告 \{#scopes-and-claims} +`fetchUserInfo()` 可與 API 資源 (API resource) 存取權杖 (access token) 一同使用。設定 `resources` 不會阻止 SDK 請求使用者資訊。 + +### 權限範圍 (Scopes) 與宣告 (Claims) \{#scopes-and-claims} diff --git a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md index f6ec44f1ee6..11d209901a7 100644 --- a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md +++ b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_guide-tip.md @@ -1,6 +1,6 @@ :::tip -- 以下示範基於 Angular 18.0.0 和 [angular-auth-oidc-client](https://github.com/damienbod/angular-auth-oidc-client)。 -- 範例專案可在 [GitHub 儲存庫](https://github.com/logto-io/js/tree/master/packages/angular-sample) 中找到。 +- 本指南使用第一方 `@logto/angular` v2 SDK,支援 Angular 20,並提供相依性注入與 Signals。 +- 範例專案可在我們的 [SDK 儲存庫](https://github.com/logto-io/js/tree/master/packages/angular-sample) 找到。 ::: diff --git a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx index 001ab16c2af..41aabde1f14 100644 --- a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx +++ b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_installation.mdx @@ -1,30 +1,3 @@ -import TabItem from '@theme/TabItem'; -import Tabs from '@theme/Tabs'; +import NpmLikeInstallation from '../../fragments/_npm-like-installation.mdx'; -安裝 Logto JS 核心 SDK 和 Angular OIDC 客戶端庫: - - - - - -```bash -npm i @logto/js angular-auth-oidc-client -``` - - - - -```bash -pnpm add @logto/js angular-auth-oidc-client -``` - - - - -```bash -yarn add @logto/js angular-auth-oidc-client -``` - - - - + diff --git a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx index 08834a7c7ff..726ef166e56 100644 --- a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx +++ b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/_integration.mdx @@ -1,34 +1,40 @@ import Checkpoint from '../../fragments/_checkpoint-test-your-application.md'; +import SignInNote from '../../fragments/_sign-in-note.mdx'; import AssumingUrl from '../../fragments/_web-assuming-url.mdx'; import WebConfigureRedirectUris from '../../fragments/_web-configure-redirect-uris.mdx'; import SignInFlowSummary from '../../fragments/_web-sign-in-flow-summary.mdx'; import SignOutNote from '../../fragments/_web-sign-out-note.md'; -### 配置應用程式 \{#configure-application} +### 初始化 Logto provider \{#init-logto-provider} -在你的 Angular 專案中,於 `app.config.ts` 中新增驗證提供者: +在你的 Angular 專案中,於 `app.config.ts` 註冊 `provideLogto` 以及你的應用程式路由: ```ts title="app/app.config.ts" -import { buildAngularAuthConfig } from '@logto/js'; -import { provideAuth } from 'angular-auth-oidc-client'; +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: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - endpoint: '', - appId: '', - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - }), + provideLogto({ + endpoint: '', + appId: '', }), - // ...其他提供者 + provideRouter(routes), + // ...other providers ], }; ``` -### 配置重定向 URI \{#configure-redirect-uris} +`provideLogto` 會在瀏覽器首次渲染後自動還原驗證 (Authentication) 狀態。你不需要在元件中呼叫初始化方法。 + +:::note +當使用伺服器端渲染(SSR, Server-side Rendering)時,驗證 (Authentication) 狀態與權杖僅在瀏覽器中可用。請使用 `isLoading()` 顯示載入狀態直到初始化完成。如果你需要在伺服器渲染期間取得已驗證資料,請使用伺服器或 BFF SDK。 +::: + +### 設定 redirect URI \{#configure-redirect-uris} @@ -36,57 +42,115 @@ export const appConfig: ApplicationConfig = { -### 處理重定向 \{#handle-redirect} +### 處理 redirect \{#handle-redirect} + +建立 callback 元件,讓 Logto 將使用者導回你的應用程式後完成登入。使用 `afterNextRender` 確保 callback 處理僅在瀏覽器端執行: + +```ts title="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) { +

{{ error.message }}

+ } @else { +

正在完成登入...

+ } + `, +}) +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 會透過 logto.error() 將 callback 錯誤暴露給模板。 + }); + }); + } +} +``` -由於我們使用 `http://localhost:3000/callback` 作為重定向 URI,現在需要妥善處理它。`angular-auth-oidc-client` 函式庫提供了內建的重定向處理支援。你只需正確配置驗證提供者的設定,函式庫會處理其餘部分。 +`isSignInRedirected()` 用來檢查 URL 是否符合有效的登入階段。如果有人在沒有登入階段的情況下訪問 callback 路由,此範例會將他們導回應用程式首頁,而不是嘗試完成登入。 -```ts title="app/app.config.ts" -export const appConfig: ApplicationConfig = { - providers: [ - provideAuth({ - config: buildAngularAuthConfig({ - // ...其他設定 - // highlight-start - redirectUri: 'http://localhost:3000/callback', - postLogoutRedirectUri: 'http://localhost:3000/', - // highlight-end - }), - }), - // ...其他提供者 - ], -}; +在 `app.routes.ts` 註冊 callback 路由。它必須與你的 redirect URI 路徑一致,且不能要求驗證。例如,若 redirect URI 以 `/callback` 結尾,則使用 `callback`: + +```ts title="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 +]; ``` -### 實作登入與登出 \{#implement-sign-in-and-sign-out} +根元件需要 `` 來渲染此路由,詳見下一步。 -在你想要實作登入與登出的元件中(例如 `app.component.ts`),注入 `OidcSecurityService` 並使用它來進行登入與登出。 +### 實作登入與登出 \{#implement-sign-in-and-sign-out} -```ts title="app/app.component.ts" -import { OidcSecurityService } from 'angular-auth-oidc-client'; +注入 `LogtoService` 以啟動登入與登出。將註冊過的 redirect URI 傳給這些方法。`postRedirectUri` 告訴 SDK 在成功處理登入 callback 後要導向哪裡: -export class AppComponent implements OnInit { - constructor(public oidcSecurityService: OidcSecurityService) {} + - signIn() { - this.oidcSecurityService.authorize(); +```ts title="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, + }); } - signOut() { - this.oidcSecurityService.logoff().subscribe((result) => { - console.log('app sign-out', result); - }); + async signOut() { + await this.logto.signOut('http://localhost:3000/'); } } ``` -然後,在模板中新增登入與登出按鈕: +直接在模板中讀取 `isLoading()`、`isAuthenticated()` 與 `error()` Signals: ```html title="app/app.component.html" - -
- +@if (logto.error(); as error) { +

{{ error.message }}

+} @if (logto.isLoading()) { +

載入中...

+} @else if (logto.isAuthenticated()) { + +} @else { + +} + + ``` +請將 `` 放在驗證 (Authentication) 條件判斷之外,這樣 callback 可以在使用者登入前正確渲染。 + diff --git a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md new file mode 100644 index 00000000000..fb4f9fb5c5d --- /dev/null +++ b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-organization-code.md @@ -0,0 +1,17 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto, UserScope } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: [UserScope.Organizations], // 權限範圍 (Scopes):[UserScope.Organizations] + // highlight-end + }), + // ...other providers + ], +}; +``` diff --git a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md index d680e1b878a..7d6a1a15342 100644 --- a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md +++ b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-code.md @@ -1,14 +1,15 @@ -```ts title="/app/app.config.ts" +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), // ...other providers ], diff --git a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md index 135f1616058..d2c0c7bba17 100644 --- a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md +++ b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-scopes-code.md @@ -1,15 +1,16 @@ ```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + export const appConfig: ApplicationConfig = { providers: [ - provideHttpClient(withFetch()), - provideAuth({ - config: buildAngularAuthConfig({ - // ...other config - // highlight-start - resource: 'https://your-api-resource.com', - scopes: ['openid', 'profile', 'offline_access', 'read', 'write'], - // highlight-end - }), + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['shopping:read', 'shopping:write', 'store:read', 'store:write'], + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], + // highlight-end }), // ...other providers ], diff --git a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md new file mode 100644 index 00000000000..a0ba6bc24db --- /dev/null +++ b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_config-resources-with-shared-scopes-code.md @@ -0,0 +1,18 @@ +```ts title="app/app.config.ts" +import { type ApplicationConfig } from '@angular/core'; +import { provideLogto } from '@logto/angular'; + +export const appConfig: ApplicationConfig = { + providers: [ + provideLogto({ + endpoint: '', + appId: '', + // highlight-start + scopes: ['read', 'write'], // 權限範圍 (Scopes) + resources: ['https://shopping.your-app.com/api', 'https://store.your-app.com/api'], // API 資源 (API resources) + // highlight-end + }), + // ...other providers + ], +}; +``` diff --git a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md new file mode 100644 index 00000000000..2585ff1cf3e --- /dev/null +++ b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-access-token-code.md @@ -0,0 +1,28 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { + +
{{ accessToken() }}
+ } + `, +}) +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')); + } +} +``` diff --git a/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md new file mode 100644 index 00000000000..ee2421cb412 --- /dev/null +++ b/i18n/zh-TW/docusaurus-plugin-content-docs/current/quick-starts/framework/angular/code/_get-organization-access-token-code.md @@ -0,0 +1,59 @@ +```ts title="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) { +

{{ error.message }}

+ } + @if (logto.isAuthenticated()) { +
    + @for (organizationId of organizationIds(); track organizationId) { +
  • + {{ organizationId }} + +
  • + } +
+
{{ organizationToken() }}
+ } + `, +}) +export class OrganizationsComponent { + readonly logto = inject(LogtoService); + readonly organizationIds = signal([]); + 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() 將錯誤暴露給模板使用。 + }); + }); + } + + async loadOrganizationToken(organizationId: string) { + this.organizationToken.set(await this.logto.getOrganizationToken(organizationId)); + } +} +```