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:
path_user_profile
path_user_role
anonymous_profile
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
usingprofiles
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.