Docs
Proxying License Server

Proxying License Server

Increased control over network requests for Enterprise Plan customers

Context

Argmax SDK requires a valid and active license to unlock licensed software functionality on each end-user device. Licenses are created and refreshed automatically when Argmax SDK is initialized like so:

import Argmax
await ArgmaxSDK.with(ArgmaxConfig(apiKey: "ax_***"))

In specific:

  • New license creation requests are sent to https://api.argmaxinc.com/v1/license-token/create
  • License refresh requests are sent to https://api.argmaxinc.com/v1/license-token/refresh

Benefits of Proxying

Proxying enables Enterprises that use Argmax SDK in their application to route all outbound network traffic originating from Argmax through their own proxy layer, rather than allowing Argmax to communicate with Argmax API endpoints directly. This design eliminates Argmax's need to “call home”.

Proxying enables:

  • Programmatic auditability for compliance
  • Control over network traffic, simplifying firewall and network policy configuration

Basic Example

Step 0: Ensure you are on Enterprise Plan

Before getting started, note that Argmax SDK will only allow proxying for Enterprise Plan customers. You can verify your subscription type with the following snippet:

import Argmax
let subscriptionType = await ArgmaxSDK.licenseInfo().subscriptionType

If you are not on the Enterprise Plan yet but need proxying, please email customer@argmaxinc.com to request an upgrade.

Step 1: Implement your custom APIRequestHandler

Argmax SDK defines the APIRequestHandler protocol. Extend this protocol to implement your custom logic for proxying. Below is an example CustomAPIRequestHandler that prints the requests and responses for inspection:

import Argmax
import Foundation
 
final class CustomAPIRequestHandler: APIRequestHandler {
    private let session: URLSession
 
    init(session: URLSession = .shared) {
        self.session = session
    }
 
    func sendRequest(_ request: ApiRequest) async throws -> (Data, HTTPURLResponse) {
        let urlRequest = try request.constructURLRequest()
 
        print("\n[>>>] Sending request:")
        print("  URL: \(urlRequest.url?.absoluteString ?? "nil")")
        print("  Method: \(urlRequest.httpMethod ?? "nil")")
        print("  Headers: \(urlRequest.allHTTPHeaderFields?.description ?? "nil")")
        if let body = urlRequest.httpBody, let bodyString = String(data: body, encoding: .utf8) {
            print("  Body: \(bodyString)")
        }
 
        let (data, response) = try await session.data(for: urlRequest)
        guard let httpResponse = response as? HTTPURLResponse else {
            throw NSError(domain: "Invalid response", code: 1, userInfo: nil)
        }
 
        print("\n[<<<] Received response:")
        print("  Status: \(httpResponse.statusCode)")
        print("  Headers: \(httpResponse.allHeaderFields.description)")
        if let responseString = String(data: data, encoding: .utf8) {
            print("  Body: \(responseString)")
        }
 
        return (data, httpResponse)
    }
}

Step 2: Register your CustomAPIRequestHandler

import Argmax
...
let config = ArgmaxConfig(apiKey: "ax_***")
config.setAPIRequestHandler(CustomRequestHandler())
await ArgmaxSDK.with(config)

An example output when using Argmax SDK with this CustomAPIRequestHandler registered is as follows:

[>>>] Sending request:
  URL: https://api.argmaxinc.com/v1/license-token/create
  Method: POST
  Headers: ["Content-Type": "application/json", "User-Agent": "argmax-sdk-swift/1.7.10", "AX-API-KEY": "ax_****", "Content-Length": "158"]
  Body: {"device_sku":"Mac15,9","app_bundle_id":"com.argmax.sdk","os_version":"15.6.1","fwk_version":"1.7.10","license_id":"****"}
 
[<<<] Received response:
  Status: 201
  Headers: [AnyHashable("x-frame-options"): DENY, AnyHashable("Content-Length"): 889, AnyHashable("content-security-policy"): style-src 'self' 'unsafe-inline'; img-src 'self' data:; script-src 'self' 'unsafe-inline'; default-src 'none'; frame-ancestors 'none', AnyHashable("cross-origin-opener-policy"): same-origin, AnyHashable("Date"): Thu, DD MM YYYY HH:MM:SS GMT, AnyHashable("referrer-policy"): same-origin, AnyHashable("Content-Type"): application/json, AnyHashable("x-content-type-options"): nosniff, AnyHashable("Server"): gunicorn]
  Body: {"access_token": "****", "refresh_token": "****"}

where ****s were manually inserted to deidentify this example.