#
2. Authentication
#
Introduction
Once your app is available in the app marketplace — whether in the development, in_review, or production phase — you can test the app installation using your developer accounts (accessible in Integrator Studio).
You'll need the Install URL and Callback URL that you specified during app creation in the studio dashboard.
Armada uses a simplified OAuth-like installation flow based on challenge-response verification using standard HMAC signatures. This approach is secure, standardized, and easy to implement in any programming language.
#
Installation Flow Overview
Watch this video walkthrough of the complete installation protocol:
sequenceDiagram
participant Merchant
participant Armada
participant Integrator
Merchant->>Armada: Click "Install App"
Armada->>Integrator: Redirect to Install URL<br/>(app_id + installation_id)
Integrator->>Integrator: Generate HMAC signature<br/>Store installation_id
Integrator->>Armada: Redirect to Verify URL<br/>(installation_id + challenge_signature)
Armada->>Armada: Validate HMAC
alt Valid
Armada->>Integrator: POST to Callback URL<br/>(access_token + merchant data)
Integrator->>Armada: 200 OK
Armada->>Merchant: Installation Success
else Invalid
Armada->>Merchant: Installation Failed
end
#
1. Install Redirect
When a merchant clicks Install App in the Armada Marketplace, they will be redirected to your Install URL with the following query parameters:
[YOUR_INSTALL_URL]?app_id=...&installation_id=...
#
Parameters
app_idString - The ID of your applicationinstallation_idString - A unique, short-lived identifier (TTL: 1 minute) for this installation attempt
#
Example Request
https://yourwebsite.com/install?app_id=66f3f4cd7ef4e922a598f147&installation_id=c314c1d8-41c8-492f-aadd-8f2c5cd59b07
Once the user is redirected to your platform, you can verify the app_id and authenticate the user on your end.
#
2. Challenge Signature Generation
After receiving the installation_id, you must:
Store the
installation_idtemporarily in a key-value database (like Redis), in-memory cache, or any other storage solution. You'll need it for the callback validation.Generate a challenge signature using HMAC-SHA256:
- Message: The
installation_id - Secret Key: Your app secret (available in Integrator Studio)
- Message: The
#
Get Your App Secret
You can find your app secret in the Integrator Studio under the Apps section. Choose "App Credentials" from the Actions dropdown:
#
HMAC Generation Examples
const crypto = require('crypto');
function generateChallengeSignature(installationId, appSecret) {
return crypto
.createHmac('sha256', appSecret)
.update(installationId)
.digest('hex');
}
// Example usage
const installationId = 'c314c1d8-41c8-492f-aadd-8f2c5cd59b07';
const appSecret = 'your_app_secret_here';
const challengeSignature = generateChallengeSignature(installationId, appSecret);
console.log(challengeSignature);
import hmac
import hashlib
def generate_challenge_signature(installation_id: str, app_secret: str) -> str:
return hmac.new(
app_secret.encode('utf-8'),
installation_id.encode('utf-8'),
hashlib.sha256
).hexdigest()
# Example usage
installation_id = 'c314c1d8-41c8-492f-aadd-8f2c5cd59b07'
app_secret = 'your_app_secret_here'
challenge_signature = generate_challenge_signature(installation_id, app_secret)
print(challenge_signature)
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
func generateChallengeSignature(installationID, appSecret string) string {
h := hmac.New(sha256.New, []byte(appSecret))
h.Write([]byte(installationID))
return hex.EncodeToString(h.Sum(nil))
}
func main() {
installationID := "c314c1d8-41c8-492f-aadd-8f2c5cd59b07"
appSecret := "your_app_secret_here"
challengeSignature := generateChallengeSignature(installationID, appSecret)
println(challengeSignature)
}
<?php
function generateChallengeSignature($installationId, $appSecret) {
return hash_hmac('sha256', $installationId, $appSecret);
}
// Example usage
$installationId = 'c314c1d8-41c8-492f-aadd-8f2c5cd59b07';
$appSecret = 'your_app_secret_here';
$challengeSignature = generateChallengeSignature($installationId, $appSecret);
echo $challengeSignature;
?>
require 'openssl'
def generate_challenge_signature(installation_id, app_secret)
OpenSSL::HMAC.hexdigest('SHA256', app_secret, installation_id)
end
# Example usage
installation_id = 'c314c1d8-41c8-492f-aadd-8f2c5cd59b07'
app_secret = 'your_app_secret_here'
challenge_signature = generate_challenge_signature(installation_id, app_secret)
puts challenge_signature
using System;
using System.Security.Cryptography;
using System.Text;
public static string GenerateChallengeSignature(string installationId, string appSecret)
{
using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(appSecret)))
{
byte[] hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(installationId));
return BitConverter.ToString(hash).Replace("-", "").ToLower();
}
}
// Example usage
string installationId = "c314c1d8-41c8-492f-aadd-8f2c5cd59b07";
string appSecret = "your_app_secret_here";
string challengeSignature = GenerateChallengeSignature(installationId, appSecret);
Console.WriteLine(challengeSignature);
import * as crypto from 'crypto';
function generateChallengeSignature(installationId: string, appSecret: string): string {
return crypto
.createHmac('sha256', appSecret)
.update(installationId)
.digest('hex');
}
// Example usage
const installationId = 'c314c1d8-41c8-492f-aadd-8f2c5cd59b07';
const appSecret = 'your_app_secret_here';
const challengeSignature = generateChallengeSignature(installationId, appSecret);
console.log(challengeSignature);
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
public class HmacGenerator {
public static String generateChallengeSignature(String installationId, String appSecret) throws Exception {
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(appSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
hmacSha256.init(secretKey);
byte[] hash = hmacSha256.doFinal(installationId.getBytes(StandardCharsets.UTF_8));
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
}
public static void main(String[] args) throws Exception {
String installationId = "c314c1d8-41c8-492f-aadd-8f2c5cd59b07";
String appSecret = "your_app_secret_here";
String challengeSignature = generateChallengeSignature(installationId, appSecret);
System.out.println(challengeSignature);
}
}
const std = @import("std");
const crypto = std.crypto;
fn generateChallengeSignature(
allocator: std.mem.Allocator,
installation_id: []const u8,
app_secret: []const u8,
) ![]u8 {
var hmac: crypto.auth.hmac.sha2.HmacSha256 = crypto.auth.hmac.sha2.HmacSha256.init(app_secret);
hmac.update(installation_id);
var hash: [32]u8 = undefined;
hmac.final(&hash);
var hex_string = try allocator.alloc(u8, 64);
_ = try std.fmt.bufPrint(hex_string, "{x}", .{std.fmt.fmtSliceHexLower(&hash)});
return hex_string;
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const installation_id = "c314c1d8-41c8-492f-aadd-8f2c5cd59b07";
const app_secret = "your_app_secret_here";
const challenge_signature = try generateChallengeSignature(allocator, installation_id, app_secret);
defer allocator.free(challenge_signature);
std.debug.print("{s}\n", .{challenge_signature});
}
#
3. Verification Redirect
After generating the challenge signature, redirect the merchant to Armada's verification endpoint:
https://api.armadadelivery.com/integrations/apps/install/verify
#
Parameters
installation_idString - The installation ID you receivedchallenge_signatureString - The HMAC signature you generated
#
Example
https://api.armadadelivery.com/integrations/apps/install/verify?installation_id=c314c1d8-41c8-492f-aadd-8f2c5cd59b07&challenge_signature=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2
If the validation is correct, Armada will send a POST request to your callback URL with the installation data and access token. Otherwise, the installation is aborted and the merchant is informed of the reason.
#
4. Callback Flow
Once the signature is validated, Armada will send a POST request to your Callback URL with the installation details.
#
Request Headers
These are the useful headers you need. The request may include additional headers, but these are the essential ones:
#
Request Body
{
"installation_id": "c314c1d8-41c8-492f-aadd-8f2c5cd59b07",
"app": {
"id": "66f3f4cd7ef4e922a598f147",
"name": "Your App Name"
},
"merchant": {
"id": "507f1f77bcf86cd799439011",
"name": "Acme Restaurant",
"email": "zakaria@armadadelivery.com",
"country": "Kuwait"
},
"inputs": [],
"access_token": "arap_363200ea05878276d75cbfa1c07c373"
}
#
Schema
installation_idString - The installation ID from the initial redirectappObject - Information about your appidString - Your app IDnameString - Your app name
merchantObject - Information about the merchant who installed the appidString - Merchant's unique identifiernameString - Merchant's business nameemailString - Merchant's email addresscountryString - Merchant's country
inputsArray - Form inputs submitted during installation (if any were configured)access_tokenString - The API access token for this merchant
#
Example with Pre-Required Inputs
If you configured form inputs during app creation, merchants will fill them out during installation:
{
"installation_id": "c314c1d8-41c8-492f-aadd-8f2c5cd59b07",
"app": {
"id": "66f3f4cd7ef4e922a598f147",
"name": "Your App Name"
},
"merchant": {
"id": "507f1f77bcf86cd799439011",
"name": "Acme Restaurant",
"email": "zakaria@armadadelivery.com",
"country": "Kuwait"
},
"inputs": [
{ "name": "Level", "value": 5 },
{ "name": "Store ID", "value": "T4857HR1B" },
{ "name": "Enable email notification?", "value": false }
],
"access_token": "arap_363200ea05878276d75cbfa1c07c373"
}
#
Response Requirements
You must respond with a 200 status code. Otherwise, the installation will fail on our end.
Your callback endpoint should:
- Validate the
installation_idmatches what you stored earlier - Save the
access_tokento your database - Store merchant information
- Return a
200 OKresponse
#
Access Token
The access_token is associated with the specific merchant who installed your app. Any API action taken with this token will affect that merchant's account.
Important: Save the access_token in your database and include it in the Armada-Access-Token header for all API requests to Armada on behalf of this merchant. This token does not expire unless the merchant uninstalls the app.
#
App Uninstallation
For information about handling app uninstallation, see the App Uninstalled webhook documentation.