尝试存储 Google OAuth 令牌时“向 SecureStore 提供的值大于 2048 字节”

问题描述 投票:0回答:2

我正在使用 Supabase 实现 Google 登录我的 Expo 应用程序,我将其设置为将令牌存储在 expo-secure-store 上,但是当我登录时,我收到以下警告:

向 SecureStore 提供的值大于 2048 字节。尝试存储此类值将在 SDK 35 中引发错误。

我正在使用 Expo 48。我尝试存储的令牌如下(已编辑):

{"access_token":"<REDACTED>","token_type":"<REDACTED>","expires_in":<REDACTED>,"refresh_token":"<REDACTED>","user":{"id":"<REDACTED>","aud":"<REDACTED>","role":"<REDACTED>","email":"<REDACTED>","email_confirmed_at":"<REDACTED>","phone":"<REDACTED>","confirmed_at":"<REDACTED>","last_sign_in_at":"<REDACTED>","app_metadata":{"provider":"<REDACTED>","providers":["<REDACTED>"]},"user_metadata":{"avatar_url":"<REDACTED>","email":"<REDACTED>","email_verified":<REDACTED>,"full_name":"<REDACTED>","iss":"<REDACTED>","name":"<REDACTED>","picture":"<REDACTED>","provider_id":"<REDACTED>","sub":"<REDACTED>"},"identities":[{"id":"<REDACTED>","user_id":"<REDACTED>","identity_data":{"avatar_url":"<REDACTED>","email":"<REDACTED>","email_verified":<REDACTED>,"full_name":"<REDACTED>","iss":"<REDACTED>","name":"<REDACTED>","picture":"<REDACTED>","provider_id":"<REDACTED>","sub":"<REDACTED>"},"provider":"<REDACTED>","last_sign_in_at":"<REDACTED>","created_at":"<REDACTED>","updated_at":"<REDACTED>"}],"created_at":"<REDACTED>","updated_at":"<REDACTED>"},"expires_at":<REDACTED>}

我想我不是第一个尝试通过 Expo 实现 Google 登录的人,所以我想知道这个问题的普遍接受的解决方案是什么?

expo google-signin
2个回答
4
投票

我们已经阐明了 SecureStore 的用法,并概述了用于加密会话数据的选项:https://supabase.com/docs/guides/getting-started/tutorials/with-expo?auth-store=secure-store#initialize -a-react-native-app

这使用常规

async-storage
进行实际值存储,但对存储在那里的值进行加密。

然后我们将这些值与随机加密的密钥相关联,然后将其保存到 SecureStore。加密密钥对于 SecureStore 来说始终足够小,并且通过安全地存储密钥,我们还可以保护异步存储中的加密数据

import { createClient } from '@supabase/supabase-js'
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as SecureStore from 'expo-secure-store';
import * as aesjs from 'aes-js';
import 'react-native-get-random-values';

// As Expo's SecureStore does not support values larger than 2048
// bytes, an AES-256 key is generated and stored in SecureStore, while
// it is used to encrypt/decrypt values stored in AsyncStorage.
class LargeSecureStore {
  private async _encrypt(key: string, value: string) {
    const encryptionKey = crypto.getRandomValues(new Uint8Array(256 / 8));

    const cipher = new aesjs.ModeOfOperation.ctr(encryptionKey, new aesjs.Counter(1));
    const encryptedBytes = cipher.encrypt(aesjs.utils.utf8.toBytes(value));

    await SecureStore.setItemAsync(key, aesjs.utils.hex.fromBytes(encryptionKey));

    return aesjs.utils.hex.fromBytes(encryptedBytes);
  }

  private async _decrypt(key: string, value: string) {
    const encryptionKeyHex = await SecureStore.getItemAsync(key);
    if (!encryptionKeyHex) {
      return encryptionKeyHex;
    }

    const cipher = new aesjs.ModeOfOperation.ctr(aesjs.utils.hex.toBytes(encryptionKeyHex), new aesjs.Counter(1));
    const decryptedBytes = cipher.decrypt(aesjs.utils.hex.toBytes(value));

    return aesjs.utils.utf8.fromBytes(decryptedBytes);
  }

  async getItem(key: string) {
    const encrypted = await AsyncStorage.getItem(key);
    if (!encrypted) { return encrypted; }

    return await this._decrypt(key, encrypted);
  }

  async removeItem(key: string) {
    await AsyncStorage.removeItem(key);
    await SecureStore.deleteItemAsync(key);
  }

  async setItem(key: string, value: string) {
    const encrypted = await this._encrypt(key, value);

    await AsyncStorage.setItem(key, encrypted);
  }
}

const supabaseUrl = YOUR_REACT_NATIVE_SUPABASE_URL
const supabaseAnonKey = YOUR_REACT_NATIVE_SUPABASE_ANON_KEY

const supabase = createClient(supabaseUrl, supabaseAnonKey, {
  auth: {
    storage: new LargeSecureStore(),
    autoRefreshToken: true,
    persistSession: true,
    detectSessionInUrl: false,
  },
});

1
投票

也许不是理想的答案,但我最终从supabase返回的会话对象中删除了不太敏感的用户元数据:

function removeUserMetaData(itemValue: string) {
    let parsedItemValue = JSON.parse(itemValue);

    // Remove properties from the object
    if (parsedItemValue) {
        delete parsedItemValue.user?.identities;
        delete parsedItemValue.user?.user_metadata;
    }
    // Convert the modified object back to a JSON string
    return JSON.stringify(parsedItemValue);
}

const ExpoSecureStoreAdapter = {
    getItem: async (key: string) => {
        return SecureStore.getItemAsync(key);
    },
    setItem: (key: string, value: string) => {
        SecureStore.setItemAsync(key, removeUserMetaData(value));
    },
    removeItem: (key: string) => {
        SecureStore.deleteItemAsync(key);
    },
};

并将用户元数据存储在单独的非安全存储中。

© www.soinside.com 2019 - 2024. All rights reserved.