Data Authorization

Authorization rules determine who can access your data and what operations they can perform. Amplify provides powerful, declarative authorization.

Authorization Modes

Amplify supports several authorization modes:

Mode Description Use Case
userPool Cognito User Pool authentication Authenticated users with Cognito
iam AWS IAM authentication Backend services, Lambda functions
apiKey API Key authentication Public data, quick prototyping
oidc OpenID Connect provider Third-party identity providers
lambda Custom Lambda authorizer Complex authorization logic

Setting Default Authorization

// amplify/data/resource.ts
export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: 'userPool',
    // Optional: API key for public access
    apiKeyAuthorizationMode: {
      expiresInDays: 30,
    },
  },
});

Model-Level Authorization

Add authorization rules directly to your models:

const schema = a.schema({
  Todo: a
    .model({
      content: a.string().required(),
      completed: a.boolean().default(false),
    })
    .authorization((allow) => [
      allow.owner(),  // Only owner can CRUD
    ]),
});

Authorization Strategies

Owner-Based Authorization

The most common strategy. Each item has an owner, and only the owner can access it.

Todo: a
  .model({
    content: a.string().required(),
    completed: a.boolean().default(false),
  })
  .authorization((allow) => [
    allow.owner(),
  ])

This automatically:

  • Adds an owner field to store the user's identity
  • Sets the owner automatically when creating items
  • Restricts access to only the owner

Specifying Operations

You can limit which operations are allowed:

Todo: a
  .model({...})
  .authorization((allow) => [
    allow.owner().to(['create', 'read', 'update']),
    // Owner can't delete
  ])

Available operations:

  • create – Create new items
  • read – Read/list items
  • update – Update existing items
  • delete – Delete items

Group-Based Authorization

Restrict access to users in specific Cognito groups:

// Only admins can manage categories
Category: a
  .model({
    name: a.string().required(),
    description: a.string(),
  })
  .authorization((allow) => [
    allow.group('admin'),  // Full CRUD for admins
    allow.authenticated().to(['read']),  // Read for all authenticated users
  ])

Public Access

For public data (like a blog or product catalog):

Article: a
  .model({
    title: a.string().required(),
    content: a.string().required(),
    published: a.boolean().default(false),
  })
  .authorization((allow) => [
    allow.guest().to(['read']),  // Anyone can read
    allow.group('editor').to(['create', 'read', 'update', 'delete']),
  ])
⚠️
API Key Required

Public/guest access requires an API key authorization mode to be enabled in your data configuration.

Authenticated Users

Allow any authenticated user to access data:

// Shared todos - any signed-in user can see all
SharedTodo: a
  .model({
    content: a.string().required(),
    completed: a.boolean().default(false),
  })
  .authorization((allow) => [
    allow.authenticated().to(['read']),  // All users can read
    allow.owner().to(['create', 'update', 'delete']),  // Only owner can modify
  ])

Multiple Authorization Rules

Combine rules for complex scenarios:

Post: a
  .model({
    title: a.string().required(),
    content: a.string().required(),
    status: a.enum(['DRAFT', 'PUBLISHED']),
  })
  .authorization((allow) => [
    // Author has full access
    allow.owner(),
    
    // Editors can read and update any post
    allow.group('editor').to(['read', 'update']),
    
    // Admins have full access
    allow.group('admin'),
    
    // Public can only read
    allow.guest().to(['read']),
  ])

Custom Owner Field

By default, the owner field is called "owner". Customize it:

Todo: a
  .model({
    content: a.string().required(),
    createdBy: a.string(),  // Custom owner field
  })
  .authorization((allow) => [
    allow.ownerDefinedIn('createdBy'),
  ])

Field-Level Authorization

Restrict access to specific fields:

User: a
  .model({
    name: a.string().required(),
    email: a.string().required(),
    // Only owner can see/modify private notes
    privateNotes: a.string().authorization((allow) => [
      allow.owner(),
    ]),
    // Only admins can modify role
    role: a.string().authorization((allow) => [
      allow.authenticated().to(['read']),
      allow.group('admin').to(['read', 'update']),
    ]),
  })
  .authorization((allow) => [
    allow.owner(),
    allow.authenticated().to(['read']),
  ])

Complete Example

Here's a complete data resource with various authorization patterns:

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

const schema = a.schema({
  // Private todos - only owner can access
  Todo: a
    .model({
      content: a.string().required(),
      completed: a.boolean().default(false),
      priority: a.enum(['LOW', 'MEDIUM', 'HIGH']),
      dueDate: a.datetime(),
    })
    .authorization((allow) => [allow.owner()]),

  // Categories managed by admins, readable by all
  Category: a
    .model({
      name: a.string().required(),
      color: a.string(),
      icon: a.string(),
    })
    .authorization((allow) => [
      allow.group('admin'),
      allow.authenticated().to(['read']),
    ]),

  // Public announcements
  Announcement: a
    .model({
      title: a.string().required(),
      content: a.string().required(),
      publishedAt: a.datetime(),
    })
    .authorization((allow) => [
      allow.group('admin'),
      allow.guest().to(['read']),
    ]),

  // Team projects with shared access
  Project: a
    .model({
      name: a.string().required(),
      description: a.string(),
      teamMembers: a.string().array(),
    })
    .authorization((allow) => [
      allow.owner(),
      allow.ownersDefinedIn('teamMembers').to(['read', 'update']),
    ]),
});

export type Schema = ClientSchema<typeof schema>;

export const data = defineData({
  schema,
  authorizationModes: {
    defaultAuthorizationMode: 'userPool',
    apiKeyAuthorizationMode: {
      expiresInDays: 30,
    },
  },
});

Testing Authorization

Test authorization rules by:

  1. Creating items as one user
  2. Signing in as a different user
  3. Attempting to access/modify the items
  4. Verifying that unauthorized operations fail
// This should fail if the user is not the owner
try {
  await client.models.Todo.update({
    id: 'someone-elses-todo',
    completed: true,
  });
} catch (error) {
  console.log('Unauthorized - as expected!');
}

Summary

  • Authorization is defined at the model and field level
  • allow.owner() – Restrict to item owner
  • allow.group('name') – Restrict to Cognito group members
  • allow.authenticated() – Allow any signed-in user
  • allow.guest() – Allow public access (requires API key)
  • Use .to(['read', 'create']) to limit operations
  • Combine multiple rules for complex authorization
🎉
Module Complete!

You now know how to define data models, perform CRUD operations, and secure your data with authorization rules. In the next module, we'll cover deploying your application to production.