Question

Authorization not found for LookApi

  • 8 March 2017
  • 5 replies
  • 716 views

I’m using the swagger-built SDK for python. I was able to get the authorization step to work well enough to get the access token, but so far the only command I’ve been able to execute with it was a simple curl GET for ‘user’, which is not what I want. What I want is to get the data from a look.


I’m trying to do something like this, where the api_client object has the access token in the default_headers:


connect = looker.LookApi(api_client=api_client)

look_id=00000 #actual number redacted

connect.look(look_id=look_id)


but I keep getting a ‘requires authorization’ error, even though the docs say ‘No authorization required’ (which clearly makes no sense, since our instance is behind a ton of security).


What would really help a lot would be better instructions and docstrings. I can’t find any information about how the API should be used, and the docstrings are not particularly helpful re: content or format of expected args.


5 replies

Userlevel 3
Badge

Hey there @szeitlin! You’re really close here, but you’re actually a few steps shy of authenticating to the API, which is why you’re getting the error. I’ll let Engineering know that the docstrings weren’t super helpful. Our Swagger-built SDK lacks some of the more helpful features of the Ruby SDK, but I think even in the Ruby SDK, these strings are pretty barebones.


Anyways, for an example of how to authenticate to the API in Python, see this example from Scott’s original guide:


# preliminaries
import swagger_client as looker
dir(looker) # to see available methods
base_url = 'your_looker_api_3_endpoint'
client_id = 'your_api_3_id'
client_secret = 'your_api_3_secret'

# instantiate Auth API
unauthenticated_client = looker.ApiClient(base_url)
unauthenticated_authApi = looker.ApiAuthApi(unauthenticated_client)

# authenticate client
token = unauthenticated_authApi.login(client_id=client_id, client_secret=client_secret)
client = looker.ApiClient(base_url, 'Authorization', 'token ' + token.access_token)

# instantiate Look API
looks = looker.LookApi(client)

# run look by its id
looks.run_look(111, 'json')

There are a few extra lines required to authenticate than what you have here. Let me know if this is helpful, and if you have any more issues, I’ll try to check this thread for replies throughout the day.

Nope. That’s almost exactly what I was doing, and I tried it with your minor modifications. So let me be clearer: this is not working. It may be something about the way our security is configured, but I don’t know how to work around it.


Here is my complete code (with company details redacted):


import os
import swagger_client as looker
from swagger_client.rest import ApiException

class Authenticate:
def __init__(self, id=None, secret=None):
if id is None:
self.client_id = os.environ.get("LOOKER_CLIENT_ID")
else:
self.client_id=id
if secret is None:
self.client_secret = os.environ.get("LOOKER_CLIENT_SECRET")
else:
self.client_secret=secret
self.base_url='https://looker.xxx.com:19999/api/3.0/'
self.token = self.get_token()

def get_token(self):
raw_client = looker.ApiClient(self.base_url)
raw_authApi = looker.ApiAuthApi(raw_client)
try:
token = raw_authApi.login(client_id=self.client_id,
client_secret=self.client_secret)
return token
except ApiException as e:
print("authentication failed: {}".format(e))

Then I have unit tests that demonstrate I’m able to get the access_token, so this is all working as expected. (another thing that would help me understand your API better would be more unit tests).


from looker_data_methods import Authenticate

authApi = Authenticate(id='xxx', secret='xxx)
api_client = looker.ApiClient(authApi.base_url, 'Authorization', 'token' + authApi.token.access_token)
connect = looker.LookApi(api_client=api_client)
look_id=xxxx
connect.run_look(look_id, 'json')

And here’s what I get back (the traceback is no help whatsoever):


ApiException: (401)
Reason: Unauthorized
HTTP response headers: HTTPHeaderDict({'X-Content-Type-Options': 'nosniff', 'Date': 'Thu, 09 Mar 2017 17:28:03 GMT', 'Server': 'nginx/1.10.1', 'Content-Length': '84', 'Vary': 'Accept-Encoding', 'Connection': 'keep-alive', 'Content-Type': 'application/json;charset=utf-8'})
HTTP response body: {"message":"Requires authentication.","documentation_url":"http://docs.looker.com/"}
Userlevel 2

Hi Sam,


There needs to be a space between the word token and the access_token characters. Something like this:


api_client = looker.ApiClient(authApi.base_url, 'Authorization', 'token ' + authApi.token.access_token)

Give that a try and let us know how it turns out!


-Danny

Thanks, that worked. FWIW This format is not consistent with any REST API I’ve ever seen (trailing whitespace of any kind is generally considered poor coding style in python, and tokens typically don’t require the word 'token ’ in front of them).


It would be easier (and much simpler for the user) if the ApiClient object formats the strings the way your service expects, i.e. there should be a ‘token’ keyword arg that takes the token value directly. But in the interim, this should be documented somewhere more clearly (this is where better docstrings would help a lot), something like:


:param: base_url (str)

:param: keyword (str)

:param: value including label, delimited with a space (str)


:return: ApiClient object with x,y,z #please list how to tell if this worked correctly, because all along I was getting a token, but I couldn’t use it because it was missing 'token ’ at the beginning of it-?





ApiClient(“http://xxx.looker.com”, “Authorization”, “token xxxxx”)




Userlevel 2

Hi Sam,


Glad that info got you going.


Use of a credential type descriptor along with the credential bytes is a common practice in HTTP requests. https://security.stackexchange.com/a/120244/45882


We definitely have room for improvement in our API documentation. We rely on a mix of API doc generated from source code (doc comments) intended primarily as structural reference and narrative articles to introduce concepts and best practices, provide tutorial content, and take deep dives into topics that just aren’t feasible for doc comment generated API reference. I will be adding more links from the API doc to articles with more depth residing on Looker.com as we flesh those out.


The best source of info about the Looker API are the API reference endpoint running on your Looker instance (https://yourserver.looker.com:19999/api-docs/index.html) and the API reference portion of the docs on Looker.com. Here’s the page with details on how Looker API auth works: https://looker.com/docs/reference/api-and-integration/api-auth


I also acknowledge that getting started with the Looker API isn’t as simple as we would like. One thing we can do to vastly simplify your life is provide prebuilt Looker API client SDKs for popular languages and tools, and make them available via popular repositories. These prepackaged SDKs would have the per-language “glue” code like authorization bindings baked in.


Our first step in this direction is the publication of the Looker Ruby client SDK on rubygems.org!


More coming soon! 😄


-Danny

Reply