Skip to main content

Use Token Claims As Contextual Tuples

Contextual Tuples allow authorization checks that depend on dynamic or contextual relationships that have not been written to the Okta FGA store, enabling some Attribute Based Access Control (ABAC) use cases.

To enable more ABAC use-cases that rely on specific attributes and conditions, you can also use Okta FGA`s conditions.

Before You Start

To follow this guide, familiarize yourself with the following Okta FGA Concepts:

  • A Relation: is a string defined in the type definition of an authorization model that defines the possibility of a relationship between an object of the same type as the type definition and a user in the system.
  • A Check Request: is a call to the Okta FGA check endpoint that returns whether the user has a certain relationship with an object.
  • A Relationship Tuple: a grouping consisting of a user, a relation and an object stored in Okta FGA

User Directories, Identity Tokens, And Relationships

User directories store user information that's accessed when making authorization decisions, like the group the user belongs to, their roles, or their department. The natural way to use those relationships in a Relationship-Based Access Control system like Okta FGA is to create tuples for each relation. However, implementing a synchronization mechanism to keep the user directory data up to date with tuples in the store can be challenging.

When applications implement authentication using an OIDC authorization service, they receive an ID Token or an Access token, with certain claims that can be customized based on the application's needs. Instead of writing tuples to the Okta FGA, you can use the content of the token in Contextual Tuples to make authorization checks, understanding that, if those relationships change while the token has not expired, users will still get access to the resources the content of the token entitled them to.

Example

In this example, the application uses the following authorization model, in which documents can be viewed by members of a group:

model
schema 1.1

type user

type group
relations
define member: [user]

type document
relations
define viewer: [group#member]

When a group is added as a viewer of a document, the application writes tuples like those below:

Initialize the SDK
// Checkout the "How to Setup the SDK Client" page for more details.
const { CredentialsMethod, OpenFgaClient } = require('@openfga/sdk'); // OR import { CredentialsMethod, OpenFgaClient } from '@openfga/sdk';

// Ensure the environment variables are set
// FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'fga.us.auth0.com'
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page

const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL,
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID,
credentials: { // Credentials are not needed if connecting to the Playground API
method: CredentialsMethod.ClientCredentials,
config: {
apiTokenIssuer: process.env.FGA_API_TOKEN_ISSUER,
apiAudience: process.env.FGA_API_AUDIENCE,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
},
},
});

await fgaClient.write({
writes: [
// Members of the marketing group can view the product-launch document
{"_description":"Members of the marketing group can view the product-launch document","user":"group:marketing#member","relation":"viewer","object":"document:product-launch"},
// Members of the everyone group can view the welcome document
{"_description":"Members of the everyone group can view the welcome document","user":"group:everyone#member","relation":"viewer","object":"document:welcome"}
],
}, {
authorization_model_id: "1uHxCSuTP0VKPYSnkq1pbb1jeZw"
});

Let's assume that the Access Token the application receives has a list of the groups the user belongs to:

{
"iss": "https://id.company.com",
"sub": "6b0b14af-59dc-4ff3-a46f-ad351f428726",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516239022,
"azp" : "yz54KAoW1KGFAUU982CEUqZgxGIdrpgg",
"groups": ["marketing", "everyone"]
}

When making a authorization check, the application uses the groups claim in the token and adds contextual tuple for each group, indicating that the user is a member of that group:

Initialize the SDK
// Checkout the "How to Setup the SDK Client" page for more details.
const { CredentialsMethod, OpenFgaClient } = require('@openfga/sdk'); // OR import { CredentialsMethod, OpenFgaClient } from '@openfga/sdk';

// Ensure the environment variables are set
// FGA_API_URL = 'https://api.us1.fga.dev' // 'https://api.eu1.fga.dev' for EU and 'https://api.au1.fga.dev' for AU
// FGA_STORE_ID = 'YOUR_STORE_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_MODEL_ID = 'YOUR_MODEL_ID' - optional, can be overridden per request, helps reduce latency
// FGA_API_TOKEN_ISSUER = 'fga.us.auth0.com'
// FGA_API_AUDIENCE = 'https://api.us1.fga.dev/' // 'https://api.eu1.fga.dev/' for EU and 'https://api.au1.fga.dev/' for AU
// FGA_CLIENT_ID = 'YOUR_CLIENT_ID' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page
// FGA_CLIENT_SECRET = 'YOUR_CLIENT_SECRET' - Get this from your store settings in the dashboard, refer to the "How to get your API Keys" page

const fgaClient = new OpenFgaClient({
apiUrl: process.env.FGA_API_URL,
storeId: process.env.FGA_STORE_ID,
authorizationModelId: process.env.FGA_MODEL_ID,
credentials: { // Credentials are not needed if connecting to the Playground API
method: CredentialsMethod.ClientCredentials,
config: {
apiTokenIssuer: process.env.FGA_API_TOKEN_ISSUER,
apiAudience: process.env.FGA_API_AUDIENCE,
clientId: process.env.FGA_CLIENT_ID,
clientSecret: process.env.FGA_CLIENT_SECRET,
},
},
});

// Run a check
const { allowed } = await fgaClient.check({
user: 'user:6b0b14af-59dc-4ff3-a46f-ad351f428726',
relation: 'viewer',
object: 'document:product-launch',
contextual_tuples: [
{"_description":"user 6b0b14af-59dc-4ff3-a46f-ad351f428726 is a member of the marketing group","user":"user:6b0b14af-59dc-4ff3-a46f-ad351f428726","relation":"member","object":"group:marketing"},{"_description":"user 6b0b14af-59dc-4ff3-a46f-ad351f428726 is a member of the everyone group","user":"user:6b0b14af-59dc-4ff3-a46f-ad351f428726","relation":"member","object":"group:everyone"}
],
}, {
authorization_model_id: '1uHxCSuTP0VKPYSnkq1pbb1jeZw',
});

// allowed = true

The authorization check returns allowed = true, as there's a stored tuple saying that members of the marketing group are viewers of the product-launch document, and there's a contextual tuple indicating that the user is a member of the marketing group.

Warning

Contextual tuples:

Contextual and Time-Based Authorization

Learn how to authorize access that depends on dynamic or contextual criteria.

Authorization Through Organization Context

Learn to model and authorize when a user belongs to multiple organizations.

Conditions

Learn to model requring dynamic attributes.

Okta FGA API

Details on the Check API in the Okta FGA reference guide.

Have Feedback?

You can use any of our support channels for any questions or suggestions you may have.