Ask one question: can this code keep a secret? A client on a server you control is confidential and can authenticate. A client on the user's device, such as a browser or mobile app, is public, and any secret you embed can be extracted. The type is about secrecy, not importance.
The definitions
RFC 6749 defines two client types based on the ability to keep credentials confidential:
- Confidential clients can maintain the confidentiality of their credentials, for example a client on a secure server with restricted access to its credentials, or capable of secure client authentication by other means.
- Public clients cannot, for example clients running on the device used by the resource owner, such as an installed native app or a browser based app.
The RFC adds that the authorization server should not make assumptions about the client type, and that the type depends on the server's definition of secure authentication.
A quick decision table
| Your client | Type | Why |
|---|---|---|
| Server rendered web app | Confidential | Credentials stay on the server |
| Backend for a single page app (BFF) | Confidential | The server holds the credentials and tokens |
| Single page app with no backend | Public | Code and storage are visible to the user |
| iOS or Android app | Public | The RFC assumes embedded credentials can be extracted |
| Desktop app or command line tool | Public | Runs on the user's machine |
| Scheduled job or service | Confidential | Runs in your infrastructure, see client credentials |
If an app has both a server part and a browser part, the RFC says to register each component as a separate client if the authorization server does not support such distributed clients.
What changes for a public client
- No client secret. RFC 6749 says that for native apps, any client authentication credentials included in the app can be assumed extractable. The server must not rely on public client authentication to identify the client.
- PKCE is mandatory. RFC 9700 says public clients must use PKCE with the authorization code flow.
- Refresh tokens need extra care. RFC 9700 says refresh tokens for public clients must be sender constrained or use refresh token rotation.
- Redirect URIs matter more. Register exact URIs, since the client cannot prove who it is.
What changes for a confidential client
- It authenticates at the token endpoint. RFC 9700 says authorization servers should enforce client authentication where feasible.
- It should prefer asymmetric methods, such as mutual TLS or signed JWTs (private key JWT), over shared secrets. See private key JWT vs client secret.
- It should still use PKCE. RFC 9700 recommends it for confidential clients too.
- It must keep its secret out of source control, logs and images.
Mistakes to avoid
- Shipping a "client secret" inside a mobile app and calling the client confidential.
- Registering a browser app as confidential because the console offered a secret field.
- Using one client registration for web, mobile and server pieces, so a leaked mobile secret exposes the server.
- Assuming client authentication proves who the user is. It proves which application is calling.
Quick check
Is a client ID a secret?
No. RFC 6749 says the client identifier is exposed to the resource owner and must not be used alone for client authentication.
Can a mobile app be confidential if I use a backend?
The backend can be a confidential client. The mobile app itself remains public. Register them as separate clients or use a design where the backend handles the OAuth exchange.
Safeguard's SBOM and dependency scanning shows which OAuth libraries each of your apps ships, which helps you audit client setups across a portfolio. There is a free scan.
Sources
- RFC 6749, The OAuth 2.0 Authorization Framework
- RFC 9700, Best Current Practice for OAuth 2.0 Security
Last reviewed: 19 October 2026.