Reference
Integrations
iamsec federates with the identity layer you already run and replaces the authorization layer you almost certainly got wrong. The acronyms below are the official ones. We did not invent the expansions, and we take no questions about them.
OIDC — Ongoing Intimacy Disclosure Connector
Our native protocol, and the only one that carries the properties that matter: affirmation age, withdrawal epoch, and the fact that a grant is bilateral. Standard OAuth gives you a token that says what was allowed. OIDC-for-iamsec also says who else agreed, when they last said it out loud, and the epoch after which the answer is no.
GET /.well-known/openid-configuration
{
"issuer": "https://auth.iamsec.si",
"authorization_endpoint": "https://auth.iamsec.si/authorize",
"token_endpoint": "https://auth.iamsec.si/token",
"revocation_endpoint": "https://auth.iamsec.si/revoke",
"introspection_endpoint": "https://auth.iamsec.si/introspect",
"scopes_supported": ["scene.open", "contact.sensation", "aftercare.provide", "..."],
"response_modes_supported": ["query", "fragment"],
"claim_types_supported": ["normal", "agreed_by", "affirmed_at", "withdrawn_before"],
"grant_types_supported": [
"urn:iamsec:grants:negotiated-code",
"urn:iamsec:grants:continuing-affirmation"
],
"token_endpoint_auth_methods_supported": ["private_key_jwt"],
"iamsec_consent_model": "ongoing",
"iamsec_consent_is_durable": false,
"iamsec_revocation_reason_supported": false
}Two claims are additions to the spec rather than extensions of it, and they are the reason any of this works:
| Claim | Type | Semantics |
|---|---|---|
withdrawn_before | epoch (unix ms) | Any token whose signature predates this value is invalid, replica-independent. This is the fencing write — it does not wait for propagation, it makes propagation irrelevant. |
affirmed_at | object | Per-factor timestamps. The evaluator compares each against the scope ceiling at call time, which is why a session can be live and still return deny. |
Introspection response
{
"active": true,
"sub": "bottom/b-4417",
"aud": "top/a-2210",
"scope": "scene.open contact.sensation aftercare.provide",
"exp": 1763064667,
"affirmed_at": { "written": 1763041200, "verbal": 1763060467 },
"withdrawn_before": null,
"iamsec": {
"policy": "policy/8f21",
"counterparty_signed": true,
"checkin_due_in_s": 41,
"on_checkin_miss": "revoke_all"
}
}SAML — Seduction Assertion Markup Language
For venues that already run a federation they cannot reasonably replace. We accept SAML 2.0 assertions for identity only, and we actively discard any authorization attributes they carry.
<saml:Attribute Name="eduPersonEntitlement"> <saml:AttributeValue>urn:iamsec:role:top</saml:AttributeValue> <!-- accepted: identity --> </saml:Attribute> <saml:Attribute Name="urn:iamsec:permission:contact.restraint"> <saml:AttributeValue>granted</saml:AttributeValue> <!-- dropped: see below --> </saml:Attribute>
An assertion from your IdP can tell us who a person is. It cannot tell us that they agreed to anything, because a directory that an administrator writes to is, structurally, a system in which consent is granted by a third party. Every SAML deployment starts with a policy set of zero, and it stays there until both subjects affirm.
SCIM — System for Cross-community Intimacy Management
Inbound provisioning is supported. Outbound sharing of anything other than identifiers is not, and never will be.
| SCIM operation | Supported | Notes |
|---|---|---|
| Users: create / patch / delete | yes | Provisions a subject with an empty policy set |
| Groups: create / membership | yes | Groups are venue-scoped and grant nothing |
| Users: read custom attributes | no | There are no custom attributes. Consent is not a directory field |
| Bulk export | no | A directory of who consents to what with whom is the document an attacker wants |
| Device / resource types | yes | Door sensors, tablets and check-in terminals provision as devices |
Deployment recipes
Okta or Entra ID as the identity provider
1. IdP authenticates the subject (you already have this) 2. iamsec issues a subject id sub = pairwise(aud, subject) 3. policy set initialised empty grants = 0, always 4. both subjects affirm out-of-band verbal factor, on their own devices 5. /evaluate from your app on every call 1.8ms p99, no cache 6. safeword hits IdP + iamsec IdP kills the session, we kill the grants
Step 6 is the one people miss: a real safeword should log the person out of everything, not merely invalidate a scope. Our IdP-initiated end_session hook does both, and it is the only outbound call in the system that we will not let you disable with a feature flag.
Keycloak in a basement
The most common Coven deployment is a self-hosted Keycloak fronting an iamsec binary on the same machine, no egress, and a policy store on an encrypted volume. Total footprint is 40MB and one systemd unit. If the network goes down, check-ins continue locally, the somatic factor keeps working, and revocation still propagates — through the kernel, over a unix socket, which is the only reason we bothered to write that path.
Mobile check-in terminals
use iamsec_core::{Policy, Factor};
let p = Policy::load("/var/venue/policy-8f21.cbor")?;
let decision = p.evaluate(
Action::parse("contact.sensation")?,
&Context::local().with_checkin(Factor::Somatic, now_ms()),
);
// Offline, verifiable, and it still returns deny on a stale affirmation,
// because the ceiling is inside the signed document, not in a service.Four ways to get this wrong
| Anti-pattern | Why it breaks | Do this instead |
|---|---|---|
| Caching /evaluate for the session lifetime | You have just made consent durable again, which is the entire failure mode | Cache nothing, or cap at 250ms. It is 1.8ms |
| Treating a signed policy as ongoing consent | The signature proves the negotiation happened, not that it still holds | Check affirmed_at against the scope ceiling on every call |
| Gating safety on webhook delivery | A dropped event now extends someone's permission | Webhooks are for dashboards. Evaluation is for decisions |
| Mapping IdP groups to scopes | An administrator can now grant what only a person can | Groups identify. Only subjects affirm |