Authentication
JWT token management, login flows, and authenticated requests with
@embarkai/core/auth.
The auth module handles the full authentication lifecycle: logging in via multiple providers, managing JWT tokens with automatic refresh, and making authenticated API requests.
Configuration
Configure the JWT module with your TSS service URL and (optionally) a project ID. The module manages tokens via a singleton jwtTokenManager with its own storage; pick storage explicitly only when you create a custom manager (see JwtTokenManager below).
import { configureJwtModule } from '@embarkai/core'
configureJwtModule({
tssUrl: 'https://api.embarkai.io',
projectId: 'your-project-id', // optional
})In React apps, Provider from @embarkai/ui-kit calls configureJwtModule for you — don’t call it manually in frontend code.
Login Methods
Login with User ID
The simplest login method — authenticates directly with a user identifier:
import { loginWithUserId } from '@embarkai/core'
const response = await loginWithUserId('user_abc123')
console.log('Access Token:', response.accessToken)
console.log('Has Keyshare:', response.hasKeyshare)
console.log('Is New User:', response.isNewUser)Login with Email
Two-step email verification flow:
import { loginWithEmail } from '@embarkai/core'
// Step 1: Request a verification code (sent to user's inbox)
// Step 2: Submit the code
const response = await loginWithEmail('user@example.com', '123456')
console.log('User ID:', response.userId)Login with Telegram
Authenticate using Telegram login widget data:
import { loginWithTelegram } from '@embarkai/core'
const response = await loginWithTelegram({
id: 123456789,
first_name: 'Alice',
last_name: 'Doe', // optional
username: 'alice', // optional
photo_url: 'https://...', // optional
auth_date: 1700000000,
hash: 'telegram_auth_hash...',
})
// Suppress automatic token persistence (useful for server-side flows)
await loginWithTelegram(telegramData, { skipTokenSave: true })All login helpers (loginWithUserId, loginWithEmail, loginWithTelegram) accept an optional { skipTokenSave?: boolean } second argument.
Login Response
All login methods return a LoginResponse:
| Field | Type | Description |
|---|---|---|
accessToken | string | Short-lived JWT for API calls |
refreshToken | string | Long-lived token for refreshing access |
userId | string | Unique user identifier |
expiresIn | number | Token TTL in seconds |
hasKeyshare | boolean | Whether the user has a TSS keyshare on the server |
isNewUser | boolean | undefined | true on first login |
avatar | string | null | User avatar URL |
displayName | string | null | User display name |
providers | string[] | Linked auth providers |
Token Management
JwtTokenManager
The JwtTokenManager class stores tokens and handles automatic refresh. Use the singleton or create your own instance:
import { jwtTokenManager, createJwtTokenManager, MemoryStorage } from '@embarkai/core'
// Singleton (uses built-in default storage)
const isLoggedIn = await jwtTokenManager.isAuthenticated()
const header = await jwtTokenManager.getAuthHeader()
// → { Authorization: 'Bearer eyJ...' }
// Custom instance with explicit storage backend
const manager = createJwtTokenManager(new MemoryStorage())Key methods:
| Method | Returns | Description |
|---|---|---|
getAccessToken() | Promise<string | null> | Current access token |
getRefreshToken() | Promise<string | null> | Current refresh token |
getUserId() | Promise<string | null> | Authenticated user ID |
isTokenExpired() | Promise<boolean> | Check if access token is expired |
isAuthenticated() | Promise<boolean> | Check if user has valid tokens |
refreshAccessToken() | Promise<JwtTokens> | Force a token refresh |
getAuthHeader() | Promise<object> | Get Authorization header object |
clearTokens() | Promise<void> | Remove all stored tokens |
Token Storage
Implement the TokenStorage interface for custom storage backends:
import type { TokenStorage } from '@embarkai/core'
class SecureStorage implements TokenStorage {
async getItem(key: string): Promise<string | null> {
return await encrypted.get(key)
}
async setItem(key: string, value: string): Promise<void> {
await encrypted.set(key, value)
}
async removeItem(key: string): Promise<void> {
await encrypted.delete(key)
}
}Built-in adapters: LocalStorageAdapter (browser localStorage) and MemoryStorage (in-memory, non-persistent — pass it to createJwtTokenManager(new MemoryStorage()) for tests or server-side flows).
Utility Functions
Verify a Token
import { verifyToken } from '@embarkai/core'
const result = await verifyToken()
if (result.valid) {
console.log('User:', result.userId)
console.log('Session:', result.sessionId)
console.log('Has Keyshare:', result.hasKeyshare)
}Ensure Valid Token
Automatically refreshes the token if expired. Throws if refresh fails:
import { ensureValidToken } from '@embarkai/core'
await ensureValidToken()
// Access token is now guaranteed to be validGet Valid Tokens
Returns current tokens, refreshing first if needed:
import { getValidTokens } from '@embarkai/core'
const tokens = await getValidTokens()
console.log('User:', tokens.userId)
console.log('Expires at:', new Date(tokens.expiresAt * 1000))Authenticated Fetch
A drop-in wrapper around fetch that injects the Authorization header and auto-refreshes expired tokens:
import { authenticatedFetch } from '@embarkai/core'
const res = await authenticatedFetch('https://api.lumiapassport.com/me')
const user = await res.json()Logout
Clears all stored tokens:
import { logout } from '@embarkai/core'
await logout()Sync Keyshare Status
Re-checks whether the server holds a keyshare for the current user and updates stored token metadata:
import { syncKeyshareStatus } from '@embarkai/core'
await syncKeyshareStatus()Next Steps
- Server Wallets — create and manage MPC wallets after authentication
- Vault Backup — back up keyshares with password encryption
- Bundler — submit UserOperations with authenticated sessions