VisualStudioでWebApp開発中にCtrl+F5を押して素のChromeを起動させる

f:id:azechi_n:20171109150238p:plain
デバッグの開始、デバッグなしで開始
Visual Studio 2017でデバッガをアタッチせずにWebアプリケーションを実行するにはCtrl+F5(「デバッグの開始」のショートカットキー)を押す。すると、ブラウザが開いてアプリケーションのアドレスにアクセスする。何も設定していない状態だとWindowsの既定のブラウザが対象になる。既定のブラウザをchromeにしているので普段使っている状態のchromeが開く。

これで別に不都合はないのだけれど、「デバッグの開始」をした場合はchromeがブックマークとかプロファイルとか無いプレーンな状態で開く。どうせなら「デバッグなしで開始」のときにもプレーンなchromeが立ち上がって欲しい。

f:id:azechi_n:20171109150239p:plain
素のchrome

デバッグの開始」のときのchromeの起動オプションをchorome://versionで調べた。

f:id:azechi_n:20171109150941p:plain
chrome起動時のコマンドライン

--user-data-dirを指定して別のユーザーデータで起動すればいいらしい。フォルダが存在しない場合は起動時に作られる。

f:id:azechi_n:20171109150235p:plain
ブラウザーの選択

ブラウザーの選択」からブラウザーの追加ができるので--user-data-dirを引数にしたchromeを追加する。

AWS LambdaでAWSサービスのロングポーリングAPIを呼ぶときには使用するSDKの設定を確認するといい

既定の設定ではロングポーリングAPIでクライアントがタイムアウトしたうえに自動で再試行する。1回のAPI呼び出しでタイムアウトまで待機してさらに間隔を延しながら5回リトライするから数分に渡って制御が返ってこない状態になる。AWS Lambdaなどでは課金がだだ漏れになってしまう。

具体的には、boto3(Python)でAWS Step Functions(SFN)のGetActivityTaskを呼び出したら遭遇した。Step FunctionsのGetActivityTaskは呼び出すと最大で60秒間サーバーが接続を保持して待機(ロングポーリング)する。このAPIはサーバーの待機時間を指定できない。

各言語向けのAWS SDKは自動再試行を実装している。AWS SDK for Pythonのboto3にもbotocoreパッケージで自動再試行が実装されている。なお、AWS CLIもbotocoreパッケージを使用しているのでbotocoreの設定ファイルはboto3と共有する。

再試行の条件と回数、間隔は設定ファイルとしてbotocoreパッケージに含まれる。パッケージレベルの設定ファイルはbotocore/model/retry.json。パッケージレベルの設定はAWS_SDK_PATHにretry.jsonを置いて上書きすることができる。再試行設定はサービス、APIごとに設定できる。S3などには用途に合せた設定があらかじめ用意されている。Step Functionsについてはいまのところ用意されていないので既定の設定が適用される。既定の再試行設定はエクスポネンシャルバックオフ(指数関数的後退)アルゴリズム(失敗するごとに再試行の間隔が長くなる)で最大5回。

qiita.com

そもそも再試行が行なわれるのは、リクエストがタイムアウトで終了するから。ロングポーリングでサーバーが接続を保持したまま待機している間にクライアントの接続がタイムアウトで終了してReadTimeout例外が発生する。boto3の既定の設定は、ReadTimeout例外で再試行する。

クライアント接続のタイムアウト設定は2つ。connect_timeoutとread_timeout。connect_timeoutはサーバーに接続するまでの時間、read_timeoutは接続してから応答まで(応答完了までか?)の時間。どちらも秒単位で設定する。ロングポーリングAPIを呼び出すにはread_timeoutがサーバーの待機時間より大きくしなければならない。read_timeoutの既定は60。

qiita.com

Step FunctionsのGetActivityTaskのドキュメントによるとクライアントの接続タイムアウト設定は、サーバーの待機時間60秒に5秒以上足した65秒以上を設定することとある。

ちゃんと設定するなら、GetActiviTaskを呼ぶときはread_timeoutを65秒にして、ReadTimeoutの場合はリトライをしない、とするべきだろう。

へんなことをするとへんなとこではまる

Windows10。「ダウンロード」とか「ビデオ」とかのフォルダはShell Folderというらしい。 Shell Folderの場所を他のShell Folderと同じフォルダに重ねて指定するとなぜかエクスプローラーからアイコンが消えていく。

「デスクトップ」以外のフォルダを一つにまとめていた。ところが、PowerShellでProfile.ps1ファイルがExecutionPolicyをRemoteSignedにしても実行できない。Shell Folderのどれかがセキュリティ的な特別扱いをされてるっぽい。

重ねたShell Folderは、Explorerのプロパティで「場所」を指定しても、重なったすべてのShell Folderが移動してしまう。レジストリーを直接書き換える必要がある。

従来のASP.NETアプリケーションの認証チケットを外部のアプリケーションで復号する

これでできる。 AspNetCore.LegacyAuthCookieCompat

従来のASP.NETでは認証クッキーには認証チケットSystem.Web.Security.FormsAuthenticationTicketシリアライズして暗号化したものが入っている。復号は、System.Web.Security.FormsAuthenticationクラスのDecryptメソッドでする。

System.Web.Security.FormsAuthenticationSystem.Web.dllにある。 System.Web.dllは、.NET Frameworkにあり、.NET Coreには含まれない。AWS Lambdaなどの.NET Frameworkが無い環境では使えない。

自分に必要な部分だけを小さくまとめてみた。 複合化とデシリアライズするだけなので、チケットのsliding expirationなどには関知しない。

  • 方式が固定のときIVのサイズも固定なのだったら不要なコードがあるけどまだよくわからなかった
  • Read7BitEncodedIntのところがなぜそうなっているのかわからなかった
  • 切り貼りして少し体裁を変えたコードをPublic Gistとして公開してよいのかが不安

今日のデスクトップ

既存のアプリケーションから認証システムを切り離したい

まずは、パスワード管理をやめることと、SMSでの電話番号確認ができるようにする。

Amazon Cognito User Poolsを使う。最初の段階では、サーバーで Admin APIを使って既存の処理を置き換えていく。Admin APIには、AWSの認証情報が必要。

  • ユーザーの追加 : AdminCreateUser
  • サインイン : AdminInitiateAuth

User Pools について

Alias
  • サインインするときにUSERNAMEパラメーターに指定できる
  • 有効なaliasの調べ方
    • [email | phone_number]_verified=true
    • preferred_username の属性が有る
  • aliasとして使用する属性はuniqueでなくていいが、verified=trueのユーザーは1人だけ
    • ただし、preferred_usernameにはverified属性がないので、aliasにするならuniqueである必要がある
  • AdminCreateUserで既にあるユーザーと同じemailphone_numberを指定した場合
    • ForceAliasCreationがfalseまたは指定なし
      • UsernameExistsException
    • ForceAliasCreateがtrueの場合
      • すでにaliasとして有効だったユーザーの[email | phone_number]_verifiedがfalseになる
Verification
  • ユーザーの属性 email_verifiedphone_number_verified で管理されている。
  • [email | phone_number]_verified=trueのときのみがverified
  • admin APIでないとverified属性は書き込みできない
  • SetUserSettingでは書き込み許可がないのでunauthorizedエラー
verificationフロー
  • GetUserAttributeVerificationCode -> VerifyUserAttribute
  • 成功するとユーザー属性 email_verified=trueが設定される
  • 既にverified=trueであってもverificationフローは実行できる
    • 失敗してもtrueのまま
ユーザーの状態

docs.aws.amazon.com このページにある図がとてもわりやすい。

サインアップ
  • ユーザーによるユーザー登録 (サインアップ)
    • AWSの認証情報を持たなくてもユーザー登録ができる
      • clientIdだけで実行できる
        • client secretを発行していればsecretHashも必要になる
          • 登録されたユーザーを承認する方法
            • 管理者の承認
              • AdminConfirmSignUp
                • {Username, UserPoolId}
                • 確認コードなしでユーザーを確認する
                • “Confirms user registration as an admin without using a confirmation code”
                • ユーザーSign-upで"Registerd"のユーザーを"Confirmed"にする
            • SMS、EmailでのVerification
              • Varificationが選択されている場合、ユーザーがサインアップすると確認コードが自動的に送信さる

ユーザーを追加する

AdminCreateUser

必須の属性を指定しなくてもユーザーを作成できる。初回サインインのときに指定すればいい。 Verificationは自動では実行されない。

初期パスワードをInvitaion Messageでのみユーザーに知らせるのであれば[email | phone_number]_verified=trueと設定してしまえばいい。 Verification済みかどうかは、サインインしたときに属性[email | phone_number]_verified=trueで判断する。サインインしたあとであればいつでもVerificationフローを実行できる。

追加されたユーザーはForceChangePassword状態。初回ログイン時の認証チャレンジで新しいパスワードと足りない必須属性を与えるとConfirmedになる

管理者によるユーザー追加の場合は、verificationではなくてInvitation MessageをEmailまたはSMSで送信する。内容は、usernamepassword。テンプレートにpreferred_usernameは指定できなかった。

引数
  • DesiredDelivaryMediums
    • “EMAIL” | “SMS” | “EMAIL, SMS”
    • Invitationメッセージを送信する
  • ForceAliasCreation
    • このパラメータが無視されない条件
      • このリクエストで追加するユーザーの属性にaliasが含まれている
        • かつ phone_numberまたはemailの確認がtrue
          • aliasが既に別のユーザーの別名として存在する
    • trueの場合
      • 前のユーザーのaliasが新しく作成されたユーザーに移行される
      • 前のユーザーはそのaliasを使用してログインできなくなる
        • 前のユーザーはverified=falseが設定される
    • falseの場合
      • AliasExistsException
  • MessageAction
    • RESEND | SUPRESS | 指定しない
    • RESENDは既存ユーザーにInvitationを再送したいとき
      • 有効期限がリセットされる
  • UserAttributes
    • [email | phone_number]_verified=trueと設定できる
      • trueにしてもCognitoの動作は変らない。アプリケーションが値を見て判断する
  • ValidationData
    • Lambdaトリガーを使う場合に使用する

その他

  • SMSメッセージを送信する場合、Amazon SNSの使用制限の引き上げをリクエストする必要がある

  • client secret

    • SecretHash
    • これは送信元のAppを特定または限定するためのものか?
    • AccessTokenも開発者アカウントも必要ないAPI
      • ConfirmForgotPassword
      • ConfirmSignUp
        • Confirms registration of a user and handles the existing alias from a previous user.
        • {clientId, ConfirmationCode, ForceAliasCreation, Username, [SecretHash]}
        • SignUpのときに送信されたverification Code を引数にして、registrationをconfirmする
      • ForgotPassword
      • ResendConfirmationCode
      • SignUp
  • SRP

    • RFC2945
    • パスワードを送信するAPIで必要になる
    • “セキュアなバックエンドサーバー"ではSRPを使わずにパスワードをプレーンテキストとして送信できる
  • MFAとverification
    • MFAはユーザーがログインするときに、SMSで受け取った認証コードで確認をする
    • verificationはユーザー属性として指定された値が使用可能であるかを確認する
    • メッセージの種類
      • SMS
        • MFA認証
          • 認証コード 3分間
        • Phone Number Varification
          • 確認コード 24時間
        • invitation
          • Username, Password
      • EMail
        • EMail Verification
          • 確認コード 24時間
        • invitation
          • Username, Password
  • バイスの追跡
    • AdminInitiateAuthではサポートされていない
      • APIリファレンスには記載があるけどフォーラムに書いてあった
    • バイスをCognitoが記憶し、ユーザープール内のアプリケーションユーザーを関連付ける
      • なお、MFAを設定したときに記憶済みデバイスへのコード送信を止めることができる
    • ログイン->access_token -> confirmDevice -> DeviceKey
    • サインインしたデバイスには初回にデバイスキーとシークレットが割り当てられる
  • id_tokenとaccess_token
    • access_tokenはuser poolのユーザーのコンテキストでユーザー自身のプロパティ変更などに使う
      • SetUserSetting APIとか
  • 電話番号は頭に+81を付けて最初の0を除く
  • preferred_username
    • require, aliasの両方を選択することはできない
      • email、phone_numberはそれぞれ属性としてverifiedの値を持っている
      • aliasはverifiedでなければならない
      • requireはユーザーのステータスをconfirmにするために必要
      • preferred_usernameはverified属性を持たないので、値が設定されたことが即ちverified
      • confirmする前にverifiedできないから
    • aliasにした場合
      • updateUserAttributesを使用してverification
      • ログインに使用するIDだから、変更するときにはverificationが必要になる
    • If preferred_username is selected as an alias, the value can be provided only when an account is confirmed and it cannot be provided during registration.
      • aliasの場合は、registrationのときに設定できない?
        • adminCreateUserのときではなく、ユーザーによるsign-upのときの場合
          • adminCrateUserでは設定できる