🔐

Angular SSRにAuth0を導入する

に公開

はじめに

本記事では、Angular SSRにAuth0のユニバーサルログインを追加し、特定のページへのアクセスを認証済みユーザーに限定する方法を紹介します。

まずはクライアントサイドで動作するRenderMode.Clientで認証機能を実装し、その後RenderMode.Prerenderへの対応方法までステップバイステップで説明します。

対象読者

この記事は、以下のような方を対象としています。

  • AngularアプリケーションにAuth0を使った認証機能を導入したい方
  • サーバーサイドレンダリング(SSR)環境下で、ログインが必要なページをプリレンダリングしたい方

Angular + Auth0の組み合わせはシンプルですが、SSRとの統合には注意点も多く、段階的な導入が重要です。

前提条件

以下のような環境を前提としています。

  • Angular Universal(SSR)が導入されたプロジェクト
  • ログインが必要なページ(この記事ではAuthorizedPageComponentとしています)

Auth0のセットアップ

Auth0のダッシュボードから新しいアプリケーションを作成し、以下のように設定します。

作成後、ドメインクライアントIDを控えておきます。

Angularアプリの実装

ライブラリのインストール

Auth0のAngular SDKをインストールします。

npm install @auth0/auth0-angular

Auth0の設定を追加

SDKが提供しているprovideAuth0メソッドを使って、Auth0を登録します。
SSR環境ではwindow is not definedのエラーが発生するため、ブラウザ環境でのみwindow.location.originを参照するようにしています。

app.config.ts
import { provideAuth0 } from '@auth0/auth0-angular';

export const appConfig: ApplicationConfig = {
  // 省略
  provideAuth0({
    domain: 'YOUR_DOMAIN',
    clientId: 'YOUR_CLIENT_ID',
    authorizationParams: {
      redirect_uri:
        globalThis.window !== undefined
          ? `${window.location.origin}/setting`
          : undefined,
    },
  }),
}

Guardの追加

SDKが提供しているAuthGuardを設定します。

app.routes.ts
import { AuthGuard } from '@auth0/auth0-angular';

export const routes: Routes = [
  // 省略
  {
    path: 'setting',
    canActivate: [AuthGuard],
    component: AuthorizedPageComponent
  }
]

動作確認

/settingに遷移した際に、Auth0のログイン画面が表示されて、ログイン後にAuthorizedPageComponentが表示されたら成功。

プリレンダリングへの対応

AuthorizedPageComponentをプリレンダリングしたい場合は、SSR環境でAuthGuardが動かないようにカスタマイズする必要があります。
SSR環境を判定する方法はいくつかありますが、ここではisPlatformServer()を使っています。

custom-guard.ts
import { isPlatformServer } from '@angular/common';
import { inject, PLATFORM_ID } from '@angular/core';
import { CanActivateFn } from '@angular/router';
import { AuthService } from '@auth0/auth0-angular';
import { tap } from 'rxjs';

export const cutomGuard: CanActivateFn = (route, state) => {
  const auth = inject(AuthService);
  const platformId = inject(PLATFORM_ID);

  // SSRでは認証しないのでスキップする
  if (isPlatformServer(platformId)) {
    return true;
  }

  return auth.isAuthenticated$.pipe(
    tap((loggedIn) => {
      if (!loggedIn) {
        auth.loginWithRedirect({
          appState: { target: state.url },
        });
      }
    })
  );
};
app.routes.ts
export const routes: Routes = [
  // 省略
  {
    path: 'setting',
    canActivate: [cutomGuard],
    component: AuthorizedPageComponent
  }
]

補足

ログインが必要なページで、SDKのAuthServiceから状態を取得している場合は、SSR環境でエラーが発生するためserverConfigでモックする必要があります。

app.config.server.ts
import { AuthService } from '@auth0/auth0-angular';

const serverConfig: ApplicationConfig = {
  // 省略
  {
    provide: AuthService,
    useValue: {
      user$: of(),
      isAuthenticated$: of(),
    },
  },
};

まとめ

  • Auth0を使うと爆速で認証機能を実装できる
  • ログインが必要なページをプリレンダリングしたい場合はエラーハンドリングが必要

Discussion