v4.2.0

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.

discovery
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:

ClaimTypeSemantics
withdrawn_beforeepoch (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_atobjectPer-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

POST /introspect
{
  "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.

attribute handling
<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 operationSupportedNotes
Users: create / patch / deleteyesProvisions a subject with an empty policy set
Groups: create / membershipyesGroups are venue-scoped and grant nothing
Users: read custom attributesnoThere are no custom attributes. Consent is not a directory field
Bulk exportnoA directory of who consents to what with whom is the document an attacker wants
Device / resource typesyesDoor sensors, tablets and check-in terminals provision as devices

Deployment recipes

Okta or Entra ID as the identity provider

sequence
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

Rust core, WASM target
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-patternWhy it breaksDo this instead
Caching /evaluate for the session lifetimeYou have just made consent durable again, which is the entire failure modeCache nothing, or cap at 250ms. It is 1.8ms
Treating a signed policy as ongoing consentThe signature proves the negotiation happened, not that it still holdsCheck affirmed_at against the scope ceiling on every call
Gating safety on webhook deliveryA dropped event now extends someone's permissionWebhooks are for dashboards. Evaluation is for decisions
Mapping IdP groups to scopesAn administrator can now grant what only a person canGroups identify. Only subjects affirm