Shopify PlusでSSO（シングルサインオン）

本記事では、自社ドメインECサイト構築プラットフォーム「Shopify」の最上位プラン「Shopify Plus」でSSO（シングルサインオン）をする方法を解説いたします。
Shopify Plusを前提とする理由は、SSOをするための「Multipass API」がShopify Plusプランでしか使えないからです。


## SSOとは
SSO（Single Sign On・シングルサインオン）とは、1つのIDとパスワードにより、ドメイン（やシステム、データベース）の異なる複数のWebサービスにログインする仕組みのことを言います。
YahooIDログインやLINEログインのような汎用的なものから、企業専用の統合ID（例えば、 [Canon ID](https://cweb.canon.jp/personal/product/canon-id/index.html?id=cig_top)）もSSOの仕組みです。

### ShopifyはSSO基盤自体は持たない
ShopifyへのSSO導入検討時に不明瞭なのが、Shopify自体がSSO基盤を持っているのか、あるいは外部のSSO基盤と連携できる認証の仕組みがあるだけなのかということですが、後者ですので、SSO基盤を作りたい場合は、それを別途用意する必要があります。


## ユースケース
Multipass APIを使ってShopify PlusでSSOさせるユースケースとしては大きく、２つに分類できます。
① LINEログインなど認知度の高いSNSアカウントを用いたログイン認証
② 企業専用統合ID基盤を用いたログイン認証

### ① LINEログインなど認知度の高いSNSアカウントを用いたログイン認証
①については、汎用的な仕組みとして用意されていることを期待したいところですが、まだない場合は開発が必要ですので、Multipass APIを使っての実装を検討することが考えられます。Shopifyアプリストアでは、Facebookログイン・Googleログインなどに対応するアプリは多数用意されておりますが、Multipass APIとも連携するアプリはごく一部ですので、Multipass APIの機能を使う理由があれば、独自実装をすることが考えられます。

### ② 企業専用統合ID基盤を用いたログイン認証
②がMultipass APIを使った実装をする場合の本命です。
具体的には、ECサイトとブランドのファンサイトのログインを共通化したり、従来よりあった店舗向けポイントプログラムへのログインとECのログインを共通化したりと、顧客にとっての認証を簡単にし、企業としてはサービス間を横断して同じユーザーを特定しやすくできるなどのメリットがあります。

日本でも、Multipass APIを使っているところは10サイトほどあり、Multipass APIの仕様的に連携できなかったという事例はこれまで聞いたこともないです。


## SSOフロー（システム構成）
SSOフロー（システム構成）は下記の通りです。
Shopify側を起点に、認証機能は外部SSO基盤で持ち、認証完了後にログインした状態でShopifyに戻ってこれるフローが実現できています。

![sso.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154/d8cbd439-855d-5655-eb3c-da772b09cd18.png)

## 実装方法
実装工程は下記の通りです。
1はShopify Plus管理画面での作業で、2〜6はSSO基盤側へのプログラム組み込みです。

1. 管理画面でマルチパスログインを有効にし、「シークレット」を取得
2. リクエストするJSONオブジェクトの準備
3. JSONオブジェクトをAES暗号化
4. 暗号化データをHMACで署名する
5. バイナリデータをBase64エンコード
6. トークンを取得したらShopifyストアにリダイレクト

これを見てどう思うかは人それぞれと思いますが、サンプルコードも提供されていることから、正常系の開発が完了するまでのリードタイムはそんなに長くないでしょう。

### サンプルコード

Ruby言語、PHP言語向けのサンプルコードはオフィシャルドキュメントに記述があります。
https://shopify.dev/docs/admin-api/rest/reference/plus/multipass

nodejs向けのサンプルコードは下記で公開されています。
https://github.com/beaucoo/multipassify

.NETもありますね。なお、下記サイトでは、IaaS（Identity as a Service）の先駆け「Auth0」を使った実装例の記事もあります。若干誤字があったため修正が必要でしたが、参考にすることでAuth0とのSSO確立ができました。
https://rovani.net/Shopify-Multipass/


その他言語についても、「言語名 Shopify Mutipass」と検索すると大体情報は出てくると思います。

### 1.「管理画面でマルチパスログインを有効にし、「シークレット」を取得」の補足

設定 > チェックアウトページにてシークレット取得するために、まずは顧客アカウントを無効化以外の設定にしておく必要があります。
![設定チェックアウトページにてシークレット取得.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154/3224fcd1-01da-48d9-0b7c-842d1ea98430.png)

また、生成されたシークレットは文字通り秘密情報です。＊画像のシークレットキーは検証環境で発番し、無効化済みです。
![設定チェックアウトページにてシークレット取得2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154/ff43abac-a6ac-e85e-d8d5-8dbef9fc97d2.png)


### 6.「トークンを取得したらShopifyストアにリダイレクト」の補足

トークンを取得しShopifyストアにリダイレクトする際、URLはこんな感じになります。
＊ブラウザのネットワークタブで見れます。
https://flagship.cc/account/login/multipass/U7gBLxKF_08BZ9ncqN7h9HuadaogWNJCBpK0iQw6fNQIAWMcyxETE_3RpdcqawTylNTHG7FNaUTU8R3G4d0BwV035s0XTYa1U7THsIaDvv3jfhNeJqPqsLYub1gF-3UzpjpbyCZjEU0NrE6zTQrenqP1bnYsCgwrdz8l-rTK4Yngl1gns1MGrl-lQY_mQsiODKzQk_ng92HJSdcJD8hZEUYk0mDrdRfyq2G_R4wLIIU_VS2RCLmGjCuOfaxfBWOiCRNWog9PJDdOhAPaiLw1u8CqgEvZ6PtSzYKdrq18ueAZgNtYdhalEVUQmVumijuYcw6kfkO6x27ddgNDPfrSwLITCSDj1aLRmCLIEdrTTXyIICivrQj-pVK8oxhQwShGOFtu2aBwOgPEw7EmzVY7BhXkFy2uo_7o70FKZr3KlF-P9AX0x27u3SjLOp9GHJg6eh-RxD1leJ5wcP3cfZGFrdP_IePS6SOsuVBFzcf6CKEAmYnA4GYQCE3WgNFr1zmlA9w24-s6VY8kYMKNv0UVPjxNlV3POkaelRK-tI46Fa6u7MdPozGUNs7l_nkirh5XxVMBkOnKRCOzUmkwfRcw2_yve2DGiEW24DHL8IT132-UFEbUXB-6u3p85oJWGUEoaz14SvGp8bYhCEeaeW7cC4hyUfmxtWTY16MvwgpB4FOnkcWuKLjXECn6EAjfrksBjb9gcxiBJRMFKlmf5d_lwamGga5FdOFCREy5sStekNCNYnijEahidlz3vl7aBxIPw08_GXrBqWovKobSD4uGNxCRIDATFA3IvZK1RKqX-TlCqB5xcvIF_t622zK_138jeRwdji4DADhwAnLejX5kCCqPkNXt3fJodHwkLkEFZ93-LRIjioIEZ44Fp8th9EkcbPYpupMHiUjid4SnGHTXljMQ3Mjm7CuUU-QHVvjPMyCmybPgnk_HP4BIrdCTKTWUGk-9ITAfcWx1vYnCsih4WYnHM-ieZ_b5H_9IsbwXGvKXIQKrDe2onTKbXInvf7UllWmrzxCFLE1UA-wvq67WTQ==


## 仕様について補足とベストプラクティス

### mutipass_identifierについて
Multipass APIを使ったビジネス・システム設計上一番重要と言っても過言ではないのが、mutipass_identifierです。
mutipass_identifierは、顧客オブジェクトの標準項目で、SSO基盤側のUIDを入れておける箱です。

顧客オブジェクト（JSON）のmultipass_identifier項目が埋まっている例

```
{
  "customer": {
    "id": 3691578818641,
    "email": "multipass@test.cc",
    "accepts_marketing": false,
    "created_at": "2020-12-01T00:34:42-05:00",
    "updated_at": "2020-12-01T00:34:44-05:00",
    "first_name": "",
    "last_name": "",
    "orders_count": 10,
    "state": "enabled",
    "total_spent": "0.00",
    "last_order_id": null,
    "note": null,
    "verified_email": true,
    "multipass_identifier": "Ufb124d27202f67e0932f8b7a0a80a090",
    "tax_exempt": false,
    "phone": null,
    "tags": "統合IDログイン",
    "last_order_name": null,
    "currency": "JPY",
    "addresses": [住所配列省略],
    "accepts_marketing_updated_at": "2020-12-01T00:34:42-05:00",
    "marketing_opt_in_level": null,
    "default_address": {デフォルト住所オブジェクト省略}
  }
}
```

ここ以外にUIDを入れる場合はMetafieldに入れる形になりますが、Metafieldの方がAPIでデータを取得するコストも高かったりとデメリットもあり、multipass_identifierに入れて済む場合はそうするのがベストです。


### Shopifyの日付フォーマット
Shopifyの日付フォーマットは稀にある例外を除き、ISO8601形式（`YYYY-MM-DDThh:mm:ss.sZ`）でリクエストが必要だったり、レスポンスに入っていたりします。
サンプル： `2020-12-02T16:30:00+09:00`

注意事項
- DDの後ろのTは文字列として固定で入る
- ssの後ろの.sはミリ秒の表現のために用いることがでいる（必須ではない）
- Zは+hh:mm/-hh:mm形式で表現できるタイムゾーンを表している

参考ドキュメント
https://www.w3.org/TR/NOTE-datetime

### セキュリティ周り
- https前提
- SSO基盤側で、メールアドレス認証なくアカウントが発行できる場合、EC側のアカウントをのっとれるためSSO基盤側のメールアドレス認証フローを要確認
- Multipassトークンは短い間しか有効でなく、一度しか使えないため、ユーザーのログインフロー以外のシナリオで生成すべきではない
- 顧客情報JSONオブジェクト内のプロパティ「remote_ip」は設定すべき。意図したブラウザ環境でしか有効でなくするためです。ただしVPN環境などによりリクエスト時と受け取り時のIPが一致せず、ときたまエラーになることも考えられますので、そのようなエラーが出る際は企業ネットワーク環境をご確認ください。
- 共通ID側でログアウトした際の対応。Shopify側は認証が保持されたままで問題ないか。

### その他
- SSO基盤からShopifyにデータを渡す際、タグ項目がある場合は既存のタグも上書きとなるので気をつけましょう。というか、使わないのが現実的ですね。
- SSO基盤からShopifyにデータを渡す際の項目は予め決まっております。定義されていないデータ項目の連携が必要な場合、独自にそういう仕組みを用意しましょう。
- MultipassログインでShopify側の顧客情報が生成された場合、パスワードはデフォルトではランダムで生成される。Shopify側のログインを使わない場合はこのままで問題ないが、Shopify側のログインもその後使う導線がある場合は、パスワード設定導線を設ける
- EC側に既にSSOログインしていないアカウントが存在する場合は、Update Customer APIを用いて顧客に multipass_identifier を設定し、対象ユーザーのその後のSSOログイン時には一貫して multipass_identifier を使う必要がある。


## デバッグについて
デバッグについては悩ましい側面はあり、Multipass APIはShopify管理のため、エラーログをエラー発生時にすぐ確認することができません。
下記のサンプルエラー画面末尾にグレーで `リクエストID` がありますが、デバッグしたい際はこのIDをShopify Plusサポートに投げて、細かいエラーの原因を教えていただくよう手配が必要です。
![MultipassAPIエラー画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154/f465b948-cf0a-5fe7-7191-a8931e0bbd83.png)

私が初回の実装時にハマった初歩的な問題としては、JSONオブジェクトが正しくJSON型になっていなかったケースがありました。。

## 宣伝：Shopify PlusでLINEログイン
Shopify PlusでLINEログインし、multipass_identifier項目にLINEのUIDを保存できるアプリを当社で提供しています。
Shopify Plus専用アプリはShopifyアプリストアで一般公開できませんが、提供可能ですのでご興味のある方はご連絡ください。

## 最後に：まずはPOC
Shopify PlusへのSSO導入は基本的には問題なくできるかと思いますが、プロジェクト途上でボトルネックやギャップが見つからないよう、まずはPOC（事前検証）をしましょう！