Skip to content

Conversation

thorrak
Copy link
Contributor

@thorrak thorrak commented Jun 8, 2025

This PR adds an optional configuration option keychainAccessGroup which sets the keychain-access-groups entitlement on both the main app and the widget during prebuild.

This entitlement is similar to #7 in both purpose and implementation, but while App Groups are typically used to insecurely share key/value pairs between an app and a widget, keychain-access-groups allow you to use packages like expo-secure-store to securely share values via the iOS Keychain.

Here's an example of implementing this in Swift:

import Security

// Function to retrieve values from expo-secure-store (iOS Keychain)
func getSecureStoreValue(forKey key: String) -> String? {
    let query: [String: Any] = [
        kSecClass as String: kSecClassGenericPassword,
        kSecAttrAccessGroup as String: "<KEYCHAIN ACCESS GROUP ID HERE>",
        kSecAttrAccount as String: key,
        kSecReturnData as String: true,
        kSecMatchLimit as String: kSecMatchLimitOne
    ]

    var item: CFTypeRef?
    let status = SecItemCopyMatching(query as CFDictionary, &item)

    guard status == errSecSuccess,
          let data = item as? Data,
          let value = String(data: data, encoding: .utf8)
    else { 
        print("Widget: Failed to retrieve secure store value for key '\(key)'. Status: \(status)")
        return nil 
    }

    print("Widget: Successfully retrieved secure store value for key '\(key)'")
    return value
}

And implementing writing the item to the keychain in React Native:

import * as SecureStore from 'expo-secure-store';
const TOKEN_KEY = "KeyToSave";
const GROUP = '<KEYCHAIN ACCESS GROUP ID HERE>';

export const saveToken = async (token: string): Promise<void> => {
  await SecureStore.setItemAsync(TOKEN_KEY, token, {
    accessGroup: GROUP,
    keychainAccessible: SecureStore.AFTER_FIRST_UNLOCK
  });
};

export const getToken = async (): Promise<string | null> => {
  return await SecureStore.getItemAsync(TOKEN_KEY, {
    accessGroup: GROUP,
    keychainAccessible: SecureStore.AFTER_FIRST_UNLOCK
  });
};

export const removeToken = async (): Promise<void> => {
  await SecureStore.deleteItemAsync(TOKEN_KEY, {
    accessGroup: GROUP
  });
};

@nathan-ahn
Copy link
Collaborator

Thank you for your PR :)

@nathan-ahn nathan-ahn merged commit 3f1846e into bndkt:main Sep 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants