RentApart and the Death of the Signup Form

A Django rental backend that uses Google OAuth, lazy onboarding, and cookie-based JWTs to turn identity into a staged workflow instead of a one-time gate.

8 min read View on GitHub More from Dallss

A rental office turned into a branching workflow. One path leads to Google sign-in, another to a small desk for missing profile details, and a third to a guarded permissions gate. The image explains that onboarding in RentApart is staged, not a single form submission.
RentApart treats the front door like a workflow engine, not a static signup page.
Key Takeaways

Most rental backends start with listings and end with auth as an afterthought. RentApart flips that order. Its most interesting idea is that signup is not a form, it is a sequence of backend-verified states.

The Front Door Is Not a Form

RentApart uses Google OAuth as the first identity layer, then checks whether the profile is actually usable. The `Profile` model tracks missing fields like display name, birthday, and phone, and the app only sends the user into onboarding when those gaps exist.

That is a quiet but important design choice. The backend is not asking the frontend to guess what is missing. It is returning a concrete state the UI can react to.

RentApart treats identity as progressive. The backend resolves who you are, what you can do, and whether you still need to finish onboarding before the UI opens up.

Why Cookie JWTs Change the Threat Model

Most DRF projects put JWTs in headers. RentApart stores them in HttpOnly cookies instead. That narrows the XSS theft problem, but it makes CSRF unavoidable, so the authentication layer explicitly enforces CSRF checks.

A secure envelope marked JWT being passed into a locked mailbox labeled HttpOnly cookies. On the far side, a small inspector stamps the request before it reaches an API gate. The image explains why safer token storage also requires explicit CSRF protection.
Cookie storage reduces token exposure, but it moves the burden to request verification.
Header JWTsCookie JWTs
Tokens travel in request headers and are easy to attach from the client.Tokens sit in HttpOnly cookies and are harder for script to steal.
XSS token theft is the main browser risk.CSRF becomes the trade-off that must be handled explicitly.
Client code decides when to send the token.The browser sends it automatically with the request.
Simple mental model, weaker browser isolation.Stronger token storage, stricter request validation.

The Backend Decides What the UI Can Do

RentApart exposes capability data from the backend, including leasing-related permissions. That means the frontend is not inferring what a user can do from scattered hints. It receives a backend-authored answer and renders accordingly.

This is more than access control. It is interface control. The same permission system that blocks writes also shapes which screens and actions should even appear.

Listings Are the Real Product Surface

The listing engine is where the product becomes tangible. Search spans title, description, city, and neighborhood. Landlord assignment is constrained with `limit_choices_to`, so only users with the right permission can be attached as managers.

That constraint matters because it prevents the data model from drifting away from the product rules. A bad UI cannot quietly assign the wrong landlord if the model itself refuses the state.

class Listing(models.Model):
    landlord = models.ForeignKey(
        Profile,
        on_delete=models.CASCADE,
        limit_choices_to=_lease_manager_profiles_q,
    )

class ListingFilter(filters.FilterSet):
    def filter_q(self, queryset, name, value):
        return queryset.filter(
            Q(title__icontains=value)
            | Q(description__icontains=value)
            | Q(city__icontains=value)
            | Q(neighborhood__icontains=value)
        )

Defensive Django in the Database Layer

RentApart uses constraints where a less careful project would rely on validation alone. Booking end times must come after start times. Duplicate rental applications are blocked at the database level. These are the kinds of rules that keep working when the client misbehaves.

RuleWhere it livesWhy it matters
Booking end after startDatabase CheckConstraintInvalid time ranges cannot slip through a buggy client.
One application per listing per renterunique_togetherDuplicate submissions are rejected even if the UI retries.
Only lease managers can be landlordsModel-level choice restrictionPermission errors are prevented before they become bad data.

Seeding a City, Not Just a Database

The seed command is one of the strongest signals in the repository. It does not generate generic filler. It uses neighborhood-specific data and realistic property imagery tied to Cebu City, which makes the system feel like it was built for a market, not for a demo.

That choice changes how the whole project reads. The backend is not just structurally sound. It is anchored to a place with enough realism to suggest a real product path.

What Is Finished, and What Is Still a Skeleton

The codebase is coherent, but not finished. The listings work feels the most mature. The applications app looks thinner. The documentation is minimal. Even so, the direction is clear: a secure, opinionated rental backend where identity and permissions are core product surfaces.

That is what makes RentApart worth noticing. It is not trying to be a generic clone. It is using Django to turn onboarding, security, and model constraints into part of the product itself.