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

> Discover the RealWorld API authorization model. Learn how JWT authentication and author ownership checks govern article access and modification in this robust system.

- Repository: [Thinkster/realworld](https://github.com/gothinkster/realworld)
- Tags: architecture
- Published: 2026-02-28

---

**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`](https://github.com/gothinkster/realworld/blob/main/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`](https://github.com/gothinkster/realworld/blob/main/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`](https://github.com/gothinkster/realworld/blob/main/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.

```bash

# 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`](https://github.com/gothinkster/realworld/blob/main/routes/api/articles/index.post.ts) passes requests through the [`auth.ts`](https://github.com/gothinkster/realworld/blob/main/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.

```bash
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.

```bash

# 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`](https://github.com/gothinkster/realworld/blob/main/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`](https://github.com/gothinkster/realworld/blob/main/utils/generate-token.ts)**: JWT generation logic for login/signup responses
- **[`utils/auth.ts`](https://github.com/gothinkster/realworld/blob/main/utils/auth.ts)**: Express middleware that validates tokens and populates `req.user`
- **[`models/article.model.ts`](https://github.com/gothinkster/realworld/blob/main/models/article.model.ts)**: Database schema defining the `authorId` relationship
- **[`routes/api/articles/index.post.ts`](https://github.com/gothinkster/realworld/blob/main/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`](https://github.com/gothinkster/realworld/blob/main/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`](https://github.com/gothinkster/realworld/blob/main/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.