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 multiple DomainAuthenticationMethods 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 all DomainAuthenticationMethods which are ACTIVATED.
  • 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 DRIVR username and password.

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:

  1. The user logs into a trusted third-party service (e.g. Azure AD) that supports OpenID authentication and obtains an OpenID token.
  2. This OpenID token can now be used within DRIVR via an HTTP Bearer Token.
  3. DRIVR verifies the token's authenticity by checking the token's signature using the public key provided by the OpenID provider.
  4. The OpenID tokens's issuer and audience is checked to ensure that it is intended for DRIVR and issued by a trusted provider.
  5. 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);
  }
})();