Complete Better Auth migration, harden security, update all docs
Console: - Rewrite auth from JWT decoding to Better Auth useSession() - Add auth-client.ts with createAuthClient - Remove elydora_token localStorage, dead auth API functions - Fix login/register pages to use signIn.email/signUp.email Server: - Upgrade better-auth ^1.2.0 → ^1.5.5 - CORS: enforce allowlist on all environments - Sync auth service and middleware with backend SDKs: - Deprecate login/register in Node, Python, Go SDKs - Update READMEs to show direct token initialization Config: - Sanitize .env with dev-only placeholder values - Update helm chart and install scripts Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
e8fa5e557e
commit
27a8f0667c
39 files changed
+1464
-643
No files matched your search
+21
-25
@@ -23,18 +23,15 @@ import (
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Authenticate
|
||||
auth, err := elydora.Login("https://api.elydora.com", "user@example.com", "password")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Create client
|
||||
// Initialize the client with your API token.
|
||||
// Obtain an API token by signing in via the Elydora console or:
|
||||
// POST /api/auth/sign-in/email -> get session token
|
||||
// POST /v1/auth/token -> exchange for long-lived API token
|
||||
client, err := elydora.NewClient(&elydora.Config{
|
||||
OrgID: auth.User.OrgID,
|
||||
OrgID: "org-123",
|
||||
AgentID: "my-agent-id",
|
||||
PrivateKey: "<base64url-encoded-ed25519-seed>",
|
||||
Token: auth.Token,
|
||||
Token: "your-api-token",
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@@ -108,36 +105,35 @@ client, err := elydora.NewClient(&elydora.Config{
|
||||
BaseURL: "https://...", // API base URL (default: https://api.elydora.com)
|
||||
TTLMs: 30000, // Operation TTL in ms (default: 30000)
|
||||
MaxRetries: 3, // Max retries on transient failures (default: 3)
|
||||
Token: "<jwt>", // JWT token for authenticated requests
|
||||
Token: "<token>", // API token for authenticated requests
|
||||
})
|
||||
```
|
||||
|
||||
### Client Methods
|
||||
|
||||
```go
|
||||
// Update the JWT token at runtime
|
||||
client.SetToken("new-jwt-token")
|
||||
// Update the API token at runtime
|
||||
client.SetToken("new-api-token")
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
Authentication uses Better Auth. Register and sign in via the Elydora console or the Better Auth endpoints, then issue a long-lived API token for SDK use:
|
||||
|
||||
```go
|
||||
// Register a new user and organization
|
||||
reg, err := elydora.Register(baseURL, email, password,
|
||||
elydora.WithDisplayName("Alice"),
|
||||
elydora.WithOrgName("Acme Corp"),
|
||||
)
|
||||
|
||||
// Login and receive a JWT
|
||||
auth, err := elydora.Login(baseURL, email, password)
|
||||
|
||||
// Get current authenticated user profile
|
||||
me, err := client.GetMe()
|
||||
|
||||
// Issue a new API token (with optional TTL)
|
||||
// Sign up (Better Auth) — use the console or call directly:
|
||||
// POST /api/auth/sign-up/email { email, password, name }
|
||||
//
|
||||
// Sign in (Better Auth) — get a session token:
|
||||
// POST /api/auth/sign-in/email { email, password }
|
||||
//
|
||||
// Issue a long-lived API token from an active session:
|
||||
tokenResp, err := client.IssueApiToken(&elydora.IssueApiTokenRequest{
|
||||
TTLSeconds: &ttlSeconds,
|
||||
})
|
||||
|
||||
// Update the token on an existing client instance at runtime
|
||||
client.SetToken("new-api-token")
|
||||
```
|
||||
|
||||
### Operations
|
||||
|
||||
+5
-1
@@ -28,6 +28,8 @@ func WithOrgName(name string) RegisterOption {
|
||||
}
|
||||
|
||||
// Register creates a new user and organization.
|
||||
//
|
||||
// Deprecated: Use Better Auth endpoints directly. See docs.
|
||||
func Register(baseURL, email, password string, opts ...RegisterOption) (*AuthRegisterResponse, error) {
|
||||
if baseURL == "" {
|
||||
baseURL = defaultBaseURL
|
||||
@@ -49,7 +51,9 @@ func Register(baseURL, email, password string, opts ...RegisterOption) (*AuthReg
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
// Login authenticates a user and returns a JWT token.
|
||||
// Login authenticates a user and returns a session token.
|
||||
//
|
||||
// Deprecated: Use Better Auth endpoints directly. See docs.
|
||||
func Login(baseURL, email, password string) (*AuthLoginResponse, error) {
|
||||
if baseURL == "" {
|
||||
baseURL = defaultBaseURL
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ type Config struct {
|
||||
BaseURL string // defaults to "https://api.elydora.com"
|
||||
TTLMs int // defaults to 30000
|
||||
MaxRetries int // defaults to 3
|
||||
Token string // JWT token for authenticated requests
|
||||
Token string // API token for authenticated requests
|
||||
}
|
||||
|
||||
// Client is the Elydora SDK client.
|
||||
@@ -172,7 +172,7 @@ func (c *Client) doPost(path string, body interface{}, result interface{}) error
|
||||
return c.doRequest(http.MethodPost, path, body, result)
|
||||
}
|
||||
|
||||
// SetToken sets the JWT token used for authenticated API requests.
|
||||
// SetToken sets the API token used for authenticated API requests.
|
||||
func (c *Client) SetToken(token string) {
|
||||
c.token = token
|
||||
}
|
||||
@@ -69,7 +69,7 @@ func cmdInstall(args []string) {
|
||||
agentID := fs.String("agent-id", "", "Agent ID (required)")
|
||||
privateKey := fs.String("private-key", "", "Base64url-encoded Ed25519 private key seed (required)")
|
||||
kid := fs.String("kid", "", "Key ID (defaults to <agent-id>-key-1)")
|
||||
token := fs.String("token", "", "JWT token for authenticated requests (optional)")
|
||||
token := fs.String("token", "", "API token for authenticated requests (optional)")
|
||||
baseURL := fs.String("base-url", "https://api.elydora.com", "Elydora API base URL")
|
||||
|
||||
fs.Parse(args)
|
||||
|
||||
+7
-7
@@ -15,17 +15,17 @@ Requires Node.js 18+ (uses built-in `crypto` module with Ed25519 support).
|
||||
```typescript
|
||||
import { ElydoraClient } from '@elydora/sdk';
|
||||
|
||||
// Authenticate
|
||||
const auth = await ElydoraClient.login('https://api.elydora.com', 'user@example.com', 'password');
|
||||
|
||||
// Create client
|
||||
// Initialize the client with your API token.
|
||||
// Obtain an API token by signing in via the Elydora console or:
|
||||
// POST /api/auth/sign-in/email → get session token
|
||||
// POST /v1/auth/token → exchange for long-lived API token
|
||||
const client = new ElydoraClient({
|
||||
orgId: auth.user.org_id,
|
||||
orgId: 'org-123',
|
||||
agentId: 'my-agent-id',
|
||||
privateKey: '<base64url-encoded-32-byte-ed25519-seed>',
|
||||
baseUrl: 'https://api.elydora.com',
|
||||
token: 'your-api-token',
|
||||
});
|
||||
client.setToken(auth.token);
|
||||
|
||||
// Create and submit an operation
|
||||
const eor = client.createOperation({
|
||||
@@ -96,7 +96,7 @@ const client = new ElydoraClient({
|
||||
// Register a new user and organization
|
||||
const reg = await ElydoraClient.register(baseUrl, email, password, displayName?, orgName?);
|
||||
|
||||
// Login and receive a JWT
|
||||
// Login and receive a session token
|
||||
const auth = await ElydoraClient.login(baseUrl, email, password);
|
||||
|
||||
// Set token on client
|
||||
|
||||
Generated
+5
-2
@@ -1,13 +1,16 @@
|
||||
{
|
||||
"name": "@elydora/sdk",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@elydora/sdk",
|
||||
"version": "1.0.0",
|
||||
"version": "1.1.0",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"elydora": "dist/cli.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"typescript": "^5.7.0"
|
||||
|
||||
@@ -41,7 +41,7 @@ function printUsage(): void {
|
||||
console.log(`Elydora CLI — Tamper-evident audit for AI coding agents
|
||||
|
||||
Usage:
|
||||
elydora install --agent <name> --org_id <id> --agent_id <id> --private_key <key> --kid <kid> [--token <jwt>] [--base_url <url>]
|
||||
elydora install --agent <name> --org_id <id> --agent_id <id> --private_key <key> --kid <kid> [--token <token>] [--base_url <url>]
|
||||
elydora uninstall --agent <name> [--agent_id <id>]
|
||||
elydora status
|
||||
elydora agents
|
||||
|
||||
@@ -65,7 +65,7 @@ export class ElydoraClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JWT token used for authenticated API calls.
|
||||
* Set the session token used for authenticated API calls.
|
||||
*/
|
||||
setToken(token: string): void {
|
||||
this.token = token;
|
||||
@@ -89,6 +89,9 @@ export class ElydoraClient {
|
||||
// Auth (static methods — no instance needed)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @deprecated Use Better Auth endpoints directly. See docs.
|
||||
*/
|
||||
static async register(
|
||||
baseUrl: string,
|
||||
email: string,
|
||||
@@ -110,6 +113,9 @@ export class ElydoraClient {
|
||||
return handleResponse<AuthRegisterResponse>(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use Better Auth endpoints directly. See docs.
|
||||
*/
|
||||
static async login(
|
||||
baseUrl: string,
|
||||
email: string,
|
||||
|
||||
+20
-18
@@ -15,16 +15,16 @@ Requires Python 3.9+.
|
||||
```python
|
||||
from elydora import ElydoraClient
|
||||
|
||||
# Authenticate
|
||||
auth = ElydoraClient.login("https://api.elydora.com", "user@example.com", "password")
|
||||
|
||||
# Create client
|
||||
# Initialize the client with your API token.
|
||||
# Obtain an API token by signing in via the Elydora console or:
|
||||
# POST /api/auth/sign-in/email -> get session token
|
||||
# POST /v1/auth/token -> exchange for long-lived API token
|
||||
client = ElydoraClient(
|
||||
org_id=auth["user"]["org_id"],
|
||||
org_id="org-123",
|
||||
agent_id="my-agent-id",
|
||||
private_key="<base64url-encoded-ed25519-seed>",
|
||||
base_url="https://api.elydora.com",
|
||||
token=auth["token"],
|
||||
token="your-api-token",
|
||||
)
|
||||
|
||||
# Create and submit an operation
|
||||
@@ -48,7 +48,7 @@ async def main():
|
||||
org_id="org-123",
|
||||
agent_id="agent-456",
|
||||
private_key="<base64url-encoded-ed25519-seed>",
|
||||
token="<jwt-token>",
|
||||
token="<api-token>",
|
||||
)
|
||||
|
||||
eor = client.create_operation(
|
||||
@@ -107,24 +107,26 @@ client = ElydoraClient(
|
||||
base_url="https://...", # API base URL (default: https://api.elydora.com)
|
||||
ttl_ms=30000, # Operation TTL in ms (default: 30000)
|
||||
max_retries=3, # Max retries on transient failures (default: 3)
|
||||
token="<jwt>", # Optional JWT bearer token
|
||||
token="<api-token>", # Optional API token
|
||||
)
|
||||
```
|
||||
|
||||
### Authentication
|
||||
|
||||
Authentication uses Better Auth. Register and sign in via the Elydora console or the Better Auth endpoints, then issue a long-lived API token for SDK use:
|
||||
|
||||
```python
|
||||
# Register a new user and organization
|
||||
reg = ElydoraClient.register(base_url, email, password, display_name=None, org_name=None)
|
||||
|
||||
# Login and receive a JWT
|
||||
auth = ElydoraClient.login(base_url, email, password)
|
||||
|
||||
# Get current authenticated user profile
|
||||
me = client.get_me()
|
||||
|
||||
# Issue a new API token (with optional TTL in seconds)
|
||||
# Sign up (Better Auth) — use the console or call directly:
|
||||
# POST /api/auth/sign-up/email { email, password, name }
|
||||
#
|
||||
# Sign in (Better Auth) — get a session token:
|
||||
# POST /api/auth/sign-in/email { email, password }
|
||||
#
|
||||
# Issue a long-lived API token from an active session:
|
||||
token_resp = client.issue_token(ttl_seconds=3600)
|
||||
|
||||
# Update the token on an existing client instance
|
||||
client.set_token("new-api-token")
|
||||
```
|
||||
|
||||
### Operations
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import time
|
||||
import asyncio
|
||||
import warnings
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
import aiohttp
|
||||
@@ -50,7 +51,7 @@ class AsyncElydoraClient:
|
||||
base_url: API base URL.
|
||||
ttl_ms: Time-to-live for operations in milliseconds.
|
||||
max_retries: Maximum number of retries on transient failures.
|
||||
token: Optional JWT bearer token for authenticated endpoints.
|
||||
token: Optional bearer token for authenticated endpoints.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
@@ -171,7 +172,16 @@ class AsyncElydoraClient:
|
||||
display_name: Optional[str] = None,
|
||||
org_name: Optional[str] = None,
|
||||
) -> AuthRegisterResponse:
|
||||
"""Register a new user and organization."""
|
||||
"""Register a new user and organization.
|
||||
|
||||
.. deprecated::
|
||||
Use Better Auth endpoints directly. See docs.
|
||||
"""
|
||||
warnings.warn(
|
||||
"AsyncElydoraClient.register() is deprecated. Use Better Auth endpoints directly. See docs.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
url = f"{base_url.rstrip('/')}/v1/auth/register"
|
||||
body: Dict[str, Any] = {"email": email, "password": password}
|
||||
if display_name is not None:
|
||||
@@ -186,7 +196,16 @@ class AsyncElydoraClient:
|
||||
|
||||
@staticmethod
|
||||
async def login(base_url: str, email: str, password: str) -> AuthLoginResponse:
|
||||
"""Authenticate and receive a JWT."""
|
||||
"""Authenticate and receive a session token.
|
||||
|
||||
.. deprecated::
|
||||
Use Better Auth endpoints directly. See docs.
|
||||
"""
|
||||
warnings.warn(
|
||||
"AsyncElydoraClient.login() is deprecated. Use Better Auth endpoints directly. See docs.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
url = f"{base_url.rstrip('/')}/v1/auth/login"
|
||||
body = {"email": email, "password": password}
|
||||
async with aiohttp.ClientSession() as session:
|
||||
|
||||
@@ -172,7 +172,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
install_parser.add_argument("--agent_id", required=True, help="Agent ID")
|
||||
install_parser.add_argument("--private_key", required=True, help="Base64url-encoded Ed25519 private key seed")
|
||||
install_parser.add_argument("--kid", required=True, help="Key ID")
|
||||
install_parser.add_argument("--token", default="", help="JWT bearer token (optional)")
|
||||
install_parser.add_argument("--token", default="", help="API bearer token (optional)")
|
||||
install_parser.add_argument("--base_url", default="https://api.elydora.com", help="API base URL")
|
||||
|
||||
# uninstall
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
import warnings
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
import requests
|
||||
@@ -49,7 +50,7 @@ class ElydoraClient:
|
||||
base_url: API base URL.
|
||||
ttl_ms: Time-to-live for operations in milliseconds.
|
||||
max_retries: Maximum number of retries on transient failures.
|
||||
token: Optional JWT bearer token for authenticated endpoints.
|
||||
token: Optional bearer token for authenticated endpoints.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
@@ -168,7 +169,16 @@ class ElydoraClient:
|
||||
display_name: Optional[str] = None,
|
||||
org_name: Optional[str] = None,
|
||||
) -> AuthRegisterResponse:
|
||||
"""Register a new user and organization."""
|
||||
"""Register a new user and organization.
|
||||
|
||||
.. deprecated::
|
||||
Use Better Auth endpoints directly. See docs.
|
||||
"""
|
||||
warnings.warn(
|
||||
"ElydoraClient.register() is deprecated. Use Better Auth endpoints directly. See docs.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
url = f"{base_url.rstrip('/')}/v1/auth/register"
|
||||
body: Dict[str, Any] = {"email": email, "password": password}
|
||||
if display_name is not None:
|
||||
@@ -180,7 +190,16 @@ class ElydoraClient:
|
||||
|
||||
@staticmethod
|
||||
def login(base_url: str, email: str, password: str) -> AuthLoginResponse:
|
||||
"""Authenticate and receive a JWT."""
|
||||
"""Authenticate and receive a session token.
|
||||
|
||||
.. deprecated::
|
||||
Use Better Auth endpoints directly. See docs.
|
||||
"""
|
||||
warnings.warn(
|
||||
"ElydoraClient.login() is deprecated. Use Better Auth endpoints directly. See docs.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
url = f"{base_url.rstrip('/')}/v1/auth/login"
|
||||
body = {"email": email, "password": password}
|
||||
resp = requests.post(url, json=body, headers={"Content-Type": "application/json"}, timeout=30)
|
||||
|
||||
Reference in new issue
Block a user