Skip to content

Commit fc1c441

Browse files
committed
fix: auth redirect loop after login, update docs — bump to v1.7.1
- signin: use full page reload after login so AuthGate re-checks auth - AuthGate: only include returnUrl for non-root paths (session expiry) - README: fix redis config format, OAuth endpoint paths, Docker Compose - example config: add server.redis section, fix scrypt cost - CLAUDE.md: update test count
1 parent 26e4feb commit fc1c441

7 files changed

Lines changed: 49 additions & 15 deletions

File tree

CLAUDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ npm run cli:dev # tsx src/cli/index.ts (no build needed)
1818

1919
Run tests:
2020
```bash
21-
npm test # all tests (1649 tests across 42 suites)
21+
npm test # all tests (1700 tests across 44 suites)
2222
npm test -- --testPathPatterns=search # specific test file
2323
npm run test:watch # watch mode
2424
npx tsx src/tests/embedder.test.ts # real model test (slow, excluded from Jest)

README.md

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,28 @@ services:
9191
- /path/to/my-app:/data/projects/my-app
9292
- models:/data/models
9393
restart: unless-stopped
94+
depends_on:
95+
redis:
96+
condition: service_healthy
97+
98+
redis:
99+
image: redis:7-alpine
100+
restart: unless-stopped
101+
volumes:
102+
- redis-data:/data
103+
healthcheck:
104+
test: ["CMD", "redis-cli", "ping"]
105+
interval: 10s
106+
timeout: 3s
107+
retries: 3
94108

95109
volumes:
96110
models:
111+
redis-data:
97112
```
98113
114+
> Redis is optional. Remove the `redis` service and `depends_on` if you don't need shared session store or embedding cache.
115+
99116
See [docs/docker.md](docs/docker.md) for details.
100117

101118
## What it does
@@ -129,8 +146,8 @@ See [docs/docker.md](docs/docker.md) for details.
129146
## Web UI
130147

131148
Dashboard, Knowledge (notes CRUD), Tasks (kanban board with drag-drop), Skills (recipes),
132-
Docs browser, Files browser, Prompts (AI prompt generator), Search (cross-graph),
133-
Graph (Cytoscape.js visualization), Tools (MCP explorer), Help.
149+
Docs browser, Code browser (symbols, edges, source), Files browser, Prompts (AI prompt generator),
150+
Search (cross-graph), Tools (MCP explorer), Help.
134151

135152
Light/dark theme. Real-time WebSocket updates. Login page when auth is configured.
136153

@@ -142,18 +159,20 @@ users:
142159
name: "Alice"
143160
email: "alice@example.com"
144161
apiKey: "mgm-key-abc123"
145-
passwordHash: "$scrypt$..." # generated by: graphmemory users add
162+
passwordHash: "$scrypt$65536$..." # generated by: graphmemory users add
146163
147164
server:
148165
jwtSecret: "your-secret"
149166
defaultAccess: rw
150-
redis: "redis://localhost:6379" # optional: session store + embedding cache
167+
redis: # optional: session store + embedding cache
168+
enabled: true
169+
url: "redis://localhost:6379"
151170
```
152171

153172
- **UI login**: email + password → JWT cookies (httpOnly, SameSite=Strict)
154173
- **API access**: `Authorization: Bearer <apiKey>`
155174
- **OAuth 2.0**: Authorization Code + PKCE (S256) with frontend consent page at `/ui/auth/authorize`; also supports client credentials and refresh tokens
156-
- **OAuth endpoints**: `/oauth/userinfo`, `/oauth/introspect`, `/oauth/revoke`, `/oauth/end-session`
175+
- **OAuth endpoints**: `/api/oauth/userinfo`, `/api/oauth/introspect`, `/api/oauth/revoke`, `/api/oauth/end-session`
157176
- **ACL**: graph > project > workspace > server > defaultAccess (`deny` / `r` / `rw`)
158177
- **Redis** (optional): when `server.redis` is configured, used for OAuth session store and embedding cache
159178

@@ -164,7 +183,7 @@ See [docs/authentication.md](docs/authentication.md).
164183
```bash
165184
npm run dev # tsc --watch (backend)
166185
cd ui && npm run dev # Vite on :5173, proxies /api → :3000
167-
npm test # 1649 tests across 42 suites
186+
npm test # 1700 tests across 44 suites
168187
```
169188

170189
## Documentation

graph-memory.yaml.example

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,12 @@
2424
# name: "Alice"
2525
# email: "alice@example.com"
2626
# apiKey: "mgm-key-abc123"
27-
# passwordHash: "$scrypt$16384$8$1$salt$hash" # generated by CLI
27+
# passwordHash: "$scrypt$65536$8$1$salt$hash" # generated by CLI
2828
# bob:
2929
# name: "Bob"
3030
# email: "bob@example.com"
3131
# apiKey: "mgm-key-def456"
32-
# passwordHash: "$scrypt$16384$8$1$salt$hash"
32+
# passwordHash: "$scrypt$65536$8$1$salt$hash"
3333

3434
# ---------------------------------------------------------------------------
3535
# Server settings (shared across all projects)
@@ -53,6 +53,14 @@ server:
5353
# maxFileSize: 1048576 # Max file size for indexing in bytes (default: 1 MB)
5454
# exclude: "**/vendor/**" # Additional glob to exclude (merged with default: **/node_modules/**, **/dist/**)
5555

56+
# Redis (optional — session store for OAuth codes/sessions + embedding cache)
57+
# Enables horizontal scaling and survives server restarts. In-memory fallback when disabled.
58+
# redis:
59+
# enabled: false # Set true to enable Redis backend
60+
# url: "redis://localhost:6379" # Redis connection URL
61+
# prefix: "mgm:" # Key prefix for all Redis keys
62+
# embeddingCacheTtl: "30d" # TTL for cached embeddings (default: 30d, 0 = no TTL)
63+
5664
# Rate limiting (requests per minute per IP)
5765
# rateLimit:
5866
# global: 600 # All endpoints (default: 600)

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@graphmemory/server",
3-
"version": "1.7.0",
3+
"version": "1.7.1",
44
"description": "MCP server for semantic graph memory from markdown files",
55
"main": "dist/cli/index.js",
66
"bin": {

ui/src/pages/auth/signin.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { useState } from 'react';
2-
import { useNavigate, useSearchParams } from 'react-router-dom';
2+
import { useSearchParams } from 'react-router-dom';
33
import { Box, Card, CardContent, TextField, Button, Typography, Alert } from '@mui/material';
44

55
export default function SignInPage() {
6-
const navigate = useNavigate();
76
const [searchParams] = useSearchParams();
87
// Only allow relative paths to prevent open redirect attacks
98
const rawReturn = searchParams.get('returnUrl') || '/';
@@ -27,7 +26,9 @@ export default function SignInPage() {
2726
body: JSON.stringify({ email: email.trim(), password }),
2827
});
2928
if (res.ok) {
30-
navigate(returnUrl, { replace: true });
29+
// Full page reload so AuthGate re-checks auth from scratch
30+
window.location.href = '/ui' + returnUrl;
31+
return;
3132
} else {
3233
const body = await res.json().catch(() => ({ error: 'Login failed' }));
3334
setError(body.error || 'Login failed');

ui/src/shared/lib/AuthGate.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ export default function AuthGate({ children }: { children: ReactNode }) {
4444
}
4545

4646
if (state === 'login') {
47+
// Only include returnUrl when user was on a meaningful page (session expired mid-use).
48+
// For root/initial visits, no returnUrl needed — signin defaults to '/'.
49+
const isRoot = location.pathname === '/' && !location.search;
50+
if (isRoot) {
51+
return <Navigate to="/auth/signin" replace />;
52+
}
4753
const returnUrl = `${location.pathname}${location.search}`;
4854
return <Navigate to={`/auth/signin?returnUrl=${encodeURIComponent(returnUrl)}`} replace />;
4955
}

0 commit comments

Comments
 (0)