Hi Developer Community,
Some of you who have built integrations with our REST API may have noticed that intermittently, when you hit the 'Allow/Deny' screen, the page loads without any static assets/styling:
This is the expected response:
And further to that, when this is the case: you cannot progress in the authorization 'flow'. The remedial action is to log out of all active sessions of the HighQ product, in your browser. The authorization then usually works. This has led a lot of you to believe this is a deliberate decision on our side to somehow stop users from authenticating 'in' an API integration as well as being logged in as a standard user. That's not the case and hopefully I can lay out here how you can configure your applications to more reliably authenticate against our service.
A Note On Terminology
Generally speaking, we refer to the 'base url' and then the 'context name': www.yourcompany.highq.com/instance
'yourcompany.highq.com' = base
'/instance' = context name
A Note on our URLS
The web development framework which underpins the main application, deal in 'actions', with regards to what happens server-side. I am sure you are all familiar seeing '.action?' in your Collaborate or Publisher URLs. Once the base of the url and its context name is valid, all the server-side 'sees', as it were, is the '.action?' portion of the url.
This example is me hitting the handler for a wiki page on a support test instance we have. Note the base and the context and then immediately after those we have the '.action' part 'wikiHome.action': 
The URL above has no path in between the action and the base/context name: we are fetching the static resources from the correct location. For the purposes of this post, lets call that the 'static root'.
If I were to then add in something arbitrary like this: 
We get, effectively, get the same thing from the server-side code, as it only cares about 'wikiHome.action', but your browser is now trying to fetch the static resources from a folder named 'anything' relative to the 'static root' and the result is this: 
We've forced the same issue which can occur on the authorization 'allow/deny' page, on this wiki page!
So how is this occuring when an API integration attempts to authorize against the service?
The OAuth2 authorization code flow is ordinarily configured with different URL namespaces for code generation and token generation. Getting the authorization code (because it requires user interaction, i.e that allow/deny screen), is not commonly considered part of the API namespace, whilst generating a token is. One returns a webpage, the other json or xml...
So we expect you to authorize against: www.yourcompany.highq.com/instance/authorize.action
And then make API calls against a 'base' of: www.yourcompany.highq.com/instance/api/.....
However, we often see that integrators configure a single base url for our APIs. Usually something like: 'www.yourocmpany.highq.com/instance/api' and then the authorization url is assumed to be an extension of that. If you have set up your integration this way, when your users hit an 'authorize/login/connect/whatever...' button, they are usually then directed to URLS like:
'www.yourcompany.highq.com/instance/api/authorize.action'
or
'www.yourcompany.highq.com/instance/api/oauth2/authorize.action'
That's when the problem described above happens: We pass you to our OAuth handlers and redirect you to the authorization screen, but the static assets are missing and ultimately the process cannot be completed.
Why does it work when a user doesn't have an active session in their browser?
This is because in this scenario, rather than redirect you straight to the OAuth handler, we need the user to authenticate first. We send the user to our login handler and then we return them to the correct location.
TLDR:
Your integrations should have configured a separate endpoint for your oauth token calls and the authorization code calls.
Here is an example of what the config saved via a web form, might look like:
Here is an example of some Python code from a Django app I have, which reads from an environment variable:
import requestsfrom allauth.socialaccount.providers.oauth2.views import (OAuth2Adapter, OAuth2LoginView, OAuth2CallbackView)from .provider import CustomProviderfrom django.conf import settingsclass CustomAdapter(OAuth2Adapter): provider_id = CustomProvider.id access_token_url = f'{settings.OAUTH_SERVER_BASEURL}/api/oauth2/token' profile_url = f'{settings.OAUTH_SERVER_BASEURL}/api/9/users/' authorize_url = f'{settings.OAUTH_SERVER_BASEURL}/supportuat/authorize.action' def complete_login(self, request, app, token, **kwargs): headers = {'Authorization': f'Bearer {token.token}', 'Accept':'application/json'} useremail = kwargs['response']['useremail'
Obviosuly each one of those examples handles the process differently, but in either, it should be clear that there is an authorization url which doesnt have '/api/' in its path!