RealWorld API Authorization Model: How Article Access and Modification Are Governed

The RealWorld API implements a role-based, token-driven authorization system where JSON Web Tokens (JWT) authenticate users, and strict author ownership checks in utils/auth.ts enforce that only article authors can update or delete content.

The gothinkster/realworld repository demonstrates production-grade API patterns through a Medium-clone application. Its RealWorld API authorization model governs every interaction with article resources, balancing open read access with secure write protections that verify both authentication and resource ownership.

How the RealWorld API Authorization Model Works

JWT Token Generation and Validation

According to the source code, the authorization flow begins at authentication. When users log in via /api/users/login or sign up via /api/users, the system generates a JWT using the helper defined in utils/generate-token.ts. This token must be included in subsequent requests using the Authorization: Token <jwt> header format.

The auth middleware exported from utils/auth.ts processes every protected route. It performs three critical operations:

  1. Extracts the token from the Authorization header
  2. Verifies the token signature and expiration
  3. Attaches the decoded userId to req.user for downstream handlers

If the token is missing or invalid, the middleware immediately returns a 401 Unauthorized response, preventing access to restricted endpoints.

Article Operation Permissions

Public Read Access (Unauthenticated)

Reading articles requires no authentication. Endpoints like GET /api/articles and GET /api/articles/:slug query the database directly through handlers that bypass the auth middleware entirely. This design allows anonymous users to browse content while protecting modification capabilities.


# Fetch public articles without authentication

curl https://api.realworld.io/api/articles?limit=10&offset=0

Creating Articles (Authenticated Users Only)

To create an article, users must provide a valid JWT. The creation handler in routes/api/articles/index.post.ts passes requests through the auth.ts middleware, which validates the token and injects the userId into req.user. The system then stores the article with authorId set to this authenticated user, establishing ownership for future authorization checks.

TOKEN=$(cat token.txt)
curl -X POST https://api.realworld.io/api/articles \
  -H "Content-Type: application/json" \
  -H "Authorization: Token $TOKEN" \
  -d '{
        "article": {
          "title": "Understanding RealWorld Auth",
          "description": "A short description",
          "body": "Full article body …",
          "tagList": ["auth","api"]
        }
      }'

Updating and Deleting Articles (Author-Only)

The strictest authorization rules apply to article modifications. Handlers in routes/api/articles/[slug]/index.put.ts and routes/api/articles/[slug]/index.delete.ts perform ownership verification after JWT validation. They fetch the target article and compare article.authorId against req.user.id. If the IDs do not match, the system throws a 403 Forbidden error, blocking the operation even for authenticated users.


# Update (author only)

TOKEN=$(cat token.txt)
curl -X PUT https://api.realworld.io/api/articles/how-to-use-auth \
  -H "Content-Type: application/json" \
  -H "Authorization: Token $TOKEN" \
  -d '{"article": {"title": "Updated Title"}}'

# Delete (author only)

curl -X DELETE https://api.realworld.io/api/articles/how-to-use-auth \
  -H "Authorization: Token $TOKEN"

Secondary Resource Authorization

Favoriting Articles

The favorite endpoints at POST /api/articles/:slug/favorite and DELETE /api/articles/:slug/favorite require authentication but impose no ownership restrictions. Any logged-in user may favorite any article. The handlers in routes/api/articles/[slug]/favorite/index.post.ts and index.delete.ts use the JWT-derived userId to create or remove favorite relationships.

Comment Operations

Comment authorization follows a similar pattern to articles. Creating comments via POST /api/articles/:slug/comments requires only authentication, with the handler storing the comment with an authorId matching the JWT user. However, deleting comments via DELETE /api/articles/:slug/comments/:id enforces author ownership, verifying that req.user.id matches the comment's authorId before removal.

Key Authorization Files in the Repository

Understanding the RealWorld API authorization model requires examining these specific source files:

  • utils/generate-token.ts: JWT generation logic for login/signup responses
  • utils/auth.ts: Express middleware that validates tokens and populates req.user
  • models/article.model.ts: Database schema defining the authorId relationship
  • routes/api/articles/index.post.ts: Article creation handler
  • routes/api/articles/[slug]/index.put.ts: Article update handler with ownership checks
  • routes/api/articles/[slug]/index.delete.ts: Article deletion handler with ownership checks
  • routes/api/articles/[slug]/favorite/index.post.ts: Favorite creation handler
  • routes/api/articles/[slug]/favorite/index.delete.ts: Favorite removal handler
  • routes/api/articles/[slug]/comments/*.ts: Comment creation and deletion handlers

Summary

  • The RealWorld API authorization model combines JWT token authentication with resource ownership verification to secure article management.
  • Public read operations require no authentication, while write operations demand valid JWT tokens passed via the Authorization: Token <jwt> header.
  • The auth.ts middleware validates tokens and attaches userId to requests, returning 401 Unauthorized for invalid credentials.
  • Article updates and deletions enforce strict author ownership by comparing article.authorId against req.user.id, returning 403 Forbidden for non-authors.
  • Comments and favorites use tiered permissions: creation requires only authentication, while comment deletion requires comment authorship.

Frequently Asked Questions

Do I need authentication to read articles in the RealWorld API?

No. The GET /api/articles and GET /api/articles/:slug endpoints are publicly accessible without any authentication headers. The handlers query the database directly without passing through the auth.ts middleware, allowing anonymous users to browse content freely.

How does the RealWorld API verify article ownership during updates?

The update handler in routes/api/articles/[slug]/index.put.ts first validates the JWT through the auth middleware, then fetches the target article from the database. It performs a strict equality check between article.authorId and req.user.id. If the values match, the update proceeds; otherwise, the API returns a 403 Forbidden error.

Can any authenticated user favorite an article?

Yes. Favoriting operations only require a valid JWT token to identify the user. The handlers in the favorite directory accept any authenticated request and create or delete favorite relationships using the JWT-derived userId, regardless of article ownership.

What happens if I try to delete another user's comment?

The deletion handler returns a 403 Forbidden error. For comment deletion at DELETE /api/articles/:slug/comments/:id, the system verifies that the authorId stored on the comment matches the req.user.id from the JWT. Only matching authors can delete their own comments, while authenticated non-authors are blocked from removing others' content.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →