Skip to content

Commit

Permalink
Checkpoint to share draft with others
Browse files Browse the repository at this point in the history
  • Loading branch information
inlined committed Jan 31, 2025
1 parent 24c8b08 commit b6d4304
Show file tree
Hide file tree
Showing 13 changed files with 505 additions and 846 deletions.
200 changes: 111 additions & 89 deletions docs/auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,134 +111,156 @@ genkit flow:run selfSummaryFlow '{"uid": "abc-def"}' --auth '{"uid": "abc-def"}'

## Cloud Functions for Firebase integration

The Firebase plugin provides convenient integration with Firebase Auth / Google
Cloud Identity Platform as well as built-in Firebase App Check support.
The Cloud Functions for Fireabse SDK natively supports Genkit Actions such as Flows
using the `onCallGenkit` method in `firebase-functions/https`. This is a special variant
of the `onCall` method and can use all features of `onCall`, including `authPolicy`

### Authorization
## Authorization

The `onFlow()` wrapper provided by the Firebase plugin works natively with the
Cloud Functions for Firebase
[client SDKs](https://firebase.google.com/docs/functions/callable?gen=2nd#call_the_function).
When using the SDK, the Firebase Auth header will automatically be included as
long as your app client is also using the
[Firebase Auth SDK](https://firebase.google.com/docs/auth).
You can use Firebase Auth to protect your flows defined with `onFlow()`:
Like all Callable cloud functions, `onCallGenkit` supports an auth policy callback. This
can be used to enforce authorization outside of the application code. Otherwise, context
will be parsed and passed as context to the flow, which can perform its own auth checks.

<!-- prettier-ignore: see note above -->

```ts
import { genkit } from 'genkit';
import { firebaseAuth } from '@genkit-ai/firebase';
import { onFlow } from '@genkit-ai/firebase/functions';
import { onCallGenkit, hasClaim } from 'firebase-functions/https';

Firebase Auth can be enforced by the `authPolicy` option, and there are multiple helpers
for auth policy.

const ai = genkit({ ... });;

export const selfSummaryFlow = onFlow(
ai,
{
name: 'selfSummaryFlow',
inputSchema: z.string(),
outputSchema: z.string(),
authPolicy: firebaseAuth((user) => {
if (!user.email_verified && !user.admin) {
throw new Error('Email not verified');
}
}),
},
async (input) => {
// Flow logic here...
}
);
const summaryFlow({
name: 'selfSummaryFlow',
inputSchema: z.string(),
outputSchema: z.string(),
}, async (input) => {
// Flow logic here...
})

export const summarize = onCallFlow({
authPolicy: hasClaim('email_verified', 'true'),
}, summaryFlow);
```

When using the Firebase Auth plugin, `user` will be returned as a
[DecodedIdToken](https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.decodedidtoken).
You can always retrieve this object at any time via `ai.currentContext()` as noted
above. When running this flow during development, you would pass the user object
in the same way:

```posix-terminal
genkit flow:run selfSummaryFlow '{"uid": "abc-def"}' --auth '{"admin": true}'
```
### Client integrity

By default the Firebase Auth plugin requires the auth header to be sent by the
client, but in cases where you wish to allow unauthenticated access with special
handling for authenticated users (upselling features, say), then you can
configure the policy like so:
Authentication on its own goes a long way to protect your app. But it's also
important to ensure that only your client apps are calling your functions. Callable
functions are directly integrated with appCheck via the options object. Simply enable
them in your `onCallGenkit()`:

<!-- prettier-ignore: see note above -->
<!-- prettier-ignore: see note above -->

```ts
authPolicy: firebaseAuth((user) => {
if (user && !user.email_verified) {
throw new Error("Logged in users must have verified emails");
}
}, {required: false}),
import { genkit } from 'genkit';
import { onCallGenkit } from 'firebase-functions/https';

const ai = genkit({ ... });;

const summaryFlow({
name: 'selfSummaryFlow',
inputSchema: z.string(),
outputSchema: z.string(),
}, async (input) => {
// Flow logic here...
})

export const summarize = onCallFlow({
enforceAppCheck: true,
consumeAppCheckToken: true,
}, summaryFlow);
```

Whenever you expose a Cloud Function to the wider internet, it is vitally
important that you use some sort of authorization mechanism to protect your data
and the data of your customers. With that said, there are times when you need
to deploy a Cloud Function with no code-based authorization checks (for example,
your Function is not world-callable but instead is protected by
[Cloud IAM](https://cloud.google.com/functions/docs/concepts/iam)). The
`authPolicy` field is always required when using `onFlow()`, but you can
indicate to the library that you are forgoing authorization checks by using the
`noAuth()` function:
## Using Firebase on non-Firebase infrastructure

The Firebase plugin provides convenient integration with Firebase Auth / Google
Cloud Identity Platform as well as built-in Firebase App Check support. The
`firebase/auth` package provides the same context available in `onCallGenkit` when
using environments that are not Cloud Functions for Firebase. To benefit from this
authorization, it is recommended to use the Cloud Functions for Firebase
[client SDKs](https://firebase.google.com/docs/functions/callable?gen=2nd#call_the_function),
which can be used with any arbitrary URL.

<!-- prettier-ignore: see note above -->

```ts
import { onFlow, noAuth } from "@genkit-ai/firebase/functions";
import { genkit } from 'genkit';
import { firebaseAuth } from '@genkit-ai/firebase';
import { expressHandler } from '@genkit-ai/express';
import * as express from 'express';

export const selfSummaryFlow = onFlow(
ai,
{
name: "selfSummaryFlow",
inputSchema: z.string(),
outputSchema: z.string(),
// WARNING: Only do this if you have some other gatekeeping in place, like
// Cloud IAM!
authPolicy: noAuth(),
},
async (input) => {
// Flow logic here...
}
);
const ai = genkit({ ... });;

const summaryFlow({
name: 'selfSummaryFlow',
inputSchema: z.string(),
outputSchema: z.string(),
}, async (input) => {
// Flow logic here...
})

const app = express();
express.post('/summary', expressHandler(summaryFlow, {
use: [firebaseAuth({hasClaim: 'email_verified'})]
}));

app.listen(process.env.PORT || 0, () => console.log("started"));
```

The `firebaseAuth` middleware will provide the `auth`, `app`, and `instanceIdToken` fields to context,
just as `onCallGenkit` does. Using the client SDKs will automatically add the headers expected by this
middleware. When using the Firebase Auth plugin, `user.token` will be returned as a
[DecodedIdToken](https://firebase.google.com/docs/reference/admin/node/firebase-admin.auth.decodedidtoken).
For convenience, `user.uid` will be set to the `sub` claim of the token. `app.token` will be a
[DecodedAppCheckToken](https://firebase.google.com/docs/reference/admin/node/firebase-admin.app-check.decodedappchecktoken).
for convenience, `app.appId` is the app ID.

You can always retrieve this object at any time via `ai.currentContext()` as noted
above. When running this flow during development, you would pass the user object
in the same way:

```posix-terminal
genkit flow:run selfSummaryFlow '{"uid": "abc-def"}' --auth '{"admin": true}'
```

### Client integrity

Authentication on its own goes a long way to protect your app. But it's also
important to ensure that only your client apps are calling your functions. The
Firebase plugin for genkit includes first-class support for
[Firebase App Check](https://firebase.google.com/docs/app-check). Simply add
the following configuration options to your `onFlow()`:
[Firebase App Check](https://firebase.google.com/docs/app-check). To declaratively
enforce App Check, use declarative policies in your middleware:

<!-- prettier-ignore: see note above -->

```ts
import { onFlow } from "@genkit-ai/firebase/functions";
import { genkit } from 'genkit';
import { firebaseAuth } from '@genkit-ai/firebase';
import { expressHandler } from '@genkit-ai/express';
import * as express from 'express';

export const selfSummaryFlow = onFlow(
ai,
{
name: "selfSummaryFlow",
inputSchema: z.string(),
outputSchema: z.string(),
const ai = genkit({ ... });;

// These two fields for app check. The consumeAppCheckToken option is for
// replay protection, and requires additional client configuration. See the
// App Check docs.
const summaryFlow({
name: 'selfSummaryFlow',
inputSchema: z.string(),
outputSchema: z.string(),
}, async (input) => {
// Flow logic here...
})

const app = express();
express.post('/summary', expressHandler(summaryFlow, {
use: [firebaseAuth({
enforceAppCheck: true,
consumeAppCheckToken: true,
})],
}));

authPolicy: ...,
},
async (input) => {
// Flow logic here...
}
);
app.listen(process.env.PORT || 0, () => console.log("started"));
```

## Non-Firebase HTTP authorization
Expand Down
Loading

0 comments on commit b6d4304

Please sign in to comment.