Dev.to Security πŸ” Cybersecurity πŸ‘ 0 πŸ“– 7 min read

API Key or OAuth? Why a Document API Doesn't Need Both

Every new integration kicks off with the same fork in the road: does this API want an API key, or does it want OAuth? Most developers pick based on habit rather than the actual shape of the problem, and for a document pr

Every new integration kicks off with the same fork in the road: does this API want an API key, or does it want OAuth? Most developers pick based on habit rather than the actual shape of the problem, and for a document processing API, that habit is usually wrong in an interesting way. The honest answer for something like PDF4me is that you don't choose between the two. You use an API key, and you stop there, because OAuth is solving a problem this kind of API doesn't have.

That sounds like a shortcut. It isn't. It's a description of what OAuth actually does.

What OAuth is actually for

OAuth 2.0 exists to answer one specific question: can this third-party application act on behalf of a specific human user, with that user's explicit, revocable consent? That's why the flow looks the way it does. A user gets redirected to a login screen they recognize, they see a consent page listing exactly what the app wants ("read your calendar," "post on your behalf"), and the app walks away with a short-lived access token plus a refresh token so it can keep working without asking the user to log in again every hour.

Every piece of that machinery exists to protect the user from the app, and to let the user cut the app off later without changing a password. It's the right tool when Slack wants read access to your Google Drive, or when a scheduling tool wants to create events on your behalf. There's a human in the middle whose consent matters, and whose access needs to be revocable independent of anyone else's.

What a document API actually is

Now look at what happens when your backend calls PDF4me's REST API to convert a file, merge two PDFs, or pull structured data out of an invoice. There's no end user in that request. Your server is talking to PDF4me's server. Nobody is being asked to log in, because nobody needs to consent to anything, because the only party whose authorization is being checked is your own application.

This is a machine-to-machine call, and machine-to-machine calls have a much simpler question to answer: is this specific request coming from someone we've already agreed to trust? That's exactly what an API key answers. Generate one from the dashboard, attach it to the request, and PDF4me's V2 API knows who's calling and whether they're allowed to. No redirect, no consent screen, no refresh cycle. One credential, one header, done.

Here's exactly what that header looks like in practice, verified against PDF4me's own official Python samples:

import base64
import requests

api_key = "get the API key from https://dev.pdf4me.com/dashboard/#/api-keys"
base_url = "https://api.pdf4me.com/"
url = f"{base_url}api/v2/GetPdfMetadata"

headers = {
    "Content-Type": "application/json",
    "Authorization": f"Basic {api_key}"
}

payload = {
    "docContent": base64.b64encode(open("sample.pdf", "rb").read()).decode("utf-8"),
    "docName": "output.pdf",
    "isAsync": True
}

response = requests.post(url, headers=headers, json=payload, timeout=30)

Worth calling out because it trips people up: that Authorization: Basic {api_key} header is not classic HTTP Basic auth (a base64-encoded username:password pair). It's PDF4me's own convention of the literal word Basic followed by the raw API key. Copy the header name from a generic HTTP client tutorial and you'll reach for Bearer out of habit; the samples across Python, C#, Java, and Apex are consistent on Basic, so that's what to type. (Two older Google Apps Script samples in the same repo still use Bearer instead, which is worth a second look if you're adapting from one of those specifically.)

Building a full OAuth client just to make that same call would mean standing up a token exchange flow, handling refresh tokens before they expire, and maintaining a client registration you never actually needed, all to protect a human who was never in the flow to begin with. That isn't more secure. It's more surface area for the exact same outcome.

Picture the actual code path. A background job picks up an uploaded contract, needs it converted to PDF/A before it lands in an archive, and calls the conversion endpoint. There's no browser tab open. There's no session. There's no user sitting there to approve a consent screen, because the "user" of this request is a cron job that runs at 2 a.m. Ask what an OAuth authorization code flow would even redirect to in that scenario, and the answer is nothing, because there's no interactive surface for it to redirect through. You'd end up faking the human step just to satisfy a protocol that assumes one exists.

Where the complexity actually goes

Ask yourself what OAuth would even protect here. There's no consent to revoke on behalf of an end user, because there's no end user. There's no third-party app you're authorizing to act "on your behalf," because your own backend is the caller. The only thing left to protect is the credential itself, and API keys protect that the same way OAuth's client secrets do: keep it out of source control, keep it out of client-side code, and rotate it when you suspect it's been exposed.

That's not a shortcut version of security. It's the actual security model, sized correctly for what's being secured. A stolen API key and a stolen OAuth refresh token cause the same damage: an attacker can now call the API as you. Neither scheme protects you from a leaked secret. What OAuth adds on top, scoped, revocable, per-user delegation, is real value when there's a user to delegate for. When there isn't, it's just more moving parts standing between your code and the request you wanted to make.

There's also a cost people rarely price in: every extra moving part is one more thing that can break at 2 a.m. and page someone. A refresh token that silently expires, a client secret that rotates on the provider's schedule instead of yours, a redirect URI that stops matching after a domain migration, all of these are OAuth-shaped incidents that a single long-lived API key simply doesn't create. Fewer moving parts means fewer 2 a.m. incidents, and that's worth something on its own, independent of which approach is theoretically more secure on paper.

None of this means an API key should be treated casually. Where does it live in your app? Ideally a secrets manager or environment variable your CI pipeline injects at deploy time, never a value typed into a config file that gets committed alongside everything else. How is it scoped? One key per environment, so a compromised staging key doesn't hand an attacker production access too. How is it monitored? Watch for a sudden spike in calls from a key that normally sits quiet overnight, because that pattern is often the first visible sign something leaked. None of that is unique to API keys, either. It's the same operational discipline any credential needs, OAuth client secrets included.

What this looks like in practice

The no-code side of PDF4me makes the point even more clearly, because it strips away anything a developer might rationalize as "extra flexibility." When you connect PDF4me to Power Automate, Zapier, Make, or n8n, the entire connection step is: paste your API key. That's it. Power Automate's authorization documentation walks through exactly what the connector needs and what it can do with it, and the getting started guide has you authenticated and running a flow in the same session. Make's own setup, n8n's, and Zapier's all follow the identical shape: one key, one field, no separate identity provider to configure.

Compare that to what an OAuth-based connector setup usually looks like: register an app with a provider, configure redirect URLs, click through a consent screen, and hope the refresh token doesn't silently expire six months later and break a workflow nobody's watching. None of that complexity buys the no-code user anything, because they aren't delegating access on behalf of someone else either. They're the same person who owns the PDF4me account and the automation that's calling it.

If you want to see this before wiring anything into your own code, the API Tester lets you paste in that same API key and fire real requests at real endpoints directly in the browser, with no client library and no auth flow beyond the one credential.

When you'd actually want OAuth

None of this means OAuth is wrong, only that it's answering a different question than "how do I authenticate my backend against a document API." If you're building a multi-tenant product where your customers each connect their own separate PDF4me account, and you need per-customer consent, audit trails, and the ability for a single customer to revoke your app's access without affecting anyone else's, that's a genuine delegation problem, and OAuth is the right shape for it. Say you're building a document-automation SaaS product where each of your customers has their own PDF4me account, and your app processes files on their behalf, one connection per customer, each revocable independently, each showing up in that customer's own usage and billing. That's real delegation, with a real third party in the middle, and it's exactly the case OAuth was built to handle. It's a different product decision than "I need to convert a file," and it's worth naming honestly instead of reaching for OAuth by default because it sounds more enterprise.

For the actual document processing call, the honest advice is smaller and less impressive: get your key from the dashboard, never commit it to a public repository, rotate it if you think it leaked, and stop there. The security work that matters is boring key hygiene, not which auth protocol looks more sophisticated on a whiteboard.

Website: pdf4me.com
Documentation: docs.pdf4me.com
Developer portal: dev.pdf4me.com

πŸ“° Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes β€” full credit and traffic to the original publisher.