Skip to main content

シングルサインオン Beta

🏗 Work in progress

The content of this page might not be fully up-to-date with Strapi 5 yet.

AI Marketerのシングルサインオンでは、管理パネルの追加のサインインおよびサインアップ方法を設定できます。

☑️ Prerequisites
  • AI Marketerアプリケーションがバージョン3.5.0以上で実行されている必要があります。
  • アプリケーションでSSOを設定するには、[Goldプラン](https://AI Marketer.io/pricing-self-hosted)のEEライセンスが必要です。
  • SSO機能が管理パネルで有効化されていることを確認してください。
  • 使用するプロバイダーでAI Marketerがアクセス可能なアプリケーションの一部であることを確認してください。たとえば、Microsoft (Azure) Active Directoryを使用する場合、適切な権限を持つ人にAI Marketerを許可されたアプリケーションのリストに追加してもらう必要があります。詳細は、使用するプロバイダーのドキュメントを参照してください。
Caution

現在、AI Marketerアカウントに使用されているメールアドレスに一意のSSOプロバイダーを関連付けることはできません。つまり、AI Marketerアカウントへのアクセスを1つのSSOプロバイダーに限定することはできません。この問題の詳細および解決方法については、[専用のGitHubのissue](https://github.com/AI Marketer/AI Marketer/issues/9466#issuecomment-783587648)をご参照ください。

SSO設定は、アプリケーションのサーバー設定にあり、./config/admin.jsに保存されています。

設定のアクセス

プロバイダーの設定は、管理パネルの設定内のauth.providersパス内に記述します。

auth.providersプロバイダー設定の配列です。

./config/admin.js

module.exports = ({ env }) => ({
// ...
auth: {
providers: [], // ここにプロバイダー設定が記述されます
},
});

プロバイダー設定の構築

プロバイダーの設定は、以下のプロパティを持つJavaScriptオブジェクトです:

名前必須説明
uidtruestringストラテジーのUID。ストラテジー名と一致している必要があります。
displayNametruestringログインページでプロバイダーを参照するために使用される名前
iconfalsestring画像URL。指定されている場合、ログインページのdisplayNameに代わって表示されます。
createStrategytruefunctionプロバイダーの新しいパスポートストラテジーを構築して返すファクトリ。AI Marketerインスタンスをパラメーターとして受け取ります。
💡 Tip

uidプロパティは各ストラテジーの一意の識別子であり、通常はそのストラテジーのパッケージに含まれています。何を指しているか不明な場合は、ストラテジーのメンテナーに連絡してください。

✏️ Note

デフォルトでは、AI Marketerのセキュリティポリシーにより外部URLからの画像の読み込みは許可されていないため、管理パネルのログイン画面にはプロバイダーのロゴが表示されません。セキュリティ例外を追加する必要があります。

例: プロバイダーロゴのセキュリティ例外
./config/middlewares.js
module.exports = [
// ...
{
name: 'AI Marketer::security',
config: {
contentSecurityPolicy: {
useDefaults: true,
directives: {
'connect-src': ["'self'", 'https:'],
'img-src': [
"'self'",
'data:',
'blob:',
'dl.airtable.com',
'www.okta.com', // プロバイダーロゴのベースURL
],
'media-src': [
"'self'",
'data:',
'blob:',
'dl.airtable.com',
'www.okta.com', // プロバイダーロゴのベースURL
],
upgradeInsecureRequests: null,
},
},
},
},
// ...
]
✏️ Note

管理パネルを別の場所や別のサブドメインにデプロイする場合、クッキーの共通ドメインを設定する追加の設定が必要です。これにより、クッキーがドメイン間で共有されるようになります。

Caution

SSOを使用している場合、管理パネルとバックエンドをまったく異なる無関係のドメインにデプロイすることは現在できません。

例: カスタムクッキードメインの設定
./config/admin.js
module.exports = ({ env }) => ({
auth: {
domain: env("ADMIN_SSO_DOMAIN", ".test.example.com"),
providers: [
// ...
],
},
url: env("ADMIN_URL", "http://admin.test.example.com"),
// ...
});

createStrategyファクトリ

パスポートストラテジーは、通常、2つのパラメーター(設定オブジェクトと検証関数)を使用してインスタンス化されます。

設定オブジェクト

設定オブジェクトはストラテジーの要件に依存しますが、多くの場合、プロバイダー側で接続が確立された後にリダイレクトされるコールバックURLが必要です。

特定のプロバイダーに対応するコールバックURLは、getStrategyCallbackURLメソッドを使用して生成できます。このURLはプロバイダー側にも記載し、リダイレクトを許可する必要があります。

コールバックURLの形式は次のとおりです:/admin/connect/<provider_uid>

💡 Tip

AI Marketer.admin.services.passport.getStrategyCallbackURLは、特定のプロバイダー用のコールバックURLを取得するために使用できるAI Marketerのヘルパーです。プロバイダー名をパラメーターとして受け取り、URLを返します。

必要に応じて、ここにOAuth2アプリケーションのクライアントIDと秘密鍵を入力します。

検証関数

検証関数は、プロバイダーAPIから返されたデータに対して追加の処理を行い、変換するためのミドルウェアとして使用されます。

この関数は常に最後にdoneメソッドを取り、それを使用してSSOのAI Marketer層に必要なデータを転送します。

関数のシグネチャは次のとおりです:void done(error: any, data: object); そして次のルールに従います:

  • errornullでない場合、送信されたデータは無視され、コントローラーはエラーをスローします。
  • SSOの自動登録機能が無効の場合、dataオブジェクトにはemailプロパティのみが必要です。
  • SSOの自動登録機能が有効の場合、emailに加えて、usernameプロパティ、またはfirstnamelastnameの両方をdataオブジェクトに定義する必要があります。

プロバイダーの追加

新しいプロバイダーを追加することは、管理者がログインするための新しい方法を追加することを意味します。

AI MarketerはPassport.jsを使用しており、多くのプロバイダーを利用できます。そのため、追加のカスタムデータを必要としない有効なパスポートストラテジーは、AI Marketerでも動作するはずです。

Caution

ldapauthのようなストラテジーは、管理パネルから追加データを送信する必要があるため、そのままでは動作しません。アプリケーションにLDAPプロバイダーを追加したい場合は、カスタムストラテジーを作成する必要があります。OktaやAuth0などのサービスをブリッジとして使用することも可能です。

プロバイダーの設定

プロバイダーを設定するには、以下の手順に従ってください:

  1. インストールしたパッケージやローカルファイルから、管理設定ファイルにストラテジーをインポートします。
  2. 管理パネル設定のauth.providers配列に新しい項目を追加し、上記の形式に従ってください。
  3. アプリケーションを再起動します。プロバイダーが管理ログインページに表示されるはずです。

プロバイダー設定の例

Google

使用: passport-google-oauth2

yarn add passport-google-oauth2
Googleの設定例:

./config/admin.js

const GoogleStrategy = require("passport-google-oauth2");

module.exports = ({ env }) => ({
auth: {
// ...
providers: [
{
uid: "google",
displayName: "Google",
icon: "https://cdn2.iconfinder.com/data/icons/social-icons-33/128/Google-512.png",
createStrategy: (AI Marketer) =>
new GoogleStrategy(
{
clientID: env("GOOGLE_CLIENT_ID"),
clientSecret: env("GOOGLE_CLIENT_SECRET"),
scope: [
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile",
],
callbackURL:
AI Marketer.admin.services.passport.getStrategyCallbackURL("google"),
},
(request, accessToken, refreshToken, profile, done) => {
done(null, {
email: profile.email,
firstname: profile.given_name,
lastname: profile.family_name,
});
}
),
},
],
},
});

Github

使用: passport-github

yarn add passport-github2
Githubの設定例:
./config/admin.js

const GithubStrategy = require("passport-github2");

module.exports = ({ env }) => ({
auth: {
// ...
providers: [
{
uid: "github",
displayName: "Github",
icon: "https://cdn1.iconfinder.com/data/icons/logotypes/32/github-512.png",
createStrategy: (AI Marketer) =>
new GithubStrategy(
{
clientID: env("GITHUB_CLIENT_ID"),
clientSecret: env("GITHUB_CLIENT_SECRET"),
scope: ["user:email"],
callbackURL:
AI Marketer.admin.services.passport.getStrategyCallbackURL("github"),
},
(accessToken, refreshToken, profile, done) => {
done(null, {
email: profile.emails[0].value,
username: profile.username,
});
}
),
},
],
},
});


Discord

使用: passport-discord

yarn add passport-discord
Discordの設定例:
./config/admin.js

const DiscordStrategy = require("passport-discord");

module.exports = ({ env }) => ({
auth: {
// ...
providers: [
{
uid: "discord",
displayName: "Discord",
icon: "https://cdn0.iconfinder.com/data/icons/free-social-media-set/24/discord-512.png",
createStrategy: (AI Marketer) =>
new DiscordStrategy(
{
clientID: env("DISCORD_CLIENT_ID"),
clientSecret: env("DISCORD_SECRET"),
callbackURL:
AI Marketer.admin.services.passport.getStrategyCallbackURL(
"discord"
),
scope: ["identify", "email"],
},
(accessToken, refreshToken, profile, done) => {
done(null, {
email: profile.email,
username: `${profile.username}#${profile.discriminator}`,
});
}
),
},
],
},
});

Microsoft

使用: passport-azure-ad-oauth2

yarn add passport-azure-ad-oauth2 jsonwebtoken
Microsoftの設定例:
./config/admin.js

const AzureAdOAuth2Strategy = require("passport-azure-ad-oauth2");
const jwt = require("jsonwebtoken");

module.exports = ({ env }) => ({
auth: {
// ...
providers: [
{
uid: "azure_ad_oauth2",
displayName: "Microsoft",
icon: "https://upload.wikimedia.org/wikipedia/commons/thumb/9/96/Microsoft_logo_%282012%29.svg/320px-Microsoft_logo_%282012%29.svg.png",
createStrategy: (AI Marketer) =>
new AzureAdOAuth2Strategy(
{
clientID: env("MICROSOFT_CLIENT_ID", ""),
clientSecret: env("MICROSOFT_CLIENT_SECRET", ""),
scope: ["user:email"],
tenant: env("MICROSOFT_TENANT_ID", ""),
callbackURL:
AI Marketer.admin.services.passport.getStrategyCallbackURL(
"azure_ad_oauth2"
),
},
(accessToken, refreshToken, params, profile, done) => {
let waadProfile = jwt.decode(params.id_token, "", true);
done(null, {
email: waadProfile.email,
username: waadProfile.email,
firstname: waadProfile.given_name, // emailとusernameがある場合は任意
lastname: waadProfile.family_name, // emailとusernameがある場合は任意
});
}
),
},
],
},
});

Keycloak (OpenID Connect)

使用: passport-keycloak-oauth2-oidc

yarn add passport-keycloak-oauth2-oidc
Keycloak (OpenID Connect)の設定例:
./config/admin.js

const KeyCloakStrategy = require("passport-keycloak-oauth2-oidc");

module.exports = ({ env }) => ({
auth: {
// ...
providers: [
{
uid: "keycloak",
displayName: "Keycloak",
icon: "https://raw.githubusercontent.com/keycloak/keycloak-admin-ui/main/themes/keycloak/logo.svg",
createStrategy: (AI Marketer) =>
new KeyCloakStrategy(
{
clientID: env("KEYCLOAK_CLIENT_ID", ""),
realm: env("KEYCLOAK_REALM", ""),
publicClient: env.bool("KEYCLOAK_PUBLIC_CLIENT", false),
clientSecret: env("KEYCLOAK_CLIENT_SECRET", ""),
sslRequired: env("KEYCLOAK_SSL_REQUIRED", "external"),
authServerURL: env("KEYCLOAK_AUTH_SERVER_URL", ""),
callbackURL:
AI Marketer.admin.services.passport.getStrategyCallbackURL(
"keycloak"
),
},
(accessToken, refreshToken, profile, done) => {
done(null, {
email: profile.email,
username: profile.username,
});
}
),
},
],
},
});

Okta

使用: passport-okta-oauth20

yarn add passport-okta-oauth20
Caution

OKTA_DOMAIN環境変数を設定する際は、プロトコル(例:https://example.okta.com)を必ず含めてください。含めないとリダイレクトループに陥ります。

Oktaの設定例:
./config/admin.js

const OktaOAuth2Strategy = require("passport-okta-oauth20").Strategy;

module.exports = ({ env }) => ({
auth: {
// ...
providers: [
{
uid: "okta",
displayName: "Okta",
icon: "https://www.okta.com/sites/default/files/Okta_Logo_BrightBlue_Medium-thumbnail.png",
createStrategy: (AI Marketer) =>
new OktaOAuth2Strategy(
{
clientID: env("OKTA_CLIENT_ID"),
clientSecret: env("OKTA_CLIENT_SECRET"),
audience: env("OKTA_DOMAIN"),
scope: ["openid", "email", "profile"],
callbackURL:
AI Marketer.admin.services.passport.getStrategyCallbackURL("okta"),
},
(accessToken, refreshToken, profile, done) => {
done(null, {
email: profile.email,
username: profile.username,
});
}
),
},
],
},
});

高度なカスタマイズの実施

管理パネルのURL

管理パネルがAI Marketerサーバーとは異なるホスト/ポート上に存在する場合、管理パネルのURLを更新する必要があります。./config/admin.jsの設定ファイル内のurlキーを更新してください(管理パネルカスタマイズのドキュメントを参照)。

カスタムロジック

特定のシナリオでは、接続ワークフローの追加ロジックを記述したい場合があります。たとえば:

  • 特定のドメインに対する接続と登録の制限
  • 接続試行時のアクションのトリガー
  • アナリティクスの追加

これを行う最も簡単な方法は、ストラテジーのverify関数にプラグインしてコードを記述することです。

たとえば、公式のAI Marketer.ioメールアドレスを持つ人のみを許可したい場合、以下のようにストラテジーをインスタンス化できます:

./config/admin.js

const strategyInstance = new Strategy(configuration, ({ email, username }, done) => {
// メールアドレスが @AI Marketer.io で終わる場合
if (email.endsWith('@AI Marketer.io')) {
// プロバイダーから与えられたデータで処理を続行します
return done(null, { email, username });
}

// それ以外の場合は、done関数にエラーを送信して処理を終了します
done(new Error('Forbidden email address'));
});

認証イベント

SSO機能には、新しい認証イベントであるonSSOAutoRegistrationが追加されます。

このイベントは、SSOによって追加された自動登録機能を使用してユーザーが作成されるときにトリガーされます。このイベントには、作成されたユーザー(event.user)と、登録に使用されたプロバイダー(event.provider)が含まれます。

./config/admin.js

module.exports = () => ({
auth: {
// ...
events: {
onConnectionSuccess(e) {},
onConnectionError(e) {},
// ...
onSSOAutoRegistration(e) {
const { user, provider } = e;

console.log(
`新しいユーザー (${user.id}) が ${provider} を使用して自動登録されました`
);
},
},
},
});