Sign-In for Every Device
Passwords, passkeys, email codes and social sign-in with Google, Apple, GitHub, Microsoft, Discord, X, Meta or any OpenID Connect provider.
Sign-In, Users and Permissions API
Passwords, passkeys, email codes and eight sign-in providers, with user records, roles and route permissions on the same platform as your data. One sign-in covers every app you run on Substrio.
Route Gates
Decided by the platform before your code runs.
Sign-in providers built in
The Platform
Your sign-in, your user records and the rule for every route live on the platform that runs your app, so there is no second user table to keep in sync.
Passwords, passkeys, email codes and social sign-in with Google, Apple, GitHub, Microsoft, Discord, X, Meta or any OpenID Connect provider.
Authenticator app codes with ten one-time recovery codes. Require a second factor of every member of an account with one setting.
Each user is a record in your app's database with your own fields and indexes. Add contacts who have never signed in, and they keep the same record when they do.
Name up to 64 permissions, grant them per organization and gate each route by name. A revoke applies from the next request.
One Sign-In
People sign in once at your identity app over OpenID Connect, and every app you register signs them in without a second password. Signing out there signs them out of every app.
Get Started
You finish with sign-in on your site, user records in your database and a permission on every route that needs one.
One Substrio account covers AuthJet, MapJet and SendJet, each on its own or together.
List your providers, roles and permission names in substrio.toml. A deploy applies them.
Add a gate word to each route in routes.json. The platform refuses every caller the gate does not admit.
The API
Each card is a request from the AuthJet reference and the JSON it returns.
{
"success": true,
"user_id": 1,
"token": "p4sH7M...",
"keyring": "0xffffffffffffffff"
} {
"user_id": 1,
"email": "[email protected]",
"keyring": "0xffffffffffffffff",
"email_verified": false,
"mfa_enabled": false,
"locked": false,
"disabled": false,
"admin": false,
"superadmin": false,
"source": "native",
"created_at": 1791374435,
"last_login": 1791374435
} {
"error": "mfa_required",
"challenge": "substrio_mfa_...",
"expires_at": 1791374735,
"attempts_left": 5
} {
"error": "Permission denied",
"code": "PERMISSION_DENIED",
"status": 403,
"retryable": false,
"next_action": "request_permission",
"detail": "This route requires the permission \"ADMIN\""
} {
"names": [
{
"name": "READ",
"bit": 0,
"implies": []
},
{
"name": "WRITE",
"bit": 1,
"implies": ["READ"]
},
{
"name": "ADMIN",
"bit": 2,
"implies": ["READ", "WRITE"]
}
],
"all": "ADMIN"
} Security
Each item below is how the platform behaves today, as its reference documents it.
Sessions are held by the server, so a sign-out ends a session at its next request, in every app the person reached.
New passwords are hashed with Argon2id. Imported bcrypt, PBKDF2 and Argon2id hashes keep working.
Authenticator secrets are sealed with AES-256-GCM. Recovery codes are stored as SHA-256 digests only.
A password reset request answers the same whether or not the address has an account.
Five wrong codes spend a sign-in challenge. Five in a row lock recovery and MFA changes for 15 minutes.
Every key, setting and rule change is recorded in a chain of digests you can verify end to end.