Skip to content
Sendora Cloud
Create account
Operate

Authentication

End-user auth across email + password, magic link, email OTP, TOTP MFA, recovery codes, OIDC / SAML SSO, Sign in with Apple / Google / Microsoft / LinkedIn / Facebook / Discord, passkeys (iOS via ASAuthorization, Android via Credential Manager).

Features

  • Email/password, magic link, 6-digit email OTP
  • Passkeys / WebAuthn (web, iOS, Android)
  • TOTP MFA with recovery codes
  • Social: Google, GitHub, Apple, Microsoft, LinkedIn, Facebook, Discord (7 providers, AES-256-GCM encrypted creds at rest)
  • OIDC + SAML 2.0 SSO (Business+) with one-click IdP metadata
  • SCIM 2.0 user + group provisioning (Business+)
  • Bulk hash import: bcrypt, scrypt, argon2 — no forced password reset on migration
  • Custom JWT claim templates (Growth+) + signing-key rotation
  • Per-IP brute-force limit, anonymous → identified merge
  • Session device list with self-service revoke
  • HttpOnly-cookie SSR sessions (Next.js / Remix / SvelteKit) via @sendoracloud/sdk-web-ssr — refresh-token rotation w/ reuse detection + CSRF double-submit + edge middleware

Common use cases

  • Replace Auth0 / Clerk + their event-stream / CRM / messaging integrations — already bundled.
  • B2B SaaS that needs SSO + SCIM + audit log + customer timeline + tiered support routing — one tenant.
  • Consumer apps that want anon→identified device-takeover (no duplicate pushes on signin) + lifecycle messaging triggered by signup events out of the box.

Key concepts

Per-project user pool
Each project gets its own auth tenant. Same email can sign up across projects independently (Firebase model).
Identity token
HMAC of `userId` signed by your backend. Required in strict-identity projects to block client-side spoofing.
Custom JWT claims
Growth+ — inject roles / orgs / org-membership into the access token via a template. Customer's backend reads them at the edge.
OIDC / SAML SSO
Business+ — Sendora hosts the IdP relay. Per-org metadata XML for one-click IdP import.

Setup

  1. 1
    Pick auth flavours
    Dashboard → Auth Service → Settings. Toggle: email+pass / magic / OTP / MFA / passkeys / OAuth providers.
  2. 2
    Configure OAuth providers (optional)
    Per-provider client id + secret in Settings → Providers. Sendora encrypts `clientSecret` + Apple `.p8` private key at rest.
  3. 3
    Custom JWT claims (Growth+)
    Settings → JWT claims template — `{{ user.role }}` interpolation; preview before save.

End-user auth

Anonymous + upgrade

FREE

Mint a stable user_id before signup. Upgrade in place when the visitor signs up — same user_id preserved, all attached events / tickets / profile / workflow runs stay bound.

// Anonymous-first
await sendora.auth.signInAnonymously();

// Upgrade in place when visitor signs up
await sendora.auth.signUp({
  email: "alice@example.com",
  password: "correct-horse-battery-staple",
});

Email + password

FREE

Standard signup + login. bcrypt by default; passwords never logged.

await sendora.auth.signUp({ email, password });
await sendora.auth.signIn({ email, password });

Magic link

FREE

One-tap email link sign-in. Sendora sends the email from auth@sendoracloud.com.

// Send the link
await sendora.auth.sendMagicLink("alice@example.com");
// Later, on the link's landing page:
const user = await sendora.auth.verifyMagicLink(tokenFromUrl);

Email OTP (6-digit code)

FREE

Cross-device-friendly alternative to magic link. 5-minute TTL, 5-attempt lockout.

await sendora.auth.sendEmailOtp("alice@example.com");
const user = await sendora.auth.verifyEmailOtp("alice@example.com", "482910");

Passkeys (WebAuthn)

FREE

Face / Touch ID + Android Credential Manager + WebAuthn. Stored in the user's password manager.

// Register a passkey for the signed-in user
await sendora.auth.passkeys.register({ name: "MacBook Pro" });

// Sign in with passkey
const user = await sendora.auth.passkeys.authenticate({ email });

Social sign-in (8 providers)

FREE

Google, GitHub, Apple, Microsoft, LinkedIn, Facebook, Discord — first-class adapters with verified-email gates. Twitter intentionally rejected (OAuth 2.0 doesn't expose verified email; takeover risk).

// SDK opens the provider's consent screen
await sendora.auth.signInWithGoogle();
await sendora.auth.signInWithGitHub();
await sendora.auth.signInWithApple();
await sendora.auth.signInWithMicrosoft();
await sendora.auth.signInWithLinkedIn();
await sendora.auth.signInWithFacebook();
await sendora.auth.signInWithDiscord();

TOTP MFA

FREE

Google Authenticator-compatible RFC 6238. Users enroll from their account page. Login returns mfaRequired+mfaChallengeToken when enrolled.

// Enroll (Bearer-auth, signed-in user)
const { otpauthUrl, recoveryCodes } = await sendora.auth.enrollMfa();
await sendora.auth.confirmMfa(codeFromAuthenticator);

// Login flow when MFA is on
const r = await sendora.auth.signIn({ email, password });
if (r.mfaRequired) {
  await sendora.auth.challengeMfa(r.mfaChallengeToken, codeOrRecovery);
}

Password reset + email verification

FREE

Sendora sends the email. SDK exposes 4 helpers for the flow.

await sendora.auth.requestPasswordReset(email);
await sendora.auth.resetPassword(token, newPassword);
await sendora.auth.verifyEmail(token);
await sendora.auth.sendVerificationEmail(); // Bearer-auth, resend

Enterprise SSO (OIDC + SAML)

BUSINESS+

Per-org OIDC + SAML 2.0. Configure in Authentication → Settings → OIDC SSO. SDK kicks off the redirect flow.

// SDK opens the IdP's authorization page
await sendora.auth.signInWithSso({
  returnTo: window.location.href,
});

// On the returnTo page, consume the token from the URL fragment
const user = await sendora.auth.consumeSsoTokenFromUrl();

SCIM 2.0 provisioning

BUSINESS+

Auto-sync users + groups from your IdP (Okta, Azure AD, JumpCloud, OneLogin). Mint a bearer token in Authentication → Settings → SCIM.

# IdP-side configuration. Paste the bearer token + base URL into your IdP's SCIM connector.
# Base URL: https://api.sendoracloud.com/api/v1/scim/v2 (paste this into IdP's SCIM connector field — IdP appends /Users etc).
# Auth: Bearer scim_…

# Verify the connection from your IdP, OR test with curl:
curl https://api.sendoracloud.com/api/v1/scim/v2/Users \
  -H "Authorization: Bearer scim_…" \
  -H "Accept: application/scim+json"

curl -X POST https://api.sendoracloud.com/api/v1/scim/v2/Users \
  -H "Authorization: Bearer scim_…" \
  -H "Content-Type: application/scim+json" \
  -d '{
    "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
    "userName": "alice@example.com",
    "name": { "givenName": "Alice", "familyName": "Example" },
    "active": true,
    "emails": [{ "value": "alice@example.com", "primary": true }]
  }'

JWT verification (your backend)

FREE

Verify access tokens server-side using Sendora's per-org JWKS endpoint. RS256, key-rotation safe via kid.

// Node.js / any JWKS-aware verifier (e.g. jose)
import { createRemoteJWKSet, jwtVerify } from "jose";

const jwks = createRemoteJWKSet(
  new URL("https://api.sendoracloud.com/api/v1/auth-service/<ORG_ID>/.well-known/jwks.json"),
);

const { payload } = await jwtVerify(accessToken, jwks);
// payload.sub, payload.email, payload.is_anonymous, ...your custom claims

Related