first commit
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
31
app/main.py
31
app/main.py
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user