first commit

This commit is contained in:
龙澳
2026-03-23 14:32:15 +08:00
parent 7045866ef3
commit 9ce8858562
3 changed files with 26 additions and 13 deletions

View File

@@ -2,7 +2,7 @@ AUTH_DB_PATH=~/.nanobot/auth_service.sqlite3
AUTH_JWT_SECRET=change-this-secret
AUTH_TOKEN_TTL_HOURS=24
AUTH_CORS_ORIGINS=*
AUTH_INVITE_CODES=invite-a,invite-b
AUTH_VERIFICATION_CODES=code-a,code-b
AUTH_ADMIN_KEY=change-this-admin-key
AUTH_HOST=0.0.0.0
AUTH_PORT=9100

View File

@@ -4,7 +4,7 @@ Standalone phone/password auth service for nanobot web chat.
## Features
- `POST /auth/register` (phone + password + invite code; returns pending)
- `POST /auth/register` (phone + password + verification code; returns pending)
- `POST /auth/login`
- `GET /auth/me` (Bearer token)
- `GET /auth/register/status/{request_id}`
@@ -30,7 +30,7 @@ uvicorn app.main:app --host ${AUTH_HOST:-0.0.0.0} --port ${AUTH_PORT:-9100}
- `AUTH_JWT_SECRET`: JWT signing secret
- `AUTH_TOKEN_TTL_HOURS`: access token ttl
- `AUTH_CORS_ORIGINS`: comma-separated origins or `*`
- `AUTH_INVITE_CODES`: comma-separated whitelist (empty means no whitelist check)
- `AUTH_VERIFICATION_CODES`: comma-separated whitelist (empty means no whitelist check)
- `AUTH_ADMIN_KEY`: required by admin endpoints
- `AUTH_HOST`: bind host (run command)
- `AUTH_PORT`: bind port (run command)
@@ -43,7 +43,7 @@ uvicorn app.main:app --host ${AUTH_HOST:-0.0.0.0} --port ${AUTH_PORT:-9100}
{
"phone": "13800000000",
"password": "secret123",
"invite_code": "invite-a"
"verification_code": "code-a"
}
```

View File

@@ -27,6 +27,11 @@ AUTH_INVITE_CODES = {
for item in os.getenv("AUTH_INVITE_CODES", "").split(",")
if item.strip()
}
AUTH_VERIFICATION_CODES = {
item.strip()
for item in os.getenv("AUTH_VERIFICATION_CODES", "").split(",")
if item.strip()
}
AUTH_ADMIN_KEY = os.getenv("AUTH_ADMIN_KEY", "")
@@ -74,7 +79,8 @@ class AuthPayload(BaseModel):
class RegisterPayload(AuthPayload):
invite_code: str
invite_code: str = ""
verification_code: str = ""
class TokenResponse(BaseModel):
@@ -170,12 +176,17 @@ def _now_iso() -> str:
return datetime.now(timezone.utc).isoformat()
def _require_invite_code(invite_code: str) -> str:
code = str(invite_code).strip()
def _allowed_verification_codes() -> set[str]:
return AUTH_VERIFICATION_CODES or AUTH_INVITE_CODES
def _require_verification_code(code_value: str) -> str:
code = str(code_value).strip()
if not code:
raise HTTPException(status_code=400, detail="invite code is required")
if AUTH_INVITE_CODES and code not in AUTH_INVITE_CODES:
raise HTTPException(status_code=400, detail="invalid invite code")
raise HTTPException(status_code=400, detail="verification code is required")
allowed = _allowed_verification_codes()
if allowed and code not in allowed:
raise HTTPException(status_code=400, detail="invalid verification code")
return code
@@ -268,7 +279,7 @@ def health() -> dict:
"ok": True,
"service": "auth",
"db": str(DB_PATH),
"invite_code_check": bool(AUTH_INVITE_CODES),
"verification_code_check": bool(_allowed_verification_codes()),
}
@@ -276,7 +287,9 @@ def health() -> dict:
def register(payload: RegisterPayload):
phone = _normalize_phone(payload.phone)
_validate_password(payload.password)
invite_code = _require_invite_code(payload.invite_code)
verification_code = _require_verification_code(
payload.verification_code or payload.invite_code
)
salt = secrets.token_bytes(16)
password_hash = _hash_password(payload.password, salt)
@@ -308,7 +321,7 @@ def register(payload: RegisterPayload):
INSERT INTO registration_requests(phone, password_hash, salt, invite_code, status, created_at)
VALUES(?, ?, ?, ?, 'pending', ?)
""",
(phone, password_hash, base64.b64encode(salt).decode("ascii"), invite_code, now),
(phone, password_hash, base64.b64encode(salt).decode("ascii"), verification_code, now),
)
request_id = int(cur.lastrowid)
conn.commit()