GraphQL and REST APIs
In order to access the DRIVR GraphQL and REST APIs the requests need to be authenticated. Once a bearer token has been obtained during one of the flows below they can be used for authentication against our REST and GraphQL APIs.
The different authentication flows are available based on the configurations made on the Domain
level within the DomainAuthenticationMethods
and on an application level by setting up ApplicationConsumers
.
Should you not yet be familiar with these concepts, please have a look at the two previous chapters.
All authentication endpoints and other open-id configuration can be retrieved via:
https://{slug}.api.drivr.cloud/.well-known/openid-configuration.
Please, replace {slug}
in the URL with the one from your own Domain
.
Currently, the following forms of authentication are supported. In general if the client_id
/client_secret
should be provided that can either be provided as request parameters client_id
and client_secret
or base64-encoded in the HTTP Basic auth header.
The Authorization Code Grant is an OAuth 2.0 authentication flow that allows users to securely authorize applications to access their resources or perform actions on their behalf. This grant type is primarily used for server-side applications that have the ability to securely store client secrets.
To setup this flow for your application create an ApplicationConsumer
with AUTHORIZATION_CODE
grantType
like follows.
Adding the grantType
for REFRESH_TOKEN
is optional and will enable/disable the issuance of refresh_tokens
together with the access_token
.
The same configuration can be done within the DRIVR-UI for your Domain
. Please replace {slug}
in the following link with the one of your DRIVR instance. https://{slug}.ui.drivr.cloud/#/en/domain/application-consumers?limit=25.
mutation createApplicationConsumer {
createApplicationConsumer(
name: "my-new-app",
slug: "my-app",
defaultRedirectUri: "http://localhost:8080/auth/callback",
redirectUris: [
"http://localhost:8080/auth/callback",
"http://localhost:8080/auth/another-callback"
],
secret: "nuq1u6k4nWSNgrPEDbHXTqWN4APERZ8X1LPcY9Hov7gHnPcfD1hcNhmMOzoYkAVs",
grantTypes: [
AUTHORIZATION_CODE,
REFRESH_TOKEN
],
scopes: [
"profile",
"email"
]
) {
uuid
identifier
secret
defaultRedirectUri
redirectUris
status
grantTypes
name
slug
scopes
}
}
Response
{
"data": {
"createApplicationConsumer": {
"uuid": "3bd72e78-1f23-2781-8b65-b84e4a2a4765",
"identifier": "my-app.localhost",
"secret": "nuq1u6k4nWSNgrPEDbHXTqWN4APERZ8X1LPcY9Hov7gHnPcfD1hcNhmMOzoYkAVs",
"defaultRedirectUri": "http://localhost:8080/auth/callback",
"redirectUris": [
"http://localhost:8080/auth/callback",
"http://localhost:8080/auth/another-callback"
],
"status": "ACTIVATED",
"grantTypes": [
"AUTHORIZATION_CODE",
"REFRESH_TOKEN"
],
"name": "my-new-app",
"slug": "my-app",
"scopes": [
"profile",
"email"
]
}
}
}
If the authentication request will be initiated the following scenarios can happen depending on the configuration of the DomainAuthenticationMethods
.
- If a
Domain
has multipleDomainAuthenticationMethods
the DRIVR Login UI will be displayed to select an authentication method hence, a redirect to https://{slug}.api.drivr.cloud/login with the same parameters will happen before the actual authentication request. The DRIVR Login UI will automatically adjust and displays allDomainAuthenticationMethods
which areACTIVATED
. - If only one external
DomainAuthenticationMethod
(e.g. Google or AzureAD) is setup the User will be redirected to these providers directly. - Using the DRIVR internal
DomainAuthenticationMethod
(e.g. default) will always display the DRIVR Login UI to prompt the User to login with their DRIVRusername
andpassword
.
The following examples show how to integrate this flow into your own application using different programming languages.
Python
# pip install authlib flask
from flask import Flask
from authlib.integrations.flask_client import OAuth
BASE_DRIVR_AUTH_URL: str = "https://{slug}.api.drivr.cloud"
APP_NAME: str = "my-app"
CLIENT_ID: str = "my-app.localhost"
CLIENT_SECRET: str = "nuq1u6k4nWSNgrPEDbHXTqWN4APERZ8X1LPcY9Hov7gHnPcfD1hcNhmMOzoYkAVs"
DRIVR_OAUTH_PARAMS: dict[str, str] = {
"api_base_url": BASE_DRIVR_AUTH_URL,
"authorize_url": f"{BASE_DRIVR_AUTH_URL}/authenticate",
"access_token_url": f"{BASE_DRIVR_AUTH_URL}/authenticate/token",
"refresh_token_url": f"{BASE_DRIVR_AUTH_URL}/authenticate/token",
"jwks_uri": f"{BASE_DRIVR_AUTH_URL}/authenticate/keys",
"userinfo_endpoint": f"{BASE_DRIVR_AUTH_URL}/authenticate/userinfo",
"client_kwargs": {"scope": "email profile"},
}
app = Flask()
oauth = OAuth()
oauth_client.init_app(app)
oauth_client.register(
name=APP_NAME,
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
**DRIVR_OAUTH_PARAMS
)
# Redirect the user to DRIVR for authentication.
# When completed, the User will be redirected back to the application at:
# /auth/callback
from flask import url_for, redirect
@app.route('/auth')
def login():
redirect_uri = url_for('auth.callback', _external=True)
return oauth_client.authorize_redirect(redirect_uri)
@app.route('/auth/callback')
def authorize():
token = oauth_client.authorize_access_token()
# do something with the token
return redirect('/')
Javascript
# https://www.passportjs.org/packages/passport-oauth2/
const BASE_DRIVR_AUTH_URL = "https://{slug}.api.drivr.cloud"
const CLIENT_ID = "my-app.localhost"
const CLIENT_SECRET = "nuq1u6k4nWSNgrPEDbHXTqWN4APERZ8X1LPcY9Hov7gHnPcfD1hcNhmMOzoYkAVs"
const REDIRECT_URL = "http://localhost:8080/auth/callback"
passport.use(new OAuth2Strategy({
authorizationURL: `${BASE_DRIVR_AUTH_URL}/authenticate`,
tokenURL: `${BASE_DRIVR_AUTH_URL}/authenticate/token`,
clientID: CLIENT_ID,
clientSecret: CLIENT_SECRET,
callbackURL: REDIRECT_URL
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({ exampleId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
// Redirect the user to DRIVR for authentication.
// When completed, the User will be redirected back to the application at:
// /auth/callback
app.get('/auth',
passport.authenticate('oauth2'));
app.get('/auth/callback',
passport.authenticate('oauth2', { failureRedirect: '/auth/failure' }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect('/');
});
The Resource Owner Password Credentials flow is an OAuth 2.0 authentication flow that allows clients to directly exchange the user's credentials for an access_token
.
In this flow, the client application requests the user's username and password, and then exchanges them for an access_token
from the authorization server.
To setup this flow for your application create an ApplicationConsumer
with PASSWORD
grantType
like follows:
The same configuration can be done within the DRIVR-UI for your Domain
. Please replace {slug}
in the following link with the one of your DRIVR instance. https://{slug}.ui.drivr.cloud/#/en/domain/application-consumers?limit=25.
mutation createApplicationConsumer {
createApplicationConsumer(
name: "my-new-app",
slug: "my-app",
secret: "nuq1u6k4nWSNgrPEDbHXTqWN4APERZ8X1LPcY9Hov7gHnPcfD1hcNhmMOzoYkAVs",
grantTypes: [
PASSWORD
],
scopes: [
"profile",
"email"
]
) {
uuid
identifier
secret
status
grantTypes
name
slug
scopes
}
}
Response
{
"data": {
"createApplicationConsumer": {
"uuid": "3bd72e78-1f23-2781-8b65-b84e4a2a4765",
"identifier": "my-app.localhost",
"secret": "nuq1u6k4nWSNgrPEDbHXTqWN4APERZ8X1LPcY9Hov7gHnPcfD1hcNhmMOzoYkAVs",
"status": "ACTIVATED",
"grantTypes": [
"PASSWORD"
],
"name": "my-new-app",
"slug": "my-app",
"scopes": [
"profile",
"email"
]
}
}
}
The following examples shows a request to use this flow.
POST /authenticate/token HTTP/1.1
Host: https://{slug}.api.drivr.cloud
Content-type: application/x-www-form-urlencoded
grant_type=password
&username=exampleuser
&password=1234luggage
&client_id=my-app.localhost
The OpenID delegated authentication method
is a way to authenticate users in DRIVR using OpenID tokens obtained from trusted third-party authentication providers.
With this method, users can login to DRIVR using their existing credentials from another service (e.g. Google or Azure AD) that supports OpenID authentication.
A new OpenID delegated DomainAuthenticationMethod
needs to be created with a clientId
and an issuerURL
.
The clientId
is the OAuth application's client ID, and the issuer URL is the OpenID provider's URL.
The status parameter is set to ACTIVATED
to activate the authentication method.
The same configuration can be done within the DRIVR-UI for your Domain
. Please replace {slug}
in the following link with the one of your DRIVR instance. https://{slug}.ui.drivr.cloud/#/en/domain/auth-methods.
mutation createOpenIdDelegatedAuthenticationMethod {
createOpenIdDelegatedAuthenticationMethod(
status:ACTIVATED,
configuration:{
clientId: "YOUR_CLIENT_ID",
issuer: "YOUR_ISSUER_URL"
}
) {
uuid
status
}
}
Response
{
"data": {
"createOpenIdDelegatedAuthenticationMethod": {
"uuid": "19f1b631-061f-4685-8619-374a56d43da2",
"status": "ACTIVATED"
}
}
}
This is everything which is required to setup this flow and it can be used as follows:
- The user logs into a trusted third-party service (e.g. Azure AD) that supports OpenID authentication and obtains an OpenID token.
- This OpenID token can now be used within DRIVR via an HTTP Bearer Token.
- DRIVR verifies the token's authenticity by checking the token's signature using the public key provided by the OpenID provider.
- The OpenID tokens's issuer and audience is checked to ensure that it is intended for DRIVR and issued by a trusted provider.
- The OpenID provider's userinfo is extracted from the passed JWT token. If the user represented by the token is not in DRIVR, the
User
is created in DRIVR. DRIVR generates an internal authentication token for the user, which can be used to authenticate the user for subsequent requests. DRIVR caches the external token until it expires so that the third-party authorization service does not need to be queried again.
The following examples show how to integrate this flow into your own application using different programming languages.
Python
# pip install requests
import requests
ACCESS_TOKEN: str = 'YOUR_ACCESS_TOKEN_HERE'
DOMAIN_SLUG: str = 'slug' # Replace this with your Domain slug
def query_current_domain() -> None:
headers: dict[str, str] = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {ACCESS_TOKEN}',
}
payload: dict[str, str] = {
'query': '{ currentDomain { uuid, name, hostNames } }',
}
response = requests.post(
f'https://{DOMAIN_SLUG}.api.drivr.cloud/graphql',
headers=headers,
json=payload
)
response.raise_for_status()
data = response.json()['data']
return data['currentDomain']
try:
current_domain = query_current_domain()
print(current_domain)
except requests.exceptions.RequestException as error:
print(error)
Javascript
const fetch = require('node-fetch');
const ACCESS_TOKEN = 'YOUR_ACCESS_TOKEN_HERE';
const DOMAIN_SLUG = 'slug' // Replace this with your Domain slug
async function queryCurrentDomain() {
const response = await fetch(`https://${DOMAIN_SLUG}.api.drivr.cloud/graphql`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${ACCESS_TOKEN}`,
},
body: JSON.stringify({ query: '{ currentDomain { uuid, name, hostNames } }' }),
});
const { data, errors } = await response.json();
if (errors) {
console.error(errors);
throw new Error('GraphQL Query Failed');
}
return data.currentDomain;
}
(async () => {
try {
const currentDomain = await queryCurrentDomain();
console.log(currentDomain);
} catch (error) {
console.error(error);
}
})();