Magic Token Link (MTL) - Authentication Pattern
A server-issued, time-limited authentication token that combines login and navigation into a single click — without embedding end-user credentials in the URL.
Version: 1.0.0 Last Updated: 2026-02-19
Table of Contents
- Overview
- Why Use MTL?
- How It Works
- URL Pattern
- Usage Examples
- Security
- Integration Patterns
- Comparison with BAL
Overview
The Magic Token Link (MTL) endpoint authenticates a user and redirects them to a target page in a single HTTP request. The authentication token is generated server-side by the API user and encodes:
- username — which user account will be authenticated
- targetUrl — where to land after authentication
- expiresAt — token expiry (tokens are self-expiring)
Endpoint: GET /mtl/{token} (optionally ?m={memento})
Purpose: Give an external system (e.g., a hospital KIS) the ability to hand off a pre-authenticated, pre-filled browser session to an end user — without putting credentials in the URL.
Typical flow:
- External system calls a product API endpoint (e.g.,
POST /api/duba/v1/memento) - The API response includes a ready-to-use
magicLinkURL (relative path) - External system prepends the host and sends the absolute URL to the end user (email, portal link, etc.)
- End user clicks link → authenticates as the API user account → lands on the pre-filled form
Why Use MTL?
The Problem with Credential-in-URL Links
Embedding credentials in a URL to give users direct, authenticated access is tempting but flawed:
https://user:password@host/duba/BetreuungAnregung?m=memento123
Problems: - Credentials exposed: Password appears in server logs, browser history, email headers - No expiry: Link is valid forever (until the password changes) - Session confusion: Browser caches Basic Auth credentials — users cannot log out cleanly
The BAL pattern (/bal/) is an improvement (credentials → session, one-time use), but it still
requires the end-user's password to be embedded in the link.
The MTL Solution
MTL replaces end-user credentials with a server-issued encrypted token:
https://host/mtl/eyJ...token.../duba/BetreuungAnregung?m=memento123
↓ MTL endpoint decrypts token
↓ Verifies expiry
↓ Authenticates the encoded user
↓ Creates secure session
↓ Redirects to target with memento
Benefits:
- No end-user credentials in the URL — only an encrypted server token
- Tokens are time-limited (configurable expiry, default 1 hour)
- Tokens are server-signed — cannot be forged or tampered with
- Standard session-based auth after redirect (logout works normally)
- The magicLink is returned by API endpoints — no manual URL construction needed
How It Works
Request Flow
1. External system (KIS) calls API:
POST /api/duba/v1/memento (Basic Auth with API credentials)
→ Response: { "memento": "...", "magicLink": "/mtl/eyJ.../duba/form?m=..." }
2. External system prepends host and delivers the URL to end user:
https://elim.example.com/mtl/eyJ.../duba/BetreuungAnregung?m=...
3. End user clicks link → Browser sends GET /mtl/{token}?m={memento}
4. MTL endpoint:
a. Decrypts and decodes the token
b. Validates expiry (rejects if expired → redirect to /login?error)
c. Validates target URL (prevents open redirects)
d. Loads the user account (verifies it still exists)
e. Creates session-based authentication
5. 302 Redirect → /duba/BetreuungAnregung?m={memento}
6. User sees pre-filled form, fully authenticated with session cookie
Token Structure
Tokens are generated using the same AES-256-GCM encryption as all other mementos in the system. A token encodes:
{
"username": "hospital-api-user",
"targetUrl": "/duba/BetreuungAnregung",
"expiresAt": "2026-02-19T15:30:00Z"
}
The token is URL-safe and cannot be modified without invalidating the signature.
The ?m= Memento Parameter
The form pre-fill memento travels separately from the token as a plain ?m= query parameter.
It is not embedded in the token. Reasons:
- The memento is already server-encrypted (AES-256-GCM) — double-encrypting adds size for no gain
- Separating them keeps token size small and tokens reusable (in principle)
- The
?m=value is appended to the redirect URL after successful authentication
URL Pattern
Syntax
/mtl/{token}
/mtl/{token}?m={memento}
Where:
- /mtl/ — Magic Token Link endpoint prefix
- {token} — Encrypted, URL-safe token (generated by product API endpoints)
- ?m={memento} — Optional form pre-fill data (appended to final redirect URL)
The magicLink Field
Product API endpoints that support form pre-fill return a magicLink field in their JSON response.
This is a relative URL — prepend your host to make it absolute:
MAGIC_LINK=$(curl ... | jq -r '.magicLink')
FULL_URL="https://elim.example.com$MAGIC_LINK"
Security Validations
The target URL embedded in the token is validated before use:
| Check | Purpose | Example Blocked |
|---|---|---|
Must start with / |
Prevent external redirects | http://evil.com |
Cannot start with // |
Prevent protocol-relative URLs | //evil.com/path |
Cannot contain .. |
Prevent path traversal | /duba/../../etc/passwd |
Cannot start with /mtl |
Prevent redirect loops | /mtl/nested/token |
Usage Examples
Example 1: DUBA Form Pre-fill (Recommended Pattern)
Scenario: Hospital KIS creates a court guardianship request and hands off a pre-filled form link to a hospital employee for review and submission.
#!/bin/bash
HOST="https://elim.example.com"
API_USER="hospital-api"
API_PASS="api-secret"
# Step 1: Create memento — response includes both memento and magicLink
RESPONSE=$(curl -s -X POST \
-u "$API_USER:$API_PASS" \
-H "Content-Type: application/json" \
-d '{
"jobId": "KH-2026-00456",
"absender": { "aktenzeichen": "KH-2024-00123", "egvp_account_id": 42 },
"empfaenger": {
"name": "Amtsgericht Musterstadt",
"type": "Gericht",
"safeId": "de.justiz.bund.egvp.bea.12345"
},
"betroffener": {
"name": { "vorname": "Max", "nachname": "Mustermann" },
"geburtsdatum": "1950-01-15"
}
}' \
"$HOST/api/duba/v1/memento")
# Step 2: Extract the magic link (already includes token + memento)
MAGIC_LINK=$(echo "$RESPONSE" | jq -r '.magicLink')
FULL_URL="$HOST$MAGIC_LINK"
# Step 3: Send to end user
echo "Please review and submit: $FULL_URL"
# Or: mail, portal display, QR code, etc.
What happens when the user clicks:
1. MTL endpoint decrypts token → identifies user as hospital-api
2. Validates expiry (default: 1 hour from generation)
3. Creates session for hospital-api account
4. Redirects to /duba/BetreuungAnregung?m={memento}
5. User sees pre-filled guardianship form ready to review and submit
Example 2: Testing MTL Tokens (curl)
# Generate token manually (for testing)
TOKEN=$(curl -s -X POST \
-u "user:pass" \
-H "Content-Type: application/json" \
-d '{"jobId":"test-job","absender":{"egvp_account_id":42}}' \
"https://elim.example.com/api/duba/v1/memento" | jq -r '.magicLink')
echo "Magic link (relative): $TOKEN"
# Follow the magic link (with redirect following)
curl -L -c cookies.txt "https://elim.example.com$TOKEN"
# After redirect, cookies.txt contains the SESSION cookie
Security
Token Properties
| Property | Value |
|---|---|
| Encryption | AES-256-GCM (same key as memento system) |
| Default expiry | 1 hour from generation |
| Forgeability | Cannot be forged — server's encryption key required |
| Tamper detection | Any modification invalidates the token (GCM auth tag) |
| Reuse | Stateless — no single-use enforcement (by design, for simplicity) |
Error Handling
All validation failures redirect to /login?error without revealing the reason:
| Failure | HTTP Result |
|---|---|
| Malformed/invalid token | 302 → /login?error |
| Expired token | 302 → /login?error |
| Unknown username | 302 → /login?error |
| Invalid target URL | 302 → /login?error |
No SESSION cookie is set on failure.
Operational Notes
- HTTPS is required in production — tokens are URL-safe but should not travel in plain text
- Token expiry is configured by the server — default is 1 hour; contact your administrator for details
- No revocation before expiry — if a token needs to be invalidated early, change the user's password (this rotates the KEK, making all existing tokens undecryptable)
- Tokens are not single-use — a valid, unexpired token can be used multiple times; this is acceptable because the token is time-limited and user-specific
Integration Patterns
Pattern 1: API → Magic Link → Email to User (DUBA)
Use case: Hospital system creates court forms; hospital employees review and submit.
Hospital KIS
↓
[1] POST /api/duba/v1/memento with patient/case data
↓
[2] Response: { "memento": "...", "magicLink": "/mtl/.../duba/form?m=..." }
↓
[3] KIS prepends host → absolute URL
↓
[4] Send URL to hospital employee via email or portal
↓
Hospital Employee clicks link (no login required)
↓
[5] GET /mtl/{token}?m={memento} → authenticated session
↓
[6] 302 → /duba/BetreuungAnregung?m={memento}
↓
[7] Employee reviews pre-filled form and submits to court
Pattern 2: Direct Console Access (Testing/Development)
Use case: Developer or administrator needs quick authenticated access to a specific page.
# Get a magic link for a known page
LINK=$(curl -s -X POST -u "$USER:$PASS" \
-H "Content-Type: application/json" \
-d '{"jobId":"dev-test"}' \
"$HOST/api/duba/v1/memento" | jq -r '.magicLink')
echo "Open in browser: $HOST$LINK"
Comparison with BAL
Both BAL (/bal/) and MTL (/mtl/) convert an authentication mechanism into a session. They serve
different use cases:
| Aspect | BAL | MTL |
|---|---|---|
| Credential in URL | Yes (username:password) | No (server-issued token) |
| Generated by | External system (constructs URL manually) | Server (API response includes magicLink) |
| Expiry | No (valid until password changes) | Yes (configurable, default 1 hour) |
| Forgeable by external party | Yes (if credentials known) | No (requires server encryption key) |
| Supports memento pre-fill | Yes (via ?m= parameter) |
Yes (via ?m= parameter, included automatically) |
| Use case | Legacy integrations; manual URL construction | New integrations; API returns link directly |
Recommendation:
- New integrations → use MTL. The API endpoint returns magicLink ready to use.
- Existing BAL integrations continue to work. BAL is not deprecated.
Related Documentation
- Basic Auth Login (BAL) — Alternative session authentication via Basic Auth credentials
- DUBA API Tutorial — Complete DUBA workflow including MTL usage
- DUBA OpenAPI Specification — API schema including
MementoResponse.magicLink
This is external-facing documentation for integration partners. For internal implementation details,
see src/main/java/de/vertama/web/magic/Controller.kt and Payload.kt in the elim repository.