Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Ishaq74/concordia/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Concordia uses Better Auth for authentication. All endpoints are automatically generated and handled through /api/auth/[...all].
Base Endpoint
All authentication flows use the Better Auth SDK, which provides type-safe client methods.
Authentication Configuration
From /src/lib/auth/auth.ts:53-133:
const sharedConfig = {
emailAndPassword: {
enabled: true,
requireEmailVerification: true,
},
session: {
expiresIn: 60 * 60 * 24 * 7, // 7 days
updateAge: 60 * 60 * 24, // 1 day
freshAge: 60 * 10, // 10 minutes
absoluteTimeout: 60 * 60 * 24 * 7, // 7 days
},
rateLimit: {
enabled: true,
storage: "database",
windows: [
{ key: "global_ip", max: 100, window: 60 * 1000 },
{ key: "sign_in", max: 5, window: 15 * 60 * 1000 },
{ key: "sign_up", max: 10, window: 60 * 60 * 1000 },
],
},
};
Registration
Sign Up
import { authClient } from '@/lib/auth-client';
const { data, error } = await authClient.signUp.email({
email: 'user@example.com',
password: 'SecurePass123!',
name: 'John Doe',
username: 'johndoe', // Optional
});
Validation Rules
From /src/lib/auth/auth.ts:57-64:
- Email: Required, valid email format
- Password:
- Minimum 10 characters
- At least 1 uppercase letter
- At least 1 lowercase letter
- At least 1 digit
- At least 1 special character
- Username: Optional, alphanumeric with hyphens/underscores
- Name: Optional
Post-Signup Actions
From /src/lib/auth/auth.ts:196-266, after successful signup:
- Profile created with derived username
- Wallet initialized (balance: 0.00 EUR)
- Default role assigned (
citizen)
- Audit log entry created
- Verification email sent (if SMTP configured)
Response
{
"user": {
"id": "usr_abc123",
"email": "user@example.com",
"name": "John Doe",
"emailVerified": false,
"createdAt": "2024-03-03T10:00:00Z"
},
"session": null // Email verification required
}
Email Verification
Send Verification Email
await authClient.sendVerificationEmail({
email: 'user@example.com',
});
Verify Email
Users receive a verification link via email:
https://your-domain.com/verify-email?token=abc123...
The frontend calls:
await authClient.verifyEmail({
token: 'abc123...',
});
Login
Sign In with Email/Password
const { data, error } = await authClient.signIn.email({
email: 'user@example.com',
password: 'SecurePass123!',
});
Rate Limiting
From /src/lib/auth/auth.ts:372-389:
- Max attempts: 5 failed logins per email
- Window: 15 minutes
- Error:
"Too many login attempts" (HTTP 429)
Response
{
"user": {
"id": "usr_abc123",
"email": "user@example.com",
"name": "John Doe",
"emailVerified": true
},
"session": {
"id": "ses_xyz789",
"userId": "usr_abc123",
"expiresAt": "2024-03-10T10:00:00Z"
}
}
Audit Logging
From /src/lib/auth/auth.ts:290-306, successful logins are logged:
await db.insert(auditLog).values({
action: "login_success",
userId: session.userId,
ip: extractedIP,
userAgent: userAgent,
data: { sessionId: session.id },
});
Failed logins also logged (login_failed).
Session Management
Get Current Session
const { data: session } = await authClient.getSession();
Response
{
"session": {
"id": "ses_xyz789",
"userId": "usr_abc123",
"expiresAt": "2024-03-10T10:00:00Z",
"activeOrganizationId": "org_123"
},
"user": {
"id": "usr_abc123",
"email": "user@example.com",
"name": "John Doe",
"emailVerified": true
}
}
Session Configuration
From /src/lib/auth/auth.ts:93-103:
Session lifetime (7 days in seconds)
Session refresh interval (1 day)
Cookie cache duration (5 minutes)
Fresh session window (10 minutes)
Maximum session lifetime (7 days)
Logout
Sign Out
await authClient.signOut();
From /src/lib/auth/auth.ts:338-346, Bearer tokens are invalidated on logout:
const origSignOut = (instance.api as any).signOut;
(instance.api as any).signOut = async (opts: any) => {
const hdr = opts?.headers?.authorization;
if (hdr && hdr.startsWith('Bearer ')) {
invalidatedTokens.add(hdr.slice(7)); // Blacklist token
}
return await origSignOut(opts);
};
Password Reset
Request Password Reset
await authClient.forgetPassword({
email: 'user@example.com',
redirectTo: 'https://your-domain.com/reset-password',
});
Email Sent
From /src/lib/auth/auth.ts:65-76, users receive an email with reset link:
<p>Click the following link to reset your password:</p>
<p><a href="{resetUrl}">{resetUrl}</a></p>
Reset Password
await authClient.resetPassword({
token: 'reset_token_123',
password: 'NewSecurePass123!',
});
Organization Authentication
Set Active Organization
await authClient.organization.setActive({
organizationId: 'org_123',
});
From /src/lib/auth/auth.ts:319, this is mapped to:
setActive: (payload: any) => (instance.api as any).setActiveOrganization(payload)
List User Organizations
const { data } = await authClient.organization.list();
Response
[
{
"id": "org_123",
"name": "My Organization",
"slug": "my-org",
"role": "owner"
}
]
Security Features
IP Tracking
From /src/lib/auth/auth.ts:16-23, IPs are extracted from:
function extractIP(ctx: any): string | null {
return (
ctx?.request?.headers?.get("x-real-ip") ||
ctx?.request?.headers?.get("x-forwarded-for") ||
ctx?.request?.headers?.get("cf-connecting-ip") ||
null
);
}
Secure Cookies
From /src/lib/auth/auth.ts:179-183:
advanced: {
useSecureCookies: true,
cookiePrefix: "astro_",
crossSubDomainCookies: { enabled: false },
}
CSRF Protection
- CSRF checks enabled by default
- Trusted origins configured via
BETTER_AUTH_URL
Error Responses
Common Errors
| Status | Error | Description |
|---|
| 400 | invalid_input | Invalid email/password format |
| 400 | weak_password | Password doesn’t meet requirements |
| 401 | invalid_credentials | Wrong email/password |
| 401 | email_not_verified | Email verification required |
| 403 | forbidden | Access denied |
| 429 | too_many_requests | Rate limit exceeded |
| 500 | internal_error | Server error |
Example
{
"message": "Invalid credentials",
"status": "UNAUTHORIZED"
}
API Overview
Rate limiting and authentication basics
Better Auth Docs
Official Better Auth documentation