first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 22:12:36 +03:00
commit e881f38e4e
278 changed files with 24095 additions and 0 deletions

904
AUTH.md Normal file
View File

@@ -0,0 +1,904 @@
# Authentication API Documentation
Bu doküman, Django REST API'nin authentication endpoint'lerini ve kullanım örneklerini içerir.
## 📋 İçindekiler
1. [Genel Bilgiler](#genel-bilgiler)
2. [Registration (Kayıt)](#registration-kayıt)
3. [Email Activation (Aktivasyon)](#email-activation-aktivasyon)
4. [Login (Giriş)](#login-giriş)
5. [Token Refresh](#token-refresh)
6. [Social Authentication](#social-authentication)
7. [User Profile](#user-profile)
8. [Password Reset](#password-reset)
9. [Frontend Entegrasyonu](#frontend-entegrasyonu)
10. [Error Handling](#error-handling)
---
## Genel Bilgiler
**Base URL:** `http://localhost:8000/api/v1/`
**Authentication:** JWT Bearer Token
```
Authorization: Bearer <access_token>
```
**Content-Type:** `application/json`
### Rate Limiting
- **Anonymous users:** 100 requests/hour
- **Authenticated users:** 1000 requests/hour
---
## Registration (Kayıt)
### Endpoint
```
POST /api/v1/auth/users/
```
### Request Body
```json
{
"email": "user@example.com",
"password": "StrongP@ssw0rd123",
"re_password": "StrongP@ssw0rd123",
"first_name": "Ali",
"last_name": "Veli"
}
```
### Response (201 Created)
```json
{
"id": 1,
"email": "user@example.com",
"first_name": "Ali",
"last_name": "Veli"
}
```
### Önemli Notlar
- Kullanıcı oluşturulur ancak **`is_active=False`** olarak ayarlanır
- Aktivasyon emaili otomatik gönderilir
- Kullanıcı email aktivasyonu yapmadan login olamaz
- Password minimum 8 karakter olmalı ve güçlü olmalı
### Curl Example
```bash
curl -X POST http://localhost:8000/api/v1/auth/users/ \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "StrongP@ssw0rd123",
"re_password": "StrongP@ssw0rd123",
"first_name": "Ali",
"last_name": "Veli"
}'
```
---
## Email Activation (Aktivasyon)
### Endpoint
```
POST /api/v1/auth/users/activation/
```
### Request Body
```json
{
"uid": "MQ",
"token": "c4h7vu-a8f3d2e1c4b5a6d7e8f9g0h1"
}
```
### Response (204 No Content)
Başarılı aktivasyon sonrası response body boş döner.
### Önemli Notlar
- `uid` ve `token` aktivasyon emailindeki linkten alınır
- Token 24 saat geçerlidir
- Başarılı aktivasyon sonrası `is_active=True` olur
- Kullanıcı artık login olabilir
### Email Link Format
```
http://localhost:3000/auth/activate/{uid}/{token}/
```
Frontend bu linki yakalayıp backend'e POST request yapmalı.
### Curl Example
```bash
curl -X POST http://localhost:8000/api/v1/auth/users/activation/ \
-H "Content-Type: application/json" \
-d '{
"uid": "MQ",
"token": "c4h7vu-a8f3d2e1c4b5a6d7e8f9g0h1"
}'
```
### Resend Activation Email
```
POST /api/v1/auth/users/resend_activation/
```
Request Body:
```json
{
"email": "user@example.com"
}
```
---
## Login (Giriş)
### Endpoint
```
POST /api/v1/auth/jwt/create/
```
### Request Body
```json
{
"email": "user@example.com",
"password": "StrongP@ssw0rd123"
}
```
### Response (200 OK)
```json
{
"access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```
### Token Bilgileri
- **Access Token:** 60 dakika geçerli
- **Refresh Token:** 7 gün geçerli
- Token rotation aktif (refresh kullanıldığında yeni refresh token döner)
### Önemli Notlar
- Kullanıcı `is_active=False` ise login başarısız olur
- Hatalı email/password için 401 Unauthorized döner
### Curl Example
```bash
curl -X POST http://localhost:8000/api/v1/auth/jwt/create/ \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "StrongP@ssw0rd123"
}'
```
### Error Response (401 Unauthorized)
```json
{
"detail": "No active account found with the given credentials"
}
```
---
## Token Refresh
### Endpoint
```
POST /api/v1/auth/jwt/refresh/
```
### Request Body
```json
{
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```
### Response (200 OK)
```json
{
"access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
```
### Önemli Notlar
- Yeni access token ve yeni refresh token döner (rotation)
- Eski refresh token blacklist'e eklenir
- Refresh token expire olduysa 401 döner
### Curl Example
```bash
curl -X POST http://localhost:8000/api/v1/auth/jwt/refresh/ \
-H "Content-Type: application/json" \
-d '{
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}'
```
---
## Social Authentication
### Supported Providers
- **Google:** `google-oauth2`
- **GitHub:** `github`
- **Facebook:** `facebook`
### Endpoint
```
POST /api/v1/auth/social/<provider>/
```
### Request Body
```json
{
"access_token": "ya29.a0AfH6SMBx..."
}
```
### Response (200 OK)
```json
{
"access": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"email": "user@example.com",
"first_name": "Ali",
"last_name": "Veli",
"is_active": true,
"date_joined": "2025-12-12T21:30:00Z"
}
}
```
### Önemli Notlar
- Social login ile gelen kullanıcılar **otomatik aktif** (`is_active=True`)
- Email aktivasyon gerekmez
- Kullanıcı yoksa otomatik oluşturulur
- Provider'dan email alınamazsa hata döner
### Google OAuth2 Example
#### 1. Frontend'de Google OAuth
```javascript
// Google OAuth2 ile token al
const googleUser = await gapi.auth2.getAuthInstance().signIn();
const accessToken = googleUser.getAuthResponse().access_token;
// Backend'e gönder
const response = await fetch('http://localhost:8000/api/v1/auth/social/google-oauth2/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
access_token: accessToken
})
});
const data = await response.json();
// data.access, data.refresh, data.user
```
#### 2. Curl Example
```bash
curl -X POST http://localhost:8000/api/v1/auth/social/google-oauth2/ \
-H "Content-Type: application/json" \
-d '{
"access_token": "ya29.a0AfH6SMBx..."
}'
```
### GitHub OAuth2 Example
```bash
curl -X POST http://localhost:8000/api/v1/auth/social/github/ \
-H "Content-Type: application/json" \
-d '{
"access_token": "gho_16C7e42F292c6912E7710c838347Ae178B4a"
}'
```
### Error Responses
**Invalid Provider (400)**
```json
{
"error": "Invalid provider. Must be one of: google-oauth2, github, facebook"
}
```
**Missing Token (400)**
```json
{
"error": "access_token is required"
}
```
**Authentication Failed (401)**
```json
{
"error": "Authentication failed. Invalid token."
}
```
**Email Not Provided (403)**
```json
{
"error": "Authentication forbidden. Email not provided by provider or permission denied."
}
```
---
## User Profile
### Get Current User
```
GET /api/v1/auth/users/me/
```
**Headers:**
```
Authorization: Bearer <access_token>
```
**Response (200 OK):**
```json
{
"id": 1,
"email": "user@example.com",
"first_name": "Ali",
"last_name": "Veli",
"is_active": true,
"date_joined": "2025-12-12T21:30:00Z"
}
```
### Update Current User
```
PATCH /api/v1/auth/users/me/
```
**Request Body:**
```json
{
"first_name": "Ahmet",
"last_name": "Yılmaz"
}
```
### Curl Example
```bash
curl -X GET http://localhost:8000/api/v1/auth/users/me/ \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
```
---
## Password Reset
### 1. Request Password Reset
```
POST /api/v1/auth/users/reset_password/
```
**Request Body:**
```json
{
"email": "user@example.com"
}
```
**Response (204 No Content)**
Email gönderilir, link formatı:
```
http://localhost:3000/auth/password/reset/confirm/{uid}/{token}/
```
### 2. Confirm Password Reset
```
POST /api/v1/auth/users/reset_password_confirm/
```
**Request Body:**
```json
{
"uid": "MQ",
"token": "c4h7vu-a8f3d2e1c4b5a6d7e8f9g0h1",
"new_password": "NewStrongP@ssw0rd123",
"re_new_password": "NewStrongP@ssw0rd123"
}
```
**Response (204 No Content)**
---
## Frontend Entegrasyonu
### Nuxt.js 3 Example
#### 1. Composable: `useAuth.ts`
```typescript
// composables/useAuth.ts
export const useAuth = () => {
const config = useRuntimeConfig();
const accessToken = useCookie('access_token');
const refreshToken = useCookie('refresh_token');
const register = async (userData: {
email: string;
password: string;
re_password: string;
first_name: string;
last_name: string;
}) => {
const { data, error } = await useFetch(`${config.public.apiBase}/auth/users/`, {
method: 'POST',
body: userData,
});
return { data, error };
};
const login = async (email: string, password: string) => {
const { data, error } = await useFetch(`${config.public.apiBase}/auth/jwt/create/`, {
method: 'POST',
body: { email, password },
});
if (data.value) {
accessToken.value = data.value.access;
refreshToken.value = data.value.refresh;
}
return { data, error };
};
const socialLogin = async (provider: string, accessTokenValue: string) => {
const { data, error } = await useFetch(
`${config.public.apiBase}/auth/social/${provider}/`,
{
method: 'POST',
body: { access_token: accessTokenValue },
}
);
if (data.value) {
accessToken.value = data.value.access;
refreshToken.value = data.value.refresh;
}
return { data, error };
};
const getUser = async () => {
if (!accessToken.value) return null;
const { data } = await useFetch(`${config.public.apiBase}/auth/users/me/`, {
headers: {
Authorization: `Bearer ${accessToken.value}`,
},
});
return data.value;
};
const logout = () => {
accessToken.value = null;
refreshToken.value = null;
};
return {
register,
login,
socialLogin,
getUser,
logout,
accessToken,
refreshToken,
};
};
```
#### 2. Register Page: `pages/auth/register.vue`
```vue
<template>
<div>
<h1>Register</h1>
<form @submit.prevent="handleRegister">
<input v-model="form.email" type="email" placeholder="Email" required />
<input v-model="form.first_name" placeholder="First Name" />
<input v-model="form.last_name" placeholder="Last Name" />
<input v-model="form.password" type="password" placeholder="Password" required />
<input v-model="form.re_password" type="password" placeholder="Confirm Password" required />
<button type="submit">Register</button>
</form>
<p v-if="message">{{ message }}</p>
</div>
</template>
<script setup lang="ts">
const { register } = useAuth();
const form = ref({
email: '',
password: '',
re_password: '',
first_name: '',
last_name: '',
});
const message = ref('');
const handleRegister = async () => {
const { data, error } = await register(form.value);
if (error.value) {
message.value = 'Registration failed';
} else {
message.value = 'Registration successful! Please check your email to activate your account.';
}
};
</script>
```
#### 3. Activation Page: `pages/auth/activate/[uid]/[token].vue`
```vue
<template>
<div>
<h1>Account Activation</h1>
<p v-if="loading">Activating your account...</p>
<p v-else-if="success"> Account activated successfully! You can now login.</p>
<p v-else-if="error"> Activation failed. Link may be expired.</p>
</div>
</template>
<script setup lang="ts">
const route = useRoute();
const config = useRuntimeConfig();
const loading = ref(true);
const success = ref(false);
const error = ref(false);
onMounted(async () => {
const { uid, token } = route.params;
try {
await $fetch(`${config.public.apiBase}/auth/users/activation/`, {
method: 'POST',
body: { uid, token },
});
success.value = true;
} catch (e) {
error.value = true;
} finally {
loading.value = false;
}
});
</script>
```
#### 4. Login Page: `pages/auth/login.vue`
```vue
<template>
<div>
<h1>Login</h1>
<form @submit.prevent="handleLogin">
<input v-model="email" type="email" placeholder="Email" required />
<input v-model="password" type="password" placeholder="Password" required />
<button type="submit">Login</button>
</form>
<div class="social-login">
<button @click="handleGoogleLogin">Login with Google</button>
<button @click="handleGithubLogin">Login with GitHub</button>
</div>
<p v-if="error">{{ error }}</p>
</div>
</template>
<script setup lang="ts">
const { login, socialLogin } = useAuth();
const router = useRouter();
const email = ref('');
const password = ref('');
const error = ref('');
const handleLogin = async () => {
const { data, error: loginError } = await login(email.value, password.value);
if (loginError.value) {
error.value = 'Login failed. Please check your credentials.';
} else {
router.push('/dashboard');
}
};
const handleGoogleLogin = async () => {
// Google OAuth2 implementation
// Use @nuxtjs/google-oauth2 or similar
const googleToken = await getGoogleAccessToken(); // Your implementation
const { data, error: socialError } = await socialLogin('google-oauth2', googleToken);
if (!socialError.value) {
router.push('/dashboard');
}
};
const handleGithubLogin = async () => {
// GitHub OAuth2 implementation
const githubToken = await getGithubAccessToken(); // Your implementation
const { data, error: socialError } = await socialLogin('github', githubToken);
if (!socialError.value) {
router.push('/dashboard');
}
};
</script>
```
### Next.js 14 Example
#### 1. Auth Context: `context/AuthContext.tsx`
```typescript
'use client';
import { createContext, useContext, useState, useEffect } from 'react';
interface User {
id: number;
email: string;
first_name: string;
last_name: string;
}
interface AuthContextType {
user: User | null;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
register: (userData: any) => Promise<void>;
}
const AuthContext = createContext<AuthContextType | undefined>(undefined);
export function AuthProvider({ children }: { children: React.ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [accessToken, setAccessToken] = useState<string | null>(null);
useEffect(() => {
// Load token from localStorage
const token = localStorage.getItem('access_token');
if (token) {
setAccessToken(token);
fetchUser(token);
}
}, []);
const fetchUser = async (token: string) => {
try {
const response = await fetch('http://localhost:8000/api/v1/auth/users/me/', {
headers: {
'Authorization': `Bearer ${token}`,
},
});
const data = await response.json();
setUser(data);
} catch (error) {
console.error('Failed to fetch user', error);
}
};
const login = async (email: string, password: string) => {
const response = await fetch('http://localhost:8000/api/v1/auth/jwt/create/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email, password }),
});
if (!response.ok) {
throw new Error('Login failed');
}
const data = await response.json();
localStorage.setItem('access_token', data.access);
localStorage.setItem('refresh_token', data.refresh);
setAccessToken(data.access);
await fetchUser(data.access);
};
const logout = () => {
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
setAccessToken(null);
setUser(null);
};
const register = async (userData: any) => {
const response = await fetch('http://localhost:8000/api/v1/auth/users/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(userData),
});
if (!response.ok) {
throw new Error('Registration failed');
}
};
return (
<AuthContext.Provider value={{ user, login, logout, register }}>
{children}
</AuthContext.Provider>
);
}
export const useAuth = () => {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
};
```
---
## Error Handling
### Common Error Codes
| Status Code | Meaning | Common Causes |
|-------------|---------|---------------|
| 400 | Bad Request | Invalid data, validation errors |
| 401 | Unauthorized | Invalid credentials, expired token |
| 403 | Forbidden | Account not activated, permission denied |
| 404 | Not Found | Endpoint doesn't exist |
| 429 | Too Many Requests | Rate limit exceeded |
| 500 | Internal Server Error | Server-side error |
### Error Response Format
```json
{
"detail": "Error message here",
"field_name": ["Field-specific error"]
}
```
### Example: Registration Validation Error
```json
{
"email": ["A user with that email already exists."],
"password": ["This password is too common."]
}
```
---
## Testing with Postman/Insomnia
### 1. Register
```
POST http://localhost:8000/api/v1/auth/users/
Content-Type: application/json
{
"email": "test@example.com",
"password": "TestP@ssw0rd123",
"re_password": "TestP@ssw0rd123",
"first_name": "Test",
"last_name": "User"
}
```
### 2. Check Email (MailPit)
Open: `http://localhost:8025`
### 3. Activate Account
```
POST http://localhost:8000/api/v1/auth/users/activation/
Content-Type: application/json
{
"uid": "MQ",
"token": "c4h7vu-a8f3d2e1c4b5a6d7e8f9g0h1"
}
```
### 4. Login
```
POST http://localhost:8000/api/v1/auth/jwt/create/
Content-Type: application/json
{
"email": "test@example.com",
"password": "TestP@ssw0rd123"
}
```
### 5. Get User Profile
```
GET http://localhost:8000/api/v1/auth/users/me/
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
```
---
## Environment Variables
### Development (.env.dev)
```bash
DEBUG=True
SECRET_KEY=your-secret-key-here
ALLOWED_HOSTS=localhost,127.0.0.1
# Database
DATABASE_URL=sqlite:///db.sqlite3
# Email (MailPit)
EMAIL_HOST=localhost
EMAIL_PORT=1025
EMAIL_USE_TLS=False
# CORS
CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173
```
### Production (.env.prod)
```bash
DEBUG=False
SECRET_KEY=your-production-secret-key
ALLOWED_HOSTS=yourdomain.com,api.yourdomain.com
# Database
DATABASE_URL=postgresql://user:pass@host:5432/dbname
# Email
EMAIL_HOST=smtp.gmail.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_HOST_USER=your-email@gmail.com
EMAIL_HOST_PASSWORD=your-app-password
# Social Auth
SOCIAL_AUTH_GOOGLE_OAUTH2_KEY=your-google-client-id
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET=your-google-client-secret
SOCIAL_AUTH_GITHUB_KEY=your-github-client-id
SOCIAL_AUTH_GITHUB_SECRET=your-github-client-secret
# CORS
CORS_ALLOWED_ORIGINS=https://yourdomain.com
```
---
## Support
Sorularınız için:
- GitHub Issues: [Your Repo]
- Email: support@yourdomain.com
- Documentation: [Your Docs URL]
---
**Last Updated:** 2025-12-12
**Version:** 1.0.0