Skip to main content

Role-Based Access Control

This tutorial demonstrates how to use role-based access control (RBAC) using Inigo's @access directive. This is an alternative approach to Inigo's declarative configurations for RBAC.

For this tutorial, Apollo Federation will also be used to demonstrate how RBAC can work in subgraphs as well.

Configuration Setup

Service Configuration

The Service should be configured with the following properties:

  1. path_user_profile
  2. path_user_role
  3. anonymous_profile
  4. anonymous_roles

A Service YAML that configures the Client Configuration along with the required properties is shown in this example:

kind: Service
name: apollo-gateway-fed-2-demo
spec:
path_client_info: "header.Client-Name"
path_user_id: jwt.email
path_organization_id: jwt.org_name
path_user_profile: jwt.profile
path_user_role: jwt.roles
anonymous_profile: guest
anonymous_roles: [acme_guest]

As with all Inigo YAML configurations, use inigo apply to apply the configuration as such:

inigo apply Service.yaml --label dev

Security Configuration

Set up profiles for the Security configuration, as shown in the following example:

kind: Security
name: apollo-gateway-fed-2-demo
spec:
profiles:
- name: guest
require_operation_name: true
- name: member
require_operation_name: true
- name: employee
require_operation_name: true
- name: manager
require_operation_name: true

Use inigo apply to apply the configuration as such:

inigo apply Security.yaml --label dev

Access Configuration

The Access should be configured for the profiles and roles, as shown in the following example:

kind: Access
name: apollo-gateway-fed-2-demo
spec:
profiles:
- name: guest
introspection_mode: partial
- name: member
introspection_mode: partial
- name: employee
introspection_mode: full
- name: manager
introspection_mode: full
roles:
- name: acme_guest
- name: acme_member
- name: acme_employee
- name: acme_manager

roles are used for the RBAC and will be referenced for the @access directive.

Note: The introspection_mode using profiles will apply to fields that have an @access directive, therefore limiting introspection using RBAC.

Use inigo apply to apply the configuration as such:

inigo apply Access.yaml --label dev

Schema Separation Concept

By having RBAC to the field-level, a high level control can be implemented across your entire schema. With introspection_mode: partial configured, clients with different roles will effectively see different schemas depending on where the @access directives are used.

Scenario 1: Hiding logout from Unauthenticated Clients

It does not make sense to allow unauthenticated clients to call logout, therefore it can be hidden from unauthenticated clients using the @access directive. The following example demonstates how the anonymous role of acme_guest is not allowed to access logout, as the other roles are allowed to have access:

extend type Mutation {
# Hide access to logout for unauthenticated clients
logout(id: Int): Boolean @access(role: ["acme_member", "acme_employee", "acme_manager"])
}

directive @access(
depth: Int
role: [String]
) on FIELD_DEFINITION

As shown in the example, the @access directive must be defined in the schema when used. Assuming that you have an accounts subgraph, the @access directive can be applied in the subgraph schema, and then it will be composed into the supergraph as well.

Should an unauthenticated client try to call logout, they will see the following error:

{
"errors": [
{
"message": "invalid access",
"path": [
"logout"
]
}
],
"data": null,
"extensions": {}
}

Scenario 2: Field-Level RBAC for Data Visibility

Assume that you have a reviews subgraph that contains product reviews.

For your GraphQL API, you want the Review author to be anonymous to all except acme_employee and acme_manager. Additionally, you want reviews limited to only acme_member, acme_employee, acme_manager. The following example demonstrates how this is possible:

type Review @key(fields: "id") {
id: ID!
body: String
author: User @access(role: ["acme_employee", "acme_manager"]) # Keep reviews anonymous to guests and members
product: Product
}

type User @key(fields: "id") {
id: ID!
username: String @shareable
reviews: [Review] @access(role: ["acme_member", "acme_employee", "acme_manager"]) # Limit access
}

directive @access(
depth: Int
role: [String]
) on FIELD_DEFINITION

Conclusion

Inigo's RBAC using @access provides an easy way to add controls to how your GraphQL API is used and introspected. If you cannot add the @access directive to the schema, you can instead use Inigo's declarative configurations as an alternative. Finally, @access works with Apollo Federation, so RBAC can be used independently in each subgraph schema.