Skip to main content

Passwordless Authentication

Passwordless authentication lets users sign in by clicking a link sent to their email address. There is no password to remember, no password to breach. Ithbat IAM issues a single-use, time-limited magic link that, when clicked, completes authentication and returns tokens.


How It Works

  1. The user enters their email address in your application.
  2. Your application calls POST /api/v1/auth/passwordless/start.
  3. Ithbat IAM generates a cryptographically random token, stores it temporarily, and sends a magic link email.
  4. The user clicks the link in their email.
  5. Your application extracts the token from the link and calls POST /api/v1/auth/passwordless/verify.
  6. Ithbat IAM validates the token and returns access, refresh, and ID tokens.
sequenceDiagram
participant User
participant App as Your App
participant Ithbat as Ithbat IAM
participant Email as Email Service

User->>App: Enter email address
App->>Ithbat: POST /api/v1/auth/passwordless/start { email }
Ithbat->>Ithbat: Generate single-use token
Ithbat->>Email: Send magic link email
Ithbat-->>App: 200 { message }

App-->>User: "Check your email"
Email-->>User: Magic link (e.g. https://app.example.com/verify?token=...)

User->>App: Click magic link
App->>Ithbat: POST /api/v1/auth/passwordless/verify { token }
Ithbat->>Ithbat: Validate token (one-time, expiry check)
Ithbat-->>App: 200 { accessToken, refreshToken, idToken, user }
App-->>User: Signed in

Start Passwordless Login

POST /api/v1/auth/passwordless/start

Request

{
"email": "[email protected]",
"tenant_id": "ten_01J8X..."
}
FieldTypeRequiredDescription
emailstringYesThe user's email address
tenant_idstringNoTenant UUID. If omitted, resolved from email domain

Response — 200 OK

{
"message": "Magic link sent to your email address"
}
note

The response is always 200 OK regardless of whether the email address exists in the system. This prevents email enumeration.


POST /api/v1/auth/passwordless/verify

Request

{
"token": "a1b2c3d4e5f6...",
"tenant_id": "ten_01J8X..."
}
FieldTypeRequiredDescription
tokenstringYesThe token extracted from the magic link URL
tenant_idstringNoTenant UUID. Usually embedded in the magic link URL

Response — 200 OK

{
"accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...",
"idToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"tokenType": "Bearer",
"expiresIn": 3600,
"user": {
"id": "usr_01J8X...",
"tenantId": "ten_01J8X...",
"email": "[email protected]",
"firstName": "Sara",
"familyName": "Al-Rashidi",
"displayName": "Sara Al-Rashidi",
"roles": ["member"],
"permissions": ["profile:read", "profile:write"]
}
}

The magic link in the email points to a URL in your application, not directly to the Ithbat IAM API. Your application receives the token as a query parameter, then calls the verify endpoint.

Recommended link format:

https://app.example.com/auth/magic-link?token=a1b2c3d4e5f6...

Your application's handler at /auth/magic-link extracts the token parameter and calls the verify API.


Security Considerations

Token Expiry

Magic link tokens are single-use and expire after a short period. Tokens that have been consumed or expired return an error. The expiry window is configured per tenant in security settings.

warning

Do not log magic link URLs or tokens. They are equivalent to passwords for a single authentication session.

One-Time Use

Each token is invalidated immediately upon successful verification. Clicking the link a second time returns an error. This prevents replay attacks.

  • Magic links are only as secure as the user's email account.
  • Consider requiring users with sensitive roles to use a stronger method (TOTP or WebAuthn).
  • Email delivery uses TLS in transit.

Token Scope

Magic link tokens are tenant-scoped. A token generated for one tenant cannot be used to authenticate against a different tenant.


Code Examples

cURL — Start Passwordless

curl -X POST https://api.ithbat.io/api/v1/auth/passwordless/start \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"tenant_id": "ten_01J8X..."
}'

cURL — Verify Token

curl -X POST https://api.ithbat.io/api/v1/auth/passwordless/verify \
-H "Content-Type: application/json" \
-d '{
"token": "a1b2c3d4e5f6...",
"tenant_id": "ten_01J8X..."
}'

JavaScript SDK

import { IthbatSDK } from '@ithbatiam/sdk';

const ithbat = new IthbatSDK({
tenantId: 'ten_01J8X...',
baseUrl: 'https://api.ithbat.io',
});

// Step 1: Request magic link
await ithbat.auth.startPasswordless({ email: '[email protected]' });
// Show: "Check your inbox for a sign-in link"

// Step 2: Handle magic link callback in your app
// URL: https://app.example.com/auth/magic-link?token=...
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');

if (token) {
const session = await ithbat.auth.verifyPasswordless({ token });
console.log('Signed in as:', session.user.displayName);
// Store session.accessToken and session.refreshToken
}

React Example

import { useState } from 'react';
import { IthbatSDK } from '@ithbatiam/sdk';

const ithbat = new IthbatSDK({ tenantId: 'ten_01J8X...' });

function PasswordlessLoginForm() {
const [email, setEmail] = useState('');
const [sent, setSent] = useState(false);

const handleSubmit = async (e) => {
e.preventDefault();
await ithbat.auth.startPasswordless({ email });
setSent(true);
};

if (sent) {
return <p>Check your inbox — a sign-in link is on its way.</p>;
}

return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Enter your email"
required
/>
<button type="submit">Send Sign-in Link</button>
</form>
);
}

Error Handling

HTTP StatusError CodeCause
400VALIDATION_ERRORemail or token field missing
401UNAUTHORIZEDToken is invalid, expired, or already used
403FORBIDDENUser account is suspended
429RATE_LIMITEDToo many requests for this email

Next Steps

  • Email & Password — traditional authentication as an alternative or fallback
  • MFA — require a second factor in addition to passwordless
  • Token Lifecycle — understand the tokens returned after verification
  • WebAuthn / Passkeys — an even stronger passwordless option using device biometrics