Introduction
Welcome to the Open Ticket API documentation. This site explains some important, overarching concepts use by our API. After you have read this information, you can find a list of available endpoints and detailed documentation for each in our Swagger Docs .
Authentication
Open Ticket uses OAuth2 for authentication using the Authorization Code grant. This grant type ensures no user credentials are shared with the client owner, while still allowing the owner’s application(s) to access the API.
It is expected of the reader to be familiar with the basic user-company-whitelabel structure used by Openticket and OAuth. This document only provides a bare-bones overview on how to connect to the Openticket services.
For this description we have the following definitions:
- User: a user of the Openticket system(s).
- Application: the system requesting a token.
- Authorization url: the url where the user will be directed to after granting/declining access to the application.
Requesting tokens in principle is a very simple and straightforward process consisting of the following steps:
- Request access to a user’s account by redirecting the user to a specific endpoint. Redirect to
https://auth.openticket.tech/token/authorize
) - Wait for a callback containing a nonce.
- Use the provided nonce to request a token. Request at
https://auth.openticket.tech/token
- Use the issued tokens to place requests.
It is assumed the application which needs access to the Openticket systems can store the issued access (and refresh) tokens securely!
Scopes
Currently, scopes are unused, any token can be used to access any service. Scopes will be introduced in the future to enable a more nuanced approach to requesting access. Tokens issued are likely to remain valid after this change. When the introduction of a scope will invalidate tokens, affected client owners will be contacted.
Creating a client
An OAuth client is required when seting up a connection to the Openticket systems. Creating clients is a simple process. Open the dashboard and navigate to: Company settings -> OAuth Clients
. Only two fields are required:
- Name: A descriptive name will be presented to the user.
- Redirect URL: The URL the user will be redirected to after approving or declining access.
After providing these two fields, a set of credentials will be given. An identifier, and a secret. Note, ensure that the secret is actually kept secret and is never made public, this includes keeping the secrit out of git repositories. Any token issued by a client having a leaked secret will be invalidated!
These credentials will be needed to request a token.
Requesting a token
As stated previously, requesting a token consists of a few steps:
- Requesting authorization to access a users’s account.
- Receive a nonce, which can be used to request a token (once).
- Send some authenticated requests.
- When the token expires, request further tokens when needed.
Each of these 4 steps will be described seperately,
Requesting authorization
Redirecting the user, do not forget to set/replace all settings accordingly
// Make sure state is actually usable and dynamically generated, not a constant!
var state = "RANDOM STRING"
http.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) {
uri := fmt.Sprintf("https://auth.openticket.tech/token/authorize?client_id=%v&redirect_url=%v&state=%v&response_type=code",
os.Getenv("OAUTH_CLIENT_ID"),
os.Getenv("OAUTH_CLIENT_REDIRECT"),
state)
http.Redirect(w, r, uri, http.StatusSeeOther)
})
http.ListenAndServe(":8080", nil)
curl -XGET "https://auth.openticket.tech/token/authorize?client_id=$OAUTH_CLIENT_ID&redirect_uri=$OAUTH_CLIENT_REDIRECT&response_type=code&state=RANDOM_STRING"
// Using Laravel
Route::get('/redirect', function (Request $request) {
$request->session()->put('state', $state = Str::random(40));
return redirect('https://auth.openticket.tech/token/authorize?' . http_build_query([
'client_id' => env('OAUTH_CLIENT_ID', ''),
'redirect_uri' => env('OAUTH_CLIENT_REDIRECT', ''),
'response_type' => 'code',
'state' => $state,
]));
});
To start the process of connecting your application to users, the following information is needed:
- OAuth client identifier: as provided after creating the OAuth client.
- OAuth client redirect url: as setup when creating the OAuth client.
- State: A (random) identifier, your application uses to identify the response. When redirecting this state is included.
To start the request, issue a redirect to https://auth.openticket.tech/token
. Examples on how to redirect are provided on the right.
Handling authorization response
// Using Laravel
Route::get('/callback', function (Request $request) {
$state = $request->session()->pull('state');
throw_unless(
strlen($state) > 0 && $state === $request->state,
InvalidArgumentException::class
);
$response = (new GuzzleHttp\Client)->post('https://auth.openticket.tech/token', [
'form_params' => [
'grant_type' => 'authorization_code',
'client_id' => env('OAUTH_CLIENT_ID', ''),
'client_secret' => env('OAUTH_CLIENT_SECRET', ''),
'redirect_uri' => env('OAUTH_CLIENT_REDIRECT', ''),
'code' => $request->code,
],
]);
return json_decode((string) $response->getBody(), true);
});
# Shells do not receive requests. Nothing to do here.
http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
resp, _ := http.PostForm("https://auth.openticket.tech/token", url.Values{
"grant_type": {"authorization_code"},
"client_id": {os.Getenv("OAUTH_CLIENT_ID")},
"client_secret": {os.Getenv("OAUTH_CLIENT_SECRET")},
"redirect_url": {os.Getenv("OAUTH_CLIENT_REDIRECT")},
"code": r.URL.Query()["code"],
})
io.Copy(w, resp.Body)
})
http.ListenAndServe(":8080", nil)
Resulting in the following response
{
"token_type": "Bearer",
"expires_in": 259200,
"access_token": "THE_ACTUAL_TOKEN",
"refresh_token": "REFRESH_TOKEN",
"refresh_token_expires_in": 31535999,
"info": {
...
}
}
Info contains information on the user, the company, and the whitelabel. The user info also includes the roles of this user
After the user has authenticated and either approved or declined your application accessing the data, your callback url will be called with the following query parameters:
code
: the authorization code that can be used to request a tokenstate
: the same value you included with the request tohttps://auth.openticket.tech/token/authorize
code
is not a token, and cannot be used to authenticate requests to the Openticket systems! It can only be used to request a token! Note, each code
can only be used once and expires quickly. To request a new token for a previously authorized user, a different request exists which is described below.
A token can be requested, by crafting a request containing the code
parameter and the OAuth Client credentials. When issueing a token, not only the token is returned, but also information on the user associated with the token is returned. Not only user information, but also information on the user’s company and whitelabel are included.
Token response
A token contains the following fields:
token_type
(string): The type of token issued, needed when using theaccess_token
to authenticate requests.access_token
(string): The actual access token, which can be used to authenticate requestsexpires_in
(uint): The time-to-live (TTL) in seconds of theaccess_token
.refresh_token
(string): A token which can be used to request newaccess_token
s.refresh_token_expires_in
(uint): The time-to-live (TTL) in seconds of therefresh_token
.info
(object):user
(object): Information on the user.roles
(array): The names of the roles of the user.company
(object): Information on the company of the user.whitelabel
(object): Information on the whitelabel of the company.
As can be seen from the expires_in
and refresh_token_expires_in
fields, neither the access_token
nor the refresh_token
are valid indefinetly, but expire at some point. In general two statements on these TTLs will hold:
refresh_token_expires_in
is much greater thanexpires_in
, to ensure access to the Openticket systems is maintained even when access is only incidental.- The TTL’s are constant.
Though these are true in general, they cannot be relied upon to hold forever. When needed, these values can be changed without any prior notice. Though TTLs will not be changed retroactively for previously issued tokens.
Issuing requests
curl -H "Authorization: Bearer $ACCESS_TOKEN" https://auth.openticket.tech/user/me
// Using a guzzle client
$response = $client->request('GET', 'https://auth.openticket.tech/user/me', [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
],
]);
echo $response->getBody();
r, _ := http.NewRequest(http.MethodGet, "https://auth.openticket.tech/user/me", nil)
r.Header["Authorization"] = []string{"Bearer: " + accessToken}
resp, _ := http.DefaultClient.Do(r)
bts, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(bts))
Resulting in the following response
{
"guid": "6e26d618-354b-11eb-9322-acde48001122",
"default_company_id": "6eac75a2-354b-11eb-9322-acde48001122",
"whitelabel_id": "6eea7dc0-354b-11eb-9322-acde48001122",
"name": "Mart Pluijmaekers",
"email": "[email protected]",
"phone": null,
"created_at": "2011-12-13T11:12:13+02:00",
"updated_at": "2020-12-13T14:15:16+01:00",
"deleted_at": null
}
Using the access_token
retrieved in the previous request, requests can be authenticated by including a header called Authorization
. The contents of this header should contain the token type (“Bearer”) and the access_token
separated by a single space.
Almost all requests require the authorization header. All available API’s and endpoints can be found here .
When a token is (no longer) valid, the API should respond with a 401 status code.
Refreshing a token
// Using Laravel
$refreshToken = $request->session()->pull('refresh_token');
$response = (new GuzzleHttp\Client)->post('https://auth.openticket.tech/token', [
'form_params' => [
'grant_type' => 'refresh_token',
'refresh_token' => $refreshToken,
'client_id' => env('OAUTH_CLIENT_ID', ''),
'client_secret' => env('OAUTH_CLIENT_SECRET', ''),
],
]);
echo $response->getBody();
curl -XPOST -F "grant_type=refresh_token" -F "refresh_token=$REFRESH_TOKEN" -F "client_id=$OAUTH_CLIENT_ID" -F "client_secret=$OAUTH_CLIENT_SECRET" https://auth.openticket.tech/token
resp, _ := http.PostForm("https://auth.openticket.tech/token", url.Values{
"grant_type": {"refresh_token"},
"refresh_token": {refreshToken},
"client_id": {os.Getenv("OAUTH_CLIENT_ID")},
"client_secret": {os.Getenv("OAUTH_CLIENT_SECRET")},
})
bts, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(bts))
Resulting in the following response
{
"token_type": "Bearer",
"expires_in": 259200,
"access_token": "NEW_ACCESS_TOKEN",
"refresh_token": "NEW_REFRESH_TOKEN",
"refresh_token_expires_in": 31535999,
"info": {
...
}
}
Access tokens expire, by default, after three days. When a token has expired it can no longer be used. To reduce the need for re-authorizing a refresh_token
is provided with the access_token
. refresh_token
s can only be used to request new access_token
s, not to access the services! By default, a refresh_token
expires in 365 days.
When an access_token
has expired, a new token can be requested using the refresh_token
grant-type. When requesting a new token using the refresh token grant type a new access and refresh token are issued. A refresh_token
can only be used once!.
The response structure is equal to requesting a token using the authorization_code
grant.
Notes
Openticket imposes some limits on tokens:
- The total number of tokens per user, per client is limited. When this limit has been reached, the token which has not been used for the longest time is invalidated.
- A token currently can be used for at most one company, and switching to different companies is (and will) not be implemented. In the future, tokens can provide access to multiple companies, but this will only hold for tokens issued after this change has been made available.
- As stated before, secrets must be kept private. Consequently, pure Javascript implementations that do not rely on a server implementation are not allowed!
- Tokens are personal and must be kept private, sharing of tokens is not allowed. Leaked tokens will be invalidated!
- Currently it is impossible to request a (short-lived) token to test applications without having to fully implement an OAuth flow, this feature is being developed and will be deployed.