サンタさんへQR鍵プレゼント (⋈◍＞◡＜◍)。✧♡

この記事は「[コラボスタイル Advent Calendar 2024](https://adventar.org/calendars/10411)」の8日目の記事です（遅刻組）
今年は技術のほか、ワークスタイルにもフィーチャーしたアドベントになっています。

[その2もあります](https://adventar.org/calendars/10412)

## 導入部

年に一度、サンタさんがおうちにやってきて**煙突から入り**、プレゼントを置いて行って・・・と。　残念ながら、我が家には煙突が無いお住まいなので**玄関からやってくるしか**ありません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934/980e32cf-0f02-9bd4-2ce6-84fc27ae1db8.png)
**NO ENTOTSU！**

合鍵を作って渡すわけにもいきませんし、、、どうしたものか。
なにせ、**合鍵を落とされ**ては困りますし、**ずっと使われる**のも困ります。

そうえば、うちは扉に後付けするタイプの電子鍵「セサミ スマートロック（SESAME）」を玄関に取り付けてます。

https://jp.candyhouse.co/products/sesame5

### ⚡⚡闇に電流走る

一定の期間だけ有効な「**仮鍵（チケット）**」なるものを渡して、それで一時的に自動解錠できるような仕組みを作れば・・・夜中にプレゼントを置いてもらえそうですね！

とはいえ鍵を無限生成されるのは困るので、ここはサンタさんに申請してもらい証跡を残しましょう。


## 作る！

ということで（？）、ワークフローを通して承認されたら利用できる「鍵作り」と、その鍵を実際に利用して「解錠できる仕組み」を作っていきます。

## 仮鍵（チケット）の仕様

まずは最も重要な「仮鍵」について要求事項をまとめておきます。
これ以降はチケットとしていきます。

- 手軽に使えること
	- 利用者（サンタさん）は専用アプリが無くても使えること
	- 気軽に鍵の破棄ができること
- 特定の時間だけ有効な鍵であること
	- 予定の日時より早くも使えないし、期限が過ぎた後にも使えない
- セキュリティが強固であること
	- 鍵を偽造されて勝手に期限を変更できないこと
	- 誰の鍵か分かること

ざっとこんな感じでしょうか。


### 技術選定

これらを実現する具体的な技術選定と、大まかな採用理由です。

#### 鍵のデータ構造に JWT / JWS

- 項目名やその役割、時間的な期限・値形式が標準化されているので応用しやすい
- 改竄検出は「HMAC-SHA256」を採用し、秘密鍵を各システムで管理する
- よく使われているライブラリを存分に活用できる

#### 鍵の保存・伝達に QR コード

最終的にメールで見える画像に出来ること、何より認識精度・速度の面で安心できること。

#### QR コードリーダー

QRコードを読み取る物理部分は、おうちに転がってるおもちゃ「M5StickC Plus2」と「QRコードスキャナーユニット」を使います。

https://www.switch-science.com/products/9350

https://www.switch-science.com/products/9508


#### ドアの施錠・解錠は SESAME 5

公式のクラウド API が小規模なら無料でも使え、リファレンスもしっかりあるのが安心できます。

何より、**爆速でドア鍵が応答する**のが魅力ですが、さらに解錠履歴に任意のメモを残せるのも超便利です。今回のように「いつ誰が解錠したか」と細かく記録と閲覧ができます。

#### 利用の申し込み管理・証跡の記録にコラボフロー

メタい話ですが、アドベントカレンダーの主役です。
いつ、だれに発行したのかは体系的に記録したいので、Slack等のチャットではなく、ワークフローとしてくみ上げます。
Webhook を細かな設定でイベント分けができ、申請データや複雑な判定者の情報も入ってるので応用が広がります。

https://www.collabo-style.co.jp/


## アーキテクチャ

発行システム：コラボフローの Webhook で段階検知とチケット発行が完了するまでの部分

```plantuml
@startuml

サンタ -> コラボフロー: 申請する

コラボフロー -> 僕:
僕 -> コラボフロー: 承認する

コラボフロー -> "API Gateway": Webhook通知
"API Gateway" -> "チケットQR発行\nLambda"

"チケットQR発行\nLambda" -> コラボフロー: QR画像ファイル送信
コラボフロー -> "チケットQR発行\nLambda": ファイルID

"チケットQR発行\nLambda" -> コラボフロー: 申請書を判定 + ファイルID

コラボフロー -> サンタ: チケット発行のお知らせ\nQRコード画像
@enduml
```

解錠システム：チケットの読み取りから、改竄・有効期限チェック、解錠まで

```plantuml
@startuml
サンタ -> QRスキャナー: QRコード提示

QRスキャナー -> "M5StickC Plus2": QRコード (JWTチケット)
"M5StickC Plus2" -> "API Gateway": 解錠要求 with JWTチケット

"API Gateway" -> "チケット改竄チェック\n解錠呼出 Lambda"

"チケット改竄チェック\n解錠呼出 Lambda" -> "SESAME WebAPI": 解錠指示

"SESAME WebAPI" -> HUB
HUB-> "ドアSESAME5": 解錠指示 Bluetooth

@enduml
```

## やってみた

### コラボフローで申請書を作る

メールアドレスと名前を「鍵の利用者特定」と「チケット案内先」に使います。
さらに重要な利用期間として、入室できる時間を分単位で入力できるようにパーツを配置します。

あとはシステム処理用に、発行されたチケット画像を受け取り、メール添付のために添付ファイルパーツも付けて完成です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934/7331e249-fa5c-6c98-9e81-ac7090374c1a.png)

### 申請経路を工夫する

QRコードを発行する仕掛けは経路自体の Webhook を利用します。Webhook を受け取る AWS 側の仕掛けが自分のターンだと判断できるように、専用の判定段階を作ります。今回は「チケット発行」としました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934/7eb6dbb4-00bc-ee62-b5f1-508ed7db25c5.png)

専用の判定段階に設定する判定者は【API 処理するときに認証で指定するユーザー】を個人判定で設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934/e2aab48d-b128-e870-bc13-e8880e7f20c4.png)

発行システムで生成されたQRコードの画像を受け取るために、添付ファイルパーツの編集を許可します。万が一の添付ミス（発行システムのバグ）で進まないように、必須にしておきましょう（１敗）。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934/79e94300-f101-a376-a9e3-6f6849887283.png)

### QR スキャナ組み立てと読み取りコード実装

さくっと組み立てまして。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934/85f49e5a-dad9-2e97-6aa8-be071f1fbecf.png)

M5 シリーズはレゴブロックに差し込める穴があるのですが。持ってないので、代わりにグッドスマイルカンパニーさんが出しているフィギュア固定台・アームが良い仕事をしてくれました。

お馴染みの UIFlow 2.0 でスキャン処理と読み取ったQRコードをテキストとしてAPIサーバーへ転送するコードを作ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934/844b23fc-ae59-282f-14ca-8365bd7b591f.png)

### バックエンドの作成

チケットQR発行と、チケット解析して整合＆有効期間内ならSESAMEに解錠指示するAPIです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934/bb1602cc-cd41-7617-9478-0b9bc05c0200.png)

AWS CDK でシステムデプロイ部分をくみ上げます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934/1a8678f8-3e10-4a06-5436-638d49faeea5.png)

## 使ってみた

サンタさんになりきって、入室申請をしましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934/483c95e9-b3d0-2f9c-59c4-576b081cd0c6.png)

承認等々はかっ飛ばしまして。。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934/3e2ce679-3145-3bb0-5a95-ebaf5f86d929.png)

チケット画像が添付されました！
そして、案内メールも届きました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934/86dc19ce-0a9c-f5c9-5779-20c2971871ce.png)

## ぴっ！うぃぃぃん

かざしましょ、そうしまｓ

<iframe width="560" height="315" src="https://www.youtube.com/embed/lXnnK_SSRpc?si=dxQn-CY1noQoOFmj" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>


おおっ！いけました。1日限定のチケットです。
撮影の都合で、今回は紙に印刷したものをスキャンしましたが、スマホで写したQRコードもいけました。

### 解錠履歴

API 実行履歴についても見てみましょう。
ユーザー名に `API Call` と書かれている部分が API 操作で解錠できた記録です。チケット内に名前が入っているのでそれを記録すれば良いです。未実装で固定の文字列ですが、うまく記録されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934/31c09838-37bd-fcd2-cacd-c10550fe4ef3.png)

### 申請・発行の証跡

[ビューフォルダ](https://www.collabo-style.co.jp/how-to/usage/0-01-0001/) の機能を使って、申請フォームをまとめれば、いつの入室分が発行されているかも一目でわかります。テストしまくったのが分かりますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3934/5fe28c42-6602-72f4-b478-cdc946d15a97.png)

## まとめ

利用者のアカウント管理におけるライフサイクルと、物理デバイスの制御をワークフローという一連の作業で統合してみました。人が行う作業は正当性を判断することに特化して、必要なデータ処理・タスク的な仕事は全てプログラムに任せて自動化している所がポイントになりました。

### 感想など

毎年、QR コードなネタをアドベントカレンダーで採用しているのですが、気づいたら過去一壮大な仕掛けになってました。実装ミスや仕様の勘違い、期待に夢を膨らませて IaC で実装チャレンジしようとしたら微妙に噛み合わないなど、トライ＆エラーが想定以上でした。

SESAME などファーストタッチした API が多く、新しい発見や今後の仕様・次一層を考えていくための良い刺激になりました。通常業務で培ったアイデアや仕組み、実装分けなんかも惜しみなくつぎ込めて満足でした。
反省として、やりすぎて予定日に間に合わなかったので、次の壮大に向けてもっと早く仕込み始めます。

ソースコードは、セキュアな部分を除いて後日公開予定です。

やっぱり、物理が動くのはロマンの塊ですね！
それではまた！

### NEXT

次の9日目は、

- [mori](https://note.com/collabomori) さんの「[【コラボフロー】日付を指定してメールを飛ばす方法](https://note.com/collabomori/n/n4b6531c1e94c)」
- [Shota](https://zenn.dev/taroyamada5963) さんの「[コラボフロー API を使って添付ファイル付きの申請書を送信する](https://zenn.dev/collabostyle/articles/93d64c567ec041)」

の2本立てです