Architecture & Headless UI
Shyntr is headless by design. The broker handles protocol state, token issuance, and trust boundaries, while login, consent, and logout UI remain outside the core service.
Headless Philosophy
Traditional identity products bundle protocol logic and UI together. That usually creates:
- limited customization
- inconsistent product UX
- tighter vendor lock-in
- harder integration with existing systems
Shyntr separates those responsibilities:
┌─────────────────────────────────────────────────────────────┐
│ Your Application │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ │
│ │ Login UI │ │ Consent UI │ │ Logout UI │ │
│ │ (React, │ │ (Your own │ │ (Custom branded │ │
│ │ Next.js, │ │ design) │ │ experience) │ │
│ │ Vue, etc.) │ │ │ │ │ │
│ └──────┬──────┘ └──────┬──────┘ └──────────┬──────────┘ │
│ │ │ │ │
│ └────────────────┼─────────────────────┘ │
│ │ │
│ Admin API Calls │
└──────────────────────────┼──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Shyntr │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ Authentication Engine │ │
│ │ • Challenge Generation • Token Issuance │ │
│ │ • Session Management • Protocol Translation │ │
│ │ • Security Validation • Multi-Tenancy │ │
│ └────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Network Boundary Enforcement
Shyntr also enforces security at the outbound network layer.
Outbound HTTP requests triggered during identity flows, such as:
- OIDC discovery
- JWKS retrieval
- SAML metadata retrieval
- webhook delivery
are evaluated through outbound policy controls before the request is executed.
This helps ensure that:
- identity flows cannot trigger arbitrary outbound requests
- internal infrastructure is not reachable by default
- tenant-specific or global policy still governs execution
Outbound requests follow a layered decision model:
- tenant-specific policy if defined
- global fallback policy otherwise
- deny when the destination does not satisfy policy
The Headless Flow
Here is the standard headless authentication pattern.
Step 1: Challenge Generation
When a user needs to authenticate, Shyntr generates a login_challenge and redirects the user to your external login UI.
User → Application → Shyntr → Your Login UI
(with login_challenge)
Step 2: User Verification
Your UI and backend verify the user. You can implement:
- username and password
- MFA
- social login
- SAML or OIDC federation
- LDAP-backed authentication
- custom application-specific authentication
import { useSearchParams } from 'react-router-dom';
function LoginPage() {
const [searchParams] = useSearchParams();
const loginChallenge = searchParams.get('login_challenge');
const handleLogin = async (credentials) => {
const user = await validateUser(credentials);
const response = await fetch('/admin/login/accept', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
login_challenge: loginChallenge,
subject: user.id,
context: {
email: user.email,
name: user.name
}
})
});
const { redirect_to } = await response.json();
window.location.href = redirect_to;
};
return (
<form onSubmit={handleLogin}>
{/* Your custom login form */}
</form>
);
}
Step 3: Accept the Challenge
Your backend calls the Admin API to accept or reject the login or consent challenge.
curl -X PUT "http://localhost:7497/admin/login/accept?login_challenge=challenge_abc123" \
-H "Content-Type: application/json" \
-d '{
"subject": "user_12345",
"remember": true,
"remember_for": 3600,
"context": {
"email": "user@example.com",
"name": "John Doe"
}
}'
Step 4: Flow Continuation and Token Issuance
Shyntr validates the protocol state, enforces flow rules, and returns the redirect needed to continue the exchange. For OIDC clients, that results in token issuance once the authorization flow completes.
Consent Flow
The consent flow follows the same pattern:
┌──────────────────────────────────────────────────────────────┐
│ Consent Flow │
├──────────────────────────────────────────────────────────────┤
│ │
│ 1. Shyntr redirects to → EXTERNAL_CONSENT_URL │
│ (with consent_challenge parameter) │
│ │
│ 2. Your Consent UI shows: │
│ • requesting application details │
│ • requested scopes │
│ • grant / deny actions │
│ │
│ 3. Your UI or backend calls: │
│ PUT /admin/consent/accept or /admin/consent/reject │
│ │
│ 4. Shyntr continues the authorization flow │
│ │
└──────────────────────────────────────────────────────────────┘
Configuration
Configure your external UI URLs with environment variables:
EXTERNAL_LOGIN_URL=https://auth.yourapp.com/login
EXTERNAL_CONSENT_URL=https://auth.yourapp.com/consent
Use HTTPS for external UI URLs in production. Shyntr relies on exact URI validation and strict redirect handling; mismatched public URLs will break flows and weaken your deployment boundary.
Admin API Endpoints
| Endpoint | Method | Description |
|---|---|---|
/admin/login | GET | Get login challenge details |
/admin/login/accept | PUT | Accept and complete login |
/admin/login/reject | PUT | Reject login |
/admin/consent | GET | Get consent challenge details |
/admin/consent/accept | PUT | Accept consent |
/admin/consent/reject | PUT | Reject consent |
/admin/management/... | GET/POST/PUT/DELETE | Internal management endpoints used by the Dashboard and automation |
Admin API Security Boundary
Shyntr's admin routes are internal control-plane endpoints.
That includes both:
- headless challenge endpoints such as
/admin/loginand/admin/consent - management endpoints under
/admin/management/...
These routes are not protected by built-in application-layer authentication inside Shyntr. In the current codebase they are exposed on the admin server with logging, error handling, and CORS configuration, but without a built-in admin auth layer.
That means:
- they must be protected at the trusted edge
- they should sit behind a gateway, reverse proxy, VPN, service mesh, or equivalent policy layer
- they should only be reachable by trusted internal callers such as your Auth Portal, Dashboard, or operator tooling
- they must never be exposed directly on a public interface
If you publish the admin port directly to the public internet, you are exposing your control plane.
Do not expose ADMIN_PORT directly to the public internet. Terminate access at a trusted edge and enforce authentication, authorization, and network policy there.
CORS in the Headless Model
Because your Auth Portal and the Admin API usually run on different origins, browser-based requests need explicit CORS configuration.
You must configure ADMIN_CORS_ALLOWED_ORIGINS to include the exact Auth Portal origin that needs to call the Admin API.
Benefits of Headless Architecture
| Aspect | Benefit |
|---|---|
| Design Freedom | Use your own frontend stack and UI patterns |
| Brand Consistency | Keep login, consent, and logout aligned with your product |
| Protocol Separation | Let Shyntr own protocol state while your app owns UX |
| Custom Identity Sources | Integrate LDAP, upstream IdPs, or your own user backend |
| Operational Flexibility | Deploy UI and broker separately |
Next Steps
- Configure Environment Variables
- Read the Headless Login & Consent Guide
- Review the Admin API with the control-plane boundary in mind