Slack Identity ACL Cookbook
Audience: Agent authors who want to allow a Slack workspace or Slack user without adding Connector-specific setup UI.
Slack identity is carried as verified IdentityEvidence on
NormalizedMessage.sender.identities. Authorization checks must use
sender.identities, not sender.profile.
sender.profile is presentation context for humans and LLM attribution: display
names, usernames, avatars, locale, timezone, and whitelisted provider facts. It
may be projected from verified evidence, but it is still not the principal. Do
not authorize from sender.profile.provider_subject,
sender.profile.extensions.slack.team_id, usernames, display names, or email
hints.
Trust Then Authorize
There are two policy layers:
- Trusted Connector issuer policy is runtime/operator policy. It decides
whether this receiver trusts a Connector Instance, method, assurance class,
and subject namespace enough to verify forwarded attestations and attach them
to
sender.identities. - Per-agent authorization is agent code or prompt policy. It decides whether the verified Slack subject is allowed to perform this agent action.
In other words, a Connector signature answers “did this Connector issue the
claim?”, trusted issuer policy answers “may this runtime rely on that
Connector for Slack workspace-member claims?”, and the agent ACL answers
“is slack:T123/U456 allowed here?”.
A typical trusted issuer entry for the Slack Connector looks like:
[
{
"issuer": "mentionable-slack.example",
"methods": ["urn:mentionable:auth:slack-workspace-member:v0.1"],
"assurance": ["platform"],
"subject_prefixes": ["slack:"]
}
]
An operator may narrow subject_prefixes to ["slack:T123/"] if the runtime
should only accept one workspace from that Connector. Even then, the agent
should keep its own per-action ACL in code or system prompt.
Workspace ACL
This check allows any verified member of Slack workspace T123. The subject
prefix is slack:T123/.
import type { NormalizedMessage } from '@mentionable/core'
const SLACK_WORKSPACE_MEMBER_METHOD = 'urn:mentionable:auth:slack-workspace-member:v0.1'
const TRUSTED_SLACK_CONNECTOR = 'mentionable-slack.example'
const ALLOWED_WORKSPACE_PREFIX = 'slack:T123/'
export function isAllowedSlackWorkspace(sender: NormalizedMessage['sender']): boolean {
return hasSlackIdentity(sender, (subject) => subject.startsWith(ALLOWED_WORKSPACE_PREFIX))
}
function hasSlackIdentity(
sender: NormalizedMessage['sender'],
subjectMatches: (subject: string) => boolean,
): boolean {
return (sender.identities ?? []).some(
(identity) =>
identity.issuer === TRUSTED_SLACK_CONNECTOR &&
identity.method === SLACK_WORKSPACE_MEMBER_METHOD &&
identity.assurance === 'platform' &&
subjectMatches(identity.subject),
)
}
Exact User ACL
This check allows only Slack user U456 in workspace T123. The exact subject
is slack:T123/U456.
import type { NormalizedMessage } from '@mentionable/core'
const SLACK_WORKSPACE_MEMBER_METHOD = 'urn:mentionable:auth:slack-workspace-member:v0.1'
const TRUSTED_SLACK_CONNECTOR = 'mentionable-slack.example'
const ALLOWED_SLACK_USER = 'slack:T123/U456'
export function isAllowedSlackUser(sender: NormalizedMessage['sender']): boolean {
return (sender.identities ?? []).some(
(identity) =>
identity.issuer === TRUSTED_SLACK_CONNECTOR &&
identity.method === SLACK_WORKSPACE_MEMBER_METHOD &&
identity.assurance === 'platform' &&
identity.subject === ALLOWED_SLACK_USER,
)
}
These snippets assume the receiving runtime only attaches Connector-issued
evidence after verifying the attestation signature, audience, freshness, and
Trusted Connector issuer policy. The explicit issuer, method, and
assurance checks make the agent’s authorization intent clear and keep the ACL
from accidentally accepting a different identity source.