まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

タグ:appid

最近関わったプロジェクトの中で「ウェブアプリケーションにユーザー管理機能/ログイン機能を付与する」ことを検討する機会がありました。これ自体は特別珍しいことではないと思っています。


こういったユーザー管理機能を実装しようとした場合、例えばログイン単体機能だけを考えればいい場合はデータベースに users テーブルを用意して、ログイン画面を作って認証、、とすればすぐに実現できそうです。が、現実的にはユーザー登録していない人向けにオンラインサインアップ機能を付けたり(指定されたメールアドレスにメールを送信して、そのメール内のリンクからユーザー登録の続きを行う、ということになるのでメール送信機能が必要になります)、パスワード変更を望むユーザーのための画面を作ったり、パスワードを忘れてしまった人のためのリセット機能を作ったり(これもメール送信が必要になりますね)、、それほど単純な機能追加だけにとどまらないことになります。加えて後述の「開発者を信用していいか?」という問題にも関わることになります。詳しくは以下でも紹介しますが、セキュリティ面まで考慮するとユーザー管理機能は「自前で用意すべきではない」機能の1つだと思っています。

というわけでアプリケーションにユーザー管理機能/ログイン機能を付与する場合の選択肢としては「専用サービスを使う(専用サービスの認証機能を自分のアプリケーション内に組み込む)」のが現実的ではないかと思っています。上述に挙げたような面倒そうな機能が一通り準備された状態で使うことができ、OAuth 2.0 をベースとしたセキュリティ要件もかなり高く設定できます。具体的には Auth0IBM AppIDAmazon CognitoGoogle Firebase などが候補になると思っています(以後、これらのサービスのことを ID as a Services という意味で "IDaaS" と呼ぶことにします)。

一方で、ログイン画面はそのアプリケーションの一部となるので、当然のように「ログイン画面をアプリケーションと(統一された CSS やヘッダー、フッダーという意味で)同じデザインにしたい」という要求もでてきます。各種 IDaaS には初めから用意されたログイン画面というものもありますが、その画面をそのまま使うのではなく、カスタマイズされたログイン画面からログインしつつ、API を使って認証機能そのものは IDaaS のものを使う、というものです。具体的な実現手順に関しては組み込み先のアプリケーションで使われているフレームワークや IDaaS によって異なりますが、OAuth 2.0 を使った SDK や API によって実現そのものは可能なものばかりです。自分も以前に IBM AppID を使ってログイン画面のカスタマイズを紹介したこともありました。


さて本ブログエントリのテーマとなる問題はここからです。IDaaS を使って自分のアプリケーションにログイン機能を組み込んだり、その画面をカスタマイズしたりすること自体は可能そうに思えるのですが、ここに「セキュリティ」という新たな要素を含めて考えてみます。一言でセキュリティと言ってもその観点はいくつもあるので「どういった観点で考えた時にどういうケースを心配する必要があるか?」を分けて考える必要があります。要は単純に1つの答にたどり着くのが難しく、ケース・バイ・ケースで考えないといけないような結構面倒な話なのでした。作成しているアプリケーションの特性や公開範囲、優先事項とその順位も考慮した上で「何をどこまで妥協できるか?」という観点でも考える必要があると思っています。以下、その優先事項の例を挙げつつ、何を優先するとどういった影響が起こりうるか、という視点をかいつまんで紹介していきます。なお、以下の説明では IBM AppID を使った場合で説明しますが、他の IDaaS を使っている場合も同様と考えてください。


【フロントエンドフレームワーク】
まずはクラウドのメリットを活かしやすい、と言われるフロントエンドフレームワーク(React, Angular, Vue など)を使ってウェブアプリケーションを開発しているか? という観点を考えてみます。このフロントエンドフレームワークを採用することでサーバーサイドにアプリケーションサーバーが不要になり(HTTP サーバーでよくなり)、負荷軽減やセキュリティ脆弱の可能性が下がるというメリットがあります。

一方でフロントエンドに全てのリソースを用意する必要があるため「アプリケーションを構成している要素(HTML や JavaScript)が全てウェブブラウザ経由で見えてしまう」という制約があります。IDaaS を使った認証ではこの制約が問題になるケースを考慮する必要があります。

より具体的な内容を説明します。たとえば通常の(フロントエンドフレームワークを使わない)サーバーサイドアプリケーションで AppID を画面カスタマイズ無しで認証機能として使う場合(サーバーサイド SDK を使う場合)、AppID から以下の情報を取得する必要があります:
  • リージョン(IDaaS インスタンスのあるロケーション)
  • テナントID(IDaaS インスタンスを特定する ID)
  • クライアントID(アプリケーションを特定するID)
  • シークレット
  • コールバック URL

実装手段にもよりますが、サーバーサイドサイドアプリケーションではこれらの情報を外部に開示する必要はなく、認証に関わる処理を全てサーバー側で行うように設計することでこれらの情報を外部から参照できないように作ることができます。重要性にランクを付けるつもりはありませんが、特にこの中でもシークレットと呼ばれる情報は(パスワードに相当するもので)機密性が高く、特定の人だけが知りうるように運用する必要があるものです。

一方でフロントエンドフレームワークを使ったアプリケーションから AppID の認証機能を使う場合(フロントエンド向け SDK を使う場合)、AppID から以下の情報を取得する必要があります:
  • クライアントID(アプリケーションを特定するID)
  • エンドポイント URL

サーバーサイドの時と比べて必要なパラメータ数は少なくなります。これは認証に用いるプロトコルが異なるためです(OAuth 2.0 コールバックと、OAuth 2.0 PKCE の違い)。ただフロントエンドフレームワークの場合はサーバーサイドの場合と異なり、これらのパラメータを全て JavaScript 内に記載する必要がある、という点が異なります。実際のプログラミング時ではこれらのパラメータを全て環境変数化してコードとは分離するのが一般的ですが、フロンドエンドアプリケーションとしてビルドする際に同環境変数も取り込まれ、静的なファイルとなった中にはプレーンテキストで含まれることになります(つまりプレーンテキストで外部公開されます)。

「とはいえサーバーサイドの時と違ってシークレットが不要だから(つまりシークレットは公開されないから)いいのでは?」と考えることもできます(実際、そのような考えでこの仕組みが提供されています)。たしかに最も機密性の高いシークレットが開示されるわけではない、と考えることはできますが、一方で「OAuth 2.0 PKCE ではこれらの情報だけで SDK を利用することができる」わけです。つまり「このアプリケーションの認証機能を実装する上で必要になる情報全て」が公開されている、とも言えます。

これはちょっと怖い事実といえます。現実的にはベンダー側(AppID 側)の設定でログイン URL を指定したりすることで情報の不正利用を防くことができるのですが、(詳しくは書きませんが)悪さをする側でも更に対策の対策のようなことができたりして、これだけでも好ましくなかったりします。なお、この件は後述の画面カスタマイズの時にも関わってくる、ということを先に記載しておきます。

ここで言いたかったのは「フロントエンドフレームワークには運用上の大きなメリットはあるが、認証セキュリティの件では通常のウェブアプリケーションよりも低くなってしまう」ということです。「低いからダメ」とまでは言いませんが、どういうことが起こり得て、それを正しく理解した上でそれは許容できるものかどうかを判断する必要があるもの、ということになります。


【認証画面のカスタマイズ】
上述の「フロントエンドフレームワークを採用するか否か」とは別に、認証画面を(ベンダー提供の標準画面を使うのではなく)自分達でカスタマイズしたものを使いたいかどうか、という観点を考えてみます。他の条件が変わらないのであれば、当然ログイン画面も(ベンダーから提供されるものではなく)アプリケーションテーマに合うようなものにカスタマイズしたくなると思っています。

ただこのカスタマイズには技術的なものとは異なる大きな壁があります。ログイン画面を開発するということは、入力したユーザー ID やパスワードをサーバーに送って、、、という処理も伴うわけですが、この処理部分を担当するアプリケーション開発者を信用してよいのか? という壁です。

「え?でもログイン画面を実装するなら当然じゃないの?」と考える人もいると思うので補足しておきます。上でも書いた OAuth 2.0 のコールバックや PKCE といったプロトコルを使い、画面カスタマイズなしで(ベンダーが用意したログイン画面を使って)認証する場合、利用者が入力したユーザー ID やパスワードをサーバーに送る、という処理は認証サービス側で用意することになります。つまりアプリケーション開発者からは手が出せない処理ということになります。この方法であれば利用者が入力した ID やパスワードが正しいかどうかを判断することも、正しかった場合に正しかったという結果を含めて次の処理に移ったり(リダイレクトしたり)、またオンラインサインアップやパスワードリセット時の処理に関しても同様にベンダーが用意済みの処理をそのまま利用することになるので、アプリケーション開発者からは手を出したくても出せない処理部分ということになります。したがってアプリケーション開発者は悪いことをしない、という前提に立つ必要もないのでした(IDaaS ベンダー側を信用する必要はありますけど、それはカスタマイズする場合も同様です)。


これがログイン画面を自分達で用意する場合、確かにユーザーインターフェース的にはアプリケーションに合ったものが用意できるというメリットがある一方、アプリケーション開発者が(例えば正しいことが分かった ID とパスワードを記録する、といった)処理を行う余地が残ってしまいます。これが「アプリケーション開発者を信用してよいか?」という壁になるのです。 上述した「ユーザー管理機能は自前で用意するべきではない」といったのはこのような背景があってのものでした。性善説に基づいて考えれば、そんな悪い開発者ばかりではないでしょうし、社内の者がそんなことをするメリットもない、と考えることはできます。ただ「ではそういう設計でよいか?」というのは別の問題であって、アプリケーションのセキュリティ設計の責任を持つ立場の人が考えることになると思っています(繰り返しますが「絶対ダメ」ではないのです。アプリケーションの特性やメリット・デメリット・優先順位を考慮した上で最終的には個別に判断するべきものだと思っています)


少しややこしい話をします。この「開発者を信用する/しない」という問題は、上で紹介した「フロントエンドフレームワークを使った場合、同じ認証機能を別のアプリで実装するために必要な情報が全てインターネット上に公開されてしまう」という問題と絡めて考えるべきとも思われます。「開発者を信用できるならよい」だけかもしれなかった話が、「信用できない開発者が作ったかもしれないログイン画面」を許すかどうかという問題も併せて考える必要が出てきてしまうのでした。


なお IDaaS を使わずに認証機能を自前で用意する場合に関しては選択肢がなく、「開発者を信用しないといけない」ことになります。試験的なアプリケーションであればこれが大きな問題とはならないと考えることもできますが、コンシューマー向けに公開して個人情報を集めるようなアプリケーションの場合、その設計は考え物だと思っています。


ここまでに紹介したようにアプリケーションの認証は「フロントエンドフレームワークを採用するか」、「ログイン画面のカスタマイズをするか」という2つの軸だけで考えても結構複雑な問題となります。現実は更にアプリケーションの特性(ユーザーは誰なのか? どの程度の負荷が想定され、どの程度の安定性が求められるか? 運用予算はどの程度か? など)も絡めて考慮する必要があるので、明確な基準を作るのが難しく、どうしても個別判断が多くなってしまうと思われます。


【フロントエンドフレームワークで IDaaS を使って画面をカスタマイズする場合】
では(ある程度のリスクを承知の上で)「フロントエンドフレームワークを使って開発するウェブアプリで、IDaaS を使ってユーザー管理を行うようなケースで、ログイン画面もカスタマイズしたい」という決定がなされた場合(作らないといけない立場になった場合)、どのような点に気を付けて実装すべきでしょうか。

※アプリをゼロから作るケースで、もし自分に設計の決定権があった場合は、おそらくこのような決定はしないと思う、ということは最初に言っておきます。わざわざフロントエンドでは作らない、たぶん。。


まず上で書いたような理由があるため、ログイン画面自体はサーバーサイドアプリケーションとして(フロントエンドフレームワークのアプリケーションとは別に)用意するべきです。そして フロントエンド → ログイン画面 → 認証 → ログイン画面で結果を取得 → フロントエンドに結果を返す  といった(リダイレクトによる)連携を行う設計にします:
img01


ここで特に気を付けるべきは最後の「ログイン画面で結果を取得 → フロントエンドに結果を返す」部分です。フロントエンドは HTTP GET リクエストしか受け付けることができないので、リダイレクト時に何か情報を渡そうとすると GET リクエストのパラメータで渡すしかありません。そのパラメータのフォーマットが推測可能な形になっていると認証を回避できてしまう可能性もあります:
img02


一例としてはこのような方法が考えられます。フロントエンドからログイン画面にリダイレクトする前に乱数を使ってランダムな値を生成し、その値を保持した上で、そのランダムな値をパラメータにログイン画面へリダイレクトします(ログイン画面でも同じ値を保持させます)。その上で IDaaS に対して認証を行い、ログインが成功した場合はログイン成功を示す情報(ユーザーIDなど)を保持していたランダム値で暗号化した上で、結果の暗号化文字列を URL パラメータに含めてフロントエンドに戻ります。最後にフロントエンドは自分が保持していたランダム値を使ってパラメータを復号化してユーザー情報を取り出す、といった具合です。この方法でもフロントエンド側で値を保持する必要があるので、そこで localStorage や sessionStorage などが必要になり、使わない場合と比べてセキュリティリスクが高くなってしまうのですが、一方でこの方法であればログイン認証を回避して(認証結果だけを偽造して)ログインしたかのように見せかける手法は使えず、また URL の一部にパラメータが表示されることがあってもその内容は暗号化されているので、ぱっと見ではその内容が推測できない、という要件はクリアできます。この方法で充分なセキュリティ要件を満たしているかどうかはアプリケーション次第の所もあり、個別に判断する必要があります。が、「この方法でも大丈夫」と判断されるケースであれば、このようなやり方もある、という例として紹介しました:
img03


【サンプルコード】
こういうケースで実装しなければならなくなった場合の1つのケースとして紹介しました。IDaaS やフロントエンドフレームワークの種類によって実装の実現方法も異なるし、ログイン画面のアプリとして作るウェブアプリのパフォーマンスも変わってくるとは思うのですが、仮に IDaaS として IBM AppID を、フロントエンドフレームワークとして ReactJS を使う場合のサンプルソースコードを作って公開してみたので紹介しておきます。Auth0 などでも同様に使えると思います:
https://github.com/dotnsf/appid-spa-customlogin


まず "git clone" などで上記 URL からソースコードを取得します。中には "spa", "web", "spa-normal" という3つのフォルダがあり、それぞれがアプリケーションになっていますが、サンプルとして使うのは "spa" と "web" の2つだけです:
2023031901


なお、spa-normal/ は React から AppID をカスタマイズ無しで使う場合のサンプルとなっています。こちらをセットアップして使ってみるとわかるのですが、AppID のログイン画面をカスタマイズ無しで使うとこのような画面になります(後述のものと比較してください)。またカスタマイズ無しの場合、この画面は別ウィンドウで開きます(この挙動も変更できません):
2023031903


ではサンプルを動かしてみましょう。web/.env ファイルだけは実行前に情報を入力しておく必要があります。IBM AppID のインスタンスを作成し、ユーザーを登録し、webapp(ウェブアプリケーション)を登録した結果として得られる Key, Secret, Client_ID, Tenant_ID, Region といった情報を書き写します(SPA_URL はこのままで構いません)。また AppID に登録したアプリの Redirect URI に http://localhost:8080/appid/callback を登録しておいてください:
2023031902


そして2つのアプリを実行します。ターミナルを2つ用意してください。まずは web/ フォルダ側を実行します:
$ cd web
$ npm install
$ npm run start

8080 番ポートでアプリケーションが起動します。そのまま spa/ フォルダ側も(別のターミナルで)実行します:
$ cd spa
$ npm install
$ npm run start

こちらは 3000 番ポートでアプリケーションが実行し、ウェブブラウザが自動で起動します(しない場合は http://localhost:3000 にアクセスします)。以下のような React の画面が表示されれば成功です:

2023031901


この時点ではまだログインできていないので "Login" ボタンが表示されています。このボタンをクリックすると web/ のログイン画面に転送されます(標準の別ウィンドウではなく、同じウィンドウで以下の画面が開きます。上述のカスタマイズ前のログイン画面の UI とも違っていることが確認できます):
2023031902


AppID 標準ではない、カスタマイズされた(日本語の)ログイン画面に遷移しました(ここでは紹介しませんが、オンラインサインアップやパスワード忘れ画面も日本語のカスタマイズ画面を実現しています)。ここで AppID に登録したユーザーの ID とパスワードを入力してログインします:
2023031903


ログインに成功すると元のアドレスに戻ります。今度はログインできているのでログインしたユーザーの情報と "Logout" ボタンが表示されています:
2023031904


Logout ボタンを押すとログイン情報が消え、同じアドレスのまま最初の(ログイン前の)画面に戻ります:
2023031905


フロントエンドフレームワークのアプリで IDaaS を使い、カスタマイズされたログイン画面で認証する、という目的は達成できていると思います。詳しくはソースコード内を参照いただきたいのですが、上述のようなアルゴリズムを実装して、(比較的)セキュアな方法で情報をやり取りしつつ、ログイン画面は標準のものではなく、カスタマイズされた画面を使えています。

注意していただきたいのは、この実現方法は充分にセキュアとは言えない点を持っています。アプリケーション開発者が信用できて、2つのアプリケーション間で情報をやり取りする方法(独自に実装したチャレンジ機能)が「この方法でもよい」と判断された場合のサンプルである、と理解した上で参照していただきたいです。





2022 年最初のブログエントリとなります。遅ればせながら本年もよろしくお願いいたします。

今年最初のエントリは React や Angular といったフロントエンドフレームワークにまつわるネタです。最近流行りのこれらのフロントエンドフレームワークを使うことで、比較的簡単に SPA(Single Page Application) を作ることができます。SPA 化のメリット/デメリットを理解した上で作る必要があるとは思いますが、SPA 化することによる大きなメリットの1つに「Amazon S3 などのオブジェクトストレージや Github ページといった、安価かつ滅多にサービスダウンしない環境でフロントエンドのウェブホスティングができる」ことがあると思っています。

この点を少し補足しておきます。「自分が作って管理しているサービスやアプリケーションを安定運用したい」というのは誰でも思うことだと思っています。ただ現実にはこれは簡単なことではありません。サービスやアプリケーションは利用者が直接参照するフロントエンド部分に加えて、データベースなどのバックエンド部分、そしてこれらを繋ぐ API サーバーなどから成り立っていて、これら全てを安定運用するのは簡単なことではありません。特にフロントエンドや API サーバーについては最近よく耳にするコンテナオーケストレーションなどによって比較的安価に安定運用することもできるようになりました。ただフロントエンド部がアプリケーションサーバーを持たないシンプルな静的ウェブコンテンツであれば、上述したような Amazon S3 のウェブコンテンツ機能を使ったり、Github ページ機能を使うことで、滅多にサービスダウンしないウェブページとして公開するという方法もあります。現実問題として、この方法だとフロントエンドページの公開は簡単ですが、API サーバーなどのバックエンドとの通信時に CORS を考慮する必要が生じたりして、これはこれで面倒な設定も必要になるのですが、一方で「ほぼ無料」レベルの安価で利用できるウェブサーバーにしてはかなり安定しているのも事実で、用途によっては(「面倒な設定」の面倒レベルがあまり高くならないケースであれば)運用方法の考慮に含めてもいいのではないかと思っています。補足は以上。


一方、最近のウェブアプリケーション開発において IDaaS の利用ケースもよくあります(個人的にも業務で多く使っている印象です)。ログインや認証、ユーザー管理といった ID 周りの機能がマネージドサービスとして提供されていて、自分で面倒な開発をしなくても API や SDK から利用可能になる、というものです。具体的なサービスとしては AWS CognitoAuth0 などが有名どころでしょうか。IBM Cloud からも AppID という IDaaS が提供されています。

そしてフロントエンドアプリケーションと IDaaS の組み合わせというのが実は相性が悪かったりします(苦笑)。多くの IDaaS では OAuth と呼ばれるプロトコルでの認証/認可が主流となっているのですが、アプリケーションサーバーを持たない静的なフロントエンドアプリケーションでは OAuth のコールバックの仕組みとの相性が悪く、実装が非常に難しいという事情があります。その結果として、各 IDaaS ベンダーから認証専用の JavaScript SDK が提供され、それらを使って認証機能を実装することになります。IBM Cloud の AppID も専用の JavaScript SDK が用意され、React などで作ったフロントエンドに組み込むことで AppID へのログインや認証を実現できます(この SDK では PKCE と呼ばれる方法を使ってログインを実現しています)。

で、ここまでのアプリ開発方法に関する内容についてはこちらのページでも紹介されているのですが、問題はこの先にありました。この方法で作った React のフロントエンドアプリを Github ページにホスティングして運用しようとすると・・・結論としては少し特殊なリダイレクト URI の設定が必要でした。これを知らないと Github ページでの運用時にトラブルが起こりそうだと思ったので、将来の自分への備忘録の意味も含めてブログに設定内容を記載しておくことにしました。

前段部分の長いブログですが、といった背景の中で「IBM AppID の JavaScript SDK を使って作った SPA アプリを Github ページで動かす」ためのアプリ開発手順と設定内容について、順に紹介します。


【React で SPA アプリを作成する】
この部分は上述ページでも紹介されている内容ではありますが、一般的な内容でもあるので、特にコメントに色をつけずに紹介します。以下のコマンドで react-appid というフォルダに React のサンプルアプリを作成します:
$ npx create-react-app react-appid

$ cd react-appid

ここまでは普通の React アプリと同じです。ここから下で AppID の認証に対応したり、Github ページで運用する場合特有の設定を加えていきます。


【IBM AppID サービスインスタンスの準備を行う】
ここは上述ページでも触れられてはいるのですが、あまり詳しくは紹介されていないので、ここで改めて手順を紹介します。

IBM Cloud にログインして AppID サービスを作成後にインスタンスを開き、「アプリケーション」タブから「アプリケーションの追加」ボタンで対象アプリケーションを追加します。その際にアプリケーションのタイプを「単一ページ・アプリケーション」(SPA) として追加するようにしてください:
2022011301


追加したアプリケーションを選択して、内容を確認します。type の値が "singlepageapp" となっていることを確認してください。確認できたらこの中の "clientId" 値と "discoveryEndpoint" 値を後で使うことになるのでメモしておきます:
2022011303


まだ AppID にユーザーを登録していない場合はここでユーザーも登録しておきましょう。メニューの「クラウド・ディレクトリー」から「ユーザー」を選択し、「ユーザーの追加」からログイン可能なユーザー(の ID とパスワード)を登録しておきます:
2022011302


またリダイレクト URL を登録しておきましょう。「認証の管理」から「認証設定」を選択して、リダイレクト URL に http://localhost:3000/ を追加しておきます:
2022011303



IBM AppID 側で準備する内容は以上です。取得した情報を使ってアプリ開発を続けます。


【React アプリに IBM AppID モジュールを追加して、AppID のログイン機能を追加する】
ここは上述ページでも詳しく記載されている内容です。まずは IBM AppID を利用するためのライブラリを追加します:
$ npm install ibmcloud-appid-js

そしてソースコードの src/App.js をテキストエディタで開き、以下の内容に書き換えます(詳しい内容は上述ページを参照してください)。この時に太字で示している clientId の値と discoveryEndpoint の値には先程 AppID の画面で確認した clientId 値と discoveryEndpoint 値をコピペして指定してください:
import React from 'react';
import logo from './logo.svg';
import './App.css';
import AppID from 'ibmcloud-appid-js';
function App() {
  const appID = React.useMemo(() => {
    return new AppID()
  }, []);
  const [errorState, setErrorState] = React.useState(false);
  const [errorMessage, setErrorMessage] = React.useState('');
  (async () => {
    try {
      await appID.init({
        clientId: 'AppID の clientID の値',
        discoveryEndpoint: 'AppID の discoveryEndpoint の値'
      });
    } catch (e) {
      setErrorState(true);
      setErrorMessage(e.message);
    }
  })();
  const [welcomeDisplayState, setWelcomeDisplayState] = React.useState(false);
  const [loginButtonDisplayState, setLoginButtonDisplayState] = React.useState(true);
  const [userName, setUserName] = React.useState('');
  const loginAction = async () => {
    try {
      const tokens = await appID.signin();
      setErrorState(false);
      setLoginButtonDisplayState(false);
      setWelcomeDisplayState(true);
      setUserName(tokens.idTokenPayload.name);
    } catch (e) {
      setErrorState(true);
      setErrorMessage(e.message);
    }
  };
  return (
    <div  classname="App">
    <header  classname="App-header">
      <img  alt="logo" classname="App-logo" src="{logo}">
        {welcomeDisplayState && <div> Welcome {userName}! You are now authenticated.</div>}
        {loginButtonDisplayState && <button  onclick="{loginAction}" id="login" style="{{fontSize:">Login</button>}
        {errorState && <div  style="{{color:">{errorMessage}</div>}
    </header>
    </div>
  );
}
export default App;

この状態で一度動作確認します:
$ npm start

ウェブブラウザが起動して http://localhost:3000/ が開き、以下のような画面が表示されます:
2022011301


"Login" と書かれた箇所をクリックするとログイン画面がポップアップ表示されます。ここで AppID に登録済みのユーザー ID とパスワードを指定してサインインします:
2022011304


正しくログインできると先程の画面に戻り、登録したユーザーの氏名が表示とともに "You are now authenticated." と表示され、ログインが成功したことが明示されます:
2022011305


【React アプリをビルドして、SPA アプリでもログインできることを確認する】
今作成した React アプリをビルドして index.html にまとめた SPA アプリにしたあとでも AppID でログインできることを確認します。nginx などの HTTP サーバーがあればそれを使ってもいいのですが、ここでは Node.js のシンプルなウェブサーバーを使って動作確認します。以下のような内容で app.js を作成し、react-appid フォルダ直下(README.md などと同じ階層)に保存します:
var express = require( 'express' ),
    app = express();

app.use( express.static( __dirname + '/build' ) );

var port = process.env.PORT || 3000;
app.listen( port );
console.log( "server starting on " + port + " ..." );

↑ build/ フォルダをコンテキストルートにして 3000 番ポートで HTTP リクエストを待ち受けるだけのシンプルなコードです。

app.js が準備できたら、まずは一度 React コードをビルドして SPA アプリ化します:
$ npm run build

ビルドが完了すると React アプリが SPA 化されて圧縮されて build/ フォルダにまとめられますので、これを app.js を使って起動します:
$ node app

改めてウェブブラウザで http://localhost:3000/ にアクセスして同じアプリが起動していることを確認し、Login から AppID アカウントでログインできることを確認します。アプリケーション・サーバーを持たない SPA アプリでも IBM AppID を使って認証できることが確認できました:
2022011305


【React アプリをビルドした SPA アプリを Github ページでも動くように調整する】
ここまでできれば後は build/ フォルダを Github ページで公開するだけで動くんじゃないか? と思うかもしれませんが、実は期待通りに動くようになるまではいくつかの落とし穴があります。1つずつ解いていきましょう。

(1)絶対パス→相対パスへの書き換え

React の SPA アプリをただビルドしただけだと、コンテキストルート直下で動く想定のアプリになってしまいます。どういうことかというと、例えば http://localhost:3000/ とか https://xxx.xxxxxxx.com/ といったサブディレクトリを持たない GET / というリクエストに対して動くアプリになります(要はビルドで作成される index.html 内で参照される外部 CSS や JavaScript は "/" ではじまる絶対パスになっています)。一方、Github ページで動かす際は https://dotnsf.github.io/react-appid/ のようなサブディレクトリ上で動くことになります。ここがコンフリクトになってしまい、絶対パスで指定された CSS や JavaScript のロードエラーが発生してしまうのでした。これを回避するために index.html 内の該当箇所を絶対パス指定から相対パス指定に変更する必要があるのでした。

具体的にはこの下の(3)までの作業後に改めて $ npm run build を実行してから、build/index.html の以下の <head> 部分で頭に . を付けて、絶対パス指定から相対パス指定に書き換えてください:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<link rel="icon" href="./favicon.ico"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="theme-color" content="#000000"/>
<meta name="description" content="Web site created using create-react-app"/>
<link rel="apple-touch-icon" href="./logo192.png"/>
<link rel="manifest" href="./manifest.json"/>
<title>React App</title>
<script defer="defer" src="./static/js/main.85d3d620.js"></script>
<link href="./static/css/main.073c9b0a.css" rel="stylesheet">
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>



(2)回転する画像の URL を絶対指定

(1)と似た内容ですが、SPA アプリ内で表示される画像も絶対パスのままだと正しく表示されません。ただこちらは JavaScript 内で {logo} と指定されている画像なので単純に絶対パスを相対パスにすればよいという対処が使えないのでした。 まあアプリケーションとしては必須ではないので単純に画像ごと削除してしまってもいいのですが、残したい場合は Git リポジトリ上の画像 URL を直接指定する、などの方法で対処する必要があります。よくわからない人は以下の例でそのまま書き換えてください:
    :
  return (
    <div className='App'>
    <header className='App-header'>
      <img src="https://raw.githubusercontent.com/dotnsf/react-appid/main/src/logo.svg" className='App-logo' alt='logo' />
        {welcomeDisplayState && <div> Welcome {userName}! You are now authenticated.</div>}
        {loginButtonDisplayState && <button style={{fontSize: '24px', backgroundColor: 'skyblue', 
          border: 'none', cursor: 'pointer'}} id='login' onClick={loginAction}>Login</button>}
        {errorState && <div style={{color: 'red'}}>{errorMessage}</div>}
    </header>
    </div>
  );
    :


(3)デフォルトの .gitignore を変更

$ npx create-react-app コマンドで作成した React プロジェクトにははじめから .gitignore ファイルが含まれています。が、この .gitignore では /build フォルダを除外するよう記述されています。今回は build/ フォルダを Github ページで運用することが目的なので、/build フォルダが除外されてしまっては意味がありません。.gitignore ファイルを編集して、/build フォルダを含めるよう(コメントアウトするなど)してください:
  :
  :
#/build
  :
  :

(1)でも紹介しましたが、ここまでの変更を行ったら再度 SPA アプリケーションをビルドし、その後に build/index.html ファイルに対して(1)の変更を行ってください。


(4)AppID のリダイレクト URL の設定が特殊

これまではアプリケーションを http://localhost:3000/ という開発時専用の URL で実行していたので、AppID のリダイレクト URL も http://localhost:3000 だけを登録して動作確認できました。では実際に Github ページでアプリケーションを動かす際にはどのようなリダイレクト URL を指定すればいいのでしょうか? 実はここがくせ者でした。

例えば私(Github ID は dotnsf )が react-appid という github リポジトリを作って、このリポジトリを Github ページとして公開して運用しようとすると、アプリケーションの URL は以下のようになります:
 https://dotnsf.github.io/react-appid

2022011306


ということは AppID に設定するリダイレクト URL にもこの値を指定するべき・・・だと思っていたのですが、なんとリポジトリ名部分が不要で、 https://dotnsf.github.io/ を指定するのが正しい設定内容のようでした:
2022011308


この URL がリダイレクト URL として設定されていれば AppID SDK が正しく動作して、認証も正しく実行できるようになりました(自分は実はここで結構つまずきました):
2022011307


(5)build フォルダを Github ページとして公開する方法
最後に作成したプロジェクトを Github に登録して、build フォルダを Github ページで公開する方法についてです。root フォルダや docs フォルダを Github ページで公開する場合は選択肢から選ぶだけなんですが、任意のフォルダを Github ページで公開するのは少しコツが必要です。

例えば react-appid というリポジトリを使う場合は、まず Github 上でこのリポジトリを公開設定で作成します。そして普通に main リポジトリに登録します:
$ git init

$ git add .

$ git commit -m 'first commit'

$ git branch -M main

$ git remote add origin https://github.com/dotnsf/react-appid.git

$ git push -u origin main

その後、build フォルダを Github ページで公開するには以下のコマンドを実行します:
$ git subtree push --prefix build origin gh-pages

これで該当フォルダが Github ページとして公開されます。実際に以下のページはこの設定を使って公開しています:

https://dotnsf.github.io/react-appid/

2022011306


ID: username1@test.com, パスワード: password1 でログインできるので、よければ試してみてください:
2022011307


React で作成した SPA アプリケーションの認証をどうするか、という問題を IBM AppID (と SDK)で解決する方法と、ビルドした SPA アプリを Github ページで運用する場合の特殊な設定やコマンドについて紹介しました。




IBM Cloud から提供されている IDaaS サービスである AppID を使っています。AWS の Cognito や Auth0 のようなユーザーログインの(オンラインサインアップやオンラインパスワード変更、多要素認証なども含めた)機能全般をまとめて提供するサービスです。IBM Cloud のライトアカウントを使って無料枠内で利用することも可能です。単なるログイン機能程度であれば自分で実装してもいいのですが、オンラインサインアップや多要素認証まで考慮すると実装は面倒だし、(実在しないアドレスを使うこともできますが)メールアドレス含めて管理するのは個人情報管理の観点からも、特に試験的/実験的な段階では可能であれば避けたいという運用側の事情もあるため、こういったマネージドサービスを使うメリットは大きいと考えています。事実、自分は公私でよく使っています。
2021082902


で、この AppID を使ってサービス開発をしている中で「ユーザーを無効化したい」という意見をいただきました。実験的な意味合いのあるサービスを作って、ある特定の期間だけはそのサービスを使ってもらい、その期間が完了した後は使わせたくない。でもユーザーを削除してしまうと、何かの際に再度ログインしてもらうこともできなくなったり、再作成しても ID のデータ紐付けが切れてしまってたまったデータとの関連が切れてしまうのでそれは都合が悪い。そのため「いったん無効化してログインできないようにしたい」という、運用上ごもっともな意見でした。

「ユーザーの無効化ね。まあ機能的に用意されてるんだろうな」と楽観視していたのですが、AppID のダッシュボードを調べてみると、どうも「ダッシュボード画面からはユーザーの削除はできるが無効化はできなそう」でした。おっと、これはまずいぞ。。。
2021082903


このユーザー無効化を実現するためのワークアラウンドとして考えたのが「パスワードの強制変更」でした。要は管理者の権限で該当ユーザーのパスワードを強制的に変更することで事実上ログインできなくしてしまう、という方法です。アカウントは残るのでデータやその紐付けも消えることはなく、再度初期パスワードに変更すれば、万が一の時にもう一度ログインしてもらうこともできるようになる、と考えました。

で、じゃあダッシュボードからどのようにパスワードを強制変更するのかというと・・・実はパスワードの強制変更もダッシュボードからできるわけではありません。2021年8月現在、AppID ダッシュボードからはユーザーを削除することはできますが、ユーザーを無効化することも、パスワードを強制変更することもできません。

ただ、AppID SDK にはパスワードを強制変更する API がありました(文字通りの「無効化」は SDK からでもできなさそう・・):
https://www.npmjs.com/package/ibmcloud-appid

2021082901
(↑このページ内の "Set User New Password" 欄参照)


つまり AppID SDK のこの setUserNewPassword 関数を使って自分でプログラムを作れば、ユーザーのパスワードを強制的に変更すること自体は実現できそうでした。それによって実質的な無効化も可能にできるのでは・・と考え、挑戦してみました。以下はその成果の共有です。


【サンプルソースコードの紹介】
AppID のユーザーパスワードを変更するサンプルの Node.js アプリケーションを作って、ソースコードを公開しました:
https://github.com/dotnsf/appid_changepassword

このリポジトリを git clone するかダウンロード&展開してください。

使い方は後述しますが、まずは依存ライブラリをまとめてインストールしておいてください(この手順はソースコードを用意した後に1回だけ行ってください):
$ cd appid_changepassword

$ npm install

このフォルダには4つの JavaScript コードファイルが含まれていますが、それぞれ以下のような役割です(create_users_to_appid.js とか、何気に便利なツールだと自負してます):
ファイル役割
app.jsApp ID の動作確認用ウェブアプリケーション。AppID にログイン&ログアウトするだけ
settings.js動作設定用ファイル
create_users_to_appid.jsCSV ファイルから AppID のユーザーをまとめて作成する CLI アプリ
change_password.jsユーザーIDと新パスワードを指定して、パスワードを強制変更する CLI アプリ


パスワードの変更処理を行うのは change_password.js ですが、app.js や create_users_to_appid.js も含めて、この3つのファイルを動かすためにはあらかじめ settings.js に AppID の接続情報を記載しておく必要があります。以下、一通りの使い方を紹介します。


【サンプルソースコードを動かすための事前設定項目】
このアプリを動かすには、何はともあれ App ID のインスタンスが必要です。IBM Cloud にログインしてダッシュボードから App ID を選択してリソースインスタンスを作成してください。その際に「ライト」プランを選択すると 1000 ユーザー&(一ヶ月)1000 ログインまで無料で利用することが可能です(ログインアカウントがライトアカウントの場合は、ライトプランしか選択できません):
2021082904


作成した AppID インスタンスの「サービス資格情報」から(必要であれば1つ作成した上で)サービス資格情報を選択して、資格情報の内容を確認します:
2021082905


資格情報の中は以下のような JSON フォーマットのテキストになっています:
{
  "apikey": "xxxxx",
  "clientId": "xxxxxxxxxxxxxxxxxxx",
  "secret": "xxxxxxxxxxxxxxxxxxx",
  "tenantId": "xxxxx",
  "profilesUrl": "https://jp-tok.appid.cloud.ibm.com",
    :
    :
}

この中の以下5つの情報が必要です:
・apikey
・secret
・clientId
・tenantId
・region(profilesUrl の "https://" と ".appid.cloud.ibm.com" の間の文字列(上例だと "jp-tok"))

これら5つの値を見つけたら、settings.js 内の該当する変数値として入力し、保存します:
//. IBM App ID
exports.region = '(region の値)';
exports.tenantId = '(tenantId の値)';
exports.apiKey = '(apikey の値)';
exports.secret = '(secret の値)';
exports.clientId = '(clientId の値)';

exports.redirectUri = 'http://localhost:8080/appid/callback';
exports.oauthServerUrl = 'https://' + exports.region + '.appid.cloud.ibm.com/oauth/v4/' + exports.tenantId;

準備の最後に App ID のコールバック URL を登録します。今回のアプリケーションはローカルホストで動かす想定をしていて、その場合はローカルホストで動かす際の URL を事前に AppID に登録しておく必要があります。

IBM Cloud ダッシュボードで作成した App ID を開き、「認証の管理」、「認証設定」を選択して、「Web リダイレクト URL の追加」から "http://localhost:8080/appid/callback" を追加します(ローカルホストで動かすのではなく、公開アドレスで動かす場合はその URL を使って "http(s)://ホスト名/appid/callback" を追加してください):
2021082906


これでアプリケーションを動かす準備ができました。


【サンプルソースコードを使ってユーザーを登録】
このブログエントリの目的は「AppID のユーザーパスワードを変更する方法の紹介」です。そのため実際にログインできるユーザーを使って動作確認する必要がありますが、まずはそのユーザーを登録する必要があり、そのためのツールを紹介します。 既に AppID にユーザーが登録されていて、そのユーザーのパスワードを変更してもいい場合、ここは無視しても構いません。

まずは登録するユーザーを以下のようなフォーマットの CSV ファイルで用意します(newusers.csv 参照):
ユーザー表示名,ユーザーID,ユーザーパスワード
ユーザー表示名,ユーザーID,ユーザーパスワード
    :
    :


1列目のユーザー表示名はログインしたユーザーをアプリケーション内で表示する際に表示される文字列です。AppID の場合、ここはなぜか「8文字以上」という制約があるので、8文字以上の表示名を指定します。

2列目のユーザーIDは「メールアドレス形式のログインID」です。App ID の設定によってはオンラインでパスワードリセットを行ったりすることも可能で、その場合のメール送信先にもなるものです(オンラインパスワードリセットを無効にして運用する場合は実在しないメールアドレスでも構いません)。

3列目のユーザーパスワードは、2列目の ID でログインするユーザーのログイン時に指定するパスワードです。 この3列を一組とした行を必要なユーザーぶんだけ用意します。 なおサンプルで newusers.csv というファイルが含まれていて、その内容は以下のような2行になっています(以下、このファイルを使って紹介しますが、自分でこのファイルに相当する内容のファイルを用意した場合は、そのファイル名に置き換えて読み進めてください):
User0001,user0001@mydomain.com,password1
User0002,user0002@mydomain.com,password2

ではこのファイルを使って AppID にユーザーを登録します。コマンドの最後に CSV ファイル名を指定して、以下のように実行します:
$ node create_users_to_appid newusers.csv

画面に色々出力された後にプロンプトに戻ります。成功していると CSV ファイル内で指定されたユーザーが AppID に登録されているはずです。ダッシュボードから「クラウド・ディレクトリー」、「ユーザー」を選択し、CSV ファイルで指定したユーザーが追加されていることを確認します:
2021082907


検証用のユーザーが AppID に登録できました。まずはこのユーザー ID(と CSV ファイルで指定したパスワード)でログインできるか一度確認しておきます。


【サンプルソースコードを使って登録したユーザーでログイン確認】
では今登録したユーザーが登録したパスワードでログインできるかどうかを確認するため、サンプルのウェブアプリケーションを起動します:
$ node app

App ID のメッセージが数行表示された後に "server starting on 8080 ..." と表示されれば起動完了です。ウェブブラウザを開いて http://localhost:8080/ にアクセスします。すると(ログイン前なので) AppID のログイン画面に転送され、以下のような画面が表示されます:
2021082901


この Email 欄と Password 欄にそれぞれユーザーのメールアドレスとパスワードを入力して "Sign in" ボタンをクリックします。先程 CSV ファイルで作成したユーザーのメールアドレスとパスワード(例えば user0001@mydomain.com と password1)を入力してみます:
2021082902


正しく入力されている状態で "Sign in" ボタンをクリックするとログイン処理が成功し、画面が遷移して以下のような画面に切り替わります。ユーザーの ID、表示名、メールアドレスが表示されています。アドレスはログイン前に指定したものと同じ http://localhost:8080/ ですが、ログインが完了しているのでログイン画面には転送されず、この画面が表示されています。"Logout" ボタンでログアウトできます:
2021082903


ログアウトするとログイン画面に戻ります。試しにもう1人のユーザー情報(ID: user0002@mydomain.com, パスワード: password2)でもログインしてみます:
2021082904


User0002 でもログインできました。CSV ファイルから作成したユーザーで正しくログインできる所まで確認できました:
2021082905


ここまで確認できたらいったんサンプルウェブアプリケーションを終了しておきます。"node app" を実行した画面で Ctrl+C を押してアプリケーションを強制終了させておきます。


【サンプルソースコードを使ってユーザーのパスワードを変更する】
さていよいよ本ブログエントリの本題となる「ユーザーパスワードの強制変更」の箇所です。change_password.js ファイルを使って以下のように実行します:
$ node change_password user0001@mydomain.com Password1

実行時に指定するパラメータは2つあります。1つ目がパスワードを変更したいユーザーのログインID(メールアドレス)、2つ目が変更後のパスワードです(つまり上の例だと user0001@mydomain.com ユーザーのパスワードを Password1 に変更します)。旧パスワードを指定せずに強制的に変更する、という点に注意してください。実行すると色々画面に表示されますが、これも処理が完了するとプロンプトに戻ります。

パスワード変更が正しく行われたかどうかを再度サンプルウェブアプリケーションで確認します:
$ node app

ウェブブラウザで https://localhost:8080/ にアクセスしてログイン画面に移動したら、最初は変更前のパスワード(password1)を指定して user0001@mydomain.com でログインを試みてください。先程はログインできましたが、今はパスワードが変更されているのでログインできないはずです(メールアドレスとパスワードが一致しない、という旨のエラーメッセージが表示されます):
2021082906


改めて変更後のパスワードを指定してログインすると、今度はログインが成功してログイン後の画面が表示されます:
2021082907


これで AppID SDK を使ってログインパスワードを強制的に変更することができることが確認できました。

ちなみにソースコード上でのこのパスワード変更処理箇所はこのようになっています:
var SelfServiceManager = require( 'ibmcloud-appid' ).SelfServiceManager;

  :
  :

selfServiceManager.setUserNewPassword( uuid, password, "en", null, null ).then( function( user ){
  resolve( user );
}).catch( function( err ){
  reject( err );
});

  :
  :


ibmcloud-appid ライブラリを呼び出して SelfServiceManager をインスタンス化し、(省略した部分で)アクセストークンを取得してメールアドレスで指定したユーザーの ID(上記コード上では uuid)を特定します。特定できたら selfServiceManager.setUserNewPassword( uuid, password, "en", null, null ); を実行してパスワードを変更しています(password が新しいパスワード)。3番目のパラメータは言語情報らしいですが現在使われていないらしく、既定値の "en" を指定しています。このあたり、詳しくは SDK の説明もご覧ください。


ともあれ、これで当初の目的である「App ID ユーザーの無効化」に近いオペレーションが実現できそうでした。めでたしめでたし。



IBM Cloud から提供されているユーザーディレクトリ(ログイン機能用サービス)である App ID で、デモ用途などでまとめて一括でユーザーを作成したいことはないでしょうか? 1件や2件程度であればサービスダッシュボードから直接作成してもいいのですが、10件とか100件とかになると面倒ですよね。

というわけで「指定した CSV ファイルに記載された情報を使って App ID のユーザーを API 経由で作成するツール」を作って公開してみました。Node.js 環境があれば実行可能です:
https://github.com/dotnsf/appid_users


上記リポジトリを git clone するかダウンロード&展開すると test.csv という CSV ファイルが見つかります。このフォーマットに従う形で、
 表示名,メールアドレス,パスワード
という順に値が1行ずつ記録された CSV ファイルを用意します(メールアドレス=ログインIDになります)。

次に settings.js ファイルを編集して、利用する App ID のサービス接続情報に書き換えます。

準備の最後に依存ライブラリをインストールします:
$ npm install


そして create_user.js を Node.js で実行すると、この CSV ファイルに記述されたユーザーをまとめて作成します(最後に対象 CSV ファイルを指定します):
$ node create_user test.csv

この作業後に App ID のコンソールからクラウド・ディレクトリーのユーザー一覧を確認すると、CSV ファイルで指定したユーザーが作成されていることを確認できます:
2021071001


作成後にログイン確認用のウェブアプリケーションを実行して、ユーザーが正しく作成されているかどうかを確認することができます。その場合はリダイレクト URL に http://localhost:8080/appid/callback を追加しておいてください:
2021071003


改めてアプリケーション起動後($ node app)に http://localhost:8080/ にアクセスし、CSV ファイルに含まれていた ID とパスワードを指定して、正しくログインできるかどうかを確認してください。ログインできるようになっていれば無事に CSV ファイルからユーザーをインポートすることができた、ことになります:
$ node app


2021071002



IBM Cloud から提供されているユーザーディレクトリ管理サービスである App ID の UI カスタマイズに挑戦しています。自分的にはまだ道半ばではありますが、途中経過という意味でいったん公開・紹介します。


【IBM Cloud AppID とは?】
IBM Cloud App ID サービス(以下「App ID」)はアプリケーションで利用するユーザーディレクトリを管理するサービスです。オンラインサインアップを含めたユーザー管理機能を持ち、アプリケーションにログイン機能を付加させたい場合に非常に簡単にログイン機能を実装できるようになります。IBM Cloud の(無料の)ライトアカウントを持っていれば(2要素認証など、一部機能が使えなかったり、1日の実行回数などに制限もありますが)無料のライトプランで利用することも可能です:
2021052900



【IBM Cloud AppID のカスタマイズに挑戦した背景】
App ID はユーザー管理機能が必要なアプリケーション開発を行う上で非常に便利な一方、UIのカスタマイズに関する情報が少なく(管理メニューに用意されているのは、ログイン画面内にロゴ画像を貼り付けることができる程度)、ほぼあらかじめ用意された(英語メインの)画面を利用する必要がありました。

機能的には満足なのですが、この UI カスタマイズがどの程度厄介なものなのかを調べる意味も含めて、AppID の UI カスタマイズに挑戦してみました。結論として 2021/06/02 のこのブログエントリ公開時点では 100% の実装ができているわけではないのですが、ログイン画面およびパスワードリセット画面のカスタマイズには成功しているため、ここでいったん公開することにしました。


【IBM Cloud AppID のカスタマイズサンプル】
AppID UI カスタマイズを使ったサンプルアプリケーションのソースコードはこちらで公開しています:
https://github.com/dotnsf/appid_fullcustom


サンプルアプリケーションを利用するには IBM Cloud にログインして、App ID サービスのインスタンスを作成し、サービス資格情報を作成・参照する必要があります:
2021060201


そして以下の値を確認します:
{
  "apikey": "*****",
  "appidServiceEndpoint": "https://us-south.appid.cloud.ibm.com",
  "clientId": "*****",
  "secret": "*****",
  "tenantId": "*****",
    :
    :
}

これらの値をソースコード内 settings.js のそれぞれの変数に代入して保存します:
//. IBM App ID
exports.region = 'us-south';
exports.tenantId = '*****';
exports.apiKey = '*****';
exports.secret = '*****';
exports.clientId = '*****';

exports.redirectUri = 'http://localhost:8080/appid/callback';
exports.oauthServerUrl = 'https://' + exports.region + '.appid.cloud.ibm.com/oauth/v4/' + exports.tenantId;


※exports.region の値は appidServiceEndpoint の値の "https://" と ".appid.cloud.ibm.com" の間の文字列です。それ以外はサービス資格情報にかかれている値をそのまま記載します。

また exports.redirectUri の値はアプリケーションの OAuth ログインのリダイレクト先 URL を記載します。このサンプルアプリケーションでは 8080 番ポートで待ち受けて /appid/callback にリダイレクトする想定で作られているのでこのような値になりますが、実際にパブリックなインターネットで利用する場合はこの値を実際の URL 値に書き換えてください。

準備の最後に AppID サービスのリダイレクト URI にここで指定した export.redirectUri の値と同じものを登録します。サービスの「認証の管理」メニューから「認証設定」タブを選択し、「Web リダイレクト URL の登録」欄に export.redirectUri に指定した値と同じものを追加してください。これで準備は完了です:
2021060202


このサンプルアプリケーションを実際に動かしてみましょう。まずは npm install して node app で起動します:
$ cd appid_fullcustom

$ npm install

$ node app

8080 番ポートで待ち受ける形で起動するので、ウェブブラウザで http://localhost:8080/ にアクセスします。正しく動作すると http://localhost:8080/login にリダイレクトされ、以下のようなシンプルなログイン画面になります(この画面はカスタムUIで作った画面です):
2021060203


AppID サービスに登録した ID とパスワードを入力して "Login" します:
2021060204


ログインに成功すると、そのユーザーの名前などが表示される画面になります。この画面で "logout" すると元のログイン画面に戻ります:
2021060205


ログイン画面下部に2つのリンクがあります。「オンラインサインアップ」と書かれたリンクをクリックすると、オンラインサインアップ用の画面に切り替わります(この画面もカスタム UI です):
2021060201


ここでメールアドレスなどを入力してサインアップ可能です:
2021060202


サインアップしてしばらく待つと、メールアドレスの有効性確認を行う必要があるため、指定したメールアドレスにメールが届きます(なお、自分の環境では「迷惑メール」扱いで届きました(苦笑))。メールを開いてリンクをクリックし、有効性確認処理をしてください:
2021060203


メールアドレスの有効性が確認されるとオンラインサインアップが完了したとみなされ、これ以降はメールアドレスとサインアップ時に指定したパスワードでログインできるようになります:
2021060203


ログイン画面下部のもう1つのリンク「パスワードを忘れた場合」はパスワードリセット用のリンクです:
2021060204


こちらをクリックするとパスワードを忘れてしまったメールアドレスを指定する画面(これもカスタムUIです)が表示されるので、ここにパスワードを忘れたアカウントのメールアドレスを指定します:
2021060205


すると指定したメールアドレスにパスワードリセット用の URL が書かれたメールが届きます。このメールを開いて、URL にアクセスします:
2021060206


すると新しいパスワードを入力する画面(この画面だけはカスタムUIではなく、あらかじめ用意されたものです)が表示されるので、新しいパスワードを入力します:
2021060207


これで新しいパスワードを使って再びログインが可能になります:
2021060203


念の為、カスタマイズ無しの場合の AppID の各種画面UIを以下に紹介しておきます。(Your Logo Here) と書かれた箇所にロゴ画像を貼り付けることは可能ですが、それ以外のカスタマイズをしようとすると今回紹介したサンプルのような方法を取る必要があります。その代わり、これらの画面を使う場合は非常に簡単に実装することができるものです。

(ログイン画面)
2021060301

(パスワード忘れ)
2021060302

(オンラインサインアップ)
2021060303



【まとめ】
以上、AppID を独自のカスタム UI で利用する手順を紹介しました。一連の手順の中でパスワードリセット時の新パスワードを指定する画面だけは元の(英語の)UIになってしまっていますが、それ以外はすべて独自の UI で実現できているのがおわかりいただけると思います。この残った箇所の対応は前後関係含めて対応する必要があってちと面倒そうな印象も持っているので、いったんこの状況で公開しました。細かな実装についてはソースコードを参照ください。

便利な AppID を好みの UI で使えるメリットは大きいと思っていますので、興味ある方の参考になれば。


このページのトップヘ