|
| 1 | +--- |
| 2 | +title: "getUserSubscriptions" |
| 3 | +linkTitle: "getUserSubscriptions" |
| 4 | +description: > |
| 5 | + The `getUserSubscriptions` function retrieves all subscriptions the authenticated user belongs to. |
| 6 | +--- |
| 7 | + |
| 8 | +The `getUserSubscriptions` Firebase Cloud Function returns every subscription in which the authenticated user holds the default permission role (typically `access`). It runs with Admin SDK privileges, bypassing Firestore security rules, so the client never needs direct collection-query access to the `subscriptions` collection. |
| 9 | + |
| 10 | +This function replaces the previous pattern of querying the `subscriptions` collection directly from the client, which required `allow list` to be open to all authenticated users and exposed every subscription document to enumeration. |
| 11 | + |
| 12 | +### Function Signature |
| 13 | + |
| 14 | +```typescript |
| 15 | +export const getUserSubscriptions = https.onCall(async (_, context) => { ... }); |
| 16 | +``` |
| 17 | + |
| 18 | +### Parameters |
| 19 | + |
| 20 | +This function takes no input data. |
| 21 | + |
| 22 | +### Context |
| 23 | + |
| 24 | +The function requires an authenticated user context (`context.auth`). If the caller is not authenticated, it throws an `unauthenticated` error. |
| 25 | + |
| 26 | +### Behavior |
| 27 | + |
| 28 | +1. **Authentication Check**: Verifies the caller is authenticated. |
| 29 | +2. **Permission Query**: Queries the `subscriptions` collection for documents where `permissions.<defaultPermission>` array contains the caller's UID. The default permission key is resolved from `global.saasConfig.permissions` at startup. |
| 30 | +3. **Success**: Returns an object containing `subscriptions` — an array of subscription documents, each including the document `id` and all fields from `SubscriptionData`. |
| 31 | + |
| 32 | +### Return Value |
| 33 | + |
| 34 | +```typescript |
| 35 | +{ |
| 36 | + subscriptions: Array<{ id: string } & SubscriptionData> |
| 37 | +} |
| 38 | +``` |
| 39 | + |
| 40 | +### Error Handling |
| 41 | + |
| 42 | +| Code | Condition | |
| 43 | +| :--- | :--- | |
| 44 | +| `unauthenticated` | Caller is not authenticated | |
| 45 | +| `internal` | Unexpected server-side error | |
| 46 | + |
| 47 | +### Example Usage (Client-side) |
| 48 | + |
| 49 | +```typescript |
| 50 | +import { getFunctions, httpsCallable } from 'firebase/functions'; |
| 51 | +import { getApp } from 'firebase/app'; |
| 52 | + |
| 53 | +const functions = getFunctions(getApp()); |
| 54 | +const getUserSubscriptions = httpsCallable(functions, 'getUserSubscriptions'); |
| 55 | + |
| 56 | +async function loadUserSubscriptions() { |
| 57 | + try { |
| 58 | + const result = await getUserSubscriptions(); |
| 59 | + console.log('Subscriptions:', result.data.subscriptions); |
| 60 | + // result.data: { subscriptions: Array<{ id: string } & SubscriptionData> } |
| 61 | + } catch (error) { |
| 62 | + console.error('Error loading subscriptions:', error.code, error.message); |
| 63 | + } |
| 64 | +} |
| 65 | +``` |
| 66 | + |
| 67 | +### Firestore Rules |
| 68 | + |
| 69 | +Because subscription loading is handled server-side by this function, the `subscriptions` collection should block all client-side list queries: |
| 70 | + |
| 71 | +``` |
| 72 | +match /subscriptions/{docId} { |
| 73 | + // List queries disabled — use getUserSubscriptions Cloud Function instead |
| 74 | + allow list: if false; |
| 75 | +
|
| 76 | + allow get: if request.auth != null |
| 77 | + && get(/databases/$(database)/documents/subscriptions/$(docId)).data.permissions.access.hasAny([request.auth.uid]); |
| 78 | + ... |
| 79 | +} |
| 80 | +``` |
| 81 | + |
| 82 | +### Deployment |
| 83 | + |
| 84 | +Export the function from your project's `functions/src/index.ts`: |
| 85 | + |
| 86 | +```typescript |
| 87 | +export { |
| 88 | + // ... other functions |
| 89 | + getUserSubscriptions |
| 90 | +} from '@fireact.dev/functions'; |
| 91 | +``` |
0 commit comments