Files
DeepTutor/.secrets.baseline
xor_xe 6688408800 feat: add optional auth with login, registration, and admin dashboard
Closes #227. Auth is disabled by default (AUTH_ENABLED=false) so
localhost usage is unaffected. Set AUTH_ENABLED=true +
NEXT_PUBLIC_AUTH_ENABLED=true to require login when hosting publicly.

Backend
- New deeptutor/services/auth.py: bcrypt password hashing, JWT
  create/decode, multi-user JSON store with role + created_at schema,
  auto-migration of old flat-hash format, first-user → admin bootstrap
- New deeptutor/api/routers/auth.py: require_auth / require_admin
  FastAPI dependencies; public endpoints /login /logout /status
  /register /is_first_user; admin-only /users /users/{u}/role
- deeptutor/api/main.py: Depends(require_auth) applied to all 14
  protected routers
- deeptutor/api/routers/unified_ws.py: cookie-based JWT check before
  ws.accept() when AUTH_ENABLED
- Added bcrypt>=4.0.0 and python-jose[cryptography]>=3.3.0 to
  requirements/server.txt and pyproject.toml extras

Frontend
- web/middleware.ts: route protection; /login and /register are public
- web/lib/api.ts: apiFetch wrapper — credentials:include + 401→login
- web/lib/auth.ts: login/logout/fetchAuthStatus + register() +
  checkIsFirstUser()
- web/lib/admin-api.ts: listUsers / deleteUser / setUserRole
- web/lib/session-api.ts: credentials:include on all fetches;
  expectJson redirects to /login on 401 instead of throwing
- web/app/(auth)/login/page.tsx: auto-redirects to /register when no
  users exist; shows success banner after registration
- web/app/(auth)/register/page.tsx: new registration page with
  first-user admin notice and password confirmation
- web/app/(admin)/admin/users/page.tsx: admin dashboard — user table
  with role toggle and delete; guards against self-demotion/deletion
- AdminLink and LogoutButton hidden when AUTH_ENABLED=false
- .env.example and README.md updated with auth vars and setup guide

Made-with: Cursor
2026-04-17 21:40:28 +04:00

378 lines
11 KiB
Plaintext

{
"version": "1.5.0",
"plugins_used": [
{
"name": "ArtifactoryDetector"
},
{
"name": "AWSKeyDetector"
},
{
"name": "AzureStorageKeyDetector"
},
{
"name": "Base64HighEntropyString",
"limit": 4.5
},
{
"name": "BasicAuthDetector"
},
{
"name": "CloudantDetector"
},
{
"name": "DiscordBotTokenDetector"
},
{
"name": "GitHubTokenDetector"
},
{
"name": "GitLabTokenDetector"
},
{
"name": "HexHighEntropyString",
"limit": 3.0
},
{
"name": "IbmCloudIamDetector"
},
{
"name": "IbmCosHmacDetector"
},
{
"name": "IPPublicDetector"
},
{
"name": "JwtTokenDetector"
},
{
"name": "KeywordDetector",
"keyword_exclude": ""
},
{
"name": "MailchimpDetector"
},
{
"name": "NpmDetector"
},
{
"name": "OpenAIDetector"
},
{
"name": "PrivateKeyDetector"
},
{
"name": "PypiTokenDetector"
},
{
"name": "SendGridDetector"
},
{
"name": "SlackDetector"
},
{
"name": "SoftlayerDetector"
},
{
"name": "SquareOAuthDetector"
},
{
"name": "StripeDetector"
},
{
"name": "TelegramBotTokenDetector"
},
{
"name": "TwilioKeyDetector"
}
],
"filters_used": [
{
"path": "detect_secrets.filters.allowlist.is_line_allowlisted"
},
{
"path": "detect_secrets.filters.common.is_ignored_due_to_verification_policies",
"min_level": 2
},
{
"path": "detect_secrets.filters.heuristic.is_indirect_reference"
},
{
"path": "detect_secrets.filters.heuristic.is_likely_id_string"
},
{
"path": "detect_secrets.filters.heuristic.is_lock_file"
},
{
"path": "detect_secrets.filters.heuristic.is_not_alphanumeric_string"
},
{
"path": "detect_secrets.filters.heuristic.is_potential_uuid"
},
{
"path": "detect_secrets.filters.heuristic.is_prefixed_with_dollar_sign"
},
{
"path": "detect_secrets.filters.heuristic.is_sequential_string"
},
{
"path": "detect_secrets.filters.heuristic.is_swagger_file"
},
{
"path": "detect_secrets.filters.heuristic.is_templated_secret"
}
],
"results": {
".env.example_CN": [
{
"type": "Secret Keyword",
"filename": ".env.example_CN",
"hashed_secret": "ec417f567082612f8fd6afafe1abcab831fca840",
"is_verified": false,
"line_number": 19
}
],
"deeptutor/agents/guide/agents/interactive_agent.py": [
{
"type": "Base64 High Entropy String",
"filename": "deeptutor/agents/guide/agents/interactive_agent.py",
"hashed_secret": "559f33e318ea8360e316ec7409de63272a7daea0",
"is_verified": false,
"line_number": 88
},
{
"type": "Base64 High Entropy String",
"filename": "deeptutor/agents/guide/agents/interactive_agent.py",
"hashed_secret": "fe9ae166dc80168f37e50564be9e45c016a48cd0",
"is_verified": false,
"line_number": 89
},
{
"type": "Base64 High Entropy String",
"filename": "deeptutor/agents/guide/agents/interactive_agent.py",
"hashed_secret": "482f1d4d250823ce0ff5fad9dfaf0d471835aedb",
"is_verified": false,
"line_number": 90
}
],
"deeptutor/agents/solve/main_solver.py": [
{
"type": "Secret Keyword",
"filename": "deeptutor/agents/solve/main_solver.py",
"hashed_secret": "985b05576c57bdeccfcaf33261ea1c43ce7e3155",
"is_verified": false,
"line_number": 184
}
],
"deeptutor/api/routers/system.py": [
{
"type": "Secret Keyword",
"filename": "deeptutor/api/routers/system.py",
"hashed_secret": "985b05576c57bdeccfcaf33261ea1c43ce7e3155",
"is_verified": false,
"line_number": 160
}
],
"deeptutor/services/config/provider_runtime.py": [
{
"type": "Secret Keyword",
"filename": "deeptutor/services/config/provider_runtime.py",
"hashed_secret": "985b05576c57bdeccfcaf33261ea1c43ce7e3155",
"is_verified": false,
"line_number": 331
}
],
"deeptutor/services/search/base.py": [
{
"type": "Secret Keyword",
"filename": "deeptutor/services/search/base.py",
"hashed_secret": "8ee9d3cc35862a8ad26f9e27fb60db0e22ffe145",
"is_verified": false,
"line_number": 18
}
],
"tests/agents/test_base_agent_binding.py": [
{
"type": "Secret Keyword",
"filename": "tests/agents/test_base_agent_binding.py",
"hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
"is_verified": false,
"line_number": 18
}
],
"tests/core/test_builtin_tools.py": [
{
"type": "Secret Keyword",
"filename": "tests/core/test_builtin_tools.py",
"hashed_secret": "a62f2225bf70bfaccbc7f1ef2a397836717377de",
"is_verified": false,
"line_number": 188
}
],
"tests/services/config/test_embedding_runtime.py": [
{
"type": "Secret Keyword",
"filename": "tests/services/config/test_embedding_runtime.py",
"hashed_secret": "9aaa910eb49fa1f278c7288f0fa6001c16965716",
"is_verified": false,
"line_number": 68
},
{
"type": "Secret Keyword",
"filename": "tests/services/config/test_embedding_runtime.py",
"hashed_secret": "985b05576c57bdeccfcaf33261ea1c43ce7e3155",
"is_verified": false,
"line_number": 134
},
{
"type": "Secret Keyword",
"filename": "tests/services/config/test_embedding_runtime.py",
"hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
"is_verified": false,
"line_number": 144
},
{
"type": "Secret Keyword",
"filename": "tests/services/config/test_embedding_runtime.py",
"hashed_secret": "8c895e1fc99e38655efea259aaa5510bde46b81c",
"is_verified": false,
"line_number": 182
}
],
"tests/services/config/test_provider_runtime.py": [
{
"type": "Secret Keyword",
"filename": "tests/services/config/test_provider_runtime.py",
"hashed_secret": "b07bfd6660c8a5e8f47ed4967498d25f095cb7c5",
"is_verified": false,
"line_number": 95
},
{
"type": "Secret Keyword",
"filename": "tests/services/config/test_provider_runtime.py",
"hashed_secret": "1e8f4accfda4813178f4f6317c767a915bfbf227",
"is_verified": false,
"line_number": 115
},
{
"type": "Secret Keyword",
"filename": "tests/services/config/test_provider_runtime.py",
"hashed_secret": "985b05576c57bdeccfcaf33261ea1c43ce7e3155",
"is_verified": false,
"line_number": 163
}
],
"tests/services/embedding/test_client_runtime.py": [
{
"type": "Secret Keyword",
"filename": "tests/services/embedding/test_client_runtime.py",
"hashed_secret": "e9a5f12a8ecbb3eb46eca5096b5c52aa5e7c9fdd",
"is_verified": false,
"line_number": 35
}
],
"tests/services/llm/test_config_module.py": [
{
"type": "Secret Keyword",
"filename": "tests/services/llm/test_config_module.py",
"hashed_secret": "6b8f36db289d14c3c7ba1bb21147b7a4fa7e7536",
"is_verified": false,
"line_number": 40
},
{
"type": "Secret Keyword",
"filename": "tests/services/llm/test_config_module.py",
"hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
"is_verified": false,
"line_number": 102
}
],
"tests/services/llm/test_factory_provider_exec.py": [
{
"type": "Secret Keyword",
"filename": "tests/services/llm/test_factory_provider_exec.py",
"hashed_secret": "6b8f36db289d14c3c7ba1bb21147b7a4fa7e7536",
"is_verified": false,
"line_number": 15
},
{
"type": "Secret Keyword",
"filename": "tests/services/llm/test_factory_provider_exec.py",
"hashed_secret": "9247e92e3d5c3957c5f5480ec8917b9100baa32d",
"is_verified": false,
"line_number": 41
},
{
"type": "Secret Keyword",
"filename": "tests/services/llm/test_factory_provider_exec.py",
"hashed_secret": "627a27ae52d38269376e0117355fefc891b60334",
"is_verified": false,
"line_number": 84
}
],
"tests/services/test_model_catalog.py": [
{
"type": "Secret Keyword",
"filename": "tests/services/test_model_catalog.py",
"hashed_secret": "f2bb1f20475e99c2af44a6f6844c1329bea9d3d2",
"is_verified": false,
"line_number": 89
},
{
"type": "Secret Keyword",
"filename": "tests/services/test_model_catalog.py",
"hashed_secret": "21729167f484c69c93d2a1956dace832faee1e6a",
"is_verified": false,
"line_number": 107
},
{
"type": "Secret Keyword",
"filename": "tests/services/test_model_catalog.py",
"hashed_secret": "3090ca86558e326f27cb6f36d0592ed339f108be",
"is_verified": false,
"line_number": 141
},
{
"type": "Secret Keyword",
"filename": "tests/services/test_model_catalog.py",
"hashed_secret": "c02d46771666140734db6f2e650f2b051f448aad",
"is_verified": false,
"line_number": 146
}
],
"tests/test_openrouter_provider.py": [
{
"type": "Secret Keyword",
"filename": "tests/test_openrouter_provider.py",
"hashed_secret": "3acfb2c2b433c0ea7ff107e33df91b18e52f960f",
"is_verified": false,
"line_number": 10
}
],
"web/app/(workspace)/guide/hooks/useKaTeXInjection.ts": [
{
"type": "Base64 High Entropy String",
"filename": "web/app/(workspace)/guide/hooks/useKaTeXInjection.ts",
"hashed_secret": "559f33e318ea8360e316ec7409de63272a7daea0",
"is_verified": false,
"line_number": 25
},
{
"type": "Base64 High Entropy String",
"filename": "web/app/(workspace)/guide/hooks/useKaTeXInjection.ts",
"hashed_secret": "fe9ae166dc80168f37e50564be9e45c016a48cd0",
"is_verified": false,
"line_number": 27
},
{
"type": "Base64 High Entropy String",
"filename": "web/app/(workspace)/guide/hooks/useKaTeXInjection.ts",
"hashed_secret": "482f1d4d250823ce0ff5fad9dfaf0d471835aedb",
"is_verified": false,
"line_number": 29
}
]
},
"generated_at": "2026-04-06T16:37:14Z"
}