Stop rebuilding login and billing for every product you ship.

A secure identity management and payment processing platform for SaaS application providers.

sysidm is the identity provider and revenue backbone your applications plug into. One standards-based login across everything you sell. Subscriptions, seats, trials and dunning enforced in a single place. Payments that settle in your merchant account. Deployed as your instance, on your issuer URL, with your keys.

OpenID Connect OAuth 2.0 + PKCE (S256) RS256 · JWKS argon2id AES-256-GCM at rest Your merchant account

Deployed for you

An enterprise deployment, not a shared account on someone else’s instance.

  • Your issuer URL and your signing keys
  • Your payment credentials, per environment
  • Your existing users, migrated with their hashes intact
  • A written integration contract and an end-to-end test

Every product you ship reinvents the same risky code

Registration, activation links, password reset, sessions, CSRF, seat counting, invitations, trials, plan changes, proration, failed payments, webhook signature verification, receipts. Each one is a security review item, and each one gets written again for the next product — slightly differently, by someone under deadline.

Leaves your codebase

Identity, accounts and money

  • Password storage, reset and recovery
  • Session issuance, rotation and revocation
  • Registration, activation and invitations
  • Seats, guests and account membership
  • Plans, trials, upgrades, renewals and dunning
  • Payment credentials and webhook verification
  • Transactional email and its delivery record

Stays yours

Your actual product

  • Your domain logic and your data model
  • Your application-specific permissions
  • Your UI — or ours, carrying your brand
  • Your pricing, expressed as configuration
  • Your merchant relationship, untouched
  • Your roadmap, minus the compliance backlog
  • Nothing else to patch at 2am

This is what your customers land on

One portal, one sign-in, every product. Each application is a tile; each tile lists the accounts that person belongs to with its own licence state, and signing in carries them straight into that account rather than dumping them on a home page to find their way.

id.eafx.net

Your applications

vikram · 4 accounts across 3 products · signed in once

WDWhyDash
Acme Opsowner · 12 seats ACTIVE
Personalowner · 1 seat TRIAL 47d
IBIOBudget
Acme Financecollaborator · seat 4 RENEWS 14 NOV
SMSmarked
EAFXguest · read only READ ONLY

The licence state on each row is not decoration — it is the same entitlement answer your application reads from the API on every request.

Standards, not a SDK

The reason an organisation can commit to this is that there is nothing proprietary to learn and nothing to be locked into. sysidm implements the published protocols and publishes its own configuration so your framework can discover them. Your engineers already know this surface.

OpenID Connect Core 1.0
authorization code flow

What it buys you: any conformant client library in any language works. No vendor-specific auth code in your product, and no bespoke token format for a future team to reverse-engineer.

OAuth 2.0
RFC 6749

Grants: authorization_code for people, client_credentials for services, refresh_token for long-lived sessions. Nothing else, because nothing else is needed.

PKCE
RFC 7636 · S256 mandatory

The part that matters: code_challenge is required, and only S256 is advertised. A browser SPA or mobile app is a public client — there is no secret in your bundle for someone to extract.

JWS with RS256
RFC 7518 · RSA 2048

Asymmetric by design: your application verifies tokens with a public key and never holds a shared secret. A compromised app server cannot mint tokens for another app.

JWKS
RFC 7517

Key rotation without a redeploy: verification keys are published at a documented endpoint, so rotating them is an operational act rather than a coordinated release across every product.

OIDC Discovery
/.well-known/openid-configuration

Self-describing: one URL gives your client library the issuer, endpoints, algorithms and supported claims. Leaving later is a configuration change, not a rewrite.

This is the live discovery document of the running instance, trimmed to the fields an integrator reads first. Not a datasheet claim — fetch it yourself.

GET /.well-known/openid-configuration
{
  "issuer": "https://id.eafx.net",
  "authorization_endpoint": ".../oidc/authorize",
  "token_endpoint":         ".../oidc/token",
  "jwks_uri":               ".../oidc/jwks",
  "userinfo_endpoint":      ".../v1/me",
  "end_session_endpoint":   ".../oidc/session/end",
  "id_token_signing_alg_values_supported": ["RS256"],
  "response_types_supported":  ["code"],
  "grant_types_supported":
      ["authorization_code", "client_credentials", "refresh_token"],
  "code_challenge_methods_supported": ["S256"]
}

discovery document  ·  public keys

Your application public client, no secret sysidm the IdP 1 · /authorize + code_challenge 2 · redirect with code exchange code + code_verifier id_token (RS256) + rotating refresh token verified locally against JWKS no shared secret, no callback per request, roles already inside the token

Security is the request path, not the roadmap

An identity provider is the highest-value target in your architecture, so it has to be measured by what it does on every single request rather than by a badge in a footer. These are the controls that are actually running.

#

Bearer artifacts are stored as hashes only

Authorization codes, refresh tokens, IdP sessions, email tokens, invitation tokens and API credentials are kept as SHA-256 digests. A stolen database dump cannot be replayed as access.

↻

Refresh tokens rotate on every use

Each refresh mints a new token and revokes the one that was used, in the same call. Replaying a consumed token fails rather than extending a stolen session.

a2

argon2id for passwords

19 MiB, time cost 2, parallelism 1. Legacy bcrypt and scrypt hashes are verified in place, so a migrated user base keeps its existing passwords and nobody is forced into a reset.

AES

Third-party secrets encrypted at rest

Payment credentials, mail credentials and confidential client secrets are stored AES-256-GCM with an authentication tag. The service refuses to start with a default or empty key instead of running insecurely.

⏹

Deactivation is enforced at every mint

Account status is re-checked when a code is issued and again before a code is redeemed, and the stale session is destroyed. Disabling a person takes away their applications, not just their password prompt.

⊚

Authorization fails closed

When sysidm is unreachable an application serves its read-only mirror and grants nothing. No role is ever inferred from local state that outlived a revocation.

CSRF

Cross-site request forgery, every state change

Double-submit validation on each form that changes anything. The session cookie is httpOnly with SameSite=Lax.

⌛

The front door is rate-limited

Login, registration and password recovery each carry their own per-address budget, and registration requires a single-use arithmetic captcha with a five-minute lifetime.

?

Recovery resists enumeration

The forgot-password form answers identically whether or not the address exists. On the machine API an unknown subject resolves to no roles, not an error — so there is no oracle to probe.

✅

Webhooks verified against raw bytes

Signatures are checked over the exact bytes received, before parsing. Events are idempotent by unique id, and an event is marked handled only once it was handled — so a failure genuinely retries.

@

A mail API that cannot become a relay

One recipient per call. The address must hold an account, or an invitation from that application, or sit in an explicitly allowlisted domain. Per-application hourly and daily quotas are counted from the delivery log itself, and CR/LF in a subject line is rejected outright.

</>

Escaped by construction

Every server-rendered page escapes & < > " ' through one shared function, and inline JSON is angle-bracket escaped so a payload cannot close a script context.

Isolation, tenancy and money

Every object in the schema hangs off the same axis: which application, which account, which person. There is no global pool of users with a visibility filter bolted on top, because a filter is one forgotten WHERE clause away from a breach.

Roles are deliberately few and deliberately ordered. A request is answered by the highest authority that applies, and the platform’s own administrators are never visible to your applications — there is no flag for an app to accidentally trust.

  • The owner counts as a seat. Never an exemption, so the arithmetic always closes.
  • Ten read-only guests per licensed seat, for the people who need to look and not edit.
  • Pending invitations reserve capacity. Two owners cannot invite past the cap at the same time, because the reservation is made when the invitation is sent.
  • Last-owner protection. An owner may only be removed by an owner, only while two remain, and the last owner cannot remove themselves.
  • Money is integer cents, timestamps are timezone-aware. No floating-point rounding in a billing path.

Platform administrator

Whoever operates sysidm. Deliberately never surfaced to applications — the machine API strips the flag, so no product can grant itself privileges.

Application administrator

Your product’s own admin, delegated per application. Designate and revoke without a code change and without touching the platform.

Owner · collaborator

Licensed seats in an account. Owners manage members, billing and seats; collaborators work in the account.

Guest

Read-only, consumes no seat. This is how you give an auditor, a client or a contractor a window into one account without hiring them.

Your prices, your merchant account, our lifecycle

sysidm integrates with all industry-standard payment providers. Each application carries its own provider credentials per environment — sandbox and live — its own product and plan catalogue, and its own webhook endpoint. Settlement lands with you. Nothing of yours passes through us.

The catalogue is data, not code

Single or team. Monthly or annual. Per-seat or flat. Each plan carries its own feature and entitlement payloads, so a price change is an edit rather than a release.

Upgrades are prorated and charged now

The target per-cycle total minus the unused fraction of the period already paid, billed as a one-time order, then the recurring subscription is revised to match. The quote and the charge are reconciled against the provider’s actual figures, not our arithmetic.

Downgrades wait for the renewal

Seat reductions and plan changes you are entitled to make yourself, applied at the next renewal. No clawback arguments and no mid-cycle surprise on the invoice.

A failed payment means read-only, immediately

The account and its subscription suspend together and the owners are told. When the renewal succeeds it restores automatically — unless the reserved seats no longer fit the renewed count, in which case the mail says precisely what to fix.

Terminal states are protected

A late, duplicated or replayed event cannot resurrect a cancelled or expired subscription. Idempotency is enforced by unique event id, not by hoping the webhook arrives once.

Reminders that do not nag

Twenty-one, fourteen, seven, three, two and one day before a deadline, idempotent per day. Customers on healthy auto-renewal are spared the “renew soon” mail because the provider already tells them — but everyone gets the expiry notice.

Trials with real rules

Sixty days by default, overridable per application. A trial can switch between single and team for free, and switching does not extend its end date. Administrators can extend a trial; customers cannot extend their own.

Plan prices verified, not assumed

Provisioning reads every plan back from the provider and asserts its price equals the catalogue in sysidm. A drift between the two is a failed deploy, not a customer dispute.

Safe to run more than one instance

The daily settlement and sync job takes a database advisory lock, so scaling the application tier out cannot cause the same renewal to be processed twice.

The API your application actually calls

Everything a product needs to answer “may this person do this, and how many seats do they have” — without the product ever looking at a password.

# who is this person, and what may they do?
GET  /v1/me                          id, email, handle, display name
GET  /v1/roles?app=…               is_app_admin + role per account
GET  /v1/entitlements?app=&tenant=   standing, seats, guests, effective role
GET  /v1/team?app=&tenant=           roster + pending invitations + caps
GET  /v1/invitations?app=…         this person’s open invitations

# the same answers for someone who is not signed in
POST /v1/roles                       subject in the body, never the URL
POST /v1/entitlements

# send as your own sender identity, under policy
POST /v1/send-mail

# what does this key actually permit?
GET  /v1/credential                  scopes, label, created, last used

Design decisions worth noticing

  • Scopes, not roles, on credentials. roles:read, entitlements:read, team:read, mail:send. Only the hash of a credential is stored and the plaintext is shown once.
  • Revocation is immediate because every call checks it. A retired key stops working on the next request, not at session expiry.
  • A subject in a URL lands in access logs, Referer headers and caches. A subject in a body lands in none. That is why the “ask about an absent person” endpoints are POST.
  • The two caller kinds are not interchangeable. A user token may only ask about its own subject. An application credential may ask about anyone — but only inside its own application. A credential that fails verification is a hard 401, never a fall-through to the other path.
  • Sending as app@… is an application capability, so the mail endpoint refuses user tokens outright.
  • The cache is never the authority. Redis sits in front of entitlements for speed only; every miss, timeout or failure goes to the database.

Built for the security questionnaire

The questions your reviewer will ask, answered the way we would want them answered if we were on the other side of it.

QuestionAnswer
Where are passwords stored? In sysidm, hashed with argon2id. Your application stores none, so your application has nothing to breach and no reset flow to get wrong.
What happens when someone leaves the company? One deactivation. It is re-checked at every token mint across every application and the live session is destroyed, so there is no per-product offboarding checklist to forget a line on.
Can a stolen database be replayed as access? No. Codes, refresh tokens, sessions, email tokens, invitations and API credentials are all stored as SHA-256 digests.
How is tenant data separated? Every row is scoped by application and account. A cross-account lookup is refused by the request path rather than filtered after the fact.
Blast radius of a leaked application secret? Your browser and mobile clients have no secret to leak — they are PKCE public clients. Confidential secrets that do exist are AES-256-GCM at rest and scoped to one application.
Is there a change history? Subscription changes are recorded with the actor and the payload, and every outbound message is recorded with its delivery result, so “we never got that email” is answerable.
How is it backed up and tested? Nightly dumps gated by a restore test and mirrored off-box. The encryption key and signing keys are copied alongside in dated pairs, because a dump without its matching keys restores as unreadable ciphertext.
How is it verified? 134 automated tests across pricing, entitlements, seat caps, the OIDC flow, trials, the machine API and the billing UI — plus a written cross-repository integration contract requiring each application to ship an end-to-end test that fails loudly when sysidm is unreachable.
What is exposed to the network? The application port, behind the reverse proxy. The database and cache are never published outside the private network.

What we are not going to claim

Because the list above is only worth reading if this one is too.

  • Sign-in is not yet behind an authenticator app. Recovery is two-factor — the emailed link plus your security questions — but TOTP and WebAuthn are not in this release.
  • sysidm is an originating identity provider, not a broker. It does not front SAML or an existing corporate directory today; it issues the identities your products consume.
  • Licensing is per account, not per individual user. Seats inside an account, not standalone user licences.
  • No tax or VAT engine, and no invoice PDF generation. Payment records and a sequential reference, yes; a jurisdiction-aware invoicing system, no.

One platform, two very different buyers

The same code, the same release, the same standards. What changes is how much you hand over.

Small team, first product

You should not be writing password-reset code

  • Point one OIDC client at your issuer and delete your auth module
  • Take trials, invitations, seats and dunning for free on day one
  • Keep your own merchant account and set your own prices
  • No card data in your stack, so no audit surface you did not need
  • One login is a feature your customers notice on the first visit

Portfolio, enterprise

One identity across everything you operate

  • Central deactivation across every product at once
  • Per-product administrators you delegate without a code change
  • Per-product catalogues, mail identities and payment credentials
  • Standards throughout, so no team is trapped by a dependency
  • Your own instance, your issuer URL, your keys, your retention
  • A contract and a test suite that survive a change of staff

How a deployment goes

It starts with a conversation rather than a card form, because the honest answer depends on what you are running.

Discovery

We look at your products, your existing user base and your billing rules, and tell you what fits and what does not. Some of it you should keep doing yourself.

Deployment

Your own instance: your issuer URL, your signing keys, your payment credentials in sandbox first, your brand on the forms.

Migration

Your users come across with their password hashes intact — bcrypt and scrypt verify in place, so nobody is emailed a reset link on the day you launch.

First integration

We wire your first application alongside your team and leave a written authority contract plus an end-to-end test that fails loudly if sysidm is unreachable.

Go-live

Live credentials, webhooks registered and read back, plan prices verified against your catalogue rather than assumed. After that, a new product is a registration and a redirect URI.

Questions, answered honestly

Do our applications have to use your login pages?
No. The endpoints are standard OIDC, so your own sign-in screen can call them and keep your own design system. Many teams use ours anyway — it is already escaped, rate-limited, captcha-gated and CSRF-protected, and it renders your logo, name and colours per application. The registration and recovery forms are the part most organisations would rather not own.
Can you migrate an existing user table?
Yes, and it is a supported path rather than a rewrite. Password hashes move across as they are: argon2id is the go-forward algorithm, and legacy bcrypt and scrypt hashes are dispatched and verified in place, so users keep signing in with the password they already have. A prior migration carried over a hundred users this way with no reset emails.
Do we keep our own merchant account?
Yes — that is the design. Each application carries its own provider credentials per environment, its own product and plan catalogue, and its own webhook endpoint. Money settles with you. sysidm orchestrates the lifecycle and keeps the records; it is not a payment facilitator sitting between you and your customers.
Which payment providers do you support?
We integrate with all industry-standard payment providers. The provider is a per-application configuration detail — credentials, environment and plan mapping all live on the application row — so a portfolio can run different providers for different products, and you can change one without touching the others. Tell us which you use during discovery and we will confirm the integration shape.
What happens if sysidm becomes unavailable?
Your application keeps serving from its read-only mirror and grants nothing new. That is deliberate: the alternative — trusting a cached role — is how a revoked administrator keeps their privileges through an outage. Availability is protected by keeping the decision path narrow and by the fact that verification of an already-issued token is local to your app.
Is this multi-tenant SaaS that we share with other companies?
No. sysidm is deployed as your instance, with your issuer URL and your signing keys. The multi-application model inside it exists so that your products stay isolated from each other, not so that strangers share your database.
Can it sit behind our existing corporate identity provider?
Not today. sysidm is an originating provider: it creates and owns the identities your products consume. If your requirement is “our staff must authenticate against the directory we already run”, say so at discovery — it is a real architectural question and we would rather flag it than discover it in week three.
How much of our billing logic has to change?
Your prices, plans and entitlements become configuration in sysidm; your application stops deciding what someone is entitled to and starts asking. The entitlement answer carries the licence standing, the seat and guest caps and the effective role, which is usually a strict superset of what the product was guessing locally.
Who owns the data?
You do — it is your instance and your keys. Nightly dumps are gated by an actual restore test rather than a listing command, mirrored off-box, and accompanied by dated copies of the encryption and signing material so a restore produces a working system instead of unreadable ciphertext.
Is there a free trial we can just sign up for?
Not for sysidm itself. The products you build on it get sixty-day trials for their own customers out of the box, but sysidm is an enterprise deployment, so it starts with a walkthrough of your environment rather than a self-serve checkout.

Put every product you ship on identity you can answer for

Tell us what you run. We will show you the deployment, the migration path for the users you already have, and the integration contract your engineers would actually receive — before anyone commits to anything.

OpenID Connect · OAuth 2.0 + PKCE · RS256 · argon2id · AES-256-GCM · your merchant account