In OAuth 2.0, how do client_secret_basic and client_secret_post differ, and which does RFC 6749 prefer?
answer
- same secret, two envelopes
- header versus form body
- one is MUST support, one is MAY
- NOT RECOMMENDED is not forbidden
- form-urlencode before base64, RFC 6749 2.3.1
basics
~20 sBoth carry the same client_secret to the token endpoint. client_secret_basic puts client_id and secret in an HTTP Basic Authorization header; client_secret_post puts them in the form body. RFC 6749 requires Basic support and marks the body form NOT RECOMMENDED.
solid answer
~40 sThey are the same credential in two envelopes. With `client_secret_basic` the client sends `Authorization: Basic` over the mutually agreed `client_id` and `client_secret`; with `client_secret_post` it sends `client_id` and `client_secret` as ordinary form parameters in the token request body. RFC 6749 §2.3.1 says the authorization server **MUST** support HTTP Basic and **MAY** support the body parameters, which it marks **NOT RECOMMENDED** and limits to clients unable to use Basic. Two details bite in practice: the header form requires `client_id` and `client_secret` to be form-urlencoded *before* they are base64-encoded, which breaks interoperability for secrets containing reserved characters; and if a registration omits `token_endpoint_auth_method`, RFC 7591 defaults it to `client_secret_basic`. Either way the secret is a symmetric value the authorization server must store and the client must transmit on every token request.
code
http · 6 linesPOST /token HTTP/1.1
Host: as.example.net
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0Mzo3RmpmcDBaQnIxS3REUmJuZlZkbUl3
grant_type=client_credentials&scope=customs.entries.writego deeper
Recall that both methods send the same shared client_secret to the token endpoint, one in an Authorization header and one in the form body, and that the header form is the one the specification prefers.
Explain the exact wording: Basic MUST be supported, the body form MAY be and is NOT RECOMMENDED, and the method is fixed at registration rather than chosen per request.
Demonstrate the diagnosis: an invalid_client that appears on one server only, traced to the form-urlencode-then-base64 rule, and a view on where each envelope leaks in a logging proxy.
The call you own is whether a shared symmetric secret is acceptable at all across a fleet, given that every authorization server holding one becomes a store worth breaching and rotation touches every caller.
## Same secret, two envelopes Both methods are shared-secret client authentication at the **token endpoint** — the back-channel POST a client makes server to server, never the browser redirect. The difference is only where the credential rides. **`client_secret_basic`** uses the HTTP Basic authentication scheme. The `client_id` becomes the user name and the `client_secret` the password, and the pair is base64-encoded into an `Authorization` header. The body then carries only the grant parameters. **`client_secret_post`** drops the header and sends two extra form parameters, `client_id` and `client_secret`, inside the `application/x-www-form-urlencoded` body beside `grant_type`. ## What RFC 6749 §2.3.1 actually says The specification is not neutral between them: - the authorization server **MUST** support HTTP Basic for clients issued a password; - it **MAY** support the two body parameters; - including the credentials in the request body is **NOT RECOMMENDED**, and should be limited to clients that cannot use the Basic scheme; - whichever is used, the request **MUST** be protected by TLS, and the authorization server **MUST** require TLS on the endpoint, because the secret is sent in the clear inside the protected channel. Note the strength of each word. "NOT RECOMMENDED" is not "MUST NOT": `client_secret_post` is a conforming method, widely deployed, and named as a registered value of `token_endpoint_auth_method`. An answer that calls it forbidden has promoted a SHOULD-class statement into a prohibition. ## The comparison an interviewer wants | | `client_secret_basic` | `client_secret_post` | |---|---|---| | Where the credential rides | `Authorization` request header | form-encoded request body | | Server support | MUST be supported | MAY be supported | | Specification's stance | the preferred form | NOT RECOMMENDED | | Encoding rule | form-urlencode each value, then base64 the pair | ordinary form encoding | | Common leak path | header captured in proxy or request logs | body captured in request-body logging | ## The encoding rule that quietly breaks interoperability RFC 6749 §2.3.1 requires the `client_id` and the `client_secret` to be encoded with the `application/x-www-form-urlencoded` algorithm *first*, and only then used as the Basic user name and password. Most generated secrets are alphanumeric, so most implementations never notice. The day a secret is issued containing a reserved character — a plus sign, a slash, a space — a client that base64-encodes the raw bytes and a server that expects the encoded ones disagree, and the token endpoint answers `invalid_client` with nothing in the message to explain why. That symptom is worth recognising on sight: one authorization server rejects the credential while another accepts the identical pair. It is an encoding mismatch, not a wrong secret. ## What neither method changes 1. **The authorization server has to store a verifier for the secret.** It is a symmetric credential, so a compromise of the server's client store is a compromise of every client that uses one. 2. **The secret crosses the network on every token request.** TLS protects it in transit; a terminating proxy that logs headers or bodies does not. 3. **Neither proves anything about the request's contents.** They authenticate the caller, not the parameters, so replacing the secret with something stronger is a change of `token_endpoint_auth_method`, not a change of grant. ## The failures an interviewer listens for - **"We switched to Basic so the secret is encrypted."** Base64 is an encoding, not encryption. Confidentiality comes from TLS on the connection, and from nothing else in either method. - **"The client can send it whichever way suits."** The method is fixed by the registration, so the wrong envelope produces `invalid_client` with a perfectly correct secret in it. - **"Rotation is easy, it is just a string."** It is a string held by two parties, so every caller and the authorization server have to change together, which is what makes a symmetric credential expensive at fleet scale. ## Choosing one, and discovering what is on offer The method is not chosen per request; it is registered. RFC 7591 defines `token_endpoint_auth_method` and the values `client_secret_basic`, `client_secret_post` and `none`, and says that when the field is omitted the default is `client_secret_basic`. The authorization server then enforces the registered method: sending the credential the other way is rejected even though the secret is right. What a given authorization server will accept is published in its metadata document as `token_endpoint_auth_methods_supported`. Reading that field before choosing is the difference between an integration that works and one that discovers the constraint during certification.
- A filing client authenticates fine against one authorization server and gets invalid_client from another with the same credentials. What do you check first?Whether the secret contains a character that form encoding changes. RFC 6749 §2.3.1 requires `client_id` and `client_secret` to be form-urlencoded before the Basic pair is base64-encoded; a client that encodes the raw bytes and a server that expects the encoded ones will disagree only for secrets with reserved characters.
- If the registration never set token_endpoint_auth_method, what will the token endpoint expect?`client_secret_basic`. RFC 7591 defines that as the default when the field is omitted. A client that assumes the body form will be rejected with `invalid_client` even though the secret is correct, because the server enforces the registered method rather than accepting either.
- Does moving from client_secret_post to client_secret_basic make the credential meaningfully harder to steal?Only marginally. Both send the same symmetric value inside the TLS channel on every token request, and both are exposed by whatever logs headers or bodies at a terminating proxy. The real step change is moving to a method where the secret is never transmitted at all.
saying these in an interview costs you the question
- Says client_secret_post is forbidden by RFC 6749
- Thinks Basic encoding provides confidentiality rather than encoding
- Believes the client may pick either method per request
- Assumes the header form hides the secret from a terminating proxy
- Forgets the form-urlencoding step before base64 in Basic
- Thinks omitting token_endpoint_auth_method means no authentication