Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions agent-identity/skyfire/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,32 @@

> **Maintained by:** Community contributors

[Skyfire](https://skyfire.xyz) is an AI-agent identity and payment platform — "Know Your Agent" (KYA) verification for autonomous agents. Integrate with Ory Hydra (OAuth2 issuance) and Ory Kratos (identity records) so AI agents authenticate to your APIs with verifiable identity and per-agent spend controls.
[Skyfire](https://skyfire.xyz) is an AI-agent identity and payment platform — "Know Your Agent" (KYA) verification for autonomous agents. Integrate with Ory Hydra (OAuth2 token issuance) so AI agents authenticate to your APIs with verifiable identity and per-agent spend controls.

**Type:** config (OAuth2 client + identity-mapping pattern — no first-party handler)
**Docs page:** [ory.com/docs/integrates-with/agent-identity/skyfire](https://www.ory.com/docs/integrates-with/agent-identity/skyfire)

## How it works

1. Skyfire issues each AI agent a verifiable credential + a Skyfire-managed identity.
2. Your application accepts Skyfire-issued credentials, then provisions a corresponding identity in Ory Kratos (one per agent) with `metadata_public.skyfire = { agent_id, kya_status }`.
3. Ory Hydra issues OAuth2 access tokens to the agent identity using standard `client_credentials` flow; the access token's `sub` is the Kratos identity id.
4. Backend APIs validate the access token at the gateway (see [`api-gateways/*`](../../api-gateways/)) and read the agent id from `metadata_public.skyfire.agent_id` if access control needs it.
2. Your application accepts Skyfire-issued credentials, then registers a corresponding OAuth2 client in Ory Hydra (one per agent) and stores the Skyfire details in the client's `metadata`, for example `{ "skyfire": { "agent_id": "...", "kya_status": "..." } }`.
3. The agent gets access tokens with the `client_credentials` grant. In these tokens, `sub` is the OAuth2 client ID, not an identity ID.
4. Backend APIs validate the access token at the gateway (see [`api-gateways/*`](../../api-gateways/)) and map `sub` to the Skyfire agent, for example by reading the client's `metadata` with `GET /admin/clients/{id}`. To put the agent ID into the token instead, add it as a claim with an [OAuth2 token webhook](https://www.ory.com/docs/hydra/guides/claims-at-refresh).
5. Payment / spend controls stay on the Skyfire side.

## Setup outline

1. Sign up at Skyfire; create an agent and obtain its credentials.
2. Build a small provisioning service that:
- Accepts Skyfire credentials from the agent.
- Creates / looks up a Kratos identity keyed by Skyfire's agent ID.
- Creates an Ory Hydra OAuth2 client (or reuses one) scoped to the agent's permissions.
3. The agent uses standard OAuth2 client-credentials flow against Hydra to obtain access tokens for API calls.
- Creates (or looks up) the agent's Ory Hydra OAuth2 client, scoped to the agent's permissions, with the Skyfire agent ID in the client `metadata`.
3. The agent uses the standard OAuth2 client-credentials flow against Hydra to obtain access tokens for API calls.

## Notable

- Agent-identity is an emerging space — patterns are still firming up; this integration is a reference architecture more than a turnkey product.
- Treat each agent as a distinct Kratos identity (not as a "user with multiple agents") so audit trails and revocation work cleanly.
- KYA verification status should be re-checked periodically — Skyfire can revoke agents and your code should respect that on token issuance.
- Treat each agent as a distinct OAuth2 client (not as a "user with multiple agents") so audit trails and revocation work cleanly.
- KYA verification status should be re-checked periodically — Skyfire can revoke agents and your code should respect that on token issuance. The OAuth2 token webhook runs on every token request and can deny it with a `403` response.

## Status

Expand Down
12 changes: 6 additions & 6 deletions agent-identity/skyfire/registry.entry.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,17 @@ maintainedBy: Community contributors
description: >-
Pair Skyfire's "Know Your Agent" (KYA) verification with Ory for AI-agent identity — Skyfire issues
verifiable credentials per agent; a customer-built provisioning service maps each Skyfire agent to an Ory
Kratos identity, and Hydra issues OAuth2 access tokens (client_credentials) for the agent to call APIs.
Hydra OAuth2 client, and Hydra issues OAuth2 access tokens (client_credentials) for the agent to call APIs.
useCase: >-
A product running autonomous AI agents that need to authenticate to backend APIs wants the agents to have
verifiable identity (so the call site can answer "which agent is this and is it KYA-verified") plus
per-agent spend controls — patterns that combining Skyfire and Ory makes natural.
coreFunctionality: |-
- Each agent = one Kratos identity (not one user with multiple agents) — keeps audit and revocation clean.
- metadata_public.skyfire stores agent_id + kya_status on the Kratos identity.
- Ory Hydra issues access tokens via client_credentials; sub = Kratos identity id.
- API gateways validate tokens at the edge (see api-gateways/*) and can read metadata_public.skyfire.agent_id for access control.
- Skyfire can revoke agents — periodically re-check KYA status before token issuance to respect revocations.
- Each agent = one Hydra OAuth2 client (not one user with multiple agents) — keeps audit and revocation clean.
- The client's metadata stores skyfire.agent_id + kya_status.
- Ory Hydra issues access tokens via client_credentials; sub = the OAuth2 client ID.
- API gateways validate tokens at the edge (see api-gateways/*) and map sub to the agent via the client's metadata, or read an agent ID claim added by an OAuth2 token webhook.
- Skyfire can revoke agents — re-check KYA status on token issuance; the OAuth2 token webhook runs on every token request and can deny it with 403.
- Payment / spend controls stay on the Skyfire side.
- Status: community/proposed — emerging space, reference architecture more than turnkey product.
oryMechanism:
Expand Down
4 changes: 2 additions & 2 deletions agent-identity/tetrate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

Two layers, each owning a distinct concern:

1. **Authorization / policy layer (Ory)** — decides what an agent or user may access at the resource level. AI agents are first-class identities authenticated through Ory; Hydra issues OAuth2/OIDC tokens (`client_credentials`), and Keto holds the fine-grained policies.
1. **Authorization / policy layer (Ory)** — decides what an agent or user may access at the resource level. Each AI agent is an OAuth2 client in Ory Hydra and gets tokens with the `client_credentials` grant. In these tokens, `sub` is the client ID, so policies in Ory Keto refer to the agent by its client ID. An agent that acts for a user needs a flow with user consent, such as the authorization code flow, so that the token's `sub` identifies the user. For delegated requests, identify the agent from the validated access token's `client_id` claim and check both the agent's and the user's permissions.
2. **Network / gateway layer (Tetrate)** — enforces those policies on live traffic when agents call models, tools, and enterprise services. When an agent invokes an MCP tool, Tetrate Agent Router Enterprise evaluates the request against the Ory-defined policies.

Because enforcement is parameter-level, Tetrate can gate **not only which tools** an agent may use **but also which request parameters** are allowed, based on policies defined in Ory Keto.
Expand All @@ -29,7 +29,7 @@ For example: an agent processes routine refunds automatically but requires appro
## Notable

- Tetrate Agent Router Enterprise is built on **Envoy AI Gateway** — see [`api-gateways/envoy`](../../api-gateways/envoy/) for the underlying pattern of validating Ory tokens at the edge.
- Closely related to [`agent-identity/skyfire`](../skyfire/): both treat AI agents as first-class Ory identities, differing in where verification and enforcement sit.
- Closely related to [`agent-identity/skyfire`](../skyfire/): both model AI agents as OAuth2 clients in Ory Hydra, differing in where verification and enforcement sit.
- Agent-identity is an emerging space — this integration reflects an announced partnership and is a reference architecture rather than a turnkey product.

## Status
Expand Down
2 changes: 1 addition & 1 deletion agent-identity/tetrate/registry.entry.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ useCase: >-
for sensitive data.
coreFunctionality: |-
- Two-layer architecture: Ory is the authorization/policy layer (what an agent or user may access at the resource level); Tetrate Agent Router Enterprise is the network/gateway layer that enforces policy on live traffic.
- Agents are first-class identities authenticated through Ory; Hydra issues OAuth2/OIDC tokens via client_credentials.
- Each agent is a Hydra OAuth2 client using client_credentials, so the token's sub is the client ID and Keto policies refer to that ID; agents acting for a user use a consented flow (e.g. authorization code), where sub is the user and the agent is the token's client_id.
- Authorization policies defined in Ory Keto; Tetrate evaluates each agent request (tool call + request parameters) against them at the point an agent invokes an MCP tool.
- Parameter-level enforcement: Tetrate can gate not just which tools an agent may use but which request parameters are allowed, based on Keto policies.
- Step-up auth: requests exceeding a risk threshold can pause, trigger an authentication/approval flow through Ory, and issue short-lived elevated access.
Expand Down
13 changes: 7 additions & 6 deletions directory-sync/bamboohr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@

> **Maintained by:** Community contributors

[BambooHR](https://bamboohr.com) is an HRIS popular with SMB and mid-market companies. This integration enriches Ory identities with employee data from BambooHR via an Ory Action webhook on login or registration, returning `{ employed, employee_id, job_title, department, hire_date }` so a post-flow Action can gate access on employment status or write the fields into identity metadata.
[BambooHR](https://bamboohr.com) is an HRIS popular with SMB and mid-market companies. This integration checks employment status in BambooHR from an Ory Action webhook on login or registration, and blocks the flow when the user isn't an active employee.

**Type:** webhook (Ory Action calls a handler during a flow)
**Docs page:** [ory.com/docs/integrates-with/directory-sync/bamboohr](https://www.ory.com/docs/integrates-with/directory-sync/bamboohr)

## Use case

A company runs internal SaaS for employees and wants login to gate on employment status — off-boarded employees automatically lose access without a separate deprovision step. BambooHR is the HRIS source of truth; the integration looks up the user's BambooHR employee record at login and returns the employment flag so Ory can fail the flow on `employed: false`.
A company runs internal SaaS for employees and wants login to gate on employment status — off-boarded employees automatically lose access without a separate deprovision step. BambooHR is the HRIS source of truth; the integration looks up the user's BambooHR employee record at login and blocks the sign-in when there is no active record.

## How it works

1. A user logs in (or registers) through an Ory flow.
2. Ory fires the sync Action webhook to this handler. The handler verifies the shared secret.
3. The handler calls `GET /employees/directory` and filters by `workEmail` (BambooHR lacks a direct lookup-by-email API).
4. On match, the handler calls `GET /employees/<id>?fields=status,jobTitle,department,hireDate,terminationDate` and returns `{ employed: status === "Active", employee_id, job_title, department, hire_date }`.
5. On no match, the handler returns `{ employed: false }` so Ory can decide (some companies want to allow non-employee logins; others gate access entirely).
4. On match, the handler calls `GET /employees/<id>?fields=status,jobTitle,department,hireDate,terminationDate`. An `Active` employee gets `204 No Content`, and the flow continues.
5. On no match or a non-`Active` status, the handler responds with `403` and a [`messages` body](https://www.ory.com/docs/kratos/hooks/configure-hooks#webhook-responses), so Ory stops the flow and shows the user why. A `200` response doesn't block anything, whatever its body says.
6. If BambooHR is slow or unreachable, the handler responds with `502`, and Ory fails the flow. This check makes every sign-in depend on BambooHR, so decide whether to keep that fail-closed behavior or let users in when BambooHR can't answer.

## Prerequisites

Expand All @@ -42,9 +43,9 @@ The server listens on the port specified in `.env` (default 3000) and exposes:

## Configure Ory

1. Configure the Action hook using the snippet in [`ory-actions.yaml`](ory-actions.yaml) — register on `after:login` and optionally `after:registration`.
1. Configure the Action hook using the snippet in [`ory-actions.yaml`](ory-actions.yaml). Add it to the login method lists, for example `selfservice.flows.login.after.password.hooks`, and optionally the registration method lists. Ory runs the global `selfservice.flows.login.after.hooks` list only for a method that has no hooks of its own.
2. The body template is [`jsonnet/identity.jsonnet`](jsonnet/identity.jsonnet); only `email` is required.
3. Keep `response.parse: true` and `response.ignore: false` so Ory consumes the enrichment payload.
3. Keep `response.parse: true` and `response.ignore: false` so Ory reads the `messages` body of a `403` and shows it to the user.
4. Set `ORY_WEBHOOK_SECRET` in the handler's `.env` to match the `X-Webhook-Secret` value in the hook config.

Off-boarded-employee gating, role-based attribute mapping, and saved-report lookups for large directories: see the [docs page](https://ory.com/docs/integrates-with/directory-sync/bamboohr).
Expand Down
16 changes: 8 additions & 8 deletions directory-sync/bamboohr/registry.entry.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,18 @@ category: directory-sync
type: webhook
maintainedBy: Community contributors
description: >-
BambooHR is an HRIS popular with SMB and mid-market companies. This integration enriches Ory identities with employee
data from BambooHR via an Ory Action webhook on login or registration, returning employment status, job title,
department, and hire date so a post-flow Action can gate access or write the fields into identity metadata.
BambooHR is an HRIS popular with SMB and mid-market companies. This integration checks employment status in
BambooHR from an Ory Action webhook on login or registration and blocks the flow for users who aren't active
employees.
useCase: >-
A company runs internal SaaS for employees and wants login to gate on employment status — off-boarded employees lose
access automatically without a separate deprovision step. BambooHR is the HRIS source of truth; the integration looks
up the user's BambooHR record at login and returns the employment flag so Ory can fail the flow on employed: false.
up the user's BambooHR record at login and blocks the sign-in when there is no active record.
coreFunctionality: |-
- Sync Ory Action webhook (response.parse: true) on after:login and optionally after:registration.
- Looks up the employee via /employees/directory filtered by workEmail (BambooHR has no direct lookup-by-email API).
- Fetches detail fields (status, jobTitle, department, hireDate, terminationDate) and returns { employed, employee_id, job_title, department, hire_date }.
- Returns { employed: false } on no match so Ory can branch on member-vs-non-member traffic instead of erroring.
- Sync Ory Action webhook (response.parse: true) in the login method hook lists (for example selfservice.flows.login.after.password.hooks) and optionally registration.
- Looks up the employee via /employees/directory filtered by workEmail (BambooHR has no direct lookup-by-email API), then fetches the employee's status.
- Active employees get 204 No Content; no match or a non-Active status gets 403 with a Kratos `messages` body, which blocks the flow and shows the reason.
- BambooHR errors return 502, which fails the flow, so every sign-in depends on BambooHR availability.
- BambooHR Basic auth: API key as username, literal "x" as password — documented in code so it doesn't look like a typo.
oryMechanism:
- actions
Expand Down
36 changes: 24 additions & 12 deletions directory-sync/bamboohr/webhook/server.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// SPDX-License-Identifier: Apache-2.0
//
// Reference webhook handler for the Ory <> BambooHR integration.
// Looks up the BambooHR employee matching an Ory identity's email and returns
// employment fields back to Ory so the post-flow Action can decide whether to
// allow the flow.
// Looks up the BambooHR employee matching an Ory identity's email. Active
// employees get 204 No Content, which lets the flow continue. Everyone else
// gets 403 with a Kratos `messages` body, which blocks the flow and shows the
// reason to the user. The hook must use `response.parse: true` for Kratos to
// read that body.

import "dotenv/config";
import crypto from "node:crypto";
Expand Down Expand Up @@ -39,6 +41,18 @@ interface BamboohrDirectoryEmployee {
interface BamboohrDirectoryResponse {
employees?: BamboohrDirectoryEmployee[];
}
// Kratos only renders messages from a 4xx body in this shape.
function denyFlow(res: Response, text: string) {
res.status(403).json({
messages: [
{
instance_ptr: "#/",
messages: [{ id: 10000001, text, type: "error", context: {} }],
},
],
});
}

interface BamboohrEmployeeDetail {
status?: string;
jobTitle?: string;
Expand Down Expand Up @@ -97,7 +111,7 @@ app.post(

if (!employee) {
console.log(`No BambooHR record for ${email} (identity ${identity_id})`);
res.json({ ok: true, employed: false });
denyFlow(res, "Only current employees can sign in.");
return;
}

Expand All @@ -116,14 +130,12 @@ app.post(
}

const detail = (await empRes.json()) as BamboohrEmployeeDetail;
res.json({
ok: true,
employed: detail.status === "Active",
employee_id: employee.id,
job_title: detail.jobTitle ?? null,
department: detail.department ?? null,
hire_date: detail.hireDate ?? null,
});
if (detail.status !== "Active") {
console.log(`BambooHR employee ${employee.id} is ${detail.status ?? "unknown"} (identity ${identity_id})`);
denyFlow(res, "Only current employees can sign in.");
return;
}
res.status(204).end();
} catch (err) {
console.error("Enrich error:", (err as Error).message);
res.status(502).json({ error: "enrich_failed" });
Expand Down
10 changes: 6 additions & 4 deletions directory-sync/google-workspace-scim/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ Automatic user provisioning and deprovisioning from Google Workspace into Ory Ne

The full walkthrough lives in the [Ory docs page above](https://www.ory.com/docs/kratos/manage-identities/scim/google-workspace). Short version:

1. In **Google Workspace Admin Console** → **Apps → Web and mobile apps** → **Add app → Search for apps**, find or add **Ory Network** (or your custom SAML app).
2. Under the app's **Auto-provisioning** section, enable SCIM and paste:
- **Endpoint URL** = SCIM endpoint from your Ory Network organization's setup-link.
- **Access token** = SCIM bearer token from the same setup-link.
Google Workspace provisions users only to the apps in its [list of supported apps](https://knowledge.workspace.google.com/admin/users/advanced/about-automated-user-provisioning), not to a custom SCIM endpoint such as Ory Network. The Ory Network guide works around this with a supported catalog app.

1. In **Google Workspace Admin Console** → **Apps → Web and mobile apps** → **Add app → Search for apps**, search for **Keeper** and select **Keeper Web (SAML)**.
2. Under the app's provisioning section, click **Configure autoprovisioning** and enter:
- **Access token** = the authorization header secret of the SCIM client on your Ory Network organization.
- **Endpoint URL** = the SCIM client's base URL.
3. Configure the **attribute mapping** — Google primary email → `userName`, given/family name → `name.givenName`/`name.familyName`, groups → `groups`.
4. Choose the provisioning scope — which OUs / groups Workspace will sync to Ory.
5. Toggle **on for all** users / OUs covered.
Expand Down
Loading
Loading