この記事はバージョン Winter ’26 において執筆しています。
現在の動作と異なる場合がありますので、ご認識おきください。
「IDとパスワードを入力して会員登録する」 このプロセスは、現代のWebユーザーにとって大きな離脱ポイントになります。ユーザーの手間を減らし、登録率を劇的に向上させるのが 「ソーシャルログイン(SSO)」 です。
Experience Cloud は標準で Google 認証に対応していますが、実務で運用するには、自動生成されるプログラム(Apex)をそのまま使うのではなく、「既存顧客との紐付け」 や 「プロファイルの出し分け」 を考慮したカスタマイズが不可欠です。
この記事では、GCP (Google Cloud Platform) の設定から、Salesforce 認証プロバイダーの作成、そして カスタムメタデータ型 (CMDT) を活用したメンテナンス性の高い Apex 実装まで、プロフェッショナルな構築手順を完全網羅します。
仕組みの理解:OAuth と「登録ハンドラ」
設定に入る前に、裏側の動きを理解しましょう。
- 認証 (Auth): Google がユーザーの本人確認を行います。
- 認可 (Authorize): Salesforce が Google からメールアドレス等の情報を受け取ります。
- 登録ハンドラ (Registration Handler): ここが重要です。
- 受け取った情報を使って、Salesforce内のユーザーを検索・作成する Apex クラスです。
- 今回は、このクラスを 「ハードコードなし」 で設計し、後から設定変更だけで挙動を変えられるようにします。

Step 1: Google Cloud Platform (GCP) での準備
まずは Google 側の設定です。
既存のプロジェクトを流用せず、新規プロジェクト を作成することを推奨します(同意画面に正しいアプリ名を表示するため)。
- プロジェクト作成: Google Cloud Console で
MY SITE LOGINなどの名前で作成。 - OAuth 同意画面:
- User Type: 「外部 (External)」を選択。
- アプリ情報: サイト名やロゴを設定。
- スコープ:
email,profile,openidを追加。 - テストユーザー: 検証に使う自分の Gmail アドレスを追加。
- 認証情報 (OAuth クライアントID):
- 種類: 「Web アプリケーション」を選択。
- リダイレクトURI: 一旦空欄のまま 作成します(後で Salesforce の値を入れに戻ってきます)。
- IDとシークレット: 生成された「クライアントID」と「クライアントシークレット」をコピーしておきます。
- プロジェクト作成
- OAuth 同意画面
- テストユーザー追加
- スコープ追加
- クライアント追加

- APIとサービス>OAuth同意画面
- 開始
- 対象は外部を選択





[更新] > [Save]
- クライアントを作成
- 情報入力(リダイレクトURLは空)


Step 2: Salesforce「認証プロバイダー」の設定
Salesforce 側に Google を受け入れる口を作ります。
- [設定] > [認証プロバイダー] > [新規] をクリック。
- プロバイダータイプ:
Googleを選択。 - コンシューマ鍵/秘密: Step 1 で取得した ID とシークレットを貼り付けます。
- 登録ハンドラ: 種別「Apex」と「登録ハンドラテンプレートを自動作成」を選択します。
- 保存後、生成された 「コールバック URL」 をコピーします。
💡忘れずに!GCP のコンソールに戻り、コピーしたコールバック URL を「承認済みのリダイレクト URI」に貼り付けて保存してください。これで Google と Salesforce の握手が完了です。
- 認証プロバイダー
- コールバックURL
- GCPに貼り付け




Step 3: カスタムメタデータ型 (CMDT) の準備
Step2 で登録ハンドラ: 種別「Apex」と「登録ハンドラテンプレートを自動作成」を選択したことによって自動的に Apex コードが出来上がっています。このコードはテンプレートなので実際の利用前にカスタマイズする必要があります。
Apex コードの中に「プロファイル名」や「取引先名」を直接書くと、環境移行時や変更時に大変です。これらを外出しして管理するために、カスタムメタデータ型を作成します。
- [設定] > [カスタムメタデータ型] で新規作成。
- 表示ラベル:
Social Login Config(Social_Login_Config__mdt)
- 表示ラベル:
- カスタム項目を作成:
DefaultAccountName__c(テキスト255): 受け皿となる取引先名ExternalProfileName__c(テキスト255): デフォルトのプロファイル名VipProfileName__c(テキスト255): 特別会員用のプロファイル名
- レコードを作成:
- ラベル:
GoogleAuthSettings - 各項目に、実際の取引先名(例:
Google Auth Users)やプロファイル名(例:Customer Community User)を入力します。
- ラベル:
- カスタムメタデータを作成
- カスタム項目を作成
- レコードを作成



Step 4: 登録ハンドラ (Apex) の実装【完全版】
自動生成された Apex クラスを、以下のロジックに書き換えます。(クラス名は上書きしないように注意)
Apex コードはこちらをクリック💡
/**
* @description Googleソーシャルログイン用の登録ハンドラ (完全版・VIP対応)
* 外部ユーザー(Community)の自動作成と紐付けを行います。
* 設定値(プロファイル名、取引先名)はすべてカスタムメタデータ型から取得します。
*/
global class AutocreatedRegHandler1764930569603 implements Auth.RegistrationHandler {
/**
* @description ユーザー作成を許可するかどうかの判定
*/
global boolean canCreateUser(Auth.UserData data) {
return true;
}
/**
* @description 新規ユーザー作成ロジック
* Google未連携の既存のユーザーがログインした場合、Google連携済みに更新します。
* Contactあり / Userなし: Google から受け取った情報で User を作成してログインさせます。
* Contactなし / Userなし: Google から受け取った情報で Contact も User も新規作成します。
*/
global User createUser(Id portalId, Auth.UserData data) {
if (!canCreateUser(data)) {
return null;
}
// Google未連携の既存のユーザーがいれば、それを返して「紐付け」完了とする
List<User> existingUsers = [
SELECT Id, Email, ContactId
FROM User
WHERE Username = :data.email + '.' + UserInfo.getOrganizationId() // ユーザー名生成ルールに合わせる
OR Email = :data.email
LIMIT 1
];
if (!existingUsers.isEmpty()) {
System.debug('既存ユーザーが見つかりました。紐付けを行います。');
return existingUsers[0]; // これを返すだけでリンクが作成されます
}
// 1. CMDTから設定値を読み込む
Social_Login_Config__mdt config = Social_Login_Config__mdt.getInstance('GoogleAuthSettings');
// デフォルト設定の取得
String accountName = (config != null) ? config.DefaultAccountName__c : 'Google Auth Users';
String defaultProfileName = (config != null) ? config.ExternalProfileName__c : 'Customer Community User';
// ★VIP用プロファイル名もCMDTから取得
String vipProfileName = (config != null && config.VipProfileName__c != null) ? config.VipProfileName__c : 'My Site VIP User';
// コミュニティ経由のログインかどうか判定
if (data.attributeMap.containsKey('sfdc_networkid')) {
// 2. 既存の取引先責任者 (Contact) をメールアドレスで検索
// これにより「既存顧客の紐付け」と「ランク判定」を同時に行います
List<Contact> existingContacts = [
SELECT Id, AccountId, Is_VIP__c // 判定に使いたい項目
FROM Contact
WHERE Email = :data.email
LIMIT 1
];
Contact c;
String targetProfileName;
if (!existingContacts.isEmpty()) {
// A. 既存Contactが見つかった場合
c = existingContacts[0];
// ★Contactの属性を見てプロファイルを出し分ける
if (c.Is_VIP__c == true) {
targetProfileName = vipProfileName;
} else {
targetProfileName = defaultProfileName;
}
// ※本来はここでAccountIDの整合性チェックなどを行うとより安全です
} else {
// B. 見つからない場合(完全新規) -> デフォルトプロファイルを使用
targetProfileName = defaultProfileName;
// 取引先の取得・作成
Account a;
try {
a = [SELECT Id FROM Account WHERE Name = :accountName LIMIT 1];
} catch (Exception e) {
a = new Account(Name = accountName);
insert a;
}
// 新規Contact作成
c = new Contact();
c.AccountId = a.Id;
c.Email = data.email;
c.FirstName = data.firstName;
c.LastName = data.lastName;
insert c;
}
// 3. 決定したプロファイル名で User を作成
// バインド変数 :targetProfileName で検索
Profile p = [SELECT Id FROM Profile WHERE Name = :targetProfileName LIMIT 1];
User u = new User();
u.Username = data.email + '.' + UserInfo.getOrganizationId();
u.Email = data.email;
u.LastName = data.lastName;
u.FirstName = data.firstName;
String alias = (data.firstName != null && data.firstName != '') ? data.firstName : 'guest';
if (alias.length() > 8) { alias = alias.substring(0, 8); }
u.Alias = alias;
// 日本語環境に合わせて明示的に指定します
u.LanguageLocaleKey = 'ja';
u.LocaleSidKey = 'ja_JP';
u.EmailEncodingKey = 'UTF-8';
u.TimeZoneSidKey = 'Asia/Tokyo';
u.ProfileId = p.Id;
u.ContactId = c.Id; // 既存または新規のContactID
return u;
} else {
return null;
}
}
/**
* @description 既存ユーザーの更新ロジック
* Google連携済みのユーザーがログインするたびに、Googleの最新情報でSalesforceのレコードを更新します。
*/
global void updateUser(Id userId, Id portalId, Auth.UserData data) {
// ------------------------------------
// 1. Userと紐付くContact情報を取得
// ------------------------------------
User u = [
SELECT Id, LastName, FirstName, Email, ContactId, Contact.LastName, Contact.FirstName
FROM User
WHERE Id = :userId
LIMIT 1
];
// 変更フラグ
boolean userChanged = false;
boolean contactChanged = false;
// ------------------------------------
// 2. User レコードの更新
// ------------------------------------
if (data.lastName != u.LastName) {
u.LastName = data.lastName;
userChanged = true;
}
if (data.firstName != u.FirstName) {
u.FirstName = data.firstName;
userChanged = true;
}
// ※メールアドレスも変更可能ですが、通常はユーザーがSalesforce側で変更しない限り同期しない方が安全です。
// if (data.email != u.Email) {
// u.Email = data.email;
// userChanged = true;
// }
// ------------------------------------
// 3. Contact レコードの更新
// ------------------------------------
if (u.ContactId != null) {
Contact c = new Contact(Id = u.ContactId);
// GoogleデータとContactデータを比較
if (data.lastName != u.Contact.LastName) {
c.LastName = data.lastName;
contactChanged = true;
}
if (data.firstName != u.Contact.FirstName) {
c.FirstName = data.firstName;
contactChanged = true;
}
// Contactの更新実行
if (contactChanged) {
update c;
}
}
// ------------------------------------
// 4. User レコードの更新
// 最終ログイン日時などの情報は自動で更新されるため、明示的に変更があったフィールドのみ更新を実行
// ------------------------------------
if (userChanged) {
update u;
}
}
}
- 特徴:
- 設定値はすべて CMDT から取得(コード修正不要)。
- メールアドレスで既存の
Contactを検索し、重複作成を防止。 Contactの属性(VIPフラグIs_VIP__cなど)を見て、割り当てるプロファイルを動的に変更。- Customer Community Plus / Partner ライセンスにも対応。
上で示した Apex コードは以下のすべてのパターンを網羅しています。
- Contactあり / Userなし: User を作成してログインさせます。
- Contactなし / Userなし: Contact も User も新規作成します。
- Contactあり / Userあり (Google未連携): User と Google を連携させます。
- Contactあり / Userあり (Google連携済): Google の最新情報で User / Contact を更新します。

💡忘れずに!Plus / Partner ライセンスを使用する場合、紐付ける取引先(カスタムメタデータに設定した sForce や Apex 上の Google Auth Users)の所有者は、必ず ロール(Role)を持つ内部ユーザー に設定してください。
Step 5: サイトのログインページに追加
最後に、LWRサイトのログイン画面にボタンを表示させます。
- ワークスペース > [管理] > [ログインと登録]。
- 「ログインページ設定」で [Google] にチェックを入れて保存。
- ビルダー > Login 画面
- 「ソーシャルログイン」コンポーネントを配置。
- 公開
これで完了です! Google アカウントでログインすると、Salesforce 側に自動的に「取引先責任者」と「ユーザ」が作成され、サイトにログインできるようになります。
- ログインオプションを追加
- ソーシャルログインコンポーネントを配置


Step6: 実際にログインしてみる
実際のログイン画面がこちら。
Google 認証を通過して、うまくユーザーが作成された後にログインできていることがわかります。
- ログイン画面
- Google認証
- ログイン成功
- ユーザー登録済み

- アカウントを選択
- アクセス許可




まとめ
ソーシャルログインは、UX向上のための強力な武器です。 しかし、単に繋ぐだけでなく、「既存顧客だったらどうする?」「VIP会員だったら?」 という業務要件を考慮した実装にしておくことで、運用の質が大きく変わります。
今回実装した「カスタムメタデータ × Apex」のパターンは、Google 以外のプロバイダ(LINEやFacebook)でも応用できる汎用的な設計です。ぜひ活用してください。



読者の声