Search code examples
androidretrofit2okhttp

Avoid '@' char getting url encoded during POST @FormUrlEncoded request


I've been busting my head for about two days now but neither I nor my colleagues seem to be able to disable URL encoding for a client_secret field in a @POST request when using Retrofit. We need to fetch an access token from an API endpoint that requires the following parameters in application/x-www-form-urlencoded format:

grant_type: "client_credentials"
scope: "CustomerService.WebApi"
client_id: "somerandomid"
client_secret: "XX@XXXXXXXXXXXXX"

The issue occurs when the '@' char in the client_secret field gets URL-encoded into %40 and our client's backend can't handle it. Using Postman, this encoding doesn't seem to happen and we successfully get a hit.

Here's what we have tried so far:

  • Specifying encoded=true inside the @Field/@FieldMap annotation
  • Removing the @FormUrlEncoded annotation and specifying it manually in the header while using a RequestBody as an In parameter and building the request body using a FormBuilder
  • Using both @FieldMap and @Field annotations but none of them worked
  • Setting disableHtmlEscaping() to the Gson instance used by the retrofit client

I've been looking at tons of stack posts and git issues but there doesn't seem to be a fix for POST requests. Some people blame the OkHTTP client, some Gson, and some Retrofit 2. JakeWharton advocates that Retrofit's test regarding the encoded=true flag does indeed work. Does anybody have a clue on how to resolve this?

P.S. Please do not mark this as a duplicate if the associated post doesn't have a valid answer (like most of the posts I've looked at).


Solution

  • OkHttp (and therefore Retrofit) follows this spec that requires @ to be percent-escaped when encoded. I expect the same behavior is true of web browsers: they will also encode the @ character.

    You should direct your server’s maintainers to conform to the corresponding parsing spec. If they cannot, you may need to manually replace %40 with @ on your outbound request body, perhaps with an OkHttp interceptor.