satray
satray

Reputation: 63

Authenticating to an API with a token

I'm working with the Zendesk API, an HTTPS-only, JSON API and authentication is required to update a resource, and the API supplies an API token to use when authenticating as different users. When updating a resource, I issue a PUT request over SSL, assign the appropriate JSON content to the request body, and specify the Content-Type request header as application/json.

Next, the API instructs its users to authenticate as the end-user by either using the user's email and password (which I can't do for several reasons) or to use the user's email address along with the API token. The following is my attempt to authorize to the API with the Authorization header:

@id = params[:id]
@comment_body = params[:comment]

uri = URI.parse "https://{subdomain}.zendesk.com/api/v2/requests/#{@id}.json"
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
req = Net::HTTP::Put.new(uri.request_uri)
req.body = '{"request": {"comment":{"value":' + "\"#{@comment_body}\"" + '}}}'
req['Content-Type'] = 'application/json'

#The following two lines do not work!
credentials = Base64::encode64("{user_email}/token:{api_token}")
request.headers['Authorization'] = "Basic #{credentials}"

response = http.request(req)

The API specifies that the format for authentication using the API token is {user_email}/token:{api_token}. I encoded that format with Base64::encode64 and passed it to the Authorization Header preceded with Basic, but the response is a 401 Unauthorized. However, replacing those two lines with req.basic_auth {user_email}, {user_password} works fine.

So my question is, how can I authenticate as a different user using the email and the given API token as authentication instead of supplying the user's email and password to req.basic_auth?

The googling I've done on the topic has revealed very little; apparently it's a lot more common to use the normal {username}:{password} format when dealing with the Authorization header than an API token.

Thanks in advance!!

Update: Weirdly, trying to authenticate as the end-user with req['Authorization'] = "Basic #{credentials}" does not return a 401 Unauthorized Error or a WWW-Authenticate header while trying to authorize as request.headers['Authorize'] = "Basic #{credentials}" does.

Upvotes: 1

Views: 2186

Answers (1)

satray
satray

Reputation: 63

Finally figured it out after much head-banging and nearly throwing my laptop out the window. Suddenly, the answer seems incredibly obvious.

When using Net::HTTP, its basic_auth method can also accept tokens depending on the API, and the Zendesk API specifies that the format for using the API token is {email}/token:{token}. Basic authentication uses the format {username}:{password}, where the two fields are separated by a colon, meaning in Zendesk's case I can place {email}/token as the first argument and {token} as the second argument (instead of the username as the first argument and the password as the second argument), so the following code is correct:

req.basic_auth "{email}/token", "{api_token}"

I hope anyone who found this useful could leave a comment. Nice to know I spared someone from this frustration.

Upvotes: 4

Related Questions