コンビニで買った育てるサラダをrasPiで撮ってKinesisに流してゴニョゴニョする 後の中編

さて、続きであります。

もういい加減タイトル変えたほうがわかりやすい気もしてますが、ゴニョゴニョの部分はおおよそ、

**cognitoで無駄にアカウント管理と認証をしつつ、コンテンツはS3に置きつつ、api gatewayを通したlambdaでkinesis video streamのエンドポイントを引っ張ってきて、それをvuejsなりで再生させる**

です。
[前回](https://qiita.com/ikegam1/items/a6a01d3c1475d06ed956)はcogniteの部分だけなんとなくやったので今日はlambdaの所でしょうか。

おおよそは・・

[AWS Cognito の認証情報を API Gateway + Lambda で受け取りたい](https://qiita.com/mach3/items/ba16a4e63f4038afd7e5)
[Cognito ユーザープールを単独で API GateWay と共に使う](https://blog.mori-soft.com/entry/2017/09/24/212913)

上記を参考にさせていただこうかと思います。
言語は迷うな。サンプルを生かすならnodejsがいいのかな。
でもboto3使えば簡単にできそうな気もするな。pythonにしよう。

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cognito-idp.html#CognitoIdentityProvider.Client.get_user

それから、実はvuejsは初めてつかってんだけど、vuejsの場合の$.ajaxみたいなのはこれかな。

https://jp.vuejs.org/v2/cookbook/using-axios-to-consume-apis.html

なんとなくイメージできました。

## lambdaとgatewayから

まずは参考サイトのように適当なHelloWorld作っておきます。
あ、でもせっかくだからskeltonみたいなのにしとこう。path決めとけばchaliceがよしなにしてくれるし。

``` app.py
from chalice import Chalice

import logging
import os
import json
import boto3
from botocore.vendored import requests

logging.basicConfig()
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

app = Chalice(app_name='auth-raspi-stream')
cognito = boto3.client('cognito-idp')
kvs = boto3.client('kinesisvideo')
streamname = os.environ.get('KVS_STREAM_NAME')
streamarn = os.environ.get('KVS_STREAM_ARN')

@app.route('/')
def index():
    return {'hello': 'world'}

@app.route('/get_kvs_endpoint', methods=['GET'], content_types=['application/json'])
def get_kvs_endpoint():
    event = app.current_request.json_body
    logger.debug(json.dumps(event))
    token = event['accessToken']

    try:
        response = cognite.get_user(
            AccessToken='string'
        )
        if 'Username' in response:
            raise Exception

        endpoint = get_data_endpoint(
            StreamName=streamname,
            StreamARN=streamarn,
            APIName='GET_HLS_STREAMING_SESSION_URL'
        )
        if 'DataEndpoint' in endpoint:
            return {status: -1, 'endpoint': endpoint['DataEndpoint']}
        else:
            raise Exception
    except:
        return {status: -1, 'endpoint': False}
```

だいたいこんなのを作っておきました。その前後に ```chalice new-project auth-raspi-stream``` と ``` chalice deploy ``` してます。

でlambdaと同時にapi-gatewayも作ってくれたのでそこの設定から。

・・お、ちょっと待って。
chaliceにauthrizerってのがあるの？

https://github.com/aws/chalice/blob/master/docs/source/topics/authorizers.rst

こんな感じにして試してみよう

``` app.py
from chalice import Chalice,CognitoUserPoolAuthorizer

import logging
import os
import json
import boto3
from botocore.vendored import requests

logging.basicConfig()
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

app = Chalice(app_name='auth-raspi-stream')
cognito = boto3.client('cognito-idp')
kvs = boto3.client('kinesisvideo')
streamname = os.environ.get('KVS_STREAM_NAME')
streamarn = os.environ.get('KVS_STREAM_ARN')
cognitopoolarn = os.environ.get('COGNITO_POOL_ARN')
cognitopoolname = os.environ.get('COGNITO_POOL_NAME')
streamarn = os.environ.get('KVS_STREAM_ARN')
authorizer = CognitoUserPoolAuthorizer(
    cognitopoolname, provider_arns=[cognitopoolarn])

@app.route('/', methods=['GET'], authorizer=authorizer)
def index():
    return {'hello': 'world'}

~~(略

```

(これとは別にconfig.jsonも追記とかしてます)
そしてdeploy...

![19.png](https://qiita-image-store.s3.amazonaws.com/0/308709/9d3eb3b7-f2a6-2556-587f-2d4e99a10af9.png)

な、

な、

なんと！

ちなみにこのURLを直叩きすると ```{"message":"Unauthorized"}``` ってかえってくるのでフィルターは機能はしているもよう。
これは素敵すぎる。/get_kvs_endpointの方にもauthrizer付けちゃおう。
でもその前にfrontからapi叩くところをやっておく。

## api 叩いてみる

```
npm install --save-dev axios
```

```import axios from 'axios';``` をsrc/main.jsに追記

で、[このsample](https://github.com/aws-samples/aws-amplify-vue)のHome.vueを少し書き換えてみる

``` src/components/Home.vue
<script>
import axios from 'axios';

export default {
  name: 'Home',
  data () {
    return {
      info: null
    }
  },
  mounted: () => {
    axios
      .get('https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api/')
      .then(response => (this.info = response))
  }
}
</script>
```

を足してみた。
それでページ見てみたら、

```
Access to XMLHttpRequest at 'https://xxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/api/' from origin 'xxxxxxxx' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
createError.js?16d0:16 Uncaught (in promise) Error: Network Error
    at createError (createError.js?16d0:16)
    at XMLHttpRequest.handleError (xhr.js?ec6c:87)
```

みたいなエラーは出てたけどまあなんか動いてそうなきがする。
でもなんかこれちょっとめんどいやつかもな。awsじゃないサーバー(gcp)上で動かしてみてたからやろか。と思ったけど、そんなめんどくもないかもしれん。

[Amazon API Gateway をクロスオリジンで呼び出す (CORS)](https://dev.classmethod.jp/cloud/aws/amazon-api-gateway-cors/)

いや、こっちか。

http://docs.aws.amazon.com/ja_jp/apigateway/latest/developerguide/how-to-cors.html

って、apigatewayもlambdaもallow-origin返すようにしたのにまだアカン。
そもそも認証の問題かな。
レスポンスヘッダに```x-amzn-errortype: UnauthorizedException``` とかあるし。実は401はかえってきてるんよね。もうちょっとchaliseのauthorizerのところ読み込まんとあかんか。

あ、普通にvuejsの方でAuthorizationヘッダ設定してないだけか。

amplifyのこれでaccessTokenとかとれるみたいなので、、

```js
import { Auth } from 'aws-amplify';

Auth.currentSession()
    .then(data => console.log(data))
    .catch(err => console.log(err));
```

んー。sessionとってAuthorizationヘッダに入れるようにしてみてるけど403が返ってきてうまくいかないなー。

・・でですねー、結局ここからかなり詰まる。
まずAmplifyにAPIクラスがあるのを発見し、これを使おうとする。

https://aws-amplify.github.io/docs/js/api

認証後にこれのAPI.getをたたくとOPTIONヘッダのとこはクリアしたのでCORSは大丈夫っぽかったんだけど、そのあとのGETリクエストでUnauthorizeが返ってくる。。
見本通りにやってるんだけどなー。
ただ、Authorizationヘッダの中身が
```AWS4-HMAC-SHA256 Credential=XXXXXXXXXXXXXXX/us-west-2/execute-api/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-security-token, Signature=XXXXXXXXXXXXXXXXXXXX```
みたいになってて、なんか思ってたトークンと違うなぁと。

結局、こんな感じでsessionからjwtTokenを引っこ抜き、マニュアルでheaderにセットしてやるというなんか微妙な方法となりました。

https://stackoverflow.com/questions/50645158/aws-amplify-including-the-cognito-authorization-header-in-the-request

まあ、いけたはいけた

![20.png](https://qiita-image-store.s3.amazonaws.com/0/308709/e8c96f6b-dec4-3fbc-2f1a-f9804ddbd2cc.png)

このendpointのURLがlambda関数が返したやつ

ちなみに主だったとこは今のとここんな感じです。
ちなみにkinesis video streamのget_data_endpointでARNとName両方指定してたらどっちかにしろって怒られた。

``` app.py
@app.route('/', methods=['GET'], content_types=['application/json'], authorizer=authorizer, cors=True)
def get_kvs_endpoint():
    endpoint = False
    try:
        endpoint = kvs.get_data_endpoint(
            StreamARN=streamarn,
            APIName='GET_HLS_STREAMING_SESSION_URL'
        )
        logger.debug(str(endpoint))
        if 'DataEndpoint' in endpoint:
            res = {"statusCode" : 200,
               "body" : endpoint["DataEndpoint"],
               "headers" : {"Content-Type" : "application/json", "Access-Control-Allow-Origin": "'*'"}}
            return res
        else:
            raise 
```

```js:HelloWorld.vue
  methods:{
    signIn: function () {
      Auth.signIn(this.userInfo.username, this.userInfo.password)
      .then((data) => {
        this.message_text = 'ログインしました';
        this.status = 'こんにちは、'+data.username+'さん';
      }).catch((err) => {
        this.message_text = 'ログインできませんでした';
      });
    },
    play: async function() {
      const self = this
      const session = await Auth.currentSession()
      //const token = await Auth.currentSession().idToken.jwtToken
      console.log(await session.idToken)
      Auth.currentSession()
        .then(session => session.idToken.jwtToken)
        .then(token => API.get('RaspiStream', '/',{"headers":{"Authorization":token}}))
        .then(response => (self.info = response.body))
        .catch(err => console.log(err));
    },
  }
```

いつまでもHelloWorld.vueじゃアカンな。。
さて、あとは再生だけだー。いよいよ次回で終わりの予定。

しかし、作業しながら流れで書いてるので基本、間違ってもそのまま突き進み、足跡も残していくスタンスです。あとで振り返ってまとめるとか苦手。というかあんまり見る人にやさしくしてない。