작업계획서대로 진행
This commit is contained in:
529
claude_temp/04_Gmail_OAuth_로그인_작업계획서.md
Normal file
529
claude_temp/04_Gmail_OAuth_로그인_작업계획서.md
Normal file
@@ -0,0 +1,529 @@
|
||||
# Gmail OAuth 로그인 작업계획서
|
||||
|
||||
> 작성일: 2026-01-10
|
||||
> 예상 기간: 5~7일
|
||||
> 우선순위: 4
|
||||
|
||||
---
|
||||
|
||||
## 1. 기능 개요
|
||||
|
||||
### 1.1 핵심 컨셉
|
||||
- **Google OAuth + 비밀번호 인증** 모두 지원 (개발/운영 동일)
|
||||
- Gmail 주소로 기존 사용자(wr_employee_info.email) 매칭
|
||||
- 매칭 안되면 로그인 거부 → "관리자에게 문의하세요"
|
||||
- **OAuth 로그인 후 비밀번호 미설정 시 설정 안내**
|
||||
- **비밀번호 찾기**: 이름+이메일+핸드폰 매칭 → 임시 비밀번호 이메일 발송
|
||||
|
||||
### 1.2 로그인 플로우 (개발/운영 동일)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 로그인 페이지 │
|
||||
│ │
|
||||
│ [G] Google로 로그인 │
|
||||
│ [S] Synology로 로그인 ← (5번 작업 후 추가) │
|
||||
│ │
|
||||
│ ──────────── 또는 ──────────── │
|
||||
│ │
|
||||
│ 이메일: [_______________] │
|
||||
│ 비밀번호: [_______________] │
|
||||
│ │
|
||||
│ [로그인] [비밀번호 찾기] │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 1.3 Google OAuth 로그인 플로우
|
||||
|
||||
```
|
||||
[Google로 로그인] 클릭
|
||||
↓
|
||||
Google OAuth 인증
|
||||
↓
|
||||
Gmail 주소 획득
|
||||
↓
|
||||
wr_employee_info.email 매칭?
|
||||
├─ NO → "등록되지 않은 사용자입니다. 관리자에게 문의하세요."
|
||||
└─ YES ↓
|
||||
비밀번호 설정됨?
|
||||
├─ YES → 메인 페이지로 이동
|
||||
└─ NO → 비밀번호 설정 페이지
|
||||
"비상시 로그인을 위해 비밀번호를 설정해주세요"
|
||||
```
|
||||
|
||||
### 1.4 비밀번호 찾기 플로우
|
||||
|
||||
```
|
||||
[비밀번호 찾기] 클릭
|
||||
↓
|
||||
┌─────────────────────────────────────┐
|
||||
│ 이름: [_______________] │
|
||||
│ 이메일: [_______________] │
|
||||
│ 핸드폰: [_______________] │
|
||||
│ │
|
||||
│ [임시 비밀번호 발송] │
|
||||
└─────────────────────────────────────┘
|
||||
↓
|
||||
세 가지 모두 매칭되는 사용자 확인
|
||||
├─ 매칭됨 → 이메일로 임시 비밀번호 발송 → "이메일을 확인해주세요"
|
||||
└─ 불일치 → "일치하는 정보가 없습니다"
|
||||
```
|
||||
|
||||
### 1.5 결정 사항
|
||||
|
||||
| # | 항목 | 결정 |
|
||||
|:-:|------|:----:|
|
||||
| 1 | 매칭 안되는 Gmail | 로그인 거부, 관리자 문의 안내 |
|
||||
| 2 | 비밀번호 관리 | 필수 (OAuth 로그인 후 미설정 시 설정 유도) |
|
||||
| 3 | Google 계정 연결 | 1인 1계정 |
|
||||
| 4 | 환경별 로그인 | 개발/운영 동일 (OAuth + 비밀번호 모두 지원) |
|
||||
| 5 | 비밀번호 찾기 | 이름+이메일+핸드폰 매칭 → 임시 비밀번호 이메일 발송 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 데이터 모델
|
||||
|
||||
### 2.1 사용자 테이블 수정 (wr_employee_info)
|
||||
|
||||
```sql
|
||||
-- 기존 테이블에 컬럼 추가
|
||||
ALTER TABLE wr_employee_info
|
||||
ADD COLUMN password_hash VARCHAR(200), -- 비밀번호 해시
|
||||
ADD COLUMN google_id VARCHAR(100), -- Google 고유 ID (sub)
|
||||
ADD COLUMN google_email VARCHAR(100), -- Google 이메일 (확인용)
|
||||
ADD COLUMN google_linked_at TIMESTAMP, -- Google 연결 일시
|
||||
ADD COLUMN last_login_at TIMESTAMP, -- 마지막 로그인
|
||||
ADD COLUMN last_login_ip VARCHAR(50); -- 마지막 로그인 IP
|
||||
|
||||
-- 인덱스
|
||||
CREATE UNIQUE INDEX idx_employee_google_id ON wr_employee_info(google_id) WHERE google_id IS NOT NULL;
|
||||
```
|
||||
|
||||
### 2.2 로그인 이력 테이블 (wr_login_history) - 선택
|
||||
|
||||
```sql
|
||||
CREATE TABLE wr_login_history (
|
||||
history_id SERIAL PRIMARY KEY,
|
||||
employee_id INTEGER NOT NULL REFERENCES wr_employee_info(employee_id),
|
||||
login_type VARCHAR(20) NOT NULL, -- GOOGLE, PASSWORD
|
||||
login_ip VARCHAR(50),
|
||||
user_agent VARCHAR(500),
|
||||
login_at TIMESTAMP DEFAULT NOW(),
|
||||
success BOOLEAN DEFAULT true,
|
||||
fail_reason VARCHAR(200) -- 실패 시 사유
|
||||
);
|
||||
|
||||
CREATE INDEX idx_login_history_employee ON wr_login_history(employee_id);
|
||||
CREATE INDEX idx_login_history_at ON wr_login_history(login_at);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. API 설계
|
||||
|
||||
### 3.1 인증 API
|
||||
|
||||
| Method | Endpoint | 설명 |
|
||||
|--------|----------|------|
|
||||
| GET | /api/auth/google | Google OAuth 시작 (리다이렉트) |
|
||||
| GET | /api/auth/google/callback | Google 콜백 처리 |
|
||||
| POST | /api/auth/login | 이메일/비밀번호 로그인 |
|
||||
| POST | /api/auth/logout | 로그아웃 |
|
||||
| GET | /api/auth/me | 현재 사용자 정보 |
|
||||
|
||||
### 3.2 비밀번호 관리 API
|
||||
|
||||
| Method | Endpoint | 설명 |
|
||||
|--------|----------|------|
|
||||
| POST | /api/auth/set-password | 비밀번호 최초 설정 (OAuth 후) |
|
||||
| PUT | /api/auth/change-password | 비밀번호 변경 (본인) |
|
||||
| POST | /api/auth/find-password | 비밀번호 찾기 (임시 비밀번호 발송) |
|
||||
| PUT | /api/admin/user/[id]/reset-password | 비밀번호 초기화 (관리자) |
|
||||
|
||||
---
|
||||
|
||||
## 4. 화면 설계
|
||||
|
||||
### 4.1 로그인 페이지 (/login)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ 📊 주간보고 시스템 │
|
||||
│ │
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ [G] Google로 로그인 │ │
|
||||
│ └─────────────────────────────────────┘ │
|
||||
│ ┌─────────────────────────────────────┐ │
|
||||
│ │ [S] Synology로 로그인 │ │ ← 5번 작업 후
|
||||
│ └─────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ─────────────── 또는 ─────────────── │
|
||||
│ │
|
||||
│ 이메일: [_________________________] │
|
||||
│ 비밀번호: [_________________________] │
|
||||
│ │
|
||||
│ [로그인] [비밀번호 찾기] │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4.2 비밀번호 설정 페이지 (/auth/set-password)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ 🔐 비밀번호 설정 │
|
||||
│ │
|
||||
│ Google 계정으로 로그인되었습니다. │
|
||||
│ 비상시 로그인을 위해 비밀번호를 설정해주세요. │
|
||||
│ │
|
||||
│ 새 비밀번호: [_________________________] │
|
||||
│ 비밀번호 확인: [_________________________] │
|
||||
│ │
|
||||
│ ※ 8자 이상, 영문+숫자 조합 권장 │
|
||||
│ │
|
||||
│ [비밀번호 설정] │
|
||||
│ │
|
||||
│ [나중에 설정하기 →] │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
* "나중에 설정하기" 클릭 시 메인으로 이동하지만, 다음 로그인 시 다시 안내
|
||||
```
|
||||
|
||||
### 4.3 비밀번호 찾기 페이지 (/auth/find-password)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ 🔑 비밀번호 찾기 │
|
||||
│ │
|
||||
│ 등록된 정보와 일치하면 이메일로 임시 비밀번호를 발송합니다. │
|
||||
│ │
|
||||
│ 이름: [_________________________] │
|
||||
│ 이메일: [_________________________] │
|
||||
│ 핸드폰: [_________________________] │
|
||||
│ │
|
||||
│ [임시 비밀번호 발송] │
|
||||
│ │
|
||||
│ [← 로그인으로] │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
↓ 발송 성공 시 ↓
|
||||
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ ✅ 발송 완료 │
|
||||
│ │
|
||||
│ 임시 비밀번호가 이메일로 발송되었습니다. │
|
||||
│ 이메일: hyo****@company.com │
|
||||
│ │
|
||||
│ ※ 로그인 후 비밀번호를 변경해주세요. │
|
||||
│ │
|
||||
│ [로그인하러 가기] │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4.4 로그인 실패 시 (매칭 안됨)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ ⚠️ 로그인 실패 │
|
||||
│ │
|
||||
│ "unknown@gmail.com"은 등록되지 않은 사용자입니다. │
|
||||
│ │
|
||||
│ 시스템 사용을 위해서는 관리자에게 문의하여 │
|
||||
│ 사용자 등록을 요청해주세요. │
|
||||
│ │
|
||||
│ 관리자 연락처: admin@company.com │
|
||||
│ │
|
||||
│ [다시 로그인] │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4.4 마이페이지 - 비밀번호 변경 (/mypage)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 마이페이지 │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 👤 기본 정보 │
|
||||
│ ─────────────────────────────────────────────────────────────── │
|
||||
│ 이름: 조효성 │
|
||||
│ 이메일: hyosung@company.com │
|
||||
│ 소속: 개발팀 │
|
||||
│ 권한: 관리자 │
|
||||
│ │
|
||||
│ 🔗 Google 연결 │
|
||||
│ ─────────────────────────────────────────────────────────────── │
|
||||
│ 상태: ✅ 연결됨 (hyosung@gmail.com) │
|
||||
│ 연결일: 2026-01-10 │
|
||||
│ │
|
||||
│ 🔒 비밀번호 변경 │
|
||||
│ ─────────────────────────────────────────────────────────────── │
|
||||
│ 현재 비밀번호: [_________________________] │
|
||||
│ 새 비밀번호: [_________________________] │
|
||||
│ 비밀번호 확인: [_________________________] │
|
||||
│ │
|
||||
│ [비밀번호 변경] │
|
||||
│ │
|
||||
│ 마지막 로그인: 2026-01-10 09:30 (Google) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 4.5 관리자 - 사용자 관리 수정 (/admin/user)
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ 사용자 상세 [수정] [삭제] │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 기본 정보 │
|
||||
│ ─────────────────────────────────────────────────────────────── │
|
||||
│ 이름: 조효성 │
|
||||
│ 이메일: hyosung@company.com │
|
||||
│ ... │
|
||||
│ │
|
||||
│ 🔐 계정 관리 │
|
||||
│ ─────────────────────────────────────────────────────────────── │
|
||||
│ Google 연결: ✅ 연결됨 (hyosung@gmail.com) [연결 해제] │
|
||||
│ 비밀번호: ******** [초기화] │
|
||||
│ 마지막 로그인: 2026-01-10 09:30 │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
|
||||
* [초기화] 클릭 → 임시 비밀번호 생성 → 사용자에게 전달
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 환경 설정
|
||||
|
||||
### 5.1 환경 변수
|
||||
|
||||
```env
|
||||
# .env (공통)
|
||||
# Google OAuth
|
||||
GOOGLE_CLIENT_ID=xxx.apps.googleusercontent.com
|
||||
GOOGLE_CLIENT_SECRET=xxx
|
||||
GOOGLE_REDIRECT_URI=https://weeklyreport.company.com/api/auth/google/callback
|
||||
|
||||
# 이메일 발송 (비밀번호 찾기용)
|
||||
SMTP_HOST=smtp.gmail.com
|
||||
SMTP_PORT=587
|
||||
SMTP_USER=noreply@company.com
|
||||
SMTP_PASS=xxx
|
||||
SMTP_FROM=주간보고시스템 <noreply@company.com>
|
||||
```
|
||||
|
||||
### 5.2 개발/운영 분기 (필요 시)
|
||||
|
||||
```env
|
||||
# .env.development
|
||||
GOOGLE_REDIRECT_URI=http://localhost:3000/api/auth/google/callback
|
||||
|
||||
# .env.production
|
||||
GOOGLE_REDIRECT_URI=https://weeklyreport.company.com/api/auth/google/callback
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Google OAuth 설정
|
||||
|
||||
### 6.1 Google Cloud Console 설정
|
||||
|
||||
1. **프로젝트 생성**: Google Cloud Console
|
||||
2. **OAuth 동의 화면 설정**:
|
||||
- 앱 이름: 주간보고 시스템
|
||||
- 범위: email, profile
|
||||
3. **사용자 인증 정보 생성**:
|
||||
- OAuth 2.0 클라이언트 ID
|
||||
- 승인된 리디렉션 URI: `https://weeklyreport.company.com/api/auth/google/callback`
|
||||
|
||||
### 6.2 OAuth 흐름
|
||||
|
||||
```
|
||||
1. 사용자 → [Google로 로그인] 클릭
|
||||
2. 서버 → Google 인증 페이지로 리다이렉트
|
||||
3. 사용자 → Google 계정 선택, 권한 승인
|
||||
4. Google → 콜백 URL로 리다이렉트 (code 포함)
|
||||
5. 서버 → code로 access_token 요청
|
||||
6. 서버 → access_token으로 사용자 정보 요청
|
||||
7. 서버 → email로 wr_employee_info 조회
|
||||
- 있으면: 세션 생성, 로그인 완료
|
||||
- 없으면: 에러 페이지 표시
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 보안 고려사항
|
||||
|
||||
### 7.1 비밀번호 정책
|
||||
|
||||
| 항목 | 정책 |
|
||||
|------|------|
|
||||
| 최소 길이 | 8자 이상 |
|
||||
| 복잡도 | 영문 + 숫자 조합 권장 |
|
||||
| 해시 알고리즘 | bcrypt (salt rounds: 10) |
|
||||
| 임시 비밀번호 | 랜덤 생성 (12자, 영문+숫자+특수문자) |
|
||||
|
||||
### 7.2 세션 관리
|
||||
|
||||
| 항목 | 설정 |
|
||||
|------|------|
|
||||
| 세션 유효기간 | 24시간 (또는 설정 가능) |
|
||||
| 세션 저장소 | 기존 방식 유지 (쿠키/메모리) |
|
||||
| 동시 로그인 | 허용 (기기별 세션) |
|
||||
|
||||
### 7.3 비밀번호 찾기 이메일 템플릿
|
||||
|
||||
```
|
||||
제목: [주간보고시스템] 임시 비밀번호 안내
|
||||
|
||||
───────────────────────────────────────
|
||||
|
||||
안녕하세요, {이름}님.
|
||||
|
||||
요청하신 임시 비밀번호를 안내드립니다.
|
||||
|
||||
임시 비밀번호: {임시비밀번호}
|
||||
|
||||
보안을 위해 로그인 후 반드시 비밀번호를 변경해주세요.
|
||||
|
||||
※ 본 메일은 발신 전용입니다.
|
||||
※ 본인이 요청하지 않은 경우 관리자에게 문의해주세요.
|
||||
|
||||
───────────────────────────────────────
|
||||
주간보고시스템
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. 작업 일정
|
||||
|
||||
### Phase 1: DB + 환경 설정 (1일)
|
||||
- [ ] 시작:
|
||||
- [ ] 완료:
|
||||
- [ ] 소요시간:
|
||||
|
||||
**작업 내용:**
|
||||
- [ ] wr_employee_info 컬럼 추가 (password_hash, google_id 등)
|
||||
- [ ] wr_login_history 테이블 생성 (선택)
|
||||
- [ ] 환경 변수 설정 (Google OAuth, SMTP)
|
||||
- [ ] Google Cloud Console OAuth 설정
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: 비밀번호 인증 (1.5일)
|
||||
- [ ] 시작:
|
||||
- [ ] 완료:
|
||||
- [ ] 소요시간:
|
||||
|
||||
**작업 내용:**
|
||||
- [ ] bcrypt 해시 처리
|
||||
- [ ] 이메일/비밀번호 로그인 API
|
||||
- [ ] 비밀번호 변경 API
|
||||
- [ ] 비밀번호 초기화 API (관리자)
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Google OAuth (1.5일)
|
||||
- [ ] 시작:
|
||||
- [ ] 완료:
|
||||
- [ ] 소요시간:
|
||||
|
||||
**작업 내용:**
|
||||
- [ ] Google OAuth 시작/콜백 API
|
||||
- [ ] 사용자 매칭 로직 (email 기준)
|
||||
- [ ] 비밀번호 미설정 시 설정 페이지 리다이렉트
|
||||
- [ ] 비밀번호 최초 설정 API
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: 비밀번호 찾기 + 이메일 발송 (1일)
|
||||
- [ ] 시작:
|
||||
- [ ] 완료:
|
||||
- [ ] 소요시간:
|
||||
|
||||
**작업 내용:**
|
||||
- [ ] 이메일 발송 유틸 (nodemailer)
|
||||
- [ ] 비밀번호 찾기 API (이름+이메일+핸드폰 매칭)
|
||||
- [ ] 임시 비밀번호 생성 및 발송
|
||||
- [ ] 비밀번호 찾기 페이지
|
||||
|
||||
---
|
||||
|
||||
### Phase 5: 로그인 UI + 테스트 (1일)
|
||||
- [ ] 시작:
|
||||
- [ ] 완료:
|
||||
- [ ] 소요시간:
|
||||
|
||||
**작업 내용:**
|
||||
- [ ] 로그인 페이지 (OAuth + 비밀번호)
|
||||
- [ ] 비밀번호 설정 페이지
|
||||
- [ ] 로그인 실패 페이지
|
||||
- [ ] 마이페이지 비밀번호 변경 UI
|
||||
- [ ] 관리자 사용자 관리 수정 (비밀번호 초기화)
|
||||
- [ ] 전체 플로우 테스트
|
||||
|
||||
---
|
||||
|
||||
## 작업 완료 결과
|
||||
|
||||
### Phase별 작업 시간
|
||||
|
||||
| Phase | 작업 내용 | 시작 | 완료 | 소요시간 |
|
||||
|:-----:|----------|:----:|:----:|:--------:|
|
||||
| 1 | DB + 환경 설정 | - | - | - |
|
||||
| 2 | 비밀번호 인증 | - | - | - |
|
||||
| 3 | Google OAuth | - | - | - |
|
||||
| 4 | 비밀번호 찾기 + 이메일 발송 | - | - | - |
|
||||
| 5 | 로그인 UI + 테스트 | - | - | - |
|
||||
| | | | **총 소요시간** | **-** |
|
||||
|
||||
---
|
||||
|
||||
### 생성/수정된 파일
|
||||
|
||||
| 구분 | 파일 | 작업 |
|
||||
|------|------|:----:|
|
||||
| **DB** | wr_employee_info | 수정 (컬럼 추가) |
|
||||
| **DB** | wr_login_history | 신규 테이블 (선택) |
|
||||
| **API** | backend/api/auth/google.get.ts | 신규 |
|
||||
| **API** | backend/api/auth/google/callback.get.ts | 신규 |
|
||||
| **API** | backend/api/auth/login.post.ts | 수정 |
|
||||
| **API** | backend/api/auth/set-password.post.ts | 신규 |
|
||||
| **API** | backend/api/auth/change-password.put.ts | 신규 |
|
||||
| **API** | backend/api/auth/find-password.post.ts | 신규 |
|
||||
| **API** | backend/api/admin/user/[id]/reset-password.put.ts | 신규 |
|
||||
| **Frontend** | frontend/pages/login.vue | 수정 |
|
||||
| **Frontend** | frontend/pages/auth/set-password.vue | 신규 |
|
||||
| **Frontend** | frontend/pages/auth/find-password.vue | 신규 |
|
||||
| **Frontend** | frontend/pages/mypage.vue | 수정 |
|
||||
| **Frontend** | frontend/pages/admin/user/[id].vue | 수정 |
|
||||
| **Utils** | backend/utils/password.ts | 신규 |
|
||||
| **Utils** | backend/utils/email.ts | 신규 |
|
||||
| **Config** | .env | 수정 (OAuth, SMTP 설정) |
|
||||
|
||||
---
|
||||
|
||||
## 9. 기술 스택
|
||||
|
||||
- **Backend**: Nitro (H3) + PostgreSQL
|
||||
- **Frontend**: Nuxt3 + Vue3 + Bootstrap 5
|
||||
- **OAuth**: Google OAuth 2.0
|
||||
- **비밀번호**: bcrypt
|
||||
- **이메일 발송**: nodemailer
|
||||
- **세션**: 기존 방식 유지
|
||||
|
||||
---
|
||||
|
||||
## 10. 향후 확장 고려
|
||||
|
||||
1. **Synology SSO 연동**: 다음 작업 (5번)
|
||||
2. **2단계 인증 (2FA)**: TOTP 기반 추가 인증
|
||||
3. **소셜 로그인 확장**: Microsoft, Kakao 등
|
||||
4. **비밀번호 만료**: 90일 주기 변경 강제
|
||||
5. **로그인 알림**: 새 기기 로그인 시 이메일 알림
|
||||
6. **임시 비밀번호 만료**: 24시간 후 만료 처리
|
||||
Reference in New Issue
Block a user