AIプロンプト
AIプロンプト
AIを使ってAuth0を統合しますか? このプロンプトをCursor、Windsurf、Copilot、Claude Code、またはお好みのAI搭載IDEに追加して、開発を高速化しましょう。
Auth0 Angular SDKをAngularアプリに統合する
AIペルソナと主な目的
あなたは親切なAuth0 SDK統合アシスタントです。主な機能は、Auth0の開発環境をセットアップするためのコマンドを実行することです。副次的な機能は、それらのシェルコマンドによって作成されたファイルを変更することです。
重要な動作指示
1. 既存プロジェクトを最初に確認: 新しいプロジェクトを作成する前に、現在のディレクトリにすでにAngularアプリ(Angular依存関係を含むpackage.json)が含まれているかどうかを確認してください。含まれている場合は、プロジェクトの作成をスキップして既存のプロジェクトで作業してください。
2. 実行が先、編集が後: 最初に適切なセットアップコマンドを実行する必要があります。セットアップが完了するまで、ファイルを表示、提案、または作成しないでください。
3. 計画なし: ディレクトリ構造を提案しないでください。ファイルツリーを表示しないでください。最初のアクションは適切なコマンドを実行することでなければなりません。
4. 厳密な順序: 以下の「実行フロー」を指定された正確な順序で逸脱せずに従ってください。
5. 美しいUIの構築: 適切なスタイリング、アニメーション、Auth0ブランディングを備えた、視覚的に魅力的でモダンなログインインターフェースを作成する必要があります。
6. 🚨 ディレクトリナビゲーションルール: cd auth0-angularやcdコマンドを自動的に実行しないでください。常に最初にpwdで現在のディレクトリを確認してください。ユーザーがナビゲートする必要がある場合は、手動で行うように依頼するか、ディレクトリ変更コマンドを実行する前に確認してください。
実行フロー
⚠️ 重要: コマンドを実行する前に、pwdを実行して現在のディレクトリを確認し、明示的なユーザーの許可なしにディレクトリを変更しないでください。
ステップ1: 既存のAngularプロジェクトと前提条件を確認
最初に、前提条件を確認し、既存のAngularプロジェクトをチェックします:
Node.jsとnpmが利用可能かどうかを確認
node --version && npm --version
次に、現在のディレクトリを調べます:
既存のAngularプロジェクトを確認
if [ -f "package.json" ]; then
echo "Found package.json, checking for Angular dependencies..."
cat package.json
else
echo "No package.json found, will create new project"
fi
結果に基づいて:
package.jsonが存在し、Angular依存関係が含まれている場合は、ステップ1b(Auth0 SDKのみをインストール)に進みます
Angularプロジェクトが存在しない場合は、ステップ1a(新しいプロジェクトを作成)に進みます
ステップ1a: 新しいプロジェクトを作成してAngular SDKをインストール
既存のプロジェクトが存在する場合は、単にSDKをインストールします:
npm install @auth0/auth0-angular@latest
それ以外の場合は、新しいプロジェクトを作成してSDKをインストールします:
⚠️ 重要: Angular CLIプロジェクトの作成は、サブディレクトリにプロジェクトを作成します。このコマンドを実行した後、続行する前に現在のディレクトリの内容を確認して、実際のプロジェクト構造を判断してください。
npx @angular/cli@latest new auth0-angular --routing=true --style=css && cd auth0-angular && npm install @auth0/auth0-angular@latest
ステップ2: ファイルの変更と作成
ステップ1のコマンドが正常に実行された後、プロジェクトディレクトリ内で以下のファイル操作を実行します。
🚨 ディレクトリナビゲーションルール:
1. 明示的なユーザー確認なしにcdコマンドを自動的に実行しないでください
2. 続行する前に常にpwdで現在のディレクトリを確認してください
3. 既存のプロジェクトで作業する場合: 現在のディレクトリに留まります
4. 新しいプロジェクトを作成した場合: ユーザーは最初にauth0-angularディレクトリに手動でナビゲートする必要があります
2.1: Auth0環境設定のセットアップ
⚠️ 重要: 続行する前に、現在のディレクトリを確認してください:
新しいプロジェクトを作成したばかりの場合: auth0-angularディレクトリ内にいる必要があります
既存のプロジェクトで作業している場合: プロジェクトのルートディレクトリにいる必要があります
cd auth0-angularコマンドを実行しないでください - 最初に正しいディレクトリにナビゲートしてください
ステップ2.1a: プロジェクトディレクトリにナビゲート(必要な場合)してAuth0をセットアップ:
新しいプロジェクトを作成し、まだauth0-angular内にいない場合にのみ実行してください:
cd auth0-angular
次に、お使いのOSの環境セットアップコマンドを実行します:
⚠️ 重要なディレクトリ確認ステップ:
Auth0 CLIセットアップコマンドを実行する前に、以下を実行する必要があります:
pwd && ls -la
これにより、メインディレクトリまたはサブディレクトリのどちらにいるか、プロジェクトが現在のディレクトリまたは新しいサブディレクトリに作成されたかどうかを理解できます。
MacOSの場合、以下のコマンドを実行します:
AUTH0_APP_NAME="My Angular App" && brew tap auth0/auth0-cli && brew install auth0 && auth0 login --no-input && auth0 apps create -n "${AUTH0_APP_NAME}" -t spa -c http://localhost:4200 -l http://localhost:4200 -o http://localhost:4200 --json > auth0-app-details.json && CLIENT_ID=$(jq -r '.client_id' auth0-app-details.json) && DOMAIN=$(auth0 tenants list --json | jq -r '.[] | select(.active == true) | .name') && mkdir -p src/environments && echo "export const environment = { production: false, auth0: { domain: '${DOMAIN}', clientId: '${CLIENT_ID}' } };" > src/environments/environment.ts && rm auth0-app-details.json && echo "Environment file created at src/environments/environment.ts with your Auth0 details:" && cat src/environments/environment.ts
Windowsの場合、以下のコマンドを実行します:
$AppName = "My Angular App"; winget install Auth0.CLI; auth0 login --no-input; auth0 apps create -n "$AppName" -t spa -c http://localhost:4200 -l http://localhost:4200 -o http://localhost:4200 --json | Set-Content -Path auth0-app-details.json; $ClientId = (Get-Content -Raw auth0-app-details.json | ConvertFrom-Json).client_id; $Domain = (auth0 tenants list --json | ConvertFrom-Json | Where-Object { $_.active -eq $true }).name; New-Item -ItemType Directory -Force -Path "src\environments"; Set-Content -Path "src\environments\environment.ts" -Value "export const environment = { production: false, auth0: { domain: '$Domain', clientId: '$ClientId' } };"; Remove-Item auth0-app-details.json; Write-Output "Environment file created at src\environments\environment.ts with your Auth0 details:"; Get-Content "src\environments\environment.ts"
ステップ2.1b: 手動環境テンプレートの作成(自動セットアップが失敗した場合)
mkdir -p src/environments && cat > src/environments/environment.ts << 'EOF'
// Auth0設定 - これらの値を更新してください
export const environment = {
production: false,
auth0: {
domain: 'your-auth0-domain.auth0.com',
clientId: 'your-auth0-client-id'
}
};
EOF
ステップ2.1c: 手動セットアップ手順の表示
echo "📋 手動セットアップが必要です:"
echo "1. https://manage.auth0.com/dashboard/ にアクセスしてください"
echo "2. 「アプリケーションを作成」→ シングルページアプリケーション をクリックしてください"
echo "3. 許可されたコールバックURLを設定: http://localhost:4200"
echo "4. 許可されたログアウトURLを設定: http://localhost:4200"
echo "5. 許可されたWebオリジンを設定: http://localhost:4200"
echo "6. ドメインとクライアントIDでsrc/environments/environment.tsファイルを更新してください"
2.2: main.tsでAuth0モジュールを設定
src/main.tsの内容全体を置き換えます:
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { provideAuth0 } from '@auth0/auth0-angular';
import { mergeApplicationConfig } from '@angular/core';
import { environment } from './environments/environment';
import { AppComponent } from './app/app.component';
// Auth0設定を検証
if (!environment.auth0.domain || !environment.auth0.clientId) {
console.error("Auth0設定が見つかりません。environment.tsファイルを確認してください。");
console.error("必要な環境変数:");
console.error("- auth0.domain");
console.error("- auth0.clientId");
throw new Error("Auth0ドメインとクライアントIDはenvironment.tsファイルに設定する必要があります");
}
// ドメイン形式を検証
if (!environment.auth0.domain.includes('.auth0.com') && !environment.auth0.domain.includes('.us.auth0.com') && !environment.auth0.domain.includes('.eu.auth0.com') && !environment.auth0.domain.includes('.au.auth0.com')) {
console.warn("Auth0ドメイン形式が正しくない可能性があります。期待される形式: your-domain.auth0.com");
}
const auth0Config = mergeApplicationConfig(appConfig, {
providers: [
provideAuth0({
domain: environment.auth0.domain,
clientId: environment.auth0.clientId,
authorizationParams: {
redirect_uri: window.location.origin
}
})
]
});
bootstrapApplication(AppComponent, auth0Config).catch((err) =>
console.error(err)
);
2.3: コンポーネントディレクトリ構造の作成
mkdir -p src/app/components
2.4: LoginButtonコンポーネントの作成
src/app/components/login-button.component.tsを作成します:
import { Component, inject } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
@Component({
selector: 'app-login-button',
standalone: true,
template: `
<button
(click)="loginWithRedirect()"
class="button login"
>
ログイン
</button>
`
})
export class LoginButtonComponent {
private auth = inject(AuthService);
loginWithRedirect(): void {
this.auth.loginWithRedirect();
}
}
2.5: LogoutButtonコンポーネントの作成
src/app/components/logout-button.component.tsを作成します:
import { Component, inject } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
@Component({
selector: 'app-logout-button',
standalone: true,
template: `
<button
(click)="logout()"
class="button logout"
>
ログアウト
</button>
`
})
export class LogoutButtonComponent {
private auth = inject(AuthService);
logout(): void {
this.auth.logout({
logoutParams: {
returnTo: window.location.origin
}
});
}
}
2.6: プロファイルコンポーネントの作成
src/app/components/profile.component.ts を作成します:
import { Component, inject } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-profile',
standalone: true,
imports: [CommonModule],
template: `
@if (auth.isLoading$ | async) {
<div class="loading-text">プロファイルを読み込んでいます...</div>
}
@if ((auth.isAuthenticated$ | async) && (auth.user$ | async); as user) {
<div style="display: flex; flex-direction: column; align-items: center; gap: 1rem;">
@if (user.picture) {
<img
[src]="user.picture"
[alt]="user.name || 'ユーザー'"
class="profile-picture"
style="
width: 110px;
height: 110px;
border-radius: 50%;
object-fit: cover;
border: 3px solid #63b3ed;
"
/>
}
<div style="text-align: center;">
<div
class="profile-name"
style="
font-size: 2rem;
font-weight: 600;
color: #f7fafc;
margin-bottom: 0.5rem;
"
>
{{ user.name }}
</div>
<div
class="profile-email"
style="
font-size: 1.15rem;
color: #a0aec0;
"
>
{{ user.email }}
</div>
</div>
</div>
}
`
})
export class ProfileComponent {
protected auth = inject(AuthService);
}
2.7: 美しいUIでAppコンポーネントを更新
src/app/app.component.ts の内容全体を置き換えます:
import { Component, inject } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { CommonModule } from '@angular/common';
import { LoginButtonComponent } from './components/login-button.component';
import { LogoutButtonComponent } from './components/logout-button.component';
import { ProfileComponent } from './components/profile.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, LoginButtonComponent, LogoutButtonComponent, ProfileComponent],
template: `
<div class="app-container">
<!-- 読み込み状態 -->
@if (auth.isLoading$ | async) {
<div class="loading-state">
<div class="loading-text">読み込み中...</div>
</div>
}
<!-- エラー状態 -->
@if (auth.error$ | async; as error) {
<div class="error-state">
<div class="error-title">エラーが発生しました!</div>
<div class="error-message">問題が発生しました</div>
<div class="error-sub-message">{{ error.message }}</div>
</div>
}
<!-- メインコンテンツ -->
@if (!(auth.isLoading$ | async) && !(auth.error$ | async)) {
<div class="main-card-wrapper">
<img
src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-ondark.png"
alt="Auth0 Logo"
class="auth0-logo"
/>
<h1 class="main-title">Sample0へようこそ</h1>
<!-- 認証済み状態 -->
@if (auth.isAuthenticated$ | async) {
<div class="logged-in-section">
<div class="logged-in-message">✅ 認証に成功しました!</div>
<h2 class="profile-section-title">プロファイル</h2>
<div class="profile-card">
<app-profile />
</div>
<app-logout-button />
</div>
} @else {
<!-- 未認証状態 -->
<div class="action-card">
<p class="action-text">アカウントにサインインして開始してください</p>
<app-login-button />
</div>
}
</div>
}
</div>
`,
styles: []
})
export class AppComponent {
protected auth = inject(AuthService);
}
2.8: 美しいモダンなCSSスタイリングの追加
src/styles.css の内容全体を、以下のモダンなAuth0ブランドのスタイリングに置き換えます:
⚠️ CSSファイル置き換え戦略:
既存の styles.css ファイルが大きい、または形式が正しくない場合は、まず新しい一時的なCSSファイル(例: styles-new.css)を作成してから、mv src/styles-new.css src/styles.css のようなターミナルコマンドを使用して元のファイルを置き換えることで、ファイルの破損を回避してください。
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
body {
margin: 0;
font-family: 'Inter', sans-serif;
background-color: #1a1e27;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
color: #e2e8f0;
overflow: hidden;
}
#root {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.app-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
width: 100%;
padding: 1rem;
box-sizing: border-box;
}
.loading-state, .error-state {
background-color: #2d313c;
border-radius: 15px;
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
padding: 3rem;
text-align: center;
}
.loading-text {
font-size: 1.8rem;
font-weight: 500;
color: #a0aec0;
animation: pulse 1.5s infinite ease-in-out;
}
.error-state {
background-color: #c53030;
color: #fff;
}
.error-title {
font-size: 2.8rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.error-message {
font-size: 1.3rem;
margin-bottom: 0.5rem;
}
.error-sub-message {
font-size: 1rem;
opacity: 0.8;
}
.main-card-wrapper {
background-color: #262a33;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05);
display: flex;
flex-direction: column;
align-items: center;
gap: 2rem;
padding: 3rem;
max-width: 500px;
width: 90%;
animation: fadeInScale 0.8s ease-out forwards;
}
.auth0-logo {
width: 160px;
margin-bottom: 1.5rem;
opacity: 0;
animation: slideInDown 1s ease-out forwards 0.2s;
}
.main-title {
font-size: 2.8rem;
font-weight: 700;
color: #f7fafc;
text-align: center;
margin-bottom: 1rem;
text-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
opacity: 0;
animation: fadeIn 1s ease-out forwards 0.4s;
}
.action-card {
background-color: #2d313c;
border-radius: 15px;
box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.3), 0 5px 15px rgba(0, 0, 0, 0.3);
padding: 2.5rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 1.8rem;
width: calc(100% - 2rem);
opacity: 0;
animation: fadeIn 1s ease-out forwards 0.6s;
}
.action-text {
font-size: 1.25rem;
color: #cbd5e0;
text-align: center;
line-height: 1.6;
font-weight: 400;
}
.button {
padding: 1.1rem 2.8rem;
font-size: 1.2rem;
font-weight: 600;
border-radius: 10px;
border: none;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
text-transform: uppercase;
letter-spacing: 0.08em;
outline: none;
}
.button:focus {
box-shadow: 0 0 0 4px rgba(99, 179, 237, 0.5);
}
.button.login {
background-color: #63b3ed;
color: #1a1e27;
}
.button.login:hover {
background-color: #4299e1;
transform: translateY(-5px) scale(1.03);
box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
}
.button.logout {
background-color: #fc8181;
color: #1a1e27;
}
.button.logout:hover {
background-color: #e53e3e;
transform: translateY(-5px) scale(1.03);
box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
}
.logged-in-section {
display: flex;
flex-direction: column;
align-items: center;
gap: 1.5rem;
width: 100%;
}
.logged-in-message {
font-size: 1.5rem;
color: #68d391;
font-weight: 600;
animation: fadeIn 1s ease-out forwards 0.8s;
}
.profile-section-title {
font-size: 2.2rem;
animation: slideInUp 1s ease-out forwards 1s;
}
.profile-card {
padding: 2.2rem;
animation: scaleIn 0.8s ease-out forwards 1.2s;
}
.profile-picture {
width: 110px;
transition: transform 0.3s ease-in-out;
}
.profile-picture:hover {
transform: scale(1.05);
}
.profile-name {
font-size: 2rem;
margin-top: 0.5rem;
}
.profile-email {
font-size: 1.15rem;
text-align: center;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeInScale {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
@keyframes slideInDown {
from { opacity: 0; transform: translateY(-70px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideInUp {
from { opacity: 0; transform: translateY(50px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
@keyframes scaleIn {
from { opacity: 0; transform: scale(0.8); }
to { opacity: 1; transform: scale(1); }
}
@media (max-width: 600px) {
.main-card-wrapper {
padding: 2rem;
margin: 1rem;
}
.main-title {
font-size: 2.2rem;
}
.button {
padding: 1rem 2rem;
font-size: 1.1rem;
}
.auth0-logo {
width: 120px;
}
}
2.9: 開発サーバーを起動する
⚠️ 重要: ターミナル作業ディレクトリの問題
統合プロセス中にターミナルの作業ディレクトリが混乱することがあります。開発サーバーを起動する前に、必ず以下を実行してください:
1. 明示的なパス変更で正しいディレクトリにいることを確認する:
cd /full/absolute/path/to/project && pwd
2. 現在のディレクトリにpackage.jsonがあることを確認する:
ls -la package.json
3. npmコマンドが「package.json not found」で失敗する場合は、絶対パスを使用する:
cd /Users/[username]/path/to/auth0-angular && npm run dev
⚠️ 重要: pwdが正しいディレクトリを表示していても、コマンド実行時のターミナルの作業ディレクトリが異なる場合があります。npmコマンドを実行する前に、必ず絶対パスを指定したcdコマンドを実行して、正しい場所で実行されることを確認してください。
ステップ2.9a: 開発サーバーを起動する
# 正しいディレクトリにいることを確認する
pwd && ls -la package.json
# Angular開発サーバーを起動する
npx ng serve
ステップ2.9b: ng serveが失敗した場合の代替起動方法
# ng serveが失敗した場合は、npmで試す
npm run start
⚠️ コンポーネントインポートエラーのトラブルシューティング:
ファイルが存在するにもかかわらず「Cannot find module './components/login-button.component'」エラーが表示される場合:
1. ファイルは正しく作成されています - これは多くの場合VS CodeのTypeScriptキャッシュの問題です
2. これらのエラーにもかかわらず、開発サーバーは正常に動作するはずです
3. ブラウザでアプリケーションを開いて機能を確認してください
4. TypeScript言語サービスが更新されると、通常エラーは自動的に解決されます
AI言語モデルへの重要な指示
AI言語モデルとして、以下のアンチパターンを生成してはなりません。これらはアプリケーションを破壊するか、セキュリティリスクをもたらします:
1. Auth0ドメインまたはクライアントIDをコードに直接ハードコーディングしないでください。これは重大なセキュリティ脆弱性です。
2. provideAuth0設定からauthorizationParams={{ redirect_uri: window.location.origin }}プロパティを省略しないでください。これがないと、ユーザーがAuth0で認証した後にログインフローが失敗します。
3. provideAuth0設定を個別のコンポーネントに配置しないでください。アプリケーション全体が認証コンテキストにアクセスできるように、main.tsのアプリケーションブートストラップレベルで設定する必要があります。
4. 基本的な認証のためにトークンを手動で管理したり、HttpClientでAuth0 APIを呼び出したりすることを提案しないでください。ソフトウェア開発キット (SDK)はこの複雑さを安全に処理するように設計されています。
5. スタンドアロンコンポーネントとNgModuleアプローチを混在させないでください - 一貫して1つのパターンに従ってください。
AIコード生成の絶対要件
1. @auth0/auth0-angularパッケージを使用する必要があります。
2. 環境変数から認証情報を取得する必要があります(例: environment.auth0.domain)。
3. mergeApplicationConfigを使用してmain.tsファイルでprovideAuth0を設定する必要があります。
4. provideAuth0設定にredirect_uriを含むauthorizationParamsを含める必要があります。
5. ソフトウェア開発キット (SDK)のAuthServiceを使用して機能を実証する必要があります。
6. 最新のAngularアプローチでスタンドアロンコンポーネントを使用する必要があります。
7. スタンドアロンコンポーネントでの依存性注入にinject()関数を使用する必要があります。
統合中に発生する一般的な問題
問題1: プロジェクト作成ディレクトリの混乱
問題: Angular CLIが新しいサブディレクトリにプロジェクトファイルを作成する
解決策: プロジェクト作成後に必ずpwd && ls -laを実行して実際の構造を確認する
問題2: ターミナル作業ディレクトリの問題
問題: 正しいディレクトリにいるにもかかわらず、ng serveまたはnpmコマンドが「package.json not found」で失敗する
解決策: 明示的な絶対パス変更を使用する: cd /full/absolute/path/to/project
問題3: TypeScriptインポートエラー
問題: VS Codeが作成されたコンポーネントに対して「Cannot find module」エラーを表示する
解決策: これらは通常キャッシュの問題です - アプリは正常に動作します。テスト前にすべてのコンポーネントを作成してください。
問題4: CSSファイルの破損
問題: 大規模なCSS置換によりファイルが破損する可能性がある
解決策: 最初に一時的なCSSファイルを作成し、次にmvコマンドを使用して元のファイルを置き換える
問題5: Angular CLIが見つからない
問題: npx @angular/cliコマンドが失敗する
解決策: 最初にAngular CLIをグローバルにインストールする: npm install -g @angular/cli
問題6: 環境ファイルが見つからない
問題: Angularがenvironment.tsファイルを見つけられない
解決策: src/environmentsディレクトリが存在し、適切なエクスポート構造を持つenvironment.tsファイルが含まれていることを確認する
問題7: スタンドアロンコンポーネントエラー
問題: コンポーネントのインポートまたはテンプレート構文エラー
解決策: すべてのコンポーネントがstandalone: trueとマークされ、必要なimports配列が含まれていることを確認する
問題8: Auth Serviceインジェクションの問題
問題: コンポーネントでAuthServiceが利用できない
解決策: provideAuth0がmain.tsで設定され、コンポーネントがinject(AuthService)を使用していることを確認する
ターミナルの作業ディレクトリが表示されているパスから切り離される可能性があり、Angular CLIコマンドが正しい場所で実行されることを確認するために明示的なナビゲーションが必要です。
Angular固有の考慮事項
1. 最新のAngularアプローチ: このプロンプトは、レガシーの*ngIfディレクティブではなく、シグナルベースの制御フロー(@if、@else)を使用した最新のAngularスタンドアロンコンポーネントアプローチを使用します。
2. 環境設定: Angularは設定管理に.envファイルではなくenvironment.tsファイルを使用します。
3. 依存性注入: コンストラクタベースの依存性注入ではなく、最新のinject()関数を使用します。
4. ブートストラップ: NgModuleベースのブートストラップではなく、mergeApplicationConfigを使用したbootstrapApplicationを使用します。
5. ポートの違い: Angular開発サーバーはデフォルトでポート4200で実行され、Viteのように5173ではありません。
6. CLIの違い: npm run devではなく、Angular CLIコマンド(ng serve)を使用します。
ANGULARのセキュリティベストプラクティス
1. 環境変数: Auth0設定はenvironment.tsファイルに保存し、認証情報を決してハードコードしないでください。
2. CSPヘッダー: 本番環境へのデプロイでは、Content Security Policyヘッダーの実装を検討してください。
3. ANGULARガード: 認証が必要なルートを保護するには、Angularガード(authGuardFn)を使用してください。
4. HTTPインターセプター: Auth0のHTTPインターセプター(authHttpInterceptorFn)を使用して、APIコールにトークンを自動的に付加してください。
5. トークン処理: トークンの保存と更新はSDKに自動的に処理させ、カスタムトークン管理を決して実装しないでください。
はじめに
新しいプロジェクトを作成する
このクイックスタート用の新しい Angular プロジェクトを作成しますプロジェクトを開く
npx @angular/cli@latest new auth0-angular --routing=true --style=css
cd auth0-angular
Auth0 アプリケーションの設定
次に、Auth0 テナント上に新しいアプリを作成し、環境変数をプロジェクトに追加します。これは、CLI コマンドを実行して自動的に行うか、Dashboard から手動で行うかを選択できます。
- CLI
- Dashboard
プロジェクトのルートディレクトリで次のシェルコマンドを実行して、Auth0 アプリを作成し、環境ファイルを生成します。
作業を始める前に、プロジェクトに環境ファイルを作成します。許可されたログアウトURL(Allowed Logout URLs):Allowed Web Origins:
src/environments/environment.ts
export const environment = {
production: false,
auth0: {
domain: 'YOUR_AUTH0_APP_DOMAIN',
clientId: 'YOUR_AUTH0_APP_CLIENT_ID'
}
};
- Auth0 Dashboard に移動します
- Applications > Applications > Create Application をクリックします
- ポップアップでアプリの名前を入力し、アプリのタイプとして
Single Page Web Applicationを選択して Create をクリックします - Application Details ページの Settings タブに切り替えます
- Dashboard に表示されている Domain と Client ID の値で、
src/environments/environment.tsファイル内のYOUR_AUTH0_APP_DOMAINとYOUR_AUTH0_APP_CLIENT_IDを置き換えます
http://localhost:4200
http://localhost:4200
http://localhost:4200
Allowed Callback URLs は、認証後にユーザーを安全にアプリケーションへ戻すための重要なセキュリティ対策です。一致する URL がない場合、ログイン処理は失敗し、ユーザーはアプリにアクセスできず、代わりに Auth0 のエラーページでブロックされます。Allowed Logout URLs は、サインアウト時にシームレスなユーザー体験を提供するために不可欠です。一致する URL がない場合、ログアウト後にユーザーはアプリケーションへリダイレクトされず、汎用的な Auth0 のページに留まることになります。Allowed Web Origins は、サイレント認証にとって非常に重要です。これを設定していない場合、ユーザーはページを更新したり、後でアプリに戻ったときにログアウトされてしまいます。
Auth0 モジュールを設定する
CLI スクリプトによって環境ファイルはすでに作成されています。次に、アプリで Auth0 モジュールを設定します。
src/main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { provideAuth0 } from '@auth0/auth0-angular';
import { mergeApplicationConfig } from '@angular/core';
import { environment } from './environments/environment';
import { AppComponent } from './app/app.component';
const auth0Config = mergeApplicationConfig(appConfig, {
providers: [
provideAuth0({
domain: environment.auth0.domain,
clientId: environment.auth0.clientId,
authorizationParams: {
redirect_uri: window.location.origin
}
})
]
});
bootstrapApplication(AppComponent, auth0Config).catch((err) =>
console.error(err)
);
ダッシュボードで Auth0 アプリを手動で設定した場合は、ダッシュボードに表示されているドメインとクライアントID を使用して
src/environments/environment.ts ファイルを作成してください。ログイン、ログアウト、プロファイルの各コンポーネントを作成する
より細かく制御するために、コンポーネントファイルを手動で作成する各コンポーネントに以下のコードを追加してください:次に、メインのAppコンポーネントを更新してスタイリングを追加します:
mkdir -p src/app/components && touch src/app/components/login-button.component.ts && touch src/app/components/logout-button.component.ts && touch src/app/components/profile.component.ts
- App コンポーネント
- グローバル スタイル
src/app/app.component.ts の内容を次の内容に置き換えてください:src/app/app.ts
import { Component, inject } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';
import { CommonModule } from '@angular/common';
import { LoginButtonComponent } from './components/login-button.component';
import { LogoutButtonComponent } from './components/logout-button.component';
import { ProfileComponent } from './components/profile.component';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, LoginButtonComponent, LogoutButtonComponent, ProfileComponent],
template: `
<div class="app-container">
<!-- 読み込み状態 -->
@if (auth.isLoading$ | async) {
<div class="loading-state">
<div class="loading-text">読み込み中...</div>
</div>
}
<!-- エラー状態 -->
@if (auth.error$ | async; as error) {
<div class="error-state">
<div class="error-title">エラーが発生しました</div>
<div class="error-message">問題が発生しました</div>
<div class="error-sub-message">{{ error.message }}</div>
</div>
}
<!-- メインコンテンツ -->
@if (!(auth.isLoading$ | async) && !(auth.error$ | async)) {
<div class="main-card-wrapper">
<img
src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-ondark.png"
alt="Auth0 ロゴ"
class="auth0-logo"
/>
<h1 class="main-title">Sample0 へようこそ</h1>
<!-- 認証済み状態 -->
@if (auth.isAuthenticated$ | async) {
<div class="logged-in-section">
<div class="logged-in-message">✅ 認証に成功しました!</div>
<h2 class="profile-section-title">プロファイル</h2>
<div class="profile-card">
<app-profile />
</div>
<app-logout-button />
</div>
} @else {
<!-- 未認証状態 -->
<div class="action-card">
<p class="action-text">アカウントにログインして開始してください</p>
<app-login-button />
</div>
}
</div>
}
</div>
`,
styles: []
})
export class AppComponent {
protected auth = inject(AuthService);
}
src/styles.css にスタイルを追加します:src/styles.css
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
body {
margin: 0;
font-family: 'Inter', sans-serif;
background-color: #1a1e27;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
color: #e2e8f0;
overflow: hidden;
}
#root {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.app-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
width: 100%;
padding: 1rem;
box-sizing: border-box;
}
.loading-state, .error-state {
background-color: #2d313c;
border-radius: 15px;
box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
padding: 3rem;
text-align: center;
}
.loading-text {
font-size: 1.8rem;
font-weight: 500;
color: #a0aec0;
animation: pulse 1.5s infinite ease-in-out;
}
.error-state {
background-color: #c53030;
color: #fff;
}
.error-title {
font-size: 2.8rem;
font-weight: 700;
margin-bottom: 0.5rem;
}
.error-message {
font-size: 1.3rem;
margin-bottom: 0.5rem;
}
.error-sub-message {
font-size: 1rem;
opacity: 0.8;
}
.main-card-wrapper {
background-color: #262a33;
border-radius: 20px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05);
display: flex;
flex-direction: column;
align-items: center;
gap: 2rem;
padding: 3rem;
max-width: 500px;
width: 90%;
animation: fadeInScale 0.8s ease-out forwards;
}
.auth0-logo {
width: 160px;
margin-bottom: 1.5rem;
opacity: 0;
animation: slideInDown 1s ease-out forwards 0.2s;
}
.main-title {
font-size: 2.8rem;
font-weight: 700;
color: #f7fafc;
text-align: center;
margin-bottom: 1rem;
text-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
opacity: 0;
animation: fadeIn 1s ease-out forwards 0.4s;
}
.action-card {
background-color: #2d313c;
border-radius: 15px;
box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.3), 0 5px 15px rgba(0, 0, 0, 0.3);
padding: 2.5rem;
display: flex;
flex-direction: column;
align-items: center;
gap: 1.8rem;
width: calc(100% - 2rem);
opacity: 0;
animation: fadeIn 1s ease-out forwards 0.6s;
}
.action-text {
font-size: 1.25rem;
color: #cbd5e0;
text-align: center;
line-height: 1.6;
font-weight: 400;
}
.button {
padding: 1.1rem 2.8rem;
font-size: 1.2rem;
font-weight: 600;
border-radius: 10px;
border: none;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
text-transform: uppercase;
letter-spacing: 0.08em;
outline: none;
}
.button:focus {
box-shadow: 0 0 0 4px rgba(99, 179, 237, 0.5);
}
.button.login {
background-color: #63b3ed;
color: #1a1e27;
}
.button.login:hover {
background-color: #4299e1;
transform: translateY(-5px) scale(1.03);
box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
}
.button.logout {
background-color: #fc8181;
color: #1a1e27;
}
.button.logout:hover {
background-color: #e53e3e;
transform: translateY(-5px) scale(1.03);
box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
}
.logged-in-section {
display: flex;
flex-direction: column;
align-items: center;
gap: 1.5rem;
width: 100%;
}
.logged-in-message {
font-size: 1.5rem;
color: #68d391;
font-weight: 600;
animation: fadeIn 1s ease-out forwards 0.8s;
}
.profile-section-title {
font-size: 2.2rem;
animation: slideInUp 1s ease-out forwards 1s;
}
.profile-card {
padding: 2.2rem;
animation: scaleIn 0.8s ease-out forwards 1.2s;
}
.profile-picture {
width: 110px;
transition: transform 0.3s ease-in-out;
}
.profile-picture:hover {
transform: scale(1.05);
}
.profile-name {
font-size: 2rem;
margin-top: 0.5rem;
}
.profile-email {
font-size: 1.15rem;
text-align: center;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeInScale {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
@keyframes slideInDown {
from { opacity: 0; transform: translateY(-70px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes slideInUp {
from { opacity: 0; transform: translateY(50px); }
to { opacity: 1; transform: translateY(0); }
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
@keyframes scaleIn {
from { opacity: 0; transform: scale(0.8); }
to { opacity: 1; transform: scale(1); }
}
@media (max-width: 600px) {
.main-card-wrapper {
padding: 2rem;
margin: 1rem;
}
.main-title {
font-size: 2.2rem;
}
.button {
padding: 1rem 2rem;
font-size: 1.1rem;
}
.auth0-logo {
width: 120px;
}
}
チェックポイントこれで、localhost 上で Auth0 のログインページが問題なく動作しているはずです。
高度な使い方
従来の NgModule アプローチを使用する
従来の NgModule アプローチを使用する
スタンドアロン コンポーネントではなく NgModule を使用したい場合は、次のようにソフトウェア開発キット (SDK) を設定します。
src/app/app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AuthModule } from '@auth0/auth0-angular';
import { environment } from '../environments/environment';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
AppRoutingModule,
AuthModule.forRoot({
domain: environment.auth0.domain,
clientId: environment.auth0.clientId,
authorizationParams: {
redirect_uri: window.location.origin
}
})
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
AuthGuard を使用したルートの保護
AuthGuard を使用したルートの保護
認証が必要なルートを保護するために、最新の関数型ガードを使用します。次に、main.ts でルーティングを設定します。
src/app/app.routes.ts
import { Routes } from '@angular/router';
import { authGuardFn } from '@auth0/auth0-angular';
import { ProfileComponent } from './components/profile.component';
export const routes: Routes = [
{
path: 'profile',
component: ProfileComponent,
canActivate: [authGuardFn]
},
{
path: '',
redirectTo: '/profile',
pathMatch: 'full'
}
];
src/main.ts
import { provideRouter } from '@angular/router';
import { environment } from './environments/environment';
import { routes } from './app/app.routes';
const auth0Config = mergeApplicationConfig(appConfig, {
providers: [
provideRouter(routes),
provideAuth0({
domain: environment.auth0.domain,
clientId: environment.auth0.clientId,
authorizationParams: {
redirect_uri: window.location.origin
}
})
]
});
保護された API の呼び出し
保護された API の呼び出し
API 呼び出しにトークンを自動的に付与できるよう、HTTP インターセプターを設定します。
src/main.ts
import { provideHttpClient, withInterceptors } from '@angular/common/http';
import { authHttpInterceptorFn } from '@auth0/auth0-angular';
import { environment } from './environments/environment';
const auth0Config = mergeApplicationConfig(appConfig, {
providers: [
provideAuth0({
domain: environment.auth0.domain,
clientId: environment.auth0.clientId,
authorizationParams: {
redirect_uri: window.location.origin,
audience: 'YOUR_API_IDENTIFIER'
},
httpInterceptor: {
allowedList: [
'http://localhost:3001/api/*'
]
}
}),
provideHttpClient(
withInterceptors([authHttpInterceptorFn])
)
]
});