# 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:

GET
[YOUR_INSTALL_URL]?app_id=...&installation_id=...

# Parameters

  • app_id String - The ID of your application
  • installation_id String - A unique, short-lived identifier (TTL: 1 minute) for this installation attempt

# Example Request

GET
https://yourwebsite.com/install?app_id=66f3f4cd7ef4e922a598f147&installation_id=c314c1d8-41c8-492f-aadd-8f2c5cd59b07

# 2. Challenge Signature Generation

After receiving the installation_id, you must:

  1. Store the installation_id temporarily in a key-value database (like Redis), in-memory cache, or any other storage solution. You'll need it for the callback validation.

  2. Generate a challenge signature using HMAC-SHA256:

    • Message: The installation_id
    • Secret Key: Your app secret (available in Integrator Studio)

# 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:

Example of App ID and Secret
Example of App ID and Secret

# 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:

GET
https://api.armadadelivery.com/integrations/apps/install/verify

# Parameters

  • installation_id String - The installation ID you received
  • challenge_signature String - The HMAC signature you generated

# Example

GET
https://api.armadadelivery.com/integrations/apps/install/verify?installation_id=c314c1d8-41c8-492f-aadd-8f2c5cd59b07&challenge_signature=a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2

# 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:

Header Description
Content-Type application/json
User-Agent Armada Delivery Solutions
x-armada-app-id Your application ID
x-armada-app-name Your application name
x-armada-installation-id Installation identifier
x-armada-api-version API version (v1)
x-armada-timestamp Webhook sent timestamp (ISO 8601)
x-armada-webhook-id Unique webhook event ID
x-armada-webhook-topic app.installation.finalized

# Request Body

POST
{
  "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_id String - The installation ID from the initial redirect
  • app Object - Information about your app
    • id String - Your app ID
    • name String - Your app name
  • merchant Object - Information about the merchant who installed the app
    • id String - Merchant's unique identifier
    • name String - Merchant's business name
    • email String - Merchant's email address
    • country String - Merchant's country
  • inputs Array - Form inputs submitted during installation (if any were configured)
  • access_token String - 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:

POST
{
  "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

Your callback endpoint should:

  1. Validate the installation_id matches what you stored earlier
  2. Save the access_token to your database
  3. Store merchant information
  4. Return a 200 OK response

# 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.


# App Uninstallation

For information about handling app uninstallation, see the App Uninstalled webhook documentation.