wheatandcatの開発ブログ

React Nativeで開発しているペペロミア & memoirの技術系記事を投稿してます

QRコードでゲストログインの機能を実装

開発中のアプリでデータ共有で招待QRコードを発行して、スキャンでゲストログインできる仕組みを実装したので記事化

PR

github.com

実装の概要

  • backend/アプリのゲストログインの実装
    • ゲストユーザーログインのトークンを発行してHeaderに設定して認証するミドルウェアを実装
  • QRコード作成/スキャン
  • QRコードをスキャンした時にアプリに指定の動作をさせる

実装

まず、backendのゲストログインの実装 詳しいtable設計は省略するが、以下のように認証のコードを修正

typescript/backend/src/common/guards/auth/auth.guard.ts

    const authorization = request.headers.authorization
    if (!authorization) {
      throw new Error('Authorization header not found')
    }

    if (authorization?.startsWith('Bearer ')) {
      const token = authorization.split('Bearer ')[1]
      const result = await admin.auth().verifyIdToken(token)

      const user = await this.prisma.user.findFirst({
        where: {
          uid: result.uid,
        },
      })

      if (!user) {
        throw new Error('User not found')
      }
      request.auth = {
        guest: false,
        uid: user.uid,
        userId: user.id,
        user: user,
      } as Auth
    } else if (authorization?.startsWith('Guest ')) {
      const guestUid = authorization.split('Guest ')[1]
      const guest = await this.prisma.guest.findFirst({
        where: {
          uid: guestUid,
        },
      })

      if (!guest) {
        throw new Error('Guest not found')
      }

      const user = await this.prisma.user.findFirst({
        where: {
          id: guest.userId,
        },
      })

      if (!user) {
        throw new Error('User not found')
      }

      request.auth = {
        guest: true,
        guestUId: guestUid,
        uid: user.uid,
        userId: user.id,
        user: user,
      } as Auth
    }

HeaderのAuthorizationの値がBearer xxxxxxの場合はFirebaseで認証、Guest xxxxxxの場合はゲストログインとして自前のdbと検索して認証を行い、招待されたユーザーのデータをAPI上で参照できるようにする。この仕組みを作った上で次にアプリを実装

アプリでは招待する側は招待用のQRコードを qr_flutterを使用して生成

pub.dev

コードは以下のように実装

dart/app/lib/features/category/components/share/bottomSheet.dart

    final inviteURL =
        "https://stock-keeper-review.web.app/guest/login/${code.value}";

    return Padding(
        padding: const EdgeInsets.only(
            top: Spacing.md,
            bottom: Spacing.xl,
            left: Spacing.md,
            right: Spacing.md), 
          (省略)

                    child: loading
                        ? const Progress(
                            color: AppColors.textDark,
                          )
                        : QrImageView(
                            data: inviteURL,  // ← dataにURLを設定
                            version: QrVersions.auto,
                            size: 125.0,
                          ),
                  )),

QrImageViewのdataには前回の記事で紹介したユニバーサルリンク/アプリリンクの設定をしたURLを指定

go_routerで上記のURLを指定した時にゲストログインの画面に遷移できるように修正

dart/app/lib/main.dart

final goRouter = GoRouter(
  initialLocation: '/',
  routes: [
    GoRoute(
        path: '/',
        name: "home",
        pageBuilder: (context, state) {
          return MaterialPage(key: state.pageKey, child: const AuthWrapper());
        },
        routes: [
          GoRoute(
            path: "guest/login/:code",  // ← ゲストログインできる画面
            name: "guest_login",
            pageBuilder: (context, state) {
              final code = state.pathParameters['code']!;
              return BottomSheetPage(builder: (_) => ShareBottomSheet(code: code));
            },
          ),

この実装でアプリでQRコードスキャンアプリでスキャンした時にゲストログイン動作の実装は完了

www.youtube.com

最後にアプリ内にも招待QRコードのスキャンの画面を実装してアプリ内からもゲストログインを可能にする

flutterでQRコードのスキャンは mobile_scannerを使用

pub.dev

コードは以下のように実装

dart/app/lib/features/login/components/bottomSheet.dart

    void onScan(BarcodeCapture capture) {
      final List<Barcode> barcodes = capture.barcodes;
      final value = barcodes.first.rawValue; // valueでQRコードの値を取得

      // 省略
    }


    return Padding(
      padding: const EdgeInsets.only(
          top: Spacing.md,
          bottom: Spacing.xl,
          left: Spacing.md,
          right: Spacing.md),
          (省略)

                      child: SizedBox(
                          height: 200,
                          width: 200,
                          child: MobileScanner(
                            controller: MobileScannerController(
                              detectionSpeed: DetectionSpeed
                                  .noDuplicates, // 同じ QR コードを連続でスキャンさせない
                            ),
                            onDetect: onScan,  // ← ここがQRコードスキャンのハンドラー
                          )),

この実装でアプリ内でQRコードをスキャンした時にゲストログイン動作の実装も完了

www.youtube.com

最後

現状の実装だとゲストログインのセキュリティが甘いので、最終的にはFirebase App Checkまで実装して機能的には完成の予定

firebase.google.com