Authentication App — Aquilia Documentation
Comprehensive guide and documentation for Authentication App in the Aquilia framework. View API reference, examples, and implementation patterns.
import from 'lucide-react' Tutorials / End-to-End Auth App Authentication Application Learn how to build a complete, production-grade Authentication and Session flow in Aquilia. We will implement user registration, Argon2id password hashing, session lifecycle management, login/logout endpoints, and a guarded profile route. ) })} shift composition operator) to clean inputs: Option A: Explicit Facet Descriptors Uses explicit Facet class descriptors: Option B: Type-Annotated Fields with Wards (Modern Hashing Way) Modern style using type annotations, validation pipelines, and an async @ward method to automatically hash passwords inside the contract itself: > strip >> lower] SlugType = Annotated[str, Facet.text() >> strip >> lower >> slugify] class RegisterContract(Contract): username: SlugType = Field(min_length=3) email: EmailType = Field() password: str = Field(min_length=8) class Spec: model = User fields = ["username", "email"] # Writable fields for imprinting extra_fields = "reject" @ward(mode="async") async def validate_and_hash_password(self, data: dict): password = data.get("password") if not password: self.reject("password", "Password is required") return data # Enforce password strength policy policy = PasswordPolicy(min_length=8) is_valid, errors = await policy.validate_async(password=password) if not is_valid: self.reject("password", errors) # Hash and inject the password_hash directly into validation data dictionary hasher = PasswordHasher() password_hash = await hasher.hash_async(password=password) self.data["password_hash"] = password_hash return data `} language="python" /> Inbound & Outbound Contracts: > strip >> lower] SlugType = Annotated[str, Facet.text() >> strip >> lower >> slugify] class LoginContract(Contract): """ Schema for validating sign-in requests. """ username: str = Field(min_length=3) password: str = Field(min_length=1) class Spec: extra_fields = "reject" class UserResponseContract(Contract): """ Schema for serializing outbound user statistics (masking secrets). """ id: int = Field(read_only=True) username: str = Field(read_only=True) email: str = Field(read_only=True) created_at: datetime = Field(read_only=True) class Spec: model = User fields = ["id", "username", "email", "created_at"] class LoginResponseContract(Contract): """ Outbound response contract after a successful sign-in. """ access_token: str = Field() refresh_token: str = Field() message: str = Field() user: UserResponseContract = Field() # Nested schema `} language="python" filename="contracts.py" highlightLines= /> Understanding Spec Configurations & Methods: • model: Links the contract schema to a database model class. • fields: Filters which model fields should be parsed or serialized. Set to "__all__" to include all fields. • exclude: Excludes specified fields (e.g. secret hash keys) from serialization outputs. • read_only_fields / write_only_fields: Sets unidirectional permissions on properties. • is_sealed(): Evaluates if a contract validation is run successfully. • imprint(): Resolves and saves validated data directly to the database model. )} already exists.") # Hash password and save manually hashed_pass = self.hasher.hash(user_data.password) user = User( username=user_data.username, email=user_data.email, password_hash=hashed_pass, ) await user.save() return user `} language="python" /> Way 2: Automatic DB Imprinting with Contract @ward (Modern Way) Since password validation and hashing occurred automatically in the contract's async @ward , the service doesn't need to manually hash anything. It simply calls imprint() directly: User: """Modern way -- validators and hashes are encapsulated inside the contract.""" existing_user = await User.query().filter(email=user_data.email).first() if existing_user: raise AuthValidationFault(f"A user with this email already exists.") # The contract automatically validated and hashed the password into 'password_hash'. # imprint() creates and saves the User model instance in one step. user = await user_data.imprint() return user `} language="python" /> Complete UserService Class: User: """Register using Way 2: Modern imprint method.""" existing_user = await User.query().filter(email=user_data.email).first() if existing_user: raise AuthValidationFault(f"A user with this email already exists.") # Imprint does the creation and writes username, email, and password_hash user = await user_data.imprint() return user async def verify_credentials(self, username: str, raw_pass: str) -> User: """Verify username exists and matches hashed password.""" try: user = await User.objects.get(username=username) except Exception: raise AuthValidationFault("Invalid credentials") if not user.is_active: raise AuthValidationFault("User account is inactive") # Validate password is_valid = self.hasher.verify(raw_pass, user.password_hash) if not is_valid: raise AuthValidationFault("Invalid credentials") return user `} language="python" filename="services.py" highlightLines= /> )} ") # .json() is an async coroutine returning the parsed dict/list print(f"-> Registration Output: ") # 2. Log in (CookieJar automatically captures and saves session cookies) login_res = await client.post( "/auth/login", json= ) print(f"-> Login Response Status: ") login_data = await login_res.json() print(f"-> Access Token: ") # 3. Request protected route (client automatically attaches the stored session cookie) me_res = await client.get("/auth/me") print(f"-> Protected Route Profile Status: ") print(f"-> User Profile: ") # 4. Log out logout_res = await client.post("/auth/logout") print(f"-> Logout Response Status: ") print(f"-> Logout Message: ") except HTTPClientFault as exc: print(f"[ERROR] HTTP outbound call failed: (Code: )") if __name__ == "__main__": asyncio.run(run_smoke_test()) `} language="python" filename="smoke_test.py" highlightLines= /> Key Features of aquilia.http AsyncHTTPClient: • Automatic Cookie Management: Built-in CookieJar automatically parses and sends back session cookies in consecutive requests, mimicking browser behaviors perfectly. • Connection Pooling: Managed connections allow highly performant reuse of TCP sockets under host limit locks. • Structured Fault Domains: All outbound HTTP request exceptions map to typed `HTTPClientFault` subclasses, allowing clean error tracking and backoff handling. )} Previous Step Next Step )
Go to Homepage