Skip to content

Commit

Permalink
Add client examples of accessing models with specific auth modes (#7080)
Browse files Browse the repository at this point in the history
* add client side code for public access

* add client side code for signed in user access

* add client side code for multi user access

* add client side code for per user/owner access

* add client side code for custom id & group claims

* add client side code for custom access

* add client side code for user pool group access

* add client side code for oidc access

* Update src/pages/gen2/build-a-backend/data/customize-authz/signed-in-user-data-access/index.mdx

Co-authored-by: Rene Brandel <4989523+renebrandel@users.noreply.github.com>

* add info callout and configuration code to public/private iam examples

* move callout up

* remove "**note**" from callouts

---------

Co-authored-by: Rene Brandel <4989523+renebrandel@users.noreply.github.com>
  • Loading branch information
chrisbonifacio and renebrandel authored Apr 12, 2024
1 parent 9324e6f commit 8360a33
Show file tree
Hide file tree
Showing 8 changed files with 255 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Amplify Data supports using custom identity and group claims if you do not wish

To use custom claims specify `identityClaim` or `groupClaim` as appropriate. In the example below, the `identityClaim` is specified and the record owner will check against this `user_id` claim. Similarly, if the `user_groups` claim contains a "Moderator" string then access will be granted.

```ts
```ts title="amplify/data/resource.ts"
import { a, defineData, type ClientSchema } from '@aws-amplify/backend';

const schema = a.schema({
Expand All @@ -37,3 +37,24 @@ export type Schema = ClientSchema<typeof schema>;
export const data = defineData({ schema });

```

In your application, you can perform CRUD operations against the model using `client.models.<model-name>` with the `userPool` auth mode.

```ts
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const client = generateClient<Schema>();

const { errors, data: newTodo } = await client.models.Todo.create(
{
postname: 'My New Post'
content: 'My post content',
},
// highlight-start
{
authMode: 'userPool',
}
// highlight-end
);
```
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export function getStaticProps(context) {

You can define your own custom authorization rule with a Lambda function.

```ts
```ts title="amplify/data/resource.ts"
import {
type ClientSchema,
a,
Expand Down Expand Up @@ -51,6 +51,26 @@ export const data = defineData({
});
```

In your application, you can perform CRUD operations against the model using `client.models.<model-name>` with the `lambda` auth mode.

```ts
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const client = generateClient<Schema>();

const { errors, data: newTodo } = await client.models.Todo.create(
{
content: 'My new todo',
},
// highlight-start
{
authMode: 'lambda',
}
// highlight-end
);
```

The Lambda function of choice will receive an authorization token from the client and execute the desired authorization logic. The AppSync GraphQL API will receive a payload from Lambda after invocation to allow or deny the API call accordingly.

To configure a Lambda function as the authorization mode, create a new file `amplify/data/custom-authorizer.ts`. You can use this Lambda function code template as a starting point for your authorization handler code:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The `multipleOwners` rule grants a set of users access to a record by automatica

If you want to grant a set of users access to a record, you use the `multipleOwners` rule. This automatically creates a `owner: a.string().array()` field to store the allowed owners.

```ts
```ts title="amplify/data/resource.ts"
const schema = a.schema({
Todo: a
.model({
Expand All @@ -28,6 +28,41 @@ const schema = a.schema({
});
```

In your application, you can perform CRUD operations against the model using `client.models.<model-name>` with the `userPool` auth mode.

```ts
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const client = generateClient<Schema>();

// Create a record with current user as first owner
const { errors, data: newTodo } = await client.models.Todo.create(
{
content: 'My new todo',
},
// highlight-start
{
authMode: 'userPool',
}
// highlight-end
);
```
```ts
// Add another user as an owner
await client.models.Todo.update(
{
id: newTodo.id,
owner: [...(newTodo.owner as string[]), otherUserId],
},
// highlight-start
{
authMode: "userPool"
}
// highlight-end
);
```

## Override to a list of owners

You can override the `inField` to a list of owners. Use this if you want a dynamic set of users to have access to a record. In the example below, the `authors` list is populated with the creator of the record upon record creation. The creator can then update the `authors` field with additional users. Any user listed in the `authors` field can access the record.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ The `owner` authorization strategy restricts operations on a record to only the

You can use the `owner` authorization strategy to restrict a record's access to a specific user. When `owner` authorization is configured, only the record's `owner` is allowed the specified operations.

```ts

```ts title="amplify/data/resource.ts"
// The "owner" of a Todo is allowed to create, read, update, and delete their own todos
const schema = a.schema({
Todo: a
Expand All @@ -29,7 +30,7 @@ const schema = a.schema({
});
```

```ts
```ts title="amplify/data/resource.ts"
// The "owner" of a Todo record is only allowed to create, read, and update it.
// The "owner" of a Todo record is denied to delete it.
const schema = a.schema({
Expand All @@ -41,6 +42,26 @@ const schema = a.schema({
});
```

In your application, you can perform CRUD operations against the model using `client.models.<model-name>` with the `userPool` auth mode.

```ts
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const client = generateClient<Schema>();

const { errors, data: newTodo } = await client.models.Todo.create(
{
content: 'My new todo',
},
// highlight-start
{
authMode: 'userPool',
}
// highlight-end
);
```

Behind the scenes, Amplify will automatically add a `owner: a.string()` field to each record which contains the record owner's identity information upon record creation.

By default, the Cognito user pool's user information is populated into the `owner` field. The value saved includes `sub` and `username` in the format `<sub>::<username>`. The API will authorize against the full value of `<sub>::<username>` or `sub` / `username` separately and return `username`. You can alternatively configure [OpenID Connect as an authorization provider](/gen2/build-a-backend/data/customize-authz/using-oidc-authorization-provider).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The public authorization strategy grants everyone access to the API, which is pr

To grant everyone access, use the `.public()` authorization strategy. Behind the scenes, the API will be protected with an API key.

```ts
```ts title="amplify/data/resource.ts"
const schema = a.schema({
Todo: a
.model({
Expand All @@ -27,11 +27,31 @@ const schema = a.schema({
});
```

In your application, you can perform CRUD operations against the model using `client.models.<model-name>` by specifying the `apiKey` auth mode.

```ts
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const client = generateClient<Schema>();

const { errors, data: newTodo } = await client.models.Todo.create(
{
content: 'My new todo',
},
// highlight-start
{
authMode: 'apiKey',
}
// highlight-end
);
```

## Add public authorization rule using Amazon Cognito identity pool's unauthenticated role

You can also override the authorization provider. In the example below, `identityPool` is specified as the provider which allows you to use an "Unauthenticated Role" from the Cognito identity pool for public access instead of an API key. Your Auth resources defined in `amplify/auth/resource.ts` generates scoped down IAM policies for the "Unauthenticated role" in the Cognito identity pool automatically.

```ts
```ts title="amplify/data/resource.ts"
const schema = a.schema({
Todo: a
.model({
Expand All @@ -40,3 +60,48 @@ const schema = a.schema({
.authorization([a.allow.public('identityPool')]),
});
```

In your application, you can perform CRUD operations against the model using `client.models.<model-name>` with the `iam` auth mode.

<Callout info>
If you're not using the auto-generated **amplifyconfiguration.json** file, then you must set the Amplify Library resource configuration's `allowGuestAccess` flag to `true`. This lets the Amplify Library use the unauthenticated role from your Cognito identity pool when your user isn't logged in.

<Accordion title="Amplify configuration">
```ts title="src/App.tsx"
import { Amplify } from "aws-amplify";
import config from "../amplifyconfiguration.json";

Amplify.configure(
{
...config,
Auth: {
Cognito: {
identityPoolId: config.aws_cognito_identity_pool_id,
userPoolClientId: config.aws_user_pools_web_client_id,
userPoolId: config.aws_user_pools_id,
allowGuestAccess: true,
},
},
}
);
```
</Accordion>
</Callout>

```ts title="src/App.tsx"
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const client = generateClient<Schema>();

const { errors, data: newTodo } = await client.models.Todo.create(
{
content: 'My new todo',
},
// highlight-start
{
authMode: 'iam',
}
// highlight-end
);
```
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ You can use the `private` authorization strategy to restrict a record's access t

In the example below, anyone with a valid JWT token from the Cognito user pool is allowed access to all Todos.

```ts
```ts title="amplify/data/resource.ts"
const schema = a.schema({
Todo: a
.model({
Expand All @@ -33,11 +33,32 @@ const schema = a.schema({
});
```

## Override the authentication provider
In your application, you can perform CRUD operations against the model using `client.models.<model-name>` with the `userPool` auth mode.

```ts
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const client = generateClient<Schema>();

const { errors, data: newTodo } = await client.models.Todo.create(
{
content: 'My new todo',
},
// highlight-start
{
authMode: 'userPool',
}
// highlight-end
);
```

## Use identity pool for signed-in user authentication

You can also override the authorization provider. In the example below, `identityPool` is specified as the provider which allows you to use an "Unauthenticated Role" from the Cognito identity pool for public access instead of an API key. Your Auth resources defined in `amplify/auth/resource.ts` generates scoped down IAM policies for the "Unauthenticated role" in the Cognito identity pool automatically.

```ts

```ts title="amplify/data/resource.ts"
const schema = a.schema({
Todo: a
.model({
Expand All @@ -47,4 +68,28 @@ const schema = a.schema({
});
```

In your application, you can perform CRUD operations against the model using `client.models.<model-name>` with the `iam` auth mode.

<Callout info>
The user must be logged in for the Amplify Library to use the authenticated role from your Cognito identity pool.
</Callout>

```ts
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const client = generateClient<Schema>();

const { errors, data: newTodo } = await client.models.Todo.create(
{
content: 'My new todo',
},
// highlight-start
{
authMode: 'iam',
}
// highlight-end
);
```

In addition, you can also use OpenID Connect with `private` authorization. See [OpenID Connect as an authorization provider](/gen2/build-a-backend/data/customize-authz/using-oidc-authorization-provider/).
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ You can use the `group` authorization strategy to restrict access based on user

When you want to restrict access to a specific set of user groups, provide the group names in the `groups` parameter. In the example below, only users that are part of the "Admin" user group are granted access to the Salary model.

```ts
```ts title="amplify/data/resource.ts"
// allow one specific group
const schema = a.schema({
Salary: a
Expand All @@ -29,6 +29,28 @@ const schema = a.schema({
});
```

In your application, you can perform CRUD operations against the model using `client.models.<model-name>` with the `userPool` auth mode.

```ts
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const client = generateClient<Schema>();

// As a signed-in user that belongs to the 'Admin' User Pool Group
const { errors, data: newSalary } = await client.models.Salary.create(
{
wage: 50.25,
currency: 'USD'
},
// highlight-start
{
authMode: 'userPool',
}
// highlight-end
);
```

This can then be updated to allow access to multiple defined groups; in this example below we added access for "Leadership".

```ts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,18 @@ export const data = defineData({
},
});
```

In your application, you can perform CRUD operations against the model using `client.models.<model-name>` by specifying the `oidc` auth mode.

```ts
import { generateClient } from 'aws-amplify/data';
import type { Schema } from '../amplify/data/resource'; // Path to your backend resource definition

const client = generateClient<Schema>();

const { errors, data: todos } = await client.models.Todo.list({
// highlight-start
authMode: "oidc",
// highlight-end
});
```

0 comments on commit 8360a33

Please sign in to comment.