Auth Rules

All rules are global. They apply to all queries and mutations, or permitted operations unless otherwise configured differently with model-level rules/field-level rules.

Grafbase supports the following strategies to control access to data:

  • Signed-in user — Allow access to any signed-in user
  • Owner-based — Allow access to the owner only
  • Group-based — Allow access to users of a group

The rules below work with all the available auth providers.

You can configure signed-in access to data using a valid provider:

schema
  @auth(
    providers: [{ type: oidc, issuer: "{{ env.ISSUER_URL }}" }]
    rules: [{ allow: private }]
  ) {
  query: Query
}

You can configure access to data so that it can only be accessed by the owner.

The owner of data is currently set using the value of the sub claim inside the authorization token with requests at the time the data is created.

Owner based auth is evaluated based on the presence of the sub claim:

  • If the sub claim is present and there are owner-based rules set, the owner-based rules are applied.
  • If the sub is present but there are no owner-based rules set, the group-based rules are applied.
schema
  @auth(
    providers: [{ type: oidc, issuer: "{{ env.ISSUER_URL }}" }]
    rules: [{ allow: owner }]
  ) {
  query: Query
}
  • Data can only have one owner.
  • Owner-based rules can only be set globally.

You can configure group-based access to data based on the groups claim of a valid JWT:

schema
  @auth(
    providers: [{ type: oidc, issuer: "{{ env.ISSUER_URL }}" }]
    rules: [{ allow: groups, groups: ["backend", "admin"] }]
  ) {
  query: Query
}

The user only has access if the JWT claims contain one of the allowed groups. The following decoded JWT contains a valid groups value admin:

{
  "exp": 1659646197,
  "groups": ["admin"],
  "iat": 1659559797,
  "iss": "https://clerk.b74v0.5y6hj.lcl.dev",
  "nbf": 1659559792,
  "sub": "user_12345"
}

You can optionally set groupsClaim for group-based auth to use a custom claim path.

Consider the following JWT provided by your issuer:

{
  "header": {
    "typ": "JWT",
    "alg": "RS256"
  },
  "payload": {
    "https://grafbase.com/jwt/claims": {
      "x-grafbase-allowed-roles": ["editor", "user", "mod"]
    }
    // ...
  }
}

Here the groups claim x-grafbase-allowed-roles is nested inside of https://grafbase.com/jwt/claims. This is declared using .. You can provide a groupsClaim path along with the provider:

schema
  @auth(
    providers: [
      {
        type: oidc
        issuer: "{{ env.ISSUER_URL }}"
        groupsClaim: "https://grafbase\\.com/jwt/claims.x-grafbase-allowed-roles"
      }
    ]
  ) {
  query: Query
}

Any . used inside of URLs will need to be escaped for the groupsClaim value.

You can set rules globally by using the @auth directive on schema:

schema
  @auth(
    providers: [{ type: oidc, issuer: "{{ env.ISSUER_URL }}" }]
    rules: [{ allow: private }]
  ) {
  query: Query
}

You can configure rules on a per-model basis using the @auth directive:

type User
  @model
  @auth(
    rules: [
      { allow: private, operations: [read] }
      { allow: groups, groups: ["admin"] }
    ]
  ) {
  name: String!
}

You can configure rules on a per-field basis using the @auth directive:

type User @model @auth(rules: [{ allow: private }]) {
  id: ID!
  title: String!
  password: String @auth(rules: [{ allow: groups, groups: ["admin"] }])
}

There are a few things to consider when creating field-level rules:

  • You can't create entities with any field you can't create
  • You can't delete entities with any field you can't delete
  • You can't link/unlink entities without being able to read the entity id
  • You can't request any fields to be returned by queries or mutations you can't read

You can restrict access to specific operations using these operation rules:

  • get — Allow to query a single entity
  • list — Allow to query entity collections
  • read — Combines both get and list
  • create — Allow create mutations
  • update — Allow update mutations
  • delete — Allow delete mutations
schema
  @auth(
    providers: [{ type: oidc, issuer: "{{ env.ISSUER_URL }}" }]
    rules: [{ allow: private, operations: [get] }]
  ) {
  query: Query
}

You can also set separate rules for operations.

The following authorization enables:

  • Signed-in users to perform read operations
  • Users of the moderator group to perform update operations
  • Users of the admin group to perform all operations
schema
  @auth(
    providers: [{ type: oidc, issuer: "{{ env.ISSUER_URL }}" }]
    rules: [
      { allow: private, operations: [read] }
      { allow: groups, groups: ["moderator"], operations: [update] }
      { allow: groups, groups: ["admin"] }
    ]
  ) {
  query: Query
}

You can combine rules at any level:

schema
  @auth(
    providers: [{ type: oidc, issuer: "{{ env.ISSUER_URL }}" }]
    rules: [
      { allow: private, operations: [read] } # Signed-in can read only
      { allow: groups, groups: ["admin"] } # Full access
      { allow: owner, operations: [create, update] } # read, create, update
    ]
  ) {
  query: Query
}
  • Model rules replace global rules.
  • Field rules replace model/global rules.
Was this page helpful?