ilert Documentation
WebsiteAPI ReferenceLoginStart for Free
  • Getting Started
    • Core concepts
    • FAQ
      • Renaming of Incidents to Alerts
    • Import from PagerDuty
    • Import from StatusPage.io
  • Alerting
    • Dashboard
    • Alert sources
    • Notification settings
      • Mute notifications
    • Support hours
    • Maintenance windows
    • Heartbeat monitoring
      • Prometheus Heartbeat Example
      • CLI Heartbeat Examples
    • Deployment events
    • 🏛️Understanding event flows
    • ilert sender IDs, domains and IPs
      • SMS and voice alerts in China
  • On-call management & Escalations
    • Escalation policies
    • On-call schedules
      • My on-call shifts
      • Recurring schedules
      • Static schedules
    • Coverage requests
  • ChatOps
    • Overview
    • Integration for Slack
      • Receive and respond to alerts in Slack
      • Create a dedicated Slack channel for an existing alert
      • Create alerts in Slack
      • Look up who is on-call
    • Microsoft Teams Integration
      • Microsoft Teams Chat Integration
        • Microsoft Teams Integration via Incoming Webhook
        • Microsoft Teams Integration via Workflows
      • Microsoft Teams Meeting Integration
      • Create a channel for an existing alert in Microsoft Teams
      • Create alerts in Microsoft Teams
      • Look up who is on-call in Microsoft Teams
  • Incident comms & status pages
    • Getting started
    • Services
    • Incidents
    • Status pages
      • Public vs private status pages
      • Audience-specific status page
    • Metrics
      • Import metrics from Datadog
      • Import metrics from Prometheus
  • 🪄ilert AI
    • Introduction
    • Using ilert AI for efficient incident communication
    • Using ilert AI for post-mortem creation
    • Using ilert AI for schedule generation
    • Using ilert AI for alert grouping
    • Global Search enhanced by ilert AI
      • Bulk resolving similar alerts
  • Call Routing
    • Getting started with call routing
    • Routing calls using call flows
    • Call routing (legacy)
      • Routing calls based on support hours
      • Voicemail only mode
      • Managing call routing alerts
      • Adding webhooks and outbound chat messages
      • Uploading custom audio responses
  • User Administration
    • User roles and permissions
    • Team-based organisation
    • Single sign on
      • Setting up SSO with GSuite
      • Setting up SSO with Microsoft Azure Active Directory
      • Setting up SSO with Okta
      • Setting up SSO with Auth0
      • Auto provisioning users & teams
    • 🔐Two-factor authentication / MFA
  • Reports
    • Overview
    • Alerts
  • Mobile App
    • Getting started with ilert mobile app
    • Mobile app notification settings
    • Critical push notifications and DND overrides
      • iOS critical alerts configuration
      • Android Push Notification DND Configuration
    • On-call widget
  • INTEGRATIONS
    • Types of integrations
    • Inbound integrations
      • 4me Integration
      • Ansible Automation Platform AWX Integration
      • Amazon CloudWatch Integration
      • Amazon SNS Integration
        • Amazon SNS Inbound Integration
        • Amazon SNS Outbound via AWS Lambda
      • Azure Alerts Integration
        • Azure Activity Logs
        • Azure Metric
        • Azure Logs
        • Azure Service Health
        • Azure Sentinel
        • Budget Alert
      • Apica Integration
      • AppDynamics Integration
      • AppSignal Integration
      • AWS Budgets Integration
      • AWS Cloudtrail Integration
      • AWS DevOps Guru Integration
      • AWS GuardDuty Integration
      • AWS Personal Health Dashboard Integration
      • AWS Security Hub Integration
      • Autotask Inbound Integration
      • Auvik Integration
      • Catchpoint Integration
      • Checkly Integration
      • Checkmk Integration
        • Checkmk Integration (v 1.x)
        • Checkmk Integration (v 2.0+ )
      • Cisco Meraki Integration
      • Cisco ThousandEyes Integration
      • Cisco Webex
      • Cloudflare Integration
      • ClusterControl Integration
      • Connectwise Manage Integration
      • Cortex Integration
      • Cortex XSOAR (formerly Demisto) Integration
      • CrowdStrike Integration
      • Dash0 Integration
      • Datadog Integration
      • Dynatrace Integration
      • Elastic Watcher Integration
      • Email Inbound Integration
        • Email Key Extraction and Resolve Examples
        • Automatically resolve Alerts with Emails
        • Migrating legacy email settings
      • FreshService Integration
      • Gatus Integration
      • GitHub Integration
        • GitHub Advanced Security Integration
        • GitHub Inbound Check Run (Actions) Integration
        • GitHub Inbound Issue Integration
        • GitHub advanced settings
      • GitLab Integration
      • Google Cloud Monitoring (formerly Stackdriver) Integration
      • Google Security Command Center
      • Grafana Integration Overview
        • Grafana Integration
        • Grafana Integration (v 9.x)
      • Graylog Integration
      • HaloITSM Integration
      • HaloPSA Integration
      • HashiCorp Consul
      • Healthchecks.io Integration
      • HetrixTools Integration
      • Honeybadger Integration
      • Honeycomb Integration
      • Hyperping Integration
      • CrowdStrike Falcon LogScale Integration
      • IBM Cloud Functions Integration
      • Icinga Integration
      • InfluxDB Integration
      • Instana Integration
      • IT-Conductor Integration
      • IXON Cloud Integration
      • Jira Inbound Integration
      • JumpCloud Integration
      • Kafka Integration
      • Kapacitor Integration
      • Kentix AlarmManager
      • Keep Integration
      • Kibana Integration
      • Kubernetes Integration
      • LibreNMS Integration
      • Lightstep Integration
      • Loki integration
      • Mezmo Integration
      • Microsoft SCOM
      • Mimir Integration
      • MongoDB Atlas Integration
      • MXToolBox Integration
      • MQTT Integration
      • Nagios Integration
      • N-central Integration
      • Netdata Integration
      • New Relic Integration
        • New Relic Integration (deprecated)
        • New Relic Workflow Integration
      • Oh Dear Integration
      • PandoraFMS Integration
      • Panther Integration
      • Particle Integration
      • Pingdom Integration
      • PostHog Integration
      • Postman Monitors Integration
      • Prometheus Integration
      • PRTG Network Monitor Integration
      • Prisma Cloud Integration
      • Push Notifications
      • RapidSpike Integration
      • Raygun Integration
      • Rollbar Integration
      • Salesforce Integration
      • Samsara Integration
      • Search Guard Integration
      • Sematext Integration
      • Sensu Integration
      • Sentry Integration
      • Server Density Integration
      • ServerGuard24 Integration
      • ServiceNow Inbound Integration
      • SignalFx Integration
      • Site24x7 Integration
      • SMS Integration
      • SolarWinds Integration
      • Splunk Integration
      • StatusCake Integration
      • StatusHub Integration
      • StatusPage Integration
      • Sumo Logic Integration
      • Sysdig Integration
      • TOPdesk Inbound Integration
      • TeamCity integration
      • Terraform Cloud / Terraform Enterprise
      • Tulip Integration
      • Twilio Alarms Integration
      • Twilio Errors Integration
      • Ubidots Integration
      • Uptime Kuma Integration
      • UptimeRobot Integration
      • VictoriaMetrics Integration
      • Zabbix Integration
        • Zabbix 4.4+ Integration
        • Zabbix 2.2 – 4.3 Integration
      • Zammad Inbound Integration
      • Zapier Inbound Integration
      • Zendesk Inbound Integration
    • Outbound integrations
      • Autotask Outbound Integration
      • DingTalk Integration
      • Discord Integration
      • Email Outbound Integration
      • Jira Outbound Integration
      • GitHub Outbound Issue Integration
      • Mattermost Integration
      • ServiceNow Outbound Integration
      • Telegram Integration
      • TOPdesk Outbound Integration
      • Webhook Integration
      • Zammad Outbound Integration
      • Zapier Outbound Integration
      • Zendesk Outbound Integration
      • Zoom Integration
        • Zoom Chat Integration
        • Zoom Meeting Integration
    • Deployment integrations
      • API deployment pipeline
      • Argo CD deployment pipeline
      • Github deployment pipeline
      • GitLab deployment pipeline
  • API
    • API Reference
    • API Version History
      • API user preference migration 2023
      • Discontinuation of Uptime Monitoring
    • Rate Limiting
    • Client Libraries
      • ilert Agent - ilagent
      • Go Client
      • Rust Client
      • Javascript / Node.js Client
    • Terraform
      • Importing ilert UI resources into Terraform state
    • 👩‍💻ICL - ilert condition language
    • ➿ITL - ilert template language
    • API endpoints / samples
      • Creating alerts through events
      • Importing public status page subscribers
    • 🔥Developing ilert Apps
      • Get started with ilert Apps
      • Understanding OAuth2
      • Developing a Backend App with OAuth2
      • Developing a web or native App with OAuth2 and PKCE
      • Token lifetimes, error codes, app verification, etc.
  • Contact us
  • ilert Release Notes
Powered by GitBook
LogoLogo

Product

  • Alerting & Notification
  • On-call Management & Escalations
  • Call Routing
  • Status Pages

Resources

  • Blog
  • Case Studies
  • Security
  • API Reference

Legal

  • Privacy policy
  • Imprint

Increase Your Uptime

  • Start for Free
  • Get a Demo

(c) 2011 - 2025 ilert GmbH

On this page
  • Preparing your ilert app
  • Redirecting the user
  • PKCE generation
  • User grant view
  • In case of an error
  • In case of successful authorization
  • Turning a code into a token
  • In case of an error
  • In case of successful token generation
  • Using the access token
  • Refreshing an access token
  • Accessing token information
  • Revoking a refresh token

Was this helpful?

Edit on GitHub
  1. API
  2. Developing ilert Apps

Developing a web or native App with OAuth2 and PKCE

Single page web applications or native apps cannot keep their client secrets private and are therefor called public clients. For such cases the client secret can be omitted by relying on PKCE.

PreviousDeveloping a Backend App with OAuth2NextToken lifetimes, error codes, app verification, etc.

Last updated 2 years ago

Was this helpful?

For this guide we assume that you have read as well as created an application as shown in .

You may find the whole source code project in .

Preparing your ilert app

Before we can start, we need to make sure that our ilert application is configured correctly. Navigate to your application and make sure that you have entered a redirect url that fits your local or deployed webapp. Also copy the client id, as we will need it to configure your application.

Redirecting the user

Make sure that the redirect_uri that is passed to the redirect url is exactly the same as the one the you have entered in your application's settings or the authorization will fail. This validation is necessary, as verifing the redirect_uri is a very important part of implementing a secure OAuth2 flow. In case you need to send optional data e.g. your own id, use the state query parameter. It will be returned to your client.

Also note that per default for PKCE-only clients no refresh token will be issued, unless the additional offline access scope is requested. Please keep refresh tokens secure.

PKCE generation

Before redirecting the user to the ilert authorization server, we need to generate a verifier and code challenge that can be used by the authorization server to ensure the origin of the (later) token request matches the origin of the authorization request.

PKCE might seem confusing at first, but in the end it is just a random identifier that verifies that the one who initially started the authorization request, is the same who comes asking for the token. And to ensure noone else knows the plain version of the identifier it is hashed before sharing it with the authorization server.

Below are helper methods to deal with PKCE generation:

function dec2hex(dec) {
    return ("0" + dec.toString(16)).substr(-2);
}

function generateCodeVerifier() {
    const array = new Uint32Array(56 / 2);
    window.crypto.getRandomValues(array);
    return Array.from(array, dec2hex).join("");
}

function sha256(plain) {
    const encoder = new TextEncoder();
    const data = encoder.encode(plain);
    return window.crypto.subtle.digest("SHA-256", data);
}

function base64urlencode(a) {
    let str = "";
    const bytes = new Uint8Array(a);
    const len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        str += String.fromCharCode(bytes[i]);
    }
    return btoa(str)
        .replace(/\+/g, "-")
        .replace(/\//g, "_")
        .replace(/=+$/, "");
}

async function generateCodeChallengeFromVerifier(v) {
    const hashed = await sha256(v);
    const base64encoded = base64urlencode(hashed);
    return base64encoded;
}

Which may be used like so:

const verifier = generateCodeVerifier();
const challenge = await generateCodeChallengeFromVerifier(verifier);
sessionStorage.setItem("verifier", verifier);

Notice how we are storing the verifier in the session storage, as we will need it later to verify to the authorization server that we are the same ones who initiated the authorize request.

With our verifier and code challenge in place, we can create our redirect url:

const url = "https://app.ilert.com/api/developers/oauth2/authorize";
const queryParams = new URLSearchParams({
    client_id: '0501439fd7dee9af0c25',
    response_type: 'code',
    redirect_uri: 'http://localhost:4597/webapp',
    scope: 'profile source:w offline_access',
    state: 'optional-state',
    code_challenge: '6K9LHF25tUPodHISsO_UoK6cKnbclW2ZY1TaSUAWaIo',
    code_challenge_method: 'S256'
});

window.location = url + "?" + queryParams.toString();

In case the user is known to your application or especially if the application is limited to a single account. We suggest to redirect the user directly to the tenant (account url) of the user, this way he might be able to skip the login step if logged in already. e.g. https://someTenant.ilert.com/api/developers/oauth2/authorize instead of https://app.ilert.com/api/developers/...

User grant view

The user will be able to see the scopes that you have requested and grant them.

In case of an error

In case of any error, ilert's authorization server will send the user back to the provided redirect_uri while adding an error and error_description query parameter to it.

In case of successful authorization

If the user has accepted your scopes, ilert's authorization server will redirect the user back to your provided redirect_uri while adding a code query parameter to it. This code parameter can be used in the next step to fetch the access token.

http://localhost:4597/webapp?code=c82ec4eca4c0ac90061fdd4225d1&state=optional-state

Turning a code into a token

After a successful redirect, we can detect the code query parameter and run an AJAX call to fetch our tokens.

You may use plain XMLHttpRequest or the fetch API, for the sake of simplicity in reading we chose the library axios to make the HTTP calls.

const verifier = sessionStorage.getItem("verifier"); // stored earlier
sessionStorage.removeItem("verifier");
const urlSearchParams = new URLSearchParams(window.location.search);
const code = Object.fromEntries(urlSearchParams.entries()).code;

const tokenParams = new URLSearchParams();
tokenParams.append("client_id", clientId);
tokenParams.append("grant_type", "authorization_code");
tokenParams.append("code", code);
tokenParams.append("code_verifier", verifier);

const tokenUrl = "https://app.ilert.com/api/developers/oauth2/token";
const tokenResult = await axios.post(tokenUrl, tokenParams, {
    headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        "Accept": "application/json"
    }
});

const {
access_token,
refresh_token
} = tokenResult.data;

console.log(access_token, refresh_token);

In case of an error

You will receive an error response.

{
	"code": "invalid_grant",
	"error": "Provided code is unknown"
}

In case of successful token generation

If code and secret are valid, the authorization server will respond with an access as well as a refresh token.

Never expose your user's tokens, especially not the refresh tokens, treat them like passwords.

{
	"token_type": "Bearer",
	"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpbF9uIjoiNWNmIiwiaWxfYSI6MiwiaWxfcyI6InByb2ZpbGUgc291cmNlOnciLCJpc3MiOiJpbGVydCIsImlsX3UiOiJrcnlzdGlhbml0eSIsImlsX3QiOiJBQ0NFU1MiLCJpbF92IjoxLCJleHAiOjE2NDM3MjY3MjN9.Ao0dTxQpcez0IsaUOdsPzWqWhj31ob9Tzf-5KSoqJEmjC4lkeOiRJ255HI8w2IQyj7aunW_SM50rRQBcDHVkt8JmfuT4TgYJ3dvZiJzeZ-YGU1QDT6pCN30wJ9a3rpmv6Y6T2cJD1JvNuEh1qyjdjmJGuBVU3-_0qZSu-Y6CBaRxaBvM27JOf2JkQ4pAN7dK4c2Yw7-MyEkBhtwI_JEMhNCp6GF5SJGhCFBq-xPF1iFqpllIrpqaTWAvRz0ZCNWZCumXCaJAfvnS_OJJoPwEvr2DBV8x0bAuFpECgycE8_--rjU9gZafqiRd2cfFas7AmzQFuvvDGQx_1Zq7leW3EA",
	"expires_in": 3600,
	"scope": "profile source:w",
	"refresh_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpbGVydCIsImlsX3QiOiJPQVVUSCIsImlsX3YiOjEsImV4cCI6MTY3NTI1OTEyMywiaWxfayI6IjM0ZTMzYzdmZGUzZjQzZTdhMTEwOWJmYTgzMmRkNDdiIn0.ol0HwWO_zAAhKLjLPLDPwvxv7PNPnUnZq8IXd5fC1FY",
	"refresh_token_expires_in": 31536000
}

Using the access token

Your application should store the access and especially the refresh token as safely as possible. You can now make requests to ilert's API on behalf of the authorized user.

Do not forget the Bearer prefix in the authorization header before your access token.

var axios = require("axios").default;

var options = {
  method: 'GET',
  url: 'https://api.ilert.com/api/users/current',
  headers: {
    Authorization: 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpbF9uIjoiNWNmIiwiaWxfYSI6MiwiaWxfcyI6InByb2ZpbGUgc291cmNlOnciLCJpc3MiOiJpbGVydCIsImlsX3UiOiJrcnlzdGlhbml0eSIsImlsX3QiOiJBQ0NFU1MiLCJpbF92IjoxLCJleHAiOjE2NDM3MjczNTR9.ZSxXJiR7yPF9BMfsfa9dYVpdv5E2o88iBTG1VFp7-vdF3wciNte7_fKQU2z2aMVudGIZ6dhpBuA8FnSJa3sjaXgsHy2r8Fjvj2XCTuPigaZgURTjAM0zqTC6bxHVkn-G9bKUoJ8mx40U8hFZFPsnD5b9qiZZ6CjqU0CCuiWjoEC8hapHe1TOYnNvqeFPRUYfOhJTXAuOoW0wmcKdrAyk6YbVN74nDVlEbG1TEGIXk893jVyLaoiShqHxkcnyliTWLYg9P_DG0JXFflxugyGttP3GaxAq28KAxI-5VAa_QptXlwybEVN9Q8072a7Xk4R0_jCKbp9uMKE3E4qH72f3PA'
  }
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

Refreshing an access token

var axios = require("axios").default;

var options = {
  method: 'POST',
  url: 'https://api.ilert.com/api/developers/oauth2/token',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  data: {
    client_id: '0501439fd7dee9af0c25',
    refresh_token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpbGVydCIsImlsX3QiOiJPQVVUSCIsImlsX3YiOjEsImV4cCI6MTY3NTI1OTEyMywiaWxfayI6IjM0ZTMzYzdmZGUzZjQzZTdhMTEwOWJmYTgzMmRkNDdiIn0.ol0HwWO_zAAhKLjLPLDPwvxv7PNPnUnZq8IXd5fC1FY',
    grant_type: 'refresh_token'
  }
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

The responses will be equal to the ones of Turning a code into a token above.

Accessing token information

You could parse the JWT token yourself, however we offer a token info endpoint for convience. Note that this endpoint only accepts access tokens.

var axios = require("axios").default;

var options = {
  method: 'POST',
  url: 'https://api.ilert.com/api/developers/oauth2/token_info',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  data: {
    token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpbF9uIjoiNWNmIiwiaWxfYSI6MiwiaWxfcyI6InByb2ZpbGUgc291cmNlOnciLCJpc3MiOiJpbGVydCIsImlsX3UiOiJrcnlzdGlhbml0eSIsImlsX3QiOiJBQ0NFU1MiLCJpbF92IjoxLCJleHAiOjE2NDM3MjczNTR9.ZSxXJiR7yPF9BMfsfa9dYVpdv5E2o88iBTG1VFp7-vdF3wciNte7_fKQU2z2aMVudGIZ6dhpBuA8FnSJa3sjaXgsHy2r8Fjvj2XCTuPigaZgURTjAM0zqTC6bxHVkn-G9bKUoJ8mx40U8hFZFPsnD5b9qiZZ6CjqU0CCuiWjoEC8hapHe1TOYnNvqeFPRUYfOhJTXAuOoW0wmcKdrAyk6YbVN74nDVlEbG1TEGIXk893jVyLaoiShqHxkcnyliTWLYg9P_DG0JXFflxugyGttP3GaxAq28KAxI-5VAa_QptXlwybEVN9Q8072a7Xk4R0_jCKbp9uMKE3E4qH72f3PA'
  }
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

You will receive an info response like this:

{
	"active": true,
	"tenant_id": "someTenant",
	"username": "someUser",
	"scope": "profile source:w",
	"app_id": 250
}

Revoking a refresh token

Just like the user may at any time revoke the refresh token that was issued to your application, your application may also proactively revoke the refresh token.

var axios = require("axios").default;

var options = {
  method: 'POST',
  url: 'https://api.ilert.com/api/developers/oauth2/revoke',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  data: {
    token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJpbGVydCIsImlsX3QiOiJPQVVUSCIsImlsX3YiOjEsImV4cCI6MTY3NTI1OTEyMywiaWxfayI6IjM0ZTMzYzdmZGUzZjQzZTdhMTEwOWJmYTgzMmRkNDdiIn0.ol0HwWO_zAAhKLjLPLDPwvxv7PNPnUnZq8IXd5fC1FY'
  }
};

axios.request(options).then(function (response) {
  console.log(response.data);
}).catch(function (error) {
  console.error(error);
});

Using the parameters generated by ilert during the creation of our app, we can build a redirect url for our application, that will take unauthorized users to ilert and ask them to login and grant their permission for our app. In the case of our app, we ask for the profile scope (find more information on other available scopes ), as well as the source:w scope which will grant us permission to read the user's profile data, as well as to read and edit alert sources on behalf of the authorizing user.

As access tokens are short lived (read more about token lifetime ), you will have to refresh them regularly, at least in case they expire and the ilert API returns a 401 HTTP status code error.

Make sure to checkout .

🔥
here
here
further information on OAuth2 apps
the general OAuth2 introduction
the getting started guide
this Github repository