auth-kit-fastapi 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,27 @@
1
+ """
2
+ Auth Kit FastAPI - Complete authentication solution for FastAPI applications
3
+ """
4
+
5
+ from .core.config import AuthConfig
6
+ from .core.app import create_auth_app
7
+ from .core.dependencies import (
8
+ get_current_user,
9
+ get_current_active_user,
10
+ require_verified_user,
11
+ require_superuser
12
+ )
13
+ from .models.user import BaseUser
14
+ from .core.events import auth_events
15
+
16
+ __version__ = "1.0.0"
17
+
18
+ __all__ = [
19
+ "AuthConfig",
20
+ "create_auth_app",
21
+ "get_current_user",
22
+ "get_current_active_user",
23
+ "require_verified_user",
24
+ "require_superuser",
25
+ "BaseUser",
26
+ "auth_events"
27
+ ]
@@ -0,0 +1,84 @@
1
+ """
2
+ Export all schemas
3
+ """
4
+
5
+ from .auth import (
6
+ UserBase,
7
+ UserCreate,
8
+ UserUpdate,
9
+ UserResponse,
10
+ LoginRequest,
11
+ TokenResponse,
12
+ LoginResponse,
13
+ RefreshTokenRequest,
14
+ LogoutRequest,
15
+ PasswordChangeRequest,
16
+ PasswordResetRequest,
17
+ PasswordResetConfirmRequest,
18
+ EmailVerificationRequest,
19
+ MessageResponse,
20
+ ErrorResponse
21
+ )
22
+
23
+ from .passkey import (
24
+ PasskeyBase,
25
+ PasskeyCreate,
26
+ PasskeyResponse,
27
+ PasskeyListResponse,
28
+ RegistrationOptionsResponse,
29
+ RegistrationCompleteRequest,
30
+ AuthenticationOptionsResponse,
31
+ AuthenticationCompleteRequest,
32
+ AuthenticationBeginRequest
33
+ )
34
+
35
+ from .two_factor import (
36
+ TwoFactorSetupResponse,
37
+ TwoFactorVerifyRequest,
38
+ TwoFactorEnableResponse,
39
+ TwoFactorDisableRequest,
40
+ RecoveryCodesRegenerateRequest,
41
+ RecoveryCodesResponse,
42
+ TwoFactorLoginVerifyRequest,
43
+ TwoFactorStatusResponse
44
+ )
45
+
46
+ __all__ = [
47
+ # Auth schemas
48
+ "UserBase",
49
+ "UserCreate",
50
+ "UserUpdate",
51
+ "UserResponse",
52
+ "LoginRequest",
53
+ "TokenResponse",
54
+ "LoginResponse",
55
+ "RefreshTokenRequest",
56
+ "LogoutRequest",
57
+ "PasswordChangeRequest",
58
+ "PasswordResetRequest",
59
+ "PasswordResetConfirmRequest",
60
+ "EmailVerificationRequest",
61
+ "MessageResponse",
62
+ "ErrorResponse",
63
+
64
+ # Passkey schemas
65
+ "PasskeyBase",
66
+ "PasskeyCreate",
67
+ "PasskeyResponse",
68
+ "PasskeyListResponse",
69
+ "RegistrationOptionsResponse",
70
+ "RegistrationCompleteRequest",
71
+ "AuthenticationOptionsResponse",
72
+ "AuthenticationCompleteRequest",
73
+ "AuthenticationBeginRequest",
74
+
75
+ # 2FA schemas
76
+ "TwoFactorSetupResponse",
77
+ "TwoFactorVerifyRequest",
78
+ "TwoFactorEnableResponse",
79
+ "TwoFactorDisableRequest",
80
+ "RecoveryCodesRegenerateRequest",
81
+ "RecoveryCodesResponse",
82
+ "TwoFactorLoginVerifyRequest",
83
+ "TwoFactorStatusResponse"
84
+ ]
@@ -0,0 +1,145 @@
1
+ """
2
+ Authentication schemas for request/response validation
3
+ """
4
+
5
+ from datetime import datetime
6
+ from typing import Optional, Dict, Any
7
+ from uuid import UUID
8
+ from pydantic import BaseModel, EmailStr, Field, validator
9
+
10
+
11
+ # Base schemas
12
+ class UserBase(BaseModel):
13
+ """Base user schema"""
14
+ email: EmailStr
15
+ first_name: Optional[str] = None
16
+ last_name: Optional[str] = None
17
+ phone_number: Optional[str] = None
18
+
19
+
20
+ class UserCreate(UserBase):
21
+ """User registration schema"""
22
+ password: str = Field(..., min_length=8, max_length=128)
23
+ confirm_password: Optional[str] = None
24
+ accept_terms: Optional[bool] = False
25
+
26
+ @validator('confirm_password')
27
+ def passwords_match(cls, v, values):
28
+ if 'password' in values and v != values['password']:
29
+ raise ValueError('Passwords do not match')
30
+ return v
31
+
32
+
33
+ class UserUpdate(BaseModel):
34
+ """User profile update schema"""
35
+ first_name: Optional[str] = None
36
+ last_name: Optional[str] = None
37
+ phone_number: Optional[str] = None
38
+ avatar_url: Optional[str] = None
39
+
40
+
41
+ class UserResponse(UserBase):
42
+ """User response schema"""
43
+ id: UUID
44
+ is_active: bool
45
+ is_verified: bool
46
+ is_superuser: bool
47
+ two_factor_enabled: bool
48
+ created_at: datetime
49
+ updated_at: datetime
50
+ last_login_at: Optional[datetime] = None
51
+
52
+ class Config:
53
+ from_attributes = True
54
+
55
+
56
+ # Authentication schemas
57
+ class LoginRequest(BaseModel):
58
+ """Login request schema (OAuth2 compatible)"""
59
+ username: EmailStr # OAuth2 spec requires 'username'
60
+ password: str
61
+ grant_type: Optional[str] = "password"
62
+ scope: Optional[str] = ""
63
+ client_id: Optional[str] = None
64
+ client_secret: Optional[str] = None
65
+
66
+
67
+ class TokenResponse(BaseModel):
68
+ """Token response schema (OAuth2 compatible)"""
69
+ access_token: str
70
+ refresh_token: Optional[str] = None
71
+ token_type: str = "bearer"
72
+ expires_in: Optional[int] = None
73
+ scope: Optional[str] = ""
74
+
75
+
76
+ class LoginResponse(BaseModel):
77
+ """Enhanced login response with user data"""
78
+ user: UserResponse
79
+ tokens: TokenResponse
80
+ requires_2fa: bool = False
81
+ message: Optional[str] = None
82
+
83
+
84
+ class RefreshTokenRequest(BaseModel):
85
+ """Refresh token request"""
86
+ refresh_token: str
87
+
88
+
89
+ class LogoutRequest(BaseModel):
90
+ """Logout request"""
91
+ refresh_token: str
92
+ everywhere: bool = False # Logout from all devices
93
+
94
+
95
+ # Password management schemas
96
+ class PasswordChangeRequest(BaseModel):
97
+ """Password change request"""
98
+ current_password: str
99
+ new_password: str = Field(..., min_length=8, max_length=128)
100
+ confirm_password: Optional[str] = None
101
+
102
+ @validator('confirm_password')
103
+ def passwords_match(cls, v, values):
104
+ if 'new_password' in values and v != values['new_password']:
105
+ raise ValueError('Passwords do not match')
106
+ return v
107
+
108
+
109
+ class PasswordResetRequest(BaseModel):
110
+ """Password reset request"""
111
+ email: EmailStr
112
+
113
+
114
+ class PasswordResetConfirmRequest(BaseModel):
115
+ """Password reset confirmation"""
116
+ token: str
117
+ new_password: str = Field(..., min_length=8, max_length=128)
118
+ confirm_password: Optional[str] = None
119
+
120
+ @validator('confirm_password')
121
+ def passwords_match(cls, v, values):
122
+ if 'new_password' in values and v != values['new_password']:
123
+ raise ValueError('Passwords do not match')
124
+ return v
125
+
126
+
127
+ # Email verification schemas
128
+ class EmailVerificationRequest(BaseModel):
129
+ """Request email verification resend"""
130
+ email: Optional[EmailStr] = None # Use current user's email if not provided
131
+
132
+
133
+ # Response messages
134
+ class MessageResponse(BaseModel):
135
+ """Generic message response"""
136
+ message: str
137
+ success: bool = True
138
+ data: Optional[Dict[str, Any]] = None
139
+
140
+
141
+ class ErrorResponse(BaseModel):
142
+ """Error response"""
143
+ detail: str
144
+ code: Optional[str] = None
145
+ field: Optional[str] = None
@@ -0,0 +1,129 @@
1
+ """
2
+ Passkey/WebAuthn schemas
3
+ """
4
+
5
+ from datetime import datetime
6
+ from typing import Optional, List, Dict, Any
7
+ from uuid import UUID
8
+ from pydantic import BaseModel, Field
9
+
10
+
11
+ class PasskeyBase(BaseModel):
12
+ """Base passkey schema"""
13
+ name: str = Field(..., min_length=3, max_length=255)
14
+
15
+
16
+ class PasskeyCreate(PasskeyBase):
17
+ """Passkey registration request"""
18
+ pass
19
+
20
+
21
+ class PasskeyResponse(PasskeyBase):
22
+ """Passkey response"""
23
+ id: UUID
24
+ credential_id: str
25
+ authenticator_type: str
26
+ is_discoverable: bool
27
+ created_at: datetime
28
+ last_used_at: Optional[datetime] = None
29
+
30
+ class Config:
31
+ from_attributes = True
32
+
33
+
34
+ class PasskeyListResponse(BaseModel):
35
+ """List of user's passkeys"""
36
+ passkeys: List[PasskeyResponse]
37
+ total: int
38
+
39
+
40
+ # WebAuthn schemas
41
+ class PublicKeyCredentialRpEntity(BaseModel):
42
+ """Relying Party entity"""
43
+ id: str
44
+ name: str
45
+
46
+
47
+ class PublicKeyCredentialUserEntity(BaseModel):
48
+ """User entity"""
49
+ id: str
50
+ name: str
51
+ displayName: str
52
+
53
+
54
+ class PublicKeyCredentialParameters(BaseModel):
55
+ """Credential parameters"""
56
+ type: str = "public-key"
57
+ alg: int
58
+
59
+
60
+ class PublicKeyCredentialDescriptor(BaseModel):
61
+ """Credential descriptor"""
62
+ type: str = "public-key"
63
+ id: str
64
+ transports: Optional[List[str]] = None
65
+
66
+
67
+ class AuthenticatorSelectionCriteria(BaseModel):
68
+ """Authenticator selection criteria"""
69
+ authenticatorAttachment: Optional[str] = None
70
+ residentKey: Optional[str] = None
71
+ userVerification: Optional[str] = "preferred"
72
+
73
+
74
+ class RegistrationOptionsResponse(BaseModel):
75
+ """WebAuthn registration options"""
76
+ challenge: str
77
+ rp: PublicKeyCredentialRpEntity
78
+ user: PublicKeyCredentialUserEntity
79
+ pubKeyCredParams: List[PublicKeyCredentialParameters]
80
+ timeout: Optional[int] = 60000
81
+ excludeCredentials: Optional[List[PublicKeyCredentialDescriptor]] = []
82
+ authenticatorSelection: Optional[AuthenticatorSelectionCriteria] = None
83
+ attestation: Optional[str] = "none"
84
+
85
+
86
+ class RegistrationCredentialResponse(BaseModel):
87
+ """Registration credential from client"""
88
+ id: str
89
+ rawId: str
90
+ type: str = "public-key"
91
+ response: Dict[str, Any]
92
+ clientExtensionResults: Optional[Dict[str, Any]] = None
93
+ authenticatorAttachment: Optional[str] = None
94
+
95
+
96
+ class RegistrationCompleteRequest(BaseModel):
97
+ """Complete registration request"""
98
+ name: str
99
+ response: RegistrationCredentialResponse
100
+ challenge: str
101
+
102
+
103
+ class AuthenticationOptionsResponse(BaseModel):
104
+ """WebAuthn authentication options"""
105
+ challenge: str
106
+ timeout: Optional[int] = 60000
107
+ rpId: Optional[str] = None
108
+ allowCredentials: Optional[List[PublicKeyCredentialDescriptor]] = []
109
+ userVerification: Optional[str] = "preferred"
110
+
111
+
112
+ class AuthenticationCredentialResponse(BaseModel):
113
+ """Authentication credential from client"""
114
+ id: str
115
+ rawId: str
116
+ type: str = "public-key"
117
+ response: Dict[str, Any]
118
+ clientExtensionResults: Optional[Dict[str, Any]] = None
119
+
120
+
121
+ class AuthenticationCompleteRequest(BaseModel):
122
+ """Complete authentication request"""
123
+ response: AuthenticationCredentialResponse
124
+ challenge: str
125
+
126
+
127
+ class AuthenticationBeginRequest(BaseModel):
128
+ """Begin authentication request"""
129
+ email: Optional[str] = None # For discoverable credentials
@@ -0,0 +1,73 @@
1
+ """
2
+ Two-Factor Authentication schemas
3
+ """
4
+
5
+ from typing import List, Optional
6
+ from pydantic import BaseModel, Field, validator
7
+
8
+
9
+ class TwoFactorSetupResponse(BaseModel):
10
+ """2FA setup response"""
11
+ secret: str
12
+ qr_code: str # Base64 encoded QR code image
13
+ manual_entry_key: str
14
+ message: str = "Scan the QR code with your authenticator app"
15
+
16
+
17
+ class TwoFactorVerifyRequest(BaseModel):
18
+ """Verify 2FA code"""
19
+ code: str = Field(..., regex="^[0-9]{6}$")
20
+
21
+ @validator('code')
22
+ def validate_code(cls, v):
23
+ if not v.isdigit() or len(v) != 6:
24
+ raise ValueError('Code must be 6 digits')
25
+ return v
26
+
27
+
28
+ class TwoFactorEnableResponse(BaseModel):
29
+ """2FA enable response"""
30
+ recovery_codes: List[str]
31
+ message: str = "Two-factor authentication has been enabled"
32
+
33
+
34
+ class TwoFactorDisableRequest(BaseModel):
35
+ """Disable 2FA request"""
36
+ password: str
37
+
38
+
39
+ class RecoveryCodesRegenerateRequest(BaseModel):
40
+ """Regenerate recovery codes request"""
41
+ password: str
42
+
43
+
44
+ class RecoveryCodesResponse(BaseModel):
45
+ """Recovery codes response"""
46
+ recovery_codes: List[str]
47
+ message: str = "New recovery codes have been generated"
48
+
49
+
50
+ class TwoFactorLoginVerifyRequest(BaseModel):
51
+ """Verify 2FA during login"""
52
+ code: str
53
+ is_recovery_code: bool = False
54
+
55
+ @validator('code')
56
+ def validate_code(cls, v, values):
57
+ if values.get('is_recovery_code'):
58
+ # Recovery code format: XXXX-XXXX-XXXX-XXXX
59
+ if not v.replace('-', '').isalnum():
60
+ raise ValueError('Invalid recovery code format')
61
+ else:
62
+ # TOTP code: 6 digits
63
+ if not v.isdigit() or len(v) != 6:
64
+ raise ValueError('Code must be 6 digits')
65
+ return v
66
+
67
+
68
+ class TwoFactorStatusResponse(BaseModel):
69
+ """2FA status response"""
70
+ enabled: bool
71
+ method: Optional[str] = None
72
+ backup_codes_remaining: Optional[int] = None
73
+ created_at: Optional[str] = None
@@ -0,0 +1,17 @@
1
+ """
2
+ Service classes for auth-kit-fastapi
3
+
4
+ Provides business logic layer for authentication operations
5
+ """
6
+
7
+ from .user_service import UserService
8
+ from .email_service import EmailService
9
+ from .passkey_service import PasskeyService
10
+ from .two_factor_service import TwoFactorService
11
+
12
+ __all__ = [
13
+ "UserService",
14
+ "EmailService",
15
+ "PasskeyService",
16
+ "TwoFactorService"
17
+ ]