skip to content

In OAuth 2.0, what makes a client confidential rather than public, and where does an installed mobile app fall?

level: juniorimportance: must knowfreq 66%

answer

  1. one property decides the whole type
  2. can this component keep a credential?
  3. runs on the resource owner's device
  4. a shipped secret is a published secret
  5. register each component separately, RFC 6749 2.1

basics

~20 s

RFC 6749 section 2.1 types a client on one property: whether it can keep its credentials confidential. Server-side code can. An installed mobile app cannot, so it is public — a secret shipped inside it is readable by whoever installs it.

solid answer

~50 s

OAuth 2.0 types a client by one property only — whether it can maintain the confidentiality of its credentials. A `confidential` client runs somewhere the resource owner and the public cannot read: a server process, a job runner, infrastructure the operator controls. It can hold a `client_secret` or a private key and authenticate at the token endpoint. A `public` client runs on the resource owner's own device — an installed native application, or script delivered to a browser — so anything shipped inside it can be unpacked, and it registers `token_endpoint_auth_method` as `none`. RFC 6749 §2.1 also covers the case candidates miss: one product may be a **distributed client** with a confidential server component and a public browser component, and the specification says each component SHOULD be registered as a separate client. The type belongs to the component, not to the brand on the box.

code

json · 15 lines
json
// server-side component: confidential
{
  "client_id": "s6BhdRkqt3",
  "token_endpoint_auth_method": "client_secret_basic",
  "grant_types": ["authorization_code", "refresh_token"],
  "redirect_uris": ["https://filings.example.com/callback"]
}

// browser component of the same product: public
{
  "client_id": "aX9pQ2mLt7",
  "token_endpoint_auth_method": "none",
  "grant_types": ["authorization_code"],
  "redirect_uris": ["https://filings.example.com/spa/callback"]
}

go deeper

for a junior

Recall the single test: can this piece of software keep a credential away from the person using it? Server-side yes, installed or browser-delivered no. Name the two words the specification uses, confidential and public.

for a middle

Explain the consequences rather than the labels: which token_endpoint_auth_method each type may register, why the client credentials grant is closed to public clients, and why a secret in a shipped build is a published constant.

for a senior

Show you handle the distributed case in a real estate: two registrations rather than one, no shared secret across components, and a plan for what happens the day the same vendor ships an installable companion.

for a principal

The trade-off you own is how many registrations an organisation can actually operate — per component and per deployment means more records, more rotation and more revocation surface, against the blast radius of one shared credential.

## The one property that decides the type OAuth 2.0 does not grade a client by how reputable the company behind it is, what language it was written in, or whether the deployment is production. RFC 6749 §2.1 types a client on exactly one property: **whether it is capable of maintaining the confidentiality of its credentials**. - A **confidential** client can. It runs where the resource owner and the general public cannot read its storage or its memory — a server process, a scheduled job, a container on infrastructure the operator controls. It can therefore hold a `client_secret` or a private key and prove possession of it at the token endpoint. - A **public** client cannot. It runs on the resource owner's own device: an installed native application, a downloadable desktop tool, or script delivered to and executed by a browser. Whatever ships inside it is readable by whoever holds the device. The specification's own client profiles make the same cut. A *web application* — code executing on a server, where only rendered pages reach the browser — is confidential. A *user-agent-based application*, whose code is downloaded and runs in the browser, and a *native application*, installed on the device, are both public. ## Why "but we compiled it" is not an argument A credential compiled into a binary is still a string inside a file the user already possesses. It can be recovered from the package, read out of memory, or simply observed on the wire by anyone who can watch the software's own traffic on a device they own. Extraction only has to succeed once, anywhere in the install base, and the value is then known to everyone. That is the judgement this leaf exists to teach: **shipping a secret inside something the user installs does not make the client confidential — it makes the secret public.** Obfuscation raises the cost of the first extraction and changes nothing afterwards. The same value is in every copy, so the authorization server cannot tell an honest install from a hostile one, and rotating it means shipping a new release to every user at once. ## The consequence at the token endpoint Client type decides what the client may claim about itself when it talks to the authorization server: | | Confidential client | Public client | |---|---|---| | Registered `token_endpoint_auth_method` | a real method — for example `client_secret_basic` | `none` | | What the token endpoint learns | which client, and that it holds the credential | only which `client_id` the request claims | | `client_credentials` grant | permitted | MUST NOT be used (RFC 6749 §4.4) | | Refresh-token policy | ordinary | the authorization server MAY refuse or shorten them | A public client is not an untrusted or second-class application; it is an application that has *admitted it cannot authenticate*. The authorization server then knows only which registration a request names. Everything a public-client flow does to stay safe follows from that admission — including the extension of the authorization code grant that binds a code to the software that started the flow, which this branch covers separately. ## The type is a property of the component RFC 6749 §2.1 explicitly allows a client to be implemented as a distributed set of components, each with its own type and security context. A single product can have a server-side back end that keeps a key, and browser-delivered script that cannot. Where the authorization server does not support that shape directly, the specification says each component SHOULD be registered as a separate client — separate `client_id`, separate `redirect_uris`, separate `token_endpoint_auth_method`. The practical failure this prevents is the tempting shortcut: one registration for the whole product, with the confidential component's `client_secret` handed to the browser half so "the same client" can call the token endpoint from either side. That does not extend confidentiality to the browser; it removes it from the server. ## Reading it off a registration Because the type is recorded rather than inferred, you can read it off the client's registered metadata. A registration whose `token_endpoint_auth_method` is `none` is a public client whatever the marketing says; one that names a secret-bearing or key-bearing method is confidential, and the authorization server will hold it to that method on every token request. A vendor that ships a mobile companion to an existing server product therefore does not extend the existing registration — it registers a second, public client beside it.

  • A broker's filing back end already runs as a confidential client. The vendor now ships a mobile companion app. What should the registration look like?
    A second registration, of a public client, with its own `client_id` and `token_endpoint_auth_method` of `none`. The back end's `client_secret` must not travel into the mobile build. Reusing one registration across both would put a credential the mobile half cannot protect into the hands of every install.
  • Why does RFC 6749 §4.4 restrict the client credentials grant to confidential clients?
    That grant has no resource owner in it: the client's own authentication is the entire basis for issuing a token. A public client cannot authenticate, so there would be nothing to check — anyone who knew the `client_id` could ask for the token. The grant is specified as MUST NOT for that reason.
  • Does a public client have a weaker security posture by definition?
    No. It has a different one. Public means the software cannot prove it is itself, so the protocol stops relying on that proof and leans on other bindings instead — exact redirect handling, and the code-grant extension that ties an authorization code to the requester that began the flow. A well-built public client can be perfectly sound.

A door code printed in every welcome pack still opens the door — it just no longer tells anyone who walked in.

saying these in an interview costs you the question

  • Thinks a mobile app is confidential because the code is compiled
  • Says obfuscating the shipped secret makes the client confidential
  • Treats client type as a property of the product, not the component
  • Believes a public client cannot use the authorization code grant
  • Assumes public simply means an application nobody trusts
  • Hands one registration's client_secret to a browser front end