@synap-core/auth
v1.2.1
Published
Synap auth client — sign in to Synap pods + Control Plane. Powers Hub, Studio, Eve, and any Synap-compatible app.
Downloads
63
Maintainers
Readme
@synap-core/auth
Centralized authentication client for all Synap apps. Platform-agnostic core with pluggable transport and storage adapters.
Architecture
AuthClient
├── AuthTransport (how HTTP requests are made)
│ ├── FetchTransport — default, uses fetch API
│ ├── ElectronTransport — routes through Electron main process IPC
│ └── ProxyTransport — routes through server-side proxy (Telegram, iframes)
│
└── TokenStorage (where session tokens are stored)
├── MemoryStorage — in-memory only (Telegram)
├── SessionStorage — browser sessionStorage (web app)
└── ElectronStorage — OS keychain via Electron safeStorageTwo-Layer Auth Model
- Account/issuer service — a Control Plane deployment is one implementation. It manages its own account session and a derived ledger of Pods a user may use.
- Data Pod —
*.synap.liveor self-hosted, Ory Kratos. It owns local users, access, workspace membership, and its trusted-issuer registry.
The bridge: a trusted issuer signs a short-lived federated assertion → the Pod verifies a locally approved issuer and its own local membership before creating a Kratos session. The Pod never needs the issuer's product-specific account or Pod IDs, and raw Pod session tokens never transit the issuer service.
Usage
import {
createAuthClient,
createFetchTransport,
createMemoryStorage,
} from "@synap-core/auth";
const auth = createAuthClient({
cpUrl: "https://api.synap.live",
transport: createFetchTransport(),
storage: createMemoryStorage(),
events: {
onPodSessionExpired: (podUrl) => console.log("Session expired:", podUrl),
},
});
// Sign in to CP
await auth.signInToCP("[email protected]", "password");
// Discover pods
const pods = await auth.fetchPods();
// Connect to a pod through a trusted-issuer federation exchange
const session = await auth.connectViaCPHandshake(pods[0].podUrl);
// Get auth headers for pod API calls
const headers = await auth.getPodAuthHeaders(pods[0].podUrl);
// → { "X-Session-Token": "kratos-session-token-value" }
// Direct login (self-hosted)
const session2 = await auth.connectDirectLogin(
"https://my-pod.example.com",
"[email protected]",
"password"
);Per-Platform Setup
Electron (Browser app)
import {
createAuthClient,
createElectronTransport,
createElectronStorage,
} from "@synap-core/auth";
const auth = createAuthClient({
cpUrl: "https://api.synap.live",
transport: createElectronTransport(window.synap.connection),
storage: createElectronStorage(window.synap.security),
});Telegram Mini App
import {
createAuthClient,
createProxyTransport,
createMemoryStorage,
} from "@synap-core/auth";
const auth = createAuthClient({
cpUrl: "https://api.synap.live",
transport: createProxyTransport({
cpProxyBase: "/api/cp",
podProxyBase: "/api/pod",
getExtraHeaders: () => ({ "X-Pod-Url": connectionStore.podUrl }),
}),
storage: createMemoryStorage(), // tokens live in memory only
});React Native (Relay)
import { createAuthClient, createFetchTransport } from "@synap-core/auth";
import * as SecureStore from "expo-secure-store";
const auth = createAuthClient({
cpUrl: "https://api.synap.live",
transport: createFetchTransport(),
storage: {
get: (key) => SecureStore.getItemAsync(key),
set: (key, value) => SecureStore.setItemAsync(key, value),
delete: (key) => SecureStore.deleteItemAsync(key),
},
});Security Model
| Concern | Approach |
| ------------------- | ---------------------------------------------------------------------------------------------------- |
| Pod session tokens | Stored via TokenStorage (OS keychain on Electron, SecureStore on RN, memory on Telegram) |
| CP session | Cookie-based where possible, token-based via proxy where not |
| Federated assertion | 5-minute expiry, ES256 asymmetric, audience-scoped; no CP ledger metadata |
| Transport | All requests go through AuthTransport — Electron uses main process (no cross-origin cookie issues) |
| No localStorage | Session tokens never touch localStorage. Period. |
