TL;DR: Your credentials work, but the identity lacks the Pub/Sub IAM role. Grant Pub/Sub Publisher on the topic (or Pub/Sub Subscriber on the subscription) to the service account, then retry.

```text
google.api_core.exceptions.PermissionDenied: 403 User not authorized to perform this action.
```

## Fix it

1. Identify the authenticated identity (service account email from your key or workload identity). Expected: you know which identity to grant.
2. In the console, open the topic, Permissions, Grant access, and add the Pub/Sub Publisher role for that identity. For subscribers, do the same on the subscription with Pub/Sub Subscriber. Expected: binding appears.
3. Retry the publish/pull. Expected: success.
4. Wait a minute and retry once if it still 403s; IAM propagation lags.

## When this applies
- PermissionDenied 403 on publish, pull, or topic admin calls.

## When it doesn't
- DefaultCredentialsError: no credentials at all; fix auth first.
- 404 on the topic: the topic name or project is wrong.

## Compatibility
- google-cloud-pubsub any version; IAM is server-side.

## Why it happens
Pub/Sub enforces per-topic/per-subscription IAM. Service accounts start with no Pub/Sub roles, so every call 403s until someone grants them.

## Edge cases
- Topic-level vs project-level grants: project-level Pub/Sub Publisher covers all topics; prefer least privilege.
- Exactly-once or ordering settings do not affect IAM; do not chase those.
