AppStream2.0 ストリーミングURLにアクセスするとエラーになる→User IDが原因かも?

2020.05.25

AppStream 2.0 Stackから「Create Streaming URL」をクリックして、いよいよアプリケーションのストリーミングだ!生成されたURLにアクセスしてストリーミング開始するぞ!

あれ、500エラー…なんで!?

試してみてほしいこと

「Create Streaming URL」時にUser IDを指定しますが、これをシンプルな文字列、例えば「hoge」などにしてみてエラーが起きるか試してみてください。 これでストリーミングが成功したのであれば、最初の500エラーはUser IDの値に拠るものと思われます。

User IDには最大文字数と使用可能文字種の仕様がある

前述の「Create Streaming URL」処理の裏で実行されているAPIは以下のCreateStreamingURLです。

こちらの Request Syntax欄に以下記載があります。

UserId
The identifier of the user.

Type: String

Length Constraints: Minimum length of 2. Maximum length of 32.

Pattern: [\w+=,.@-]*

Required: Yes
  • 文字数については、2文字以上32文字以下
  • 文字種については、[\w+=,.@-]*つまり以下の文字種から構成される必要がある
    • アルファベット(大文字小文字どちらも)
    • 数字
    • アンダースコア(_)
    • プラス(+)
    • イコール(=)
    • カンマ(,)
    • ピリオド(.)
    • アットマーク(@)
    • ハイフン(-)

このいずれかに抵触して500エラーになったのだと思います。 エラーになるのはURLを生成した際(CreateStreamingURLAPI実行時)ではなく、生成されたURLにアクセスした際です。

この仕様を守れない場合の解決案

私がこの仕様に出くわしたのは、以下の「Amazon AppStream 2.0 を使用して SaaS ポータルを作成する」Getting Startedをやっていた際です。

このガイドでは、Cognito User Poolのユーザーのメールアドレスを、AppStreamのUser IDに指定してストリーミングURLを生成しています。

// API GatewayのCognitoオーソライザーからusernameを取得(usernameがメールアドレスになっています)
    const username = event.requestContext.authorizer.claims['cognito:username'];

    //
    // 省略
    //

    // パラメータを作成
    // UserIdに先程のusernameを代入
    var params = {
        FleetName: '<Fleet-Name>', /* required */
        StackName: '<Stack-Name>', /* required */
        UserId: username,
        Validity: 5
    };
    
    //
    // 省略
    //

    // そのパラメーターをAppStreamのcreateStreamingURLに渡す
    var request = appstream.createStreamingURL(params);

ここのステップ3の中に全体のコードがあります

一意なユーザーの情報としてメールアドレスを使うのは一般的だと思います。が、メールアドレス文字数が32文字以下であるという保障は無いですよね。どうすればメールアドレスをUser ID値として使いつつ前述のエラーを回避できるでしょうか。

例えば、メールアドレスをハッシュ化してみてはどうでしょう。md5でハッシュ化すると(元が33文字以上の文字列でも)32文字の一意な文字列になります。これをUser IDに使います。

+ const crypto = require("crypto");

    // API GatewayのCognitoオーソライザーからusernameを取得(usernameがメールアドレスになっています)
    const username = event.requestContext.authorizer.claims['cognito:username'];

+   // ハッシュ化
+   const hashedUsername = crypto
+      .createHash("md5")
+      .update(username)
+      .digest("hex");

    //
    // 省略
    //

    // パラメータを作成
-   // UserIdに先程のusernameを代入
+   // UserIdに先程のhashedUsernameを代入
    var params = {
        FleetName: '<Fleet-Name>', /* required */
        StackName: '<Stack-Name>', /* required */
-       UserId: username,
+       UserId: hashedUsername,
        Validity: 5
    };
    
    //
    // 省略
    //

    // そのパラメーターをAppStreamのcreateStreamingURLに渡す
    var request = appstream.createStreamingURL(params);

このコードに修正して先程のエラーになったユーザーで再度試してみると… 無事ストリーミングURLへのアクセスが成功しました!