*この記事は、Classiqのバックエンド(Dockerコンテナなど)を構築する際、認証トークンCLASSIQ_TOKENの取得方法が分からず、つまづいてしまった Macユーザー向けの記事です。
ただし、Windowsユーザーでも問題の本質はまったく同じです。
この記事で登場するMacの「キーチェーンアクセス」を、Windowsの「資格情報マネージャー」(Credential Manager)と読み替えれば、隠されたトークンを見つけるためのヒントになるはずです。
はじめに
こんにちは!
量子コンピュータ、最近話題になっていますね。
量子開発言語 / プラットフォームとしては、IBM社のQiskitや、Xanadu社のPennyLane、NVIDIA社のCUDA-Qなどが有名かと思いますが、Classiq社様の*Qmod を使うと、数式ベースの機能的な記述で複雑な量子回路を自動合成できて非常に便利です。
実際に著者はQiskitやCUDA-Qは使ったことがありますが、Qmodの方が手作業でゲートを実装する手間が大きく省けるので、とてもありがたいなと感じております。
*参考 - Qmodのご紹介:https://ja.classiq.io/insights/introducing-qmod-streamlining-quantum-algorithm-design

今回やりたかったこと
仕事でQmodを使って実装をしていると、ある日こういった願望が出てきました。
「これ(Qmod)をバックエンドにして、フロントエンドから動的に、そして柔軟に量子回路を生成して量子コンピュータを実行できるWebアプリを作ったら最高なのでは??」
そう思い立った私は、あくまで個人利用の範疇で、FastAPIとDockerを使って、Qmodで書いたプログラムをAPI化することを思い立ちました。
問題発生:認証トークン、どこ?
しかし、すぐに認証という点で問題が発生しました。
Classiq社のQmodは有償の量子開発ツールであるため、認証が必要になります。
その仕方なのですが、ローカルPCで classiq.authenticate() を実行する必要があり、このコマンドを実行すると以下の図のようなブラウザが自動で開いてGUIから簡単に対話型で認証することができます。
ここで何が問題なのかと言うと、プログラムからのリクエストだけで動作するヘッドレスなバックエンドサーバー(グラフィカルな画面は持たないDockerコンテナ等) では、ブラウザ認証は使えないということです。
このような場合、通常は環境変数 CLASSIQ_TOKEN に静的なAPIキーを設定します。
...で、ClassiqのQmodだと、その CLASSIQ_TOKEN って、どこで発行するの?
まずは、Classiqのダッシュボード(Web UI)のアカウント設定に行き、探しました。
しかし、...それらしきものは見つかりません。
- 次に、公式ドキュメントを「API Key」「Token」等で検索しました。
...
classiq.authenticate()のことしか書かれていません。
「あれ?どこだ...」
デバッグ:ファイルを探すも見つからない
開発者なら誰でも、まずはこう考えるでしょう。
「classiq.authenticate() で認証したなら、その認証情報はローカルのどこかにファイルとして保存されているはずだ」
ステップ1:ローカルでの認証
バックエンドで動かす以前に、まずはローカルマシン(Mac)で認証を通さないと、キーチェーンに何も保存されません。
# Python3を起動 (※classiq がインストール済みなら、ここは飛ばしてください)
My-Mac ~ % python3
>>> import classiq
ModuleNotFoundError: No module named 'classiq'
# ライブラリをインストール
My-Mac ~ % pip3 install classiq
... (インストール成功) ...
# 再度Python3を起動し、認証を試みる
My-Mac ~ % python3
>>> import classiq
<stdin>:1: ClassiqDeprecationWarning: Python version 3.9 ...
>>> classiq.authenticate() # ここでClassiqの認証用ブラウザが立ち上がる
Your user code: 〇〇〇〇-〇〇〇〇
...
# ブラウザで認証を「完了」させ、ターミナル側で待機が完了するのを待つ
>>> exit() # 終了
ここでの注意点として、classiq.authenticate()は、ブラウザで認証が完了すると、ターミナル側の待機が解除され、新しい >>> が表示されます。それを見てから必ず exit() するようにしてください。
ステップ2:第1のつまづき - ファイルが存在しない
よし、認証は成功した!ということで次に進んでいきます。
定番の場所は ~/.classiq/token.json のような隠しフォルダです。
さっそくターミナルで探します。
# 定番の .classiq フォルダを開く
My-Mac ~ % open ~/.classiq
The file /Users/MyUser/.classiq does not exist.
...あれ?
# ファイルが作成されないのはおかしいので、隠しファイルを含めて全表示
My-Mac ~ % ls -la ~
... (大量のファイル一覧) ...
# この一覧に .classiq や .classiq-credentials らしきフォルダはどこにもない...
ファイルが存在しない。
ここでつまづきました。
サーバー側は「認証成功」と認識しているのに、ローカルにはファイルがない。
しかし、Pythonで認証を試みると、奇妙なことが起こります。
>>> import classiq
>>> classiq.authenticate()
UserWarning: Device is already registered.
...
「デバイスは既に登録されています」...だと...?
サーバー側は「登録済み」と認識しているのに、ローカルにはファイルがない。
発見:トークンは「ファイル」ではなく「キーチェーン」にいた
なぜこんな奇妙なことが起きるのか?
それは、ClassiqのSDKが(良い意味で)お節介で、OSのセキュリティ機能に適応していたからです。
SDKの内部を調べてみると、認証トークンを管理する方法として、主に2つのクラスが用意されていました。
-
FilePasswordManager
トークンを~/.classiq-credentialsのような平文ファイルに保存する方式。 -
KeyringPasswordManager
トークンをOSのセキュアな認証情報マネージャに保存する方式。
そして、SDKはMac OSやWindowsでは、自動的に後者(KeyringPasswordManager)を選択します。
つまり、私のトークンは ~/.classiq フォルダ等ではなく、Mac OSの「キーチェーンアクセス」(Windowsなら「資格情報マネージャー」→ 「Windows資格情報」)に保存されていたのです!
これはセキュリティ的には非常に正しい挙動です(トークンを平文でディスクに置かないので)。
しかし、Dockerコンテナにトークンを移植したい開発者にとっては、見つけにくいトラップでした。
解決手順:リフレッシュトークンを取得する
バックエンドサーバーに必要なのは、短時間で切れる「アクセストークン」ではなく、長期間使える「リフレッシュトークン」です。
(※リフレッシュトークンは「年間パスポート」のようなもので、SDKがこれを使って新しい「1日券(アクセストークン)」を自動で取得し続けてくれます)
以下の手順で、そのリフレッシュトークンをキーチェーンからコピーします。
ステップ1:キーチェーンアクセスを起動
Macであれば、Spotlight検索(Cmd + Space)で、「キーチェーンアクセス」と入力し、アプリを起動します。
Winであれば、下のバーから「資格情報マネージャー」と入力し、アプリを起動します。
ステップ2: classiqRefershTokenAccount を検索
これが最重要ステップです。
Macであれば、キーチェーンアクセスアプリの右上の検索バーに、以下の正確な名前を入力します。
classiqRefershTokenAccount
(ちなみに、SDKの内部をデバッグして KeyringPasswordManager._SERVICE_NAME が classiqTokenService で、リフレッシュトークン用のアカウント名が classiqRefershTokenAccount であることを突き止めました。この経緯は補足で...)
Winであれば、「資格情報マネージャー」を開けたら、「Windows資格情報」タブを選択します。
そうすると同じように、classiqRefershTokenAccountがみつかります。
ステップ3:パスワードをコピー
検索すると、直前の図のように、classiqRefershTokenAccount という名前の「アプリケーションパスワード」が見つかるはずです。
- これをダブルクリックして詳細を開きます。
- ウィンドウ下部の 「パスワードを表示」 にチェックを入れます。
- Macのログインパスワードを求められるので、入力します。
- パスワード欄に、長い文字列が表示されます。
おめでとうございます! それがあなたの CLASSIQ_TOKEN です! (Winも同じ)
コンテナを起動する
コピーした「リフレッシュトークン」を、docker run コマンドの環境変数 CLASSIQ_TOKEN に設定します。
以下は実行例です。
docker run -p 8000:8000 \
-e CLASSIQ_TOKEN="[ここにキーチェーンからコピーした長いトークンを貼り付け]"
-d your-quantum-backend-image
※私自身はまだ試したことはないですが、近いうちに試してみます。おそらく理論上はこのようにして動かせるはず...!
まとめ
今回の記事の内容は、認証周りに詳しい人でしたらつまづかないポイントかもしれませんが、Classiqの認証が特殊で困っているMac / Windowsユーザーの助けになれば幸いです!
今回の教訓:「認証情報が見つからない時は、OSのセキュアな認証基盤(キーチェーン/資格情報マネージャー)を疑え」
補足:デバッグの(ターミナル全履歴)
「どうやって KeyringPasswordManager や classiqRefershTokenAccount の場所を知ることができたか?」
という興味のある方向けに、格闘のログを残しておきます。
設定ファイルの確認 (初回)
まず、設定ファイルが存在しないことを確認します。
# ホームディレクトリにあるはずの .classiq ファイルを開こうとする
My-Mac ~ % open ~/.classiq
# "does not exist" (存在しない) とエラーが返る
The file /Users/MyUser/.classiq does not exist.
Python環境の確認 (インストール前)
次に、Python環境で classiq ライブラリがインストールされているか確認します。
# Python3 インタプリタを起動
My-Mac ~ % python3
# classiq ライブラリをインポートしようとする
>>> import classiq
# "No module named 'classiq'" (モジュールがない) とエラーになる
ModuleNotFoundError: No module named 'classiq'
classiq ライブラリのインストール
ライブラリがなかったので、pip3 を使ってインストールします。
# (Pythonインタプリタは exit() した前提)
$ pip3 install classiq
... (インストール成功のメッセージ) ...
classiq の認証実行
インストール後、再度Pythonを起動し、認証(authenticate)を実行します。
# 再度 Python3 インタプリタを起動
My-Mac ~ % python3
# classiq ライブラリをインポート (今度は成功する)
>>> import classiq
<stdin>:1: ClassiqDeprecationWarning: Python version 3.9 ...
# 認証を実行
>>> classiq.authenticate()
Your user code: 〇〇〇〇-〇〇〇〇
# (ここでブラウザが開き、認証が成功する)
# Pythonを終了
>>> exit()
設定ファイルの確認 (認証後)
認証が成功したので、設定ファイルが作成されたか再度確認します。
# 認証後に .classiq ファイルを開こうとする
My-Mac ~ % open ~/.classiq
# "does not exist" (存在しない) と、またエラーが返る
The file /Users/MyUser/.classiq does not exist.
隠しファイルを含めた全ファイル表示
ファイルが作成されないのはおかしいので、隠しファイル (. から始まるファイル) も含めてホームディレクトリをすべて表示します。
# 隠しファイル(-a)を詳細表示(-l)
My-Mac ~ % ls -la ~
... (大量のファイル一覧が表示される) ...
# (この結果に .classiq や .classiq-credentials 等のそれらしきファイルは存在せず)
SDK内部構造の調査 (TokenManager)
ファイルがないのに認証は記憶されているため、SDKがファイル以外の場所(キーチェーンなど)に情報を保存していると推測し、内部構造を調査します。
# 調査のため Python3 インタプリタを起動
My-Mac ~ % python3
>>> import classiq
# SDKの内部モジュールである token_manager を直接インポート
>>> import classiq._internals.authentication.token_manager as token_manager
# token_manager が持つ属性やメソッドを一覧表示
>>> print(dir(token_manager))
['Auth0', ..., 'TokenManager', 'pm', ...]
# (一覧の中に 'pm' という怪しい名前を発見)
pm (Password Manager) の調査
Password Manager(の略と推測)の中身をさらに調査します。
# (Pythonインタプリタは継続中)
# 'pm' の中身を一覧表示
>>> print(dir(token_manager.pm))
[..., 'FilePasswordManager', 'KeyringPasswordManager', ...]
# ★ 発見!★
# (ファイル型とキーリング型の2種類があることが判明)
# (Mac/Windowsでは KeyringPasswordManager が使われていると推測)
FilePasswordManager の使用パス調査 (念のため)
もしファイル型が使われていた場合、どのパスに保存されるのかを(念のため)確認します。
# (Pythonインタプリタは継続中)
# FilePasswordManager が持つ属性を一覧表示
>>> print(dir(token_manager.pm.FilePasswordManager))
['_CLASSIQ_CREDENTIALS_FILE_PATH', ..., 'access_token', 'refresh_token']
# 保存に使われるファイルパスを表示させる
>>> print(token_manager.pm.FilePasswordManager._CLASSIQ_CREDENTIALS_FILE_PATH)
/Users/MyUser/.classiq-credentials
# (このパスもls -la で存在しないことを確認済み)
KeyringPasswordManager のサービス名特定 (本命)
Macの「キーチェーンアクセス」で検索するために、KeyringPasswordManager が使用する「サービス名」を特定します。
# (Pythonインタプリタは継続中)
# KeyringPasswordManager が持つ属性を一覧表示
>>> print(dir(token_manager.pm.KeyringPasswordManager))
['_SERVICE_NAME', ..., 'access_token', 'refresh_token']
# キーチェーン検索に使うサービス名を表示させる
>>> print(token_manager.pm.KeyringPasswordManager._SERVICE_NAME)
classiqTokenService
この classiqTokenService をMacの「キーチェーンアクセス」で検索します。
すると、先ほどのclassiqTokenAccount (アクセストークン用) と
classiqRefershTokenAccount (リフレッシュトークン用) が見つかります。
サーバーに必要なのが後者というわけです!



