メインコンテンツまでスキップ

Protected App — SDK を使わない認証 (Authentication) 統合

Protected App は、SDK 統合 の複雑さを排除し、認証 (Authentication) レイヤーをアプリケーションから分離するために設計されています。認証 (Authentication) は当社が処理するため、コア機能に集中できます。ユーザーが認証 (Authentication) されると、Protected App がサーバーからコンテンツを配信します。

Protected App の仕組み

Cloudflare により提供される Protected App は、グローバルなエッジネットワーク上で動作し、アプリケーションに低遅延かつ高可用性を実現します。

Protected App はセッション状態とユーザー情報を管理します。ユーザーが認証 (Authentication) されていない場合、Protected App はサインインページへリダイレクトします。認証 (Authentication) 後、Protected App はユーザーのリクエストに認証 (Authentication) とユーザー情報を付与し、オリジンサーバーへ転送します。

このプロセスは以下のフローチャートで視覚化されています:

オリジンサーバーの保護

オリジンサーバーは、物理または仮想デバイスであり、Logto の Protected App が所有しない場合もあります。アプリケーションのコンテンツはここに存在します。CDN サーバーと同様に、Protected App は認証 (Authentication) 処理を管理し、オリジンサーバーからコンテンツを取得します。そのため、ユーザーがオリジンサーバーへ直接アクセスできる場合、認証 (Authentication) をバイパスでき、アプリケーションは保護されなくなります。

したがって、オリジン接続のセキュリティ確保は重要です。これにより、攻撃者が認証 (Authentication) なしでオリジンサーバーを発見・アクセスすることを防げます。主な方法は以下の通りです:

  1. HTTP ヘッダー検証
  2. JSON Web Token (JWT) 検証

HTTP ヘッダー検証

HTTP Basic 認証 (Authentication) を利用してオリジンサーバーを保護できます。

Protected App からの各リクエストには、次のヘッダーが含まれます:

Authorization: Basic base64(appId:appSecret)

このヘッダーを検証することで、リクエストが Protected App からのものであることを確認し、このヘッダーが含まれないリクエストを拒否できます。

Nginx や Apache を利用している場合、オリジンサーバーで HTTP Basic 認証 (Authentication) を実装するには以下のガイドを参照してください:

  1. Nginx: HTTP Basic 認証 (Authentication) の設定
  2. Apache: 認証 (Authentication) と認可 (Authorization)

アプリケーション内でヘッダーを確認するには、Cloudflare の HTTP Basic 認証 (Authentication) の例 を参照し、HTTP Basic スキーマでアクセス制限する方法を学べます。

JSON Web Token (JWT) 検証

もう一つの方法は、JSON Web Token (JWT) を利用してオリジンサーバーを保護することです。

Protected App からの認証済みリクエストには、次のヘッダーが含まれます:

Logto-ID-Token: <JWT>

この JWT は ID トークン (ID token) と呼ばれ、Logto により署名され、ユーザー情報が含まれています。この JWT を検証することで、リクエストが Protected App からのものであることを確認し、このヘッダーが含まれないリクエストを拒否できます。

トークンは JWS トークンとして暗号化・署名されています。

検証手順:

  1. JWT の検証
  2. JWS 署名の検証
  3. トークンの発行者 (Issuer) は https://<your-logto-domain>/oidc(Logto 認証 (Authentication) サーバーによる発行)
const express = require('express');
const jwksClient = require('jwks-rsa');
const jwt = require('jsonwebtoken');

const ISSUER = 'https://<your-logto-domain>/oidc';
const CERTS_URL = 'https://<your-logto-domain>/oidc/jwks';

const client = jwksClient({
jwksUri: CERTS_URL,
});

const getKey = (header, callback) => {
client.getSigningKey(header.kid, function (err, key) {
callback(err, key?.getPublicKey());
});
};

const verifyToken = (req, res, next) => {
const token = req.headers['Logto-ID-Token'];

// リクエストにトークンヘッダーが含まれていることを確認
if (!token) {
return res
.status(403)
.send({ status: false, message: 'missing required Logto-ID-Token header' });
}

jwt.verify(token, getKey, { issuer: ISSUER }, (err, decoded) => {
if (err) {
return res.status(403).send({ status: false, message: 'invalid id token' });
}

req.user = decoded;
next();
});
};

const app = express();

app.use(verifyToken);

app.get('/', (req, res) => {
res.send('Hello World!');
});

app.listen(3000);

認証 (Authentication) 状態とユーザー情報の取得

アプリケーションで認証 (Authentication) 状態やユーザー情報を取得したい場合も、Logto-ID-Token ヘッダーを利用できます。

トークンをデコードするだけでよい場合は、次のコードを利用できます:

const express = require('express');

const decodeIdToken = (req, res, next) => {
const token = req.headers['Logto-ID-Token'];

if (!token) {
return res.status(403).send({
status: false,
message: 'missing required Logto-ID-Token header',
});
}

const parts = token.split('.');
if (parts.length !== 3) {
throw new Error('Invalid ID token');
}

const payload = parts[1];
const decodedPayload = atob(payload.replace(/-/g, '+').replace(/_/g, '/'));
const claims = JSON.parse(decodedPayload);

req.user = claims;
next();
};

const app = express();

app.use(decodeIdToken);

app.get('/', (req, res) => {
res.json(req.user);
});

app.listen(3000);

ID トークン (ID token) のクレーム (Claims) をカスタマイズ

デフォルトでは、Logto-ID-Token ヘッダーには標準 OIDC クレーム (Claims)(例:subnameemail)が含まれます。拡張クレーム (Claims)(ロールや組織データなど)を含めるには、以下の両方の設定が必要です:

  1. テナントのトグルConsole > Custom JWT > ID token でクレーム (Claims) を有効化。
  2. Protected App のスコープ:Protected App の設定で、ID token claims > Additional scopes から該当スコープを選択。

拡張クレーム (Claims) は、Custom JWT で有効化され、かつ Protected App で該当スコープが選択されている場合のみ、転送される ID トークン (ID token) に含まれます。拡張スコープとクレーム (Claims) の一覧は Custom ID token を参照してください。

ScopeClaims
custom_datacustom_data
identitiesidentities, sso_identities
rolesroles
urn:logto:scope:organizationsorganizations, organization_data
urn:logto:scope:organization_rolesorganization_roles

オリジナルのホストを取得

クライアントがリクエストした元のホストを取得したい場合、Logto-Host または x-forwarded-host ヘッダーを利用できます。

認証 (Authentication) ルールのカスタマイズ

デフォルトでは、Protected App はすべてのルートを保護します。認証 (Authentication) ルールをカスタマイズしたい場合は、Console の「Custom authentication rules」フィールドで設定できます。

正規表現に対応しており、以下のようなケースがあります:

  1. /admin および /privacy のみ認証 (Authentication) で保護したい場合:^/(admin|privacy)/.*
  2. JPG 画像を認証 (Authentication) から除外したい場合:^(?!.*\.jpg$).*$

ローカル開発

Protected App はオリジンサーバーと連携するよう設計されています。ただし、オリジンサーバーが公開されていない場合は、ngrokCloudflare Tunnels などのツールを使ってローカルサーバーをインターネットに公開できます。

SDK 統合への移行

Protected App は認証 (Authentication) プロセスを簡素化するために設計されていますが、より細かな制御やカスタマイズのために SDK 統合へ移行したい場合は、Logto で 新しいアプリケーションを作成 し、SDK 統合 を設定できます。スムーズな移行のため、Protected App のアプリケーション設定を再利用できます。Protected App は Logto で「Traditional Web App」として扱われており、アプリケーション設定で「AppId」や「AppSecret」を確認できます。移行完了後は、アプリケーションから Protected App を削除できます。

Protected App: 数クリックでアプリの認証 (Authentication) を構築。ノーコードで実現。

Protected App の背景と動機

最速で認証 (Authentication) システムを構築する方法