142 lines
4.1 KiB
Markdown
142 lines
4.1 KiB
Markdown
# Admin Node SDK
|
|
|
|
Consult this file when writing server-side code (e.g., Cloud Functions) that
|
|
needs elevated privileges or needs to impersonate specific users.
|
|
|
|
### Best Practices for Agents
|
|
|
|
- **Understand Operation Storage**: SQL Connect queries and mutations are stored
|
|
on the server like Cloud Functions. Clients do not submit the raw operations.
|
|
Therefore, **whenever you update operations, you must regenerate the SDK and
|
|
redeploy services** that use it.
|
|
- **Follow Least Privilege**: Admin SDKs have unrestricted access by default.
|
|
Always use impersonation when possible to limit access.
|
|
- **Impersonation**: Use the `impersonate` parameter to run operations as a
|
|
specific user or as an unauthenticated user.
|
|
- **Impersonation Variables**: If you call an operation with optional variables
|
|
and want to pass impersonation options but without variables, you **MUST**
|
|
pass `undefined` as the first argument (variables) to clearly indicate no
|
|
variables are being provided.
|
|
- **Admin Operations**: If you create operations intended only for
|
|
administration, define them with `@auth(level: NO_ACCESS)`. This ensures they
|
|
can only be called via the Admin SDK with unrestricted access.
|
|
- **Resilient Enum Handling**: JavaScript/TypeScript does not enforce exhaustive
|
|
checks on enums. Always add a `default` branch to `switch` statements or an
|
|
`else` branch to handle unknown values gracefully when schemas evolve.
|
|
|
|
### Configuration in `connector.yaml`
|
|
|
|
To generate an Admin SDK, add the `adminNodeSdk` block to your `connector.yaml`:
|
|
|
|
```yaml
|
|
connectorId: my-connector
|
|
generate:
|
|
adminNodeSdk:
|
|
outputDir: "./admin-sdk"
|
|
package: "@dataconnect/admin-generated"
|
|
packageJsonDir: "." # Directory containing package.json
|
|
```
|
|
|
|
### Generation
|
|
|
|
Run the generation command:
|
|
|
|
```bash
|
|
npx -y firebase-tools@latest dataconnect:sdk:generate
|
|
```
|
|
|
|
### Usage Examples
|
|
|
|
#### 1. Impersonating an Unauthenticated User
|
|
|
|
Unauthenticated users can only run operations marked as `PUBLIC`.
|
|
|
|
```typescript
|
|
import { initializeApp } from "firebase-admin/app";
|
|
import { getDataConnect } from "firebase-admin/data-connect";
|
|
import { connectorConfig, getSongs } from "@dataconnect/admin-generated";
|
|
|
|
const adminApp = initializeApp();
|
|
const adminDc = getDataConnect(connectorConfig);
|
|
|
|
const songs = await getSongs(
|
|
adminDc,
|
|
{ limit: 4 },
|
|
{ impersonate: { unauthenticated: true } }
|
|
);
|
|
```
|
|
|
|
#### 2. Impersonating a Specific User (Cloud Functions)
|
|
|
|
When using callable Cloud Functions, the authentication token is automatically
|
|
verified.
|
|
|
|
```typescript
|
|
import { HttpsError, onCall } from "firebase-functions/https";
|
|
import { getMyFavoriteSongs } from "@dataconnect/admin-generated";
|
|
|
|
export const callableExample = onCall(async (req) => {
|
|
const authClaims = req.auth?.token;
|
|
if (!authClaims) {
|
|
throw new HttpsError("unauthenticated", "Unauthorized");
|
|
}
|
|
|
|
const favoriteSongs = await getMyFavoriteSongs(
|
|
adminDc,
|
|
undefined,
|
|
{ impersonate: { authClaims } }
|
|
);
|
|
|
|
return favoriteSongs;
|
|
});
|
|
```
|
|
|
|
#### 3. Impersonating a Specific User (Plain HTTP)
|
|
|
|
For non-callable endpoints, you must verify the token yourself.
|
|
|
|
```typescript
|
|
import { getAuth } from "firebase-admin/auth";
|
|
import { onRequest } from "firebase-functions/https";
|
|
import { getMyFavoriteSongs } from "@dataconnect/admin-generated";
|
|
|
|
const auth = getAuth();
|
|
|
|
export const httpExample = onRequest(async (req, res) => {
|
|
const token = req.header("authorization")?.replace(/^bearer\s+/i, "");
|
|
if (!token) {
|
|
res.sendStatus(401);
|
|
return;
|
|
}
|
|
let authClaims;
|
|
try {
|
|
authClaims = await auth.verifyIdToken(token);
|
|
} catch {
|
|
res.sendStatus(401);
|
|
return;
|
|
}
|
|
|
|
const favoriteSongs = await getMyFavoriteSongs(
|
|
adminDc,
|
|
undefined,
|
|
{ impersonate: { authClaims } }
|
|
);
|
|
|
|
res.send(favoriteSongs);
|
|
});
|
|
```
|
|
|
|
#### 4. Running with Unrestricted Access
|
|
|
|
Omit the `impersonate` parameter to run with full admin access. Only do this for
|
|
true administrative tasks.
|
|
|
|
```typescript
|
|
import { upsertSong } from "@dataconnect/admin-generated";
|
|
|
|
await upsertSong(adminDc, {
|
|
title: "New Song",
|
|
genre: "Rock"
|
|
});
|
|
```
|