mail@mabbaz.com Abu Dhabi, UAE

Enterprise Integration · Security · OAuth 2.0

OAuth 2.0 Explained: Secure Access Without Sharing Passwords

OAuth 2.0 is how modern systems grant access without handing over passwords. It is the quiet machinery behind almost every "Sign in with" button, every API integration, and every mobile app that talks to a backend. This is a clear, practitioner's explanation of what OAuth actually does, the flows that matter in real integration work, and the mistakes that turn a good security standard into a breach waiting to happen.

Muhammad Abbas July 10, 2026 ~13 min read

Every time you click "Connect your Google account" or "Sign in with Microsoft," a small piece of security engineering runs behind the scenes that most people never think about. You are not typing your password into the app in front of you. Instead, you are being handed off to a trusted authority, asked to approve a specific set of permissions, and sent back with a token that lets the app act on your behalf without ever seeing your credentials. That mechanism is OAuth 2.0, and it is one of the load-bearing standards of the modern internet. If you build or connect enterprise systems, understanding it is not optional. This article is the plain-language version, grounded in how it actually gets used. It sits inside the broader picture I lay out in the enterprise system integration pillar, which frames where authorization fits among the other moving parts of connecting systems together.

The message up front: OAuth 2.0 is an authorization framework, not a login screen and not an encryption protocol. Its single job is delegated access, letting one system act on a user's behalf against another system, scoped to exactly what was approved, without ever sharing the password. Get that core idea right and every flow, token and grant type in this article falls into place.

1. The problem OAuth solves

Rewind to the internet before OAuth and you find a genuinely bad pattern. If a photo-printing site wanted to pull your pictures from an online album, the only way to let it was to give the printing site your username and password for the album. You were handing your full credentials, and therefore full control, to a third party you barely knew. That third party could now do anything you could do: read everything, delete everything, change your password, lock you out. There was no way to grant limited access, no way to see what the app was doing, and no clean way to revoke it short of changing your password everywhere.

This is the password anti-pattern, and it fails on every axis that matters. It over-shares, because a password grants total access when the app only needs a slice. It cannot be scoped, because a password does not say "read photos only." It cannot be revoked selectively, because pulling access from one app means changing the credential that every other app also uses. And it destroys accountability, because once several apps hold the same password, no system can tell which of them took a given action.

OAuth 2.0 exists to replace that anti-pattern with delegated access. The core insight is simple but powerful: separate the act of proving who you are from the act of granting an application permission to do specific things. You authenticate once, with the party you already trust, and that party issues the application a limited-purpose credential, a token, that represents your consent to a defined set of actions. The application never sees your password. It only ever holds a token, and that token can be scoped narrowly, given a short lifetime, and revoked at any moment without touching your real credentials or any other application's access.

Notice what that unlocks. You can grant a calendar app read-only access to your events without letting it send email. You can revoke one misbehaving integration while leaving the other twenty untouched. You can see a list of every application you have authorized and what each is allowed to do. None of that is possible when everyone holds the same password. This is why OAuth swept through the industry: it made delegated access safe, granular and reversible, which is exactly what a world of interconnected apps and APIs needs.

2. The four roles

OAuth defines four actors, and almost all the confusion people have with it comes from blurring these roles together. Keep them distinct and the whole model becomes readable. RFC 6749, the core specification, names them precisely.

  • Resource owner: the entity that owns the data and can grant access to it. In the common case this is you, the human user. You own your photos, your calendar, your profile, and only you can authorize an application to touch them.
  • Client: the application that wants to access the resource on the owner's behalf. The photo-printing site, the mobile app, the analytics dashboard, the integration service. The client is never trusted with the password. It is only ever trusted with a token, and only after the owner approves.
  • Authorization server: the system that authenticates the resource owner, presents the consent screen, and issues tokens. This is the trusted authority in the middle. When you see a Google or Microsoft login-and-consent page pop up, you are talking to their authorization server, not to the client application.
  • Resource server: the API that holds the protected data and accepts tokens. When the client wants the actual photos or calendar entries, it presents its token to the resource server, which validates the token and returns the data the token is authorized to see.

The relationship between them is the whole story. The resource owner trusts the authorization server. The client trusts the authorization server to vouch for it. The resource server trusts tokens the authorization server issued. The client and the resource server never need to share a password, because the token carries the proof of authorization between them. In enterprise settings the authorization server and resource server are sometimes the same platform and sometimes separate products, but the logical roles stay the same. Whenever an OAuth interaction confuses you, name the four actors first and the confusion usually dissolves.

3. Tokens: access tokens and refresh tokens

Tokens are the currency of OAuth. The client never holds your password; it holds tokens, and there are two kinds that matter. Understanding the difference between them is central to using OAuth safely.

An access token is the credential the client presents to the resource server to get at protected data. It is short-lived by design, often measured in minutes to an hour, and it is scoped, meaning it carries the specific permissions the resource owner approved. When the client calls the API, it sends the access token in the HTTP Authorization header as a bearer token, a usage pattern defined in RFC 6750. "Bearer" means exactly what it sounds like: whoever bears the token can use it, so anyone who steals it can impersonate the client until it expires. That is precisely why access tokens are kept short-lived and why they must only ever travel over TLS.

A refresh token solves the problem that short-lived access tokens create. If an access token expires in an hour, you do not want to drag the user back through a login-and-consent screen every hour. So the authorization server can also issue a refresh token, a longer-lived credential the client stores securely and uses to obtain fresh access tokens without user interaction. The client sends the refresh token to the authorization server's token endpoint and gets back a new access token, silently, in the background. Because refresh tokens are long-lived and powerful, they are far more sensitive than access tokens and must be stored with real care, ideally in server-side storage or secure OS-level credential stores, never in a browser's local storage or in client-side JavaScript.

The division of labour is elegant once you see it. Access tokens are cheap, disposable and low-blast-radius: if one leaks, it expires quickly and only grants the narrow scope it was issued for. Refresh tokens are precious and closely guarded, because they are the key to minting more access tokens. A good OAuth implementation treats them accordingly, and a bad one treats a refresh token as carelessly as an access token, which is one of the mistakes we return to later. Some access tokens are self-contained JSON Web Tokens that carry claims the resource server can validate without a database lookup, a topic worth its own read; if you want the internals of how that token format works, the JWT spoke in this cluster covers structure, signing and validation in depth.

4. The authorization code flow, step by step

The authorization code flow is the flagship of OAuth 2.0 and the one you should reach for by default in server-side web applications. It is designed so that the most sensitive step, the exchange that produces the access token, happens over a secure back-channel between the client and the authorization server, never exposing the token to the browser or the user's device. Here is the sequence across the four actors.

Resource Owner Client Authorization Server Resource Server 1. Start login 2. Redirect to authorize 3. Log in & approve scopes 4. Redirect back with code back channel (server to server) 5. Send code + secret 6. Access + refresh token 7. Call API with access token 8. Protected data Steps 1 to 4 travel through the browser; steps 5 and 6 use a secure back channel.

Walk through it slowly. In step one the resource owner tells the client they want to connect. In step two the client redirects the owner's browser to the authorization server, passing its client identifier, the scopes it wants, and a redirect URI. In step three the authorization server authenticates the owner, this is where the owner actually types their password, into the trusted authority, not into the client, and shows the consent screen listing exactly what access is being requested. In step four, once the owner approves, the authorization server redirects the browser back to the client's redirect URI carrying a short-lived authorization code, not a token.

The clever part is what happens next. In step five the client takes that authorization code and, over a direct server-to-server back channel, sends it to the authorization server's token endpoint together with its client secret. Because this exchange happens server-side and is authenticated by the client secret, the code alone is useless to anyone who might have intercepted the browser redirect. In step six the authorization server validates the code and the secret and returns the actual access token and, optionally, a refresh token. Only now, in steps seven and eight, does the client present the access token to the resource server and receive the protected data.

The reason this flow is the gold standard is that the powerful, long-lived credentials never touch the browser. The browser only ever carries the one-time authorization code, which is short-lived, single-use, and worthless without the client secret. This separation between the front channel, the browser redirects anyone can observe, and the back channel, the secure server exchange, is the security backbone of OAuth 2.0. When you understand why the code exists as an intermediate step, you understand the flow.

5. Grant types and when to use each

OAuth calls its different flows "grant types," and choosing the right one for your scenario is one of the most consequential decisions in an integration. Picking the wrong grant is a security mistake, not just an inconvenience. The table below covers the grant types that matter today. Two older flows, the implicit grant and the resource owner password credentials grant, are deliberately left out of the "use this" column because current guidance actively discourages them, a point I expand on below the table.

Grant type What it is for Typical use
Authorization code Delegated user access with a confidential client that can keep a secret. The default, most secure flow. Server-side web apps acting for a logged-in user.
Authorization code with PKCE Same flow, hardened for clients that cannot safely hold a secret, using a dynamic proof instead. Mobile apps, single-page apps, native desktop apps.
Client credentials Machine-to-machine access where no user is involved and the client acts as itself. Backend services, scheduled jobs, service-to-service API calls.
Device code Delegated access on devices with no browser or keyboard, by pairing with a second device. Smart TVs, consoles, CLI tools, IoT devices.

Read the table as a decision tree. Is a human user granting access? If yes, use authorization code, adding PKCE whenever the client is a browser or a device that cannot protect a secret. Is it a backend talking to another backend with no user at all? Use client credentials, where the service authenticates as itself with its own client identifier and secret. Is the client a TV or a headless device with no way to type a password? Use the device code flow, where the device shows a short code and asks you to approve it from your phone or laptop.

The two omissions are deliberate and important. The implicit grant returned an access token directly in the browser redirect, skipping the authorization code exchange, which was a compromise made in an era before browsers could safely run PKCE. It leaks tokens into browser history and referrer headers and is now discouraged; the modern answer for single-page apps is authorization code with PKCE. The resource owner password credentials grant, sometimes just called the password grant, has the client collect the user's actual username and password and send them to the authorization server. That reintroduces the exact anti-pattern OAuth was invented to eliminate, so it is discouraged except in a few narrow legacy migration cases and is being removed from the standard going forward. If a vendor's integration guide tells you to use either of these, treat it as a red flag and ask whether a code-based flow is available instead.

6. PKCE and why it matters for public clients

The authorization code flow depends on the client secret to protect the code exchange. But some clients cannot keep a secret. A mobile app is downloaded to thousands of devices, and anyone can decompile it and read anything baked inside, so a "secret" shipped in the app binary is not secret at all. A single-page JavaScript app runs entirely in the browser where the user can read every line. These are called public clients, as opposed to confidential clients like a server-side app that can guard a secret in backend configuration.

Without a usable secret, the back-channel code exchange loses its protection. An attacker who intercepts the authorization code, for instance through a malicious app registered to the same redirect URI on a mobile device, could exchange it for tokens themselves. PKCE, Proof Key for Code Exchange, defined in RFC 7636 and pronounced "pixy," closes this gap without needing a static secret.

The mechanism is clean. Before starting the flow, the client generates a random secret value called the code verifier, then hashes it to produce a code challenge. It sends only the code challenge along in the initial authorization request. When it later exchanges the authorization code for tokens, it must also present the original code verifier. The authorization server hashes the verifier it just received and checks that it matches the code challenge it stored at the start. Because only the legitimate client knows the original random verifier, an attacker who stole the authorization code cannot complete the exchange; they have the code but not the matching proof. The secret is generated fresh for every single flow and never travels in a form that is useful to intercept.

The practical rule: use authorization code with PKCE for every public client, and increasingly for confidential clients too, because it adds defense in depth at almost no cost. Current guidance treats PKCE as the default rather than the exception. If you are building a mobile app, a single-page app, or a native desktop client and PKCE is not in your flow, your integration is behind the standard and quietly less safe than it should be. This is the single most important modern refinement to the classic OAuth flow. For the wider view of how these authorization decisions sit alongside transport, data mapping and error handling, return to the enterprise system integration pillar.

7. OAuth versus OpenID Connect

Here is the distinction that trips up more engineers than any other: OAuth 2.0 is about authorization, not authentication. Authorization answers "is this client allowed to do this thing," while authentication answers "who is this user." OAuth was designed for the first question. It grants an application permission to access resources. It was never designed to tell the client who the user is, and using a raw access token as proof of identity is a well-known security mistake, because an access token proves that access was granted, not who granted it.

OpenID Connect, usually shortened to OIDC, is a thin identity layer built on top of OAuth 2.0 to answer the authentication question properly. It reuses the same flows, the same roles, the same tokens, and adds one crucial new artifact: the ID token, a signed JWT that makes verifiable claims about who the user is, when they authenticated, and which authorization server vouched for them. When you click "Sign in with Google," you are using OpenID Connect, not bare OAuth. The client gets an ID token it can validate to establish the user's identity, plus, if it wants, an access token to call Google APIs on that user's behalf.

The mental model I give teams: OAuth gives an app a key to a room; OpenID Connect also gives the app a verified name badge for the person who handed over the key. If you only need to call an API on the user's behalf, OAuth is enough. If you need to log the user in and know who they are, you want OpenID Connect. The overlap is why people conflate them, but keeping the two questions separate, permission versus identity, prevents a whole category of security bugs. The most common of those bugs is treating an OAuth access token as a login assertion, which OpenID Connect exists specifically to fix.

8. Scopes and least privilege

Scopes are how OAuth expresses granularity, and they are where the principle of least privilege gets real teeth. A scope is a named permission the client requests and the resource owner approves, things like read:calendar, write:files, or profile:email. When the client redirects the owner to the authorization server, it lists the scopes it wants; the consent screen shows the owner exactly those scopes; and the access token that comes back is stamped with the approved set. The resource server then enforces them, allowing a token to read calendars only if the calendar-read scope is present.

This is what turns "access" from an all-or-nothing decision into a precise grant. A client that only needs to display your name and email should request just those scopes and nothing more. A backup tool that only reads files should never request write access. Least privilege means requesting the minimum scopes required for the feature, and no more, so that if the token is ever compromised the blast radius is limited to what those narrow scopes allow. A read-only token that leaks is a bad day; a token with full read-write-delete that leaks is a catastrophe.

There is a user-trust dimension too. Consent screens that ask for a long list of broad permissions train users either to distrust the app or, worse, to click through without reading. Requesting tight, obviously-justified scopes builds trust and makes the consent meaningful. From the resource server side, enforcing scopes rigorously on every endpoint is essential, because scope enforcement that exists on the consent screen but not in the API is theatre. The scope told the user what would happen; only the resource server's checks make it true.

In enterprise integration, scopes map naturally onto the permission models of the platforms you are connecting. When I wire a service into a business platform's API, I define the OAuth scopes to match exactly the operations the integration performs and nothing broader, so the integration's credentials cannot be turned into a lateral-movement tool if they are ever exposed. For a concrete look at how one major platform structures its API access and permissions around this model, see the Business Central APIs and integrations guide, and for the resource-server side of the story, how APIs are shaped and secured, the REST API explained guide.

9. Common OAuth mistakes

OAuth is a good standard implemented badly more often than it is implemented well. The failures I see are consistent, and every one of them is avoidable. This is the honest list.

  • Leaking tokens through careless storage. Access and refresh tokens stored in browser local storage, embedded in URLs, written to logs, or passed in query strings are tokens waiting to be stolen. Tokens belong in secure server-side storage or, on native clients, in the operating system's protected credential store. A bearer token in a log file is a credential in a log file.
  • Choosing the wrong grant type. Using the implicit grant for a single-page app instead of authorization code with PKCE, or the password grant instead of a proper delegated flow, reintroduces exactly the weaknesses OAuth was built to remove. The grant type is a security decision; treat it as one.
  • Never rotating refresh tokens. A long-lived refresh token that is never rotated is a permanent skeleton key. Modern practice is refresh token rotation, issuing a new refresh token on every use and invalidating the old one, so that a stolen refresh token is detected and revoked the moment the legitimate client tries to use its now-invalidated copy.
  • Not validating the redirect URI. Authorization servers must only redirect back to pre-registered, exactly-matched redirect URIs. Loose matching, wildcards, or open redirects let an attacker steal authorization codes by redirecting them to a URI they control. This is one of the most exploited OAuth weaknesses in the wild.
  • Skipping the state parameter. Omitting the anti-forgery state value on the authorization request leaves the flow open to cross-site request forgery, where an attacker tricks a victim's browser into completing a flow the victim never intended. The state parameter, checked on return, is a small piece of code that closes a real hole.
  • Treating an access token as proof of identity. Using an OAuth access token to decide who the user is, rather than an OpenID Connect ID token, is the confused-deputy mistake from the previous section. Access tokens prove access was granted, not identity.
  • Over-requesting scopes. Asking for broad permissions "to be safe" inflates the blast radius of any compromise and erodes user trust. Request the minimum, always.

The honest caution: OAuth 2.0 gives you a secure framework, but it does not enforce secure usage. Every mistake above passes functional testing, because the happy path works fine; the flaw only shows up under attack. That is exactly why OAuth security bugs survive to production so often, and why picking a well-maintained OAuth library and following current guidance beats hand-rolling flows from the raw specification. The framework is sound. The way people wire it up is where the risk lives. If your integration only needs a static shared secret between two servers you fully control, a plain API key is sometimes simpler and adequate; the API-key-versus-OAuth spoke in this cluster weighs that trade-off in detail.

10. References

The primary sources for OAuth 2.0 are the IETF Request for Comments documents, published by the Internet Engineering Task Force and available on the RFC Editor website. These are the authoritative specifications, and they are more readable than their reputation suggests.

  • RFC 6749, "The OAuth 2.0 Authorization Framework." The core specification that defines the four roles, the grant types, and the authorization and token endpoints. Published by the IETF, available at rfc-editor.org.
  • RFC 6750, "The OAuth 2.0 Authorization Framework: Bearer Token Usage." Defines how bearer access tokens are presented to resource servers, including the Authorization header format. IETF, rfc-editor.org.
  • RFC 7636, "Proof Key for Code Exchange by OAuth Public Clients." Specifies PKCE, the code verifier and code challenge mechanism that hardens the authorization code flow for public clients. IETF, rfc-editor.org.
  • OAuth 2.1, an in-progress IETF draft that consolidates OAuth 2.0 and its accumulated best current practices into a single, simpler specification. It makes PKCE mandatory for the authorization code flow and removes the implicit and password grants. As a draft it is still evolving, but it reflects the direction of current guidance and is worth tracking through the IETF OAuth working group.

For identity specifically, OpenID Connect is specified separately by the OpenID Foundation rather than the IETF, and its core specification is the authoritative reference for the ID token and the authentication layer described earlier. When in doubt about behaviour, the specification is the source of truth, and most OAuth confusion online comes from blog posts that paraphrase the specs loosely rather than from the specs themselves.

Final thoughts

OAuth 2.0 earned its place at the center of the modern internet by solving a real problem cleanly: how to let one system act on your behalf against another without handing over your password. Once you hold the four roles clearly in mind, the flows stop being intimidating. The authorization code flow keeps the powerful credentials off the browser. PKCE extends that safety to clients that cannot keep a secret. Scopes make access precise. Refresh tokens keep the experience smooth without keeping long-lived power in risky places. And OpenID Connect adds the identity answer that OAuth deliberately left out.

The reason OAuth still trips people up is not that the framework is bad; it is that the framework gives you enough rope to secure your integration or to hang it, and the difference is in the details. Choose the right grant, store tokens like the credentials they are, validate redirect URIs, use the state parameter, rotate refresh tokens, request minimal scopes, and never mistake an access token for a login. Do those things and OAuth delivers exactly what it promises: secure, granular, revocable, delegated access without password sharing. Skip them and you have a standards-compliant implementation that is still quietly unsafe. As with every part of enterprise integration, the standard is the easy part and the disciplined use of it is where the real work lives.

Wiring OAuth into an enterprise integration?

Independent advice on authorization design, grant-type selection, token handling and secure API integration across ERP, EAM, CAFM and cloud platforms. 22+ years connecting enterprise systems safely, no vendor margins and no reseller arrangements.

Book a conversation

Related reading: Enterprise system integration explained (pillar), REST API explained, Business Central APIs and integrations.

Muhammad Abbas

CMMS / CAFM Manager & Enterprise Integration Specialist · 22+ years across ERP, EAM, CAFM and enterprise integration.

Work with me
MAbbaz.com
© MAbbaz.com