Public and confidential clients
Whether your client can keep a secret decides the whole flow.
Open this lesson in the learning hubKey points
- A confidential client can keep a secret - a server-side application where the secret lives in configuration the user never sees.
- A public client cannot. A single-page app, a mobile app or a desktop app ships its code to the user, so anything embedded in it is readable.
- This is not a matter of care. Minified JavaScript can be read, and a mobile binary can be decompiled - a secret in either is public by definition.
- That is why a public client must use PKCE and must never use the client credentials grant or embed a client secret.
- The distinction also decides token storage: a confidential client can hold a refresh token safely, while a public client needs rotation and short lifetimes to compensate.
- A backend-for-frontend turns a public client into a confidential one: the browser talks to your server with a cookie, and your server holds the tokens.
Example
# CONFIDENTIAL - server-side, holds a real secret
#
# Spring Boot app, Django app, any backend
# client_id + client_secret
# authorization_code, or client_credentials for machine-to-machine
# can store refresh tokens safely
# PUBLIC - ships to the user, cannot hold a secret
#
# SPA, mobile app, CLI, desktop app
# client_id ONLY
# authorization_code + PKCE, always
# refresh tokens must be rotated and short-lived
---
# THE BFF PATTERN - the usual answer for a browser app.
#
# browser --session cookie--> your backend --tokens--> API
# (httpOnly, Secure) (confidential client)
#
# The browser never sees a token at all. XSS cannot steal what is
# not there, and logout works because the session is revocable.
---
# Spring, as a confidential client:
spring:
security:
oauth2:
client:
registration:
google:
client-id: ${GOOGLE_CLIENT_ID}
client-secret: ${GOOGLE_CLIENT_SECRET} # env, never committed
scope: openid,profile,email
authorization-grant-type: authorization_code
# A public client config has NO client-secret line. If you find yourself
# wanting to add one to a SPA, you want a BFF instead.
A client that ships to the user cannot hold a secret - use PKCE, or move the tokens behind a backend.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the OAuth 2.0 Course course, and every lesson in it is listed on the OAuth 2.0 Course contents page.