Identity

⚠️

We now support integration with 3rd-party authentication providers, and will be deprecating the authenticate action that ships with the Identity model.

Almost every application requires authentication and for this purpose Keel comes with a built-in Identity model. This model represents the different identities that have authenticated with your app and includes a few actions that can be used to build authentication into your product.

The Identity model is intended to be a building block for further authentication logic and is not intended to be a complete authentication solution.

Usage in models

Since Identity is a regular model, you can use it as a relationship field. A common use-case for this is to add Identity to the model that represents users in your application. For instance, you might add Identity to a Customer model.

model Customer {
  fields {
    identity Identity @unique
  }
}

When using Identity like this you will probably want to mark the field as unique, so that an identity relates to a single customer.

Working with application users

To use the Identity model for authentication, you can associate it with a User model. A User model represents a user in the context of your application, and may have additional fields such as name, email, avatar, etc.

model User {
  fields {
    name String
    email String
    avatar String
    identity Identity @unique
  }
}

Then in your application, after a user has authenticated, you can create an Identity for them and associate it with their User record.

Usage in expressions

When writing an expression in your schema or writing a function, ctx.identity refers to the currently authenticated Identity. If the caller of an action is not authenticated then ctx.identity will be null.

Filtering records by Identity

It is common to want to make APIs that only return data owned by the caller. This can be done in a Keel app by filtering the returned records by the authenticated Identity. As an example in the following schema there is a myPosts action that only returns the posts created by a profile that is linked to the calling Identity.

model Profile {
  fields {
    identity Identity @unique
    posts Post[]
  }
}
 
model Post {
  fields {
    profile Profile
  }
  actions {
    list myPosts() {
      @where(post.profile.identity == ctx.identity)
    }
  }
}

Note that filtering records like this is different from defining permissions.

Setting a field to the caller's Identity

When Identity is used in a model as a relationship field, you should always use ctx.identity to set it, rather than trying to accept it as an input (which won't work).

model Profile {
  fields {
    identity Identity @unique
  }
  actions {
    create createProfile() {
      @set(profile.identity = ctx.identity)
    }
  }
}

Permissions

You can use ctx.identity in a permission expression to restrict who is allowed to perform certain actions.

model Profile {
  fields {
    identity Identity @unique
  }
}
 
model Post {
  fields {
    profile Profile
  }
 
  actions {
    get getPost(id)
    delete deletePost(id)
  }
 
  @permission(
    expression: true,
    actions: [get]
  )
 
  @permission(
    expression: post.profile.identity == ctx.identity,
    actions: [delete]
  )
}

@unique relationships with identity

When creating a @unique relationship between some model and Identity, as with many of the examples above, you will then be able to traverse from Identity to that model in expressions. See how useful this can be in the example below.

model Customer {
  fields {
    name Text
    identity Identity @unique
  }
}
 
model Order {
  fields {
    items OrderItems[]
    orderedBy Customer
  }
 
  actions {
    create createOrder(items.product.id) {
      @set(order.orderedBy = ctx.identity.customer)
    }
 
    update cancelOrder(id) {
      @where(order.status != Status.Shipped)
      @set(order.status = Status.Cancelled)
      @permission(expression: ctx.identity.customer == order.orderedBy)
    }
  }
}