【ソーシャルログイン】Experience Cloud に Google アカウントでログインさせる完全ガイド:Apex とカスタムメタデータ型による「実務級」の実装【決定版】

この記事はバージョン Winter ’26 において執筆しています。
現在の動作と異なる場合がありますので、ご認識おきください。

「IDとパスワードを入力して会員登録する」 このプロセスは、現代のWebユーザーにとって大きな離脱ポイントになります。ユーザーの手間を減らし、登録率を劇的に向上させるのが 「ソーシャルログイン(SSO)」 です。

Experience Cloud は標準で Google 認証に対応していますが、実務で運用するには、自動生成されるプログラム(Apex)をそのまま使うのではなく、「既存顧客との紐付け」「プロファイルの出し分け」 を考慮したカスタマイズが不可欠です。

この記事では、GCP (Google Cloud Platform) の設定から、Salesforce 認証プロバイダーの作成、そして カスタムメタデータ型 (CMDT) を活用したメンテナンス性の高い Apex 実装まで、プロフェッショナルな構築手順を完全網羅します。

仕組みの理解:OAuth と「登録ハンドラ」

設定に入る前に、裏側の動きを理解しましょう。

  1. 認証 (Auth): Google がユーザーの本人確認を行います。
  2. 認可 (Authorize): Salesforce が Google からメールアドレス等の情報を受け取ります。
  3. 登録ハンドラ (Registration Handler): ここが重要です。
    • 受け取った情報を使って、Salesforce内のユーザーを検索・作成する Apex クラスです。
    • 今回は、このクラスを 「ハードコードなし」 で設計し、後から設定変更だけで挙動を変えられるようにします。

Step 1: Google Cloud Platform (GCP) での準備

まずは Google 側の設定です。
既存のプロジェクトを流用せず、新規プロジェクト を作成することを推奨します(同意画面に正しいアプリ名を表示するため)。

  1. プロジェクト作成: Google Cloud Console で MY SITE LOGIN などの名前で作成。
  2. OAuth 同意画面:
    • User Type: 「外部 (External)」を選択。
    • アプリ情報: サイト名やロゴを設定。
    • スコープ: email, profile, openid を追加。
    • テストユーザー: 検証に使う自分の Gmail アドレスを追加。
  3. 認証情報 (OAuth クライアントID):
    • 種類: 「Web アプリケーション」を選択。
    • リダイレクトURI: 一旦空欄のまま 作成します(後で Salesforce の値を入れに戻ってきます)。
    • IDとシークレット: 生成された「クライアントID」と「クライアントシークレット」をコピーしておきます。
  • プロジェクト作成
  • OAuth 同意画面
  • テストユーザー追加
  • スコープ追加
  • クライアント追加
  • APIとサービス>OAuth同意画面
  • 開始
  • 対象は外部を選択

[更新] > [Save]

  • クライアントを作成
  • 情報入力(リダイレクトURLは空)

Step 2: Salesforce「認証プロバイダー」の設定

Salesforce 側に Google を受け入れる口を作ります。

  1. [設定] > [認証プロバイダー] > [新規] をクリック。
  2. プロバイダータイプ: Google を選択。
  3. コンシューマ鍵/秘密: Step 1 で取得した ID とシークレットを貼り付けます。
  4. 登録ハンドラ: 種別「Apex」と「登録ハンドラテンプレートを自動作成」を選択します。
  5. 保存後、生成された 「コールバック URL」 をコピーします。

💡忘れずに!GCP のコンソールに戻り、コピーしたコールバック URL を「承認済みのリダイレクト URI」に貼り付けて保存してください。これで Google と Salesforce の握手が完了です。

  • 認証プロバイダー
  • コールバックURL
  • GCPに貼り付け

Step 3: カスタムメタデータ型 (CMDT) の準備

Step2 で登録ハンドラ: 種別「Apex」と「登録ハンドラテンプレートを自動作成」を選択したことによって自動的に Apex コードが出来上がっています。このコードはテンプレートなので実際の利用前にカスタマイズする必要があります。

Apex コードの中に「プロファイル名」や「取引先名」を直接書くと、環境移行時や変更時に大変です。これらを外出しして管理するために、カスタムメタデータ型を作成します。

  1. [設定] > [カスタムメタデータ型] で新規作成。
    • 表示ラベル: Social Login Config (Social_Login_Config__mdt)
  2. カスタム項目を作成:
    • DefaultAccountName__c (テキスト255): 受け皿となる取引先名
    • ExternalProfileName__c (テキスト255): デフォルトのプロファイル名
    • VipProfileName__c (テキスト255): 特別会員用のプロファイル名
  3. レコードを作成:
    • ラベル: 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サイトのログイン画面にボタンを表示させます。

  1. ワークスペース > [管理] > [ログインと登録]
  2. 「ログインページ設定」で [Google] にチェックを入れて保存。
  3. ビルダー > Login 画面
  4. 「ソーシャルログイン」コンポーネントを配置。
  5. 公開

これで完了です! Google アカウントでログインすると、Salesforce 側に自動的に「取引先責任者」と「ユーザ」が作成され、サイトにログインできるようになります。

  • ログインオプションを追加
  • ソーシャルログインコンポーネントを配置

Step6: 実際にログインしてみる

実際のログイン画面がこちら。

Google 認証を通過して、うまくユーザーが作成された後にログインできていることがわかります。

  • ログイン画面
  • Google認証
  • ログイン成功
  • ユーザー登録済み
  • アカウントを選択
  • アクセス許可

まとめ

ソーシャルログインは、UX向上のための強力な武器です。 しかし、単に繋ぐだけでなく、「既存顧客だったらどうする?」「VIP会員だったら?」 という業務要件を考慮した実装にしておくことで、運用の質が大きく変わります。

今回実装した「カスタムメタデータ × Apex」のパターンは、Google 以外のプロバイダ(LINEやFacebook)でも応用できる汎用的な設計です。ぜひ活用してください。

読者の声

タイトルとURLをコピーしました