Access Token
All http requests to HxDR's API must carry a valid access token to be accepted.
The goal of this article is to describe the setup needed for your client to obtain such tokens.
Intro
The tokens we are talking about are standard Oauth 2.0 tokens (opens in a new tab) encoded as Json Web Tokens (opens in a new tab). You are likely familiar with these terms, but if you are not, don't worry: Oauth 2.0 is popular, almost omnipresent, and mature technology. For this reason there are solid libraries (opens in a new tab) for most mainstream programming language and frameworks. This means that when your client code wants to retrieve a token, you just likely will have to configure the library of your choice with the parameters we'll discuss in a minute.
As for a JWT token, here it suffices to say that it is a string generated by HxDR servers, most commonly after a successful login. It conveys information about the permissions granted to the consumer of HxDR's APIs. It must be treated as an opaque string by your code, meant to be consumed by HxDR's API only. HxDR's API servers read it and check it's validity on each request, thus the request will fail if the token is invalid, expired or absent. An Oauth 2.0 JWT token has a short lifespan, is hard to tamper, and has other desirable features; nowadays it is considered best practice to protect public APIs with them.
Multiple invocations to HxDR APIs can carry the same token, as long as it is not expired.
An example of an http request with such a token looks like the following; in there, the token is the string contained in the header authorization: Bearer <jwt token>
, trimmed for clarity.
POST /graphql HTTP/2
Host: hxdr.app
content-type: application/json
authorization: Bearer eyJraWQiOiJ3dUl...1NiJ9
{"query":"{getUser{id}}"}
Setup
As you will see in a moment, there are a few things that HxDR (and you) must configure before your client code can request tokens. Ask Sales @ HxDR (opens in a new tab) if you want to get started.
In the remainder, your client is the Client, while the API are HxDR's APIs.
The entity which authenticates a user and issues tokens is the Authorization Server, and that's provided by HxDR
Note: if the Authorization Server is provided by you, that's a federation, which requires extra setup on the Authorization Servers, outside the scope of this article, but functions the same from the point of view of the Client.
There are two supported scenarios:
- When your Client is consuming HxDR APIs on behalf of a human user, you will ask HxDR to setup a so called Authorization Code Flow. At the beginning of such a flow the user is directed to interact with HxDR to authenticate (e.g. a web form where they will enter their credentials). At the end of the flow, the Client will receive the tokens. From that moment, and during the validity period of the tokens, the Client will be entitled to act on behalf of that user.
- When your Client is invoking HxDR APIs autonomously, without user interaction, you will ask HxDR to set a Client Credentials Flow. This is for machine to machine communication; you Client is able to authenticate by itself, and receives a tokens in exchange. An example of this case is when your services need to run periodic nightly jobs against HxDR APIs. Or when fetching data on behalf of your system, not of a specific user.
Note: there are other OAuth 2.0 flows optionally supported by HxDR, but we favour Authorization Code Flow and Client Credential Flow, which are considered to be the highly secure and currently the best common practice in our situation. We discourage insecure flows even if they are still in use and part of OAuth 2.0, for instance the Password Grant Type (opens in a new tab). Ask HxDR if you need to configure other flows not explicitly listed here.
The Authorization Code flow involves the user, the Authorization Server and the Client, for the latter to obtain the Access Token and use the APIs:
The Authorization Code flow needs the interaction of the Authorization Server and the Client only:
So, what data should you exchange in advance with HxDR so your Client can request tokens to HxDR?
For an Authorization Code flow, this is what we need to setup:
you tell HxDR | HxDR tells you | |
---|---|---|
redirect_uri | x | |
authorization_endpoint | x | |
token_endpoint | x | |
client_id | x | |
client_secret | x | |
scopes | x |
Instead, when you want to setup a Client Credentials flow, there is a bit less configuration involved
HxDR tells you | |
---|---|
token_endpoint | x |
client_id | x |
client_secret | x |
scopes | x |
After those pieces of info have been communicated/generated, they all will be fed to your Client configuration (the library you have chosen).
In the section following this one, a sequence diagram is briefly depicted for each flow, so that if there is any doubt as to what a particular term is, it can be seen in place and in its context.
redirect_uri
: this the endpoint of your Client where you want your user to be redirected after authentication. It doesn't serve any of your business logic, nor has to show any UI to the user. It is part of the machinery needed for you Client to be Oauth 2.0 compliant and, as with almost everything here, your library just needs to know what url you have chosen, and will implement the rest for you.
If you are curious about what the latter serves: your Client (the library) will be listening there to grab the result of the user's authentication (just after they fill in HxDR's web form login, or something equivalent), called the Authorization Code, and will exchange it for a token at the Authorization Server.
The rest of the parameters are even less relevant for the developer, because HxDR provides them to you and you will enter this standard information in your library configuration. So if you are in a hurry, you can stop reading at this point.
authorization_endpoint
: when a user wants to authenticate, your client application will redirect the user to the authorization endpoint. Often it will be a login form.token_endpoint
: at the end of the flow, your client will finally fetch the Access Token there. (A Refresh Token, when previously obtained, can be sent there as well, to obtain another access token. More about refresh tokens in the next section).client_id
: it is a unique identifier of your client associated to all instances of your Clientclient_secret
: it is optional, but strongly advised, in the Authorization Code Flow. If it is used, your Client should be able to keep this secret actually secret. Leaking the secret is no more and no less secure than not having it in the first place. It is mandatory in the Client Credentials Flow.scopes
: they are a set of permissions granted to the Client, modelled as a fixed list of strings; they are case sensitive and multiple scopes are separated with a space. When the Client asks for a token, it also has to specify what permissions wants encoded in the token; those scopes can be all, or a subset, of those scopes configured during the setup; no other scopes are permitted. When invoked via its APIs, HxDR will extract those granted scopes from the token, and act based on those permissions. You'll probably want to configure your Client (library) to request all scopes that HxDR provided to you
Further details
When a flow has started, there is a number of items generated at runtime, tied to that specific instance of the flow. In general, your Client's library and HxDR Authorization Server will handle the generation and delivery of them for you, and you won't have to worry about them.
The following is what your Client library and the HxDR's Authorization Server generate and share with each other during a specific instance of a flow. These are standard elements of OAuth, which are fundamental to the workings of OAuth and interesting to know about, but are outside the scope of this article.
Client | HxDR | generated when: | |
---|---|---|---|
code_verifier | x | at the start of an auth code flow | |
state + nonce | x | idem | |
code_challenge | x | idem (derived from the verifier) | |
authorization code | x | the user authenticated successfully | |
access + refresh token | x | exchanged for an authorization code | |
access token | x | when presented with a refresh token |
The access tokens are involved in both the Authorization Code Flow and Client Credentials flow. The other parameters are generated and exchanged in the Authorization Code flow only.
The only thing to keep in mind is that the items created by your Client (code_verifier
, code_challenge
, state
and nonce
) are optional, but actually recommended. HxDR's Authorization Servers do not reject a flow if they are not there, but those pieces of information mitigate the consequences of having the flow started or intercepted by malicious parties.
Therefore, if your library offers you the option of disabling them, it is better that you do not. The code_verifier
and code_challenge
go under the term of "PKCE", and you want that to be enabled.
Finally, here we summarize into a couple of diagrams how the flows actually flow, just as a reference to check what the names used in this article are referencing in the protocols.
First, the Authorization Code flow, including the optional PKCE (we omit nonce
and state
which are not extremely useful in this context):
At the end of a successful flow the Client will get the Access Token, and the Refresh Token (we'll get to this in a second).
Next, a Client Credential flow. As you can see, getting an Access Token with this flow is drastically simpler than getting the Access Token from an Authorization Code flow:
A difference with the Code Authorization flow is that with the latter you also obtain a Refresh Token. A Refresh Token is another device in standard OAuth 2.0 implementations, meant to allow for longer access to HxDR's API: when an Access Token is expired (and also before) your Client can send the Refresh Token to get a new Access Token, without starting another Authorization Code flow. Again, it is simpler than the Authorization Code flow, being a request-response scenario:
With this, we have covered the basic you need to get your Client configured, and should cover all that you Client needs to get an Access Token and start consuming HxDR's API.
There is more to OAuth 2.0, and if you need to know more about the OAuth 2.0 protocols and what actual messages are exchanged, there are many resources freely available on the internet for a good overview.
Examples
The following examples are presented, again, just to make sure that we are on the same page; in other words, you can use them to keep in check the terms you have been reading so far, which essentially are what OAuth 2.0 expects them to be (are consistent to the http level calls).
If you library is naming things slightly differently in its configuration, it should ultimately use the terms mentioned below in their network calls, you may notice it while debugging.
The token_endpoint
you'll see used below is Amazon's Authorization Servers, but this is not important to our conversation, and subject to change. When setting up a new Client, HxDR will tell you what the current token_endpoint
is.
Client Credentials flow
token_endpoint=https://oauth-dev-demo.auth.eu-west-1.amazoncognito.com/oauth2/token
client_id=7s1r3j4...bdf7il
client_secret=9o0q01...uoso5
redirect_uri=https://client.app/the/callback/uri
scopes="a_custom_scope and_another_one"
# POST
curl \
--data "grant_type=client_credentials" \
--data "client_id=$client_id" \
--data "client_secret=$client_secret" \
--data "scope=$scopes" \
"$token_endpoint"
Result
{
"access_token": "eyJraWQi...vO3QeQ",
"expires_in": 3600,
"token_type": "Bearer"
}
Authorization Code flow
After the user has authenticated, the Client Application gets the Authorization Code via a call to its redirect_uri
and uses the Code like so:
token_endpoint=https://oauth-dev-demo.auth.eu-west-1.amazoncognito.com/oauth2/token
client_id=7s1r3j4...bdf7il
client_secret=9o0q01...uoso5
redirect_uri=https://client.app/the/callback/uri
scopes="a_custom_scope and_another_one"
# POST
curl \
--data "grant_type=authorization_code" \
--data "code=$authorization_code" \
--data "client_id=$client_id" \
--data "client_secret=$client_secret" \
--data "scope=$scopes" \
--data "redirect_uri=$redirect_uri" \
"$token_endpoint"
The response contains both the Authentication Token and the Refresh Token:
{
"access_token": "eyJraWQ...72Hmw",
"refresh_token": "eyJjdHk...4dq-Q",
"expires_in": 3600,
"token_type": "Bearer"
}
Regarding the PKCE, there is not a lot to to add, from the point of view of a developer. The code_verifier
must be secure (i.e. hard to guess), but you'll library will likely create that for you.
The user has to be directed by the Client Application to the authorization_endpoint
including the code_challenge
query parameter.
For instance it can have generated the pair looking like the following:
code_verifier=ZmE1Y2ZkNjktMzBmNS00ZjkxLTg0MDEtNzU3Y2FhYWVhM2UzNGU5MDZjMTMtNzA0Ny00MWE2LTkxY2UtYWIxNDEwZjNjYWE1
code_challenge=LebQV3kC2S2CWoNHWHkgPNOgenCH5m8N5dZCulnlfh4
So the flow is initiated with the Client Application redirecting the user to a url like:
GET https://authorization.serve.r/authorization?
response_type=code&
client_id=7s1r3...7bdf7il&
redirect_uri=http://client.application/callback&
scope=the scopes&
code_challenge=LebQV3kC2...lfh4&
code_challenge_method=S256
The Client Application will end up getting the Authorization Code as always.
But then it exchanges it and the code_verifier
(the one relevant for that user session) with the Authorization Server, which will perform the validation. Overall, the request to the Authorization Server is augmented with the code verifier.
# POST
curl \
--data "grant_type=authorization_code" \
--data "code=$authorization_code" \
--data "client_id=$client_id" \
--data "client_secret=$client_secret" \
--data "scope=$scopes" \
--data "redirect_uri=$redirect_uri" \
--data "code_verifier=$code_verifier" \
"$token_endpoint"
Refresh Token flow
# POST request
token_endpoint=https://oauth-dev-demo.auth.eu-west-1.amazoncognito.com/oauth2/token
client_id=7s1r3j4...bdf7il
client_secret=9o0q01...uoso5
refresh_token=eyJjdH...LFQCCrO7DpFb3z55ZWmCnVTrPL1a.LD5VZy0HzoWdAnFKH4dq-Q
curl -s \
--data "grant_type=refresh_token" \
--data "client_id=$client_id" \
--data "client_secret=$client_secret" \
--data "refresh_token=$refresh_token" \
"$token_endpoint"
Result
{
"access_token": "eyJr...BzAh7hgQ",
"expires_in": 3600,
"token_type": "Bearer"
}
JS-SDK reference
Auth
Identity authentication