😄

Overview Multipass of Shopify

2022/06/09に公開

はじめに

Multipassとは

  • Multipassは、別Webサイトでカスタマーアカウントが存在する場合、そのカスタマーアカウントにてShopifyストアにリダイレクトさせてログインさせる機能のこと。
  • Multipass機能を利用するには、ストアの「Multipassログインシークレット(以下、ログインシークレット)」が必要になる。ログインシークレットはストア設定のチェックアウト画面から取得する。
  • 認証情報を持つリダイレクト元は「別WEBサイト(separate website)」で表現統一する。
  • Shopify ECサイト側はログインシークレットを発行するのみ。トークン生成までの実装は、外部(別Webサイト側)にて実装する必要がある。

Multipass login is for store owners who have a separate website and a Shopify store. It redirects users from the website to the Shopify store and seamlessly logs them in with the same email address they used to sign up for the original website. If no account with that email address exists yet, one is created. There is no need to synchronize any customer databases.(引用はすべて公式サイト https://shopify.dev/api/multipass より)

マルチパスログイン機能は、別WebサイトとShopifyストアを持っているストアオーナー向け機能である。ユーザーを別WebサイトからShopifyストアにリダイレクトし、元の別Webサイトへのサインアップに使用したのと同じ電子メールアドレスで途切れなくログインできる。そのメールアドレスのアカウントがまだ存在しない場合は、アカウントが作成される。(Shopify側にEmailアカウントが存在しない場合は、アカウント作成される)顧客データベースを同期する必要はない。

Overview Multipass of Shopify

公式サイトから処理概要を理解する

1) Enable Multipass login in the Shopify admin (Shopify Adminでマルチパスログインを有効化)/Shopify ECサイト側にて実施

  • ショップ管理者権限でログインし、[設定]>[チェックアウト]ページに移動する。[顧客アカウント]セクションまでスクロールして、[Accounts are optional]または[Accounts are required]のいずれかが選択されていることを確認する。
  • [Enable Multipass]を選択する。有効にすると、ログインシークレットが共有される。顧客をShopifyストアにログインさせるためのトークンを生成するには、ログインシークレットが必要となる。ログインシークレットは漏洩しないよう保管し、別Webサイトへ連携する。

2) Encode your customer information using JSON (JSONを使用して顧客情報をエンコード化)/別Webサイト側で実施

  • 顧客情報はハッシュとして表され、顧客メールアドレスと現在のタイムスタンプ(ISO8601エンコーディングが必須フィールドである。顧客姓、顧客名前、または複数の配送先住所を含めることもできる。オプションで、顧客の現在のブラウザセッションのIPアドレスを含めることができ、このIPアドレスから発信されたリクエストに対してのみトークンが有効となる。
  • 「tag_string」フィールドはカンマ区切りの1単語のリストにて設定し、タグを顧客に関連付けることができる。これらのタグは、この顧客にすでに設定されている既存のタグ設定を上書きする。
  • ユーザーにShopifyストアの特定のページを表示させたい場合は、return_toフィールドが使用できる。(Shopify側でトークン認証が環境した際に遷移するページを指定できる。)
  • Shopifyは、ショップ顧客の一意識別子として電子メールアドレスを使用する。

3) Encrypt the JSON data using AES (AESを使用してJSONデータを暗号化)/別Webサイトにて実施

  • 有効なトークンを生成するために、Shopify管理者に与えられたログインシークレットが必要となる。ログインシークレットは、2つの暗号化キーを導出するために使用される。1つは暗号化用、もう1つは署名用。この鍵の導出は、SHA-256ハッシュ関数を使用する(最初の128ビットは暗号化鍵として使用され、最後の128ビットは署名鍵として使用される)。

4) Sign the encrypted data using HMAC (HMACを使用して暗号化データに署名)/別Webサイトにて実施

  • 署名(メッセージ認証コードとも呼ばれる)は、信憑性を提供する。トークンが本物であり、改ざんされていないことを証明する。SHA-256ハッシュ関数でHMACアルゴリズムを使用し、ステップ3の暗号化されたJSONデータに署名する(ステップ2のプレーンテキストJSONデータではない)。

5) Base64 encode the binary data (Base64でバイナリデータをエンコード)/別Webサイトにて実施

  • トークンは、128ビットの初期化ベクトル, 可変長の暗号文, および256ビットの署名(この順序で)で構成される。このデータは、base64(RFC4648)でエンコードされる。

6) Redirect your customer to your Shopify store (顧客をShopifyストアにリダイレクト)/別Webサイトにて実施

  • トークンを取得したら、Shopifyストアに対してHTTP GETリクエストを送る。
HTTP GET request
/account/login/multipass/insert_token_here
  • リクエストが成功すると(例えば、トークンが有効でかつ、有効期限が切れていない場合)、顧客はShopifyストアにログインできる。
  • マルチパストークンは非常に短い時間枠内でのみ有効で、各トークンは一度きりの使用となる。これらの理由から、HTMLサイトにレンダリングするためのトークンを事前に生成するべきではない。リダイレクトURLは利用するタイミングで生成し、その後、ブラウザを自動的にリダイレクトさせる。

Example implementation (実装例)

  • 公式サイトにて、実装例が掲載されている。上記までの処理の流れが実装されているか確認する。

The following shows a basic example implementation of the token generation in the Ruby and PHP languages. You can also view a Node.js module for Shopify Multipass, called Multipassify.

require "openssl"
require "base64"
require "time"
require "json"

class ShopifyMultipass
  def initialize(multipass_secret)
    # ログインシークレットより、暗号化用、署名用の鍵を導出する処理
    # 最初の128ビット(16バイト)は暗号化鍵、最後の128ビット(16バイト)は署名鍵
    key_material = OpenSSL::Digest.new("sha256").digest(multipass_secret)
    @encryption_key = key_material[ 0,16]
    @signature_key  = key_material[16,16]
  end

  def generate_token(customer_data_hash)
    # custmer_dataに必須項目であるcreated_atフィールド追加
    customer_data_hash["created_at"] = Time.now.iso8601
    # encryptメソッドを呼び出しJSONデータをAESで暗号化
    ciphertext = encrypt(customer_data_hash.to_json)
    # HMACを使用してJSON暗号化データに署名
    # signメソッドを呼び出し導出した署名用鍵を利用
    Base64.urlsafe_encode64(ciphertext + sign(ciphertext))
  end

  private

  def encrypt(plaintext)
    cipher = OpenSSL::Cipher.new("aes-128-cbc")
    cipher.encrypt
    cipher.key = @encryption_key
    # random initialization vector
    # 初期化ベクトルをランダムに生成し、毎回異なるTOKENを発行
    cipher.iv = iv = cipher.random_iv
    iv + cipher.update(plaintext) + cipher.final
  end

  def sign(data)
    OpenSSL::HMAC.digest("sha256", @signature_key, data)
  end

end


# カスタマーデータ作成
# 別WEBサイト管理のDBより抽出してJSON形式へ成形
customer_data = {
    email: "bob@shopify.com",
    remote_ip: "107.20.160.121"
}

# Shopify Adminより渡されたログインシークレットを記載
multipass_secret = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"

token = ShopifyMultipass.new(multipass_secret).generate_token(customer_data)
p "redirect URL is: " + "https://shopify_domain/account/login/multipass/" + token

実装処理の流れ

  • 別Webサイト(認証元)にて実装する。
  • Shopify管理者ログインシークレットを取得
  • 認証元システムのログイン認証が通った場合は、以下処理にてトークンを生成
    • ログインシークレットより、暗号化用、署名用の鍵を導出
    • 登録するもしくはログイン認証するカスタマーデータをJSON形式で抽出
    • 抽出したJSONデータをAESで暗号化
    • HMACを使用してJSON暗号化データに署名
  • 生成したトークンを含むリダイレクトURLよりShopify ECサイトへ移動

留意事項

  • 別WebサイトとShopifyストアは、データベース同期されていない。
    • 別Webサイト側でアカウント情報が更新された場合、Shopifyストア側への更新が必要となる。例えば、Shopifyで一意なアカウントして認識されるメールアドレスが別Webサイト側で更新可能であった場合、その変更をShopify側に反映させる仕組みが必要となる。
    • 別Webサイト側の解約処理についても同様。
  • 別Webサイト側で、メールアカウント発行された場合、Shopifyのアカウントがログイン連携できてしまう。トークン呼び出し元で、既存Shopifyに登録されているメールアカウントでマルチパスログイン認証されると、ログイン成功となりのっとりが可能となる。

You should make sure that registering new accounts at your main website requires validation of the email address which is used. Otherwise, someone could sign up to your main site using somebody else's email address, thus getting access to his customer account in your Shopify store.

  • remote_ipフィールドを常に設定して、目的のブラウザのみがトークンを使用できるようにすること。

Shopify encourages you to always set the remote_ip field in the customer data hash, so that only the intended browser can use the token. We also encourage you to send tokens to the browser using secure HTTPS connections.

  • 別Webサイトからの導線をどう実装するか。必須事項であるメールアドレスを取得するには、別Webサイトの認証を通す必要がある。
    • 別Webサイトログイン入力画面の認証処理 → TOKEN生成 → Shopifyへリダイレクト
    • 別Webサイトの認証後の画面からShopifyへのリンク → セッションオブジェクトからメールアドレスを取得し、TOKEN生成 → Shopifyへリダイレクト

Discussion