Infor Authentication: IFS OAuth 2.0, ION API Gateway, and Service Accounts

Type: ERP Integration System: Infor CloudSuite (Infor OS 2025.x / 2026.x) Confidence: 0.88 Sources: 8 Verified: 2026-03-02 Freshness: 2026-03-02

TL;DR

System Profile

This card covers authentication for all Infor CloudSuite products that use Infor OS and the ION API Gateway as their integration platform. This includes CloudSuite Industrial (SyteLine), CloudSuite Distribution, CloudSuite Fashion, M3, and LN, among others. The ION API Gateway acts as a centralized OAuth 2.0 authorization server, while Infor Federated Services (IFS) handles identity federation and claims management. This card does NOT cover on-premises Infor OS deployments (which use IFS with ADFS/claims-based auth) or IFS Cloud from IFS AB (a completely different vendor and product).

PropertyValue
VendorInfor
SystemInfor OS / ION API Gateway (2025.x / 2026.x)
API SurfaceREST (ION API Gateway)
Current API VersionION API 2025.x / 2026.x
Editions CoveredAll CloudSuite editions (Industrial, Distribution, Fashion, M3, LN)
DeploymentCloud (Infor multi-tenant)
API DocsInfor ION API Gateway Administration Guide
StatusGA

API Surfaces & Capabilities

The ION API Gateway is the single entry point for all API traffic to Infor CloudSuite applications. All external API calls must be authenticated through the gateway using OAuth 2.0 Bearer tokens.

API SurfaceProtocolBest ForAuth Flows SupportedReal-time?Notes
ION API Gateway (REST)HTTPS/JSONAll external API integrationAll four OAuth 2.0 grantsYesCentral gateway for all Infor CloudSuite APIs
ION Connect (Messaging)ION messagingEvent-driven, async document exchangeSAML Bearer (via ION)AsyncBODs (Business Object Documents) for ERP events
Infor Data LakeHTTPS/JSONAnalytics, reporting, bulk data exportBackend Service (Resource Owner)No (batch)Read-only access to replicated ERP data
Infor Ming.le APIsHTTPS/JSONPortal widgets, embedded appsSAML Bearer (SSO token reuse)YesApps embedded in Ming.le portal reuse SSO token

Rate Limits & Quotas

Per-Request Limits

Limit TypeValueApplies ToNotes
Default request timeout1 minuteAll ION API Gateway callsExtensible to max 5 minutes via policy [src7]
Max timeout (buffered policies)5 minutesPolicies with buffering enabledHard ceiling, cannot be extended further [src7]
Payload limit (buffered policies)10 MBEndpoints with buffered policiesUnbuffered endpoints have no payload limit [src7]
Payload limit (unbuffered)No limitStandard API Gateway callsSubject to backend service limits [src7]

Rolling / Daily Limits

Limit TypeDefault ValueWindowConfiguration
Quota (requests)1,000 (configurable)60 seconds (configurable)Per-user or global, set via endpoint policy [src4]
Spike arrest1,000 requests/period (configurable)60,000 ms (configurable)Rate smoothing delays after threshold [src4]
Rate smoothing delayConfigurable (ms)Per-request after thresholdAdds delay to requests exceeding delayAfterCount [src4]
Cache response TTL3,600 seconds (configurable)Per-endpointReduces backend load for repeated queries [src4]

Authentication

All ION API Gateway authentication flows are mediated by Infor Federated Services (IFS), which acts as the claims provider and identity federation hub. The IFS authorization server issues OAuth 2.0 tokens after authenticating the user or service account.

FlowUse WhenToken LifetimeRefresh?Notes
Resource Owner Grant (Backend Service)Server-to-server, no user interaction, daemons, ETLAccess: ~2hOnly if enabled; rotates every ~8hRequires 4 credentials: ClientID, ClientSecret, saak, sask. Recommended for integrations. [src2, src3]
Authorization Code GrantWeb apps, mobile/desktop apps requiring user loginAccess: ~2h; Refresh: configurableYes (if enabled)Requires redirect URI registration. User authenticates via IFS. [src1, src2]
SAML Bearer GrantApps embedded in Infor Ming.le portal (already SSO'd)Access: ~2hDepends on SAML assertionReuses the SSO token from Ming.le login. Scopes derived from SAML assertion. [src2]
Implicit GrantSingle-page applications (SPAs)Access: ~2hNoLess secure; being deprecated industry-wide. Use Authorization Code + PKCE where possible. [src3]

Authentication Gotchas

Constraints

Integration Pattern Decision Tree

START -- Authenticate with Infor CloudSuite via ION API Gateway
|-- What type of application?
|   |-- Backend service / daemon / ETL (no user present)
|   |   |-- Have .ionapi credentials file?
|   |   |   |-- YES --> Resource Owner Grant using saak + sask + ci + cs
|   |   |   |-- NO --> Register Backend Service app in ION API Gateway,
|   |   |       enable "Create Service Account", download .ionapi
|   |   |-- Need long-running session (>2h)?
|   |       |-- YES --> Enable "Issue Refresh Token" on the authorized app
|   |       |-- NO --> Request new access token per session (simple)
|   |-- Web application (user login required)
|   |   |-- Server-side web app
|   |   |   --> Authorization Code Grant (register as Web Application type)
|   |   |-- SPA / client-side only
|   |       --> Implicit Grant (consider Auth Code + PKCE if supported)
|   |-- App embedded in Infor Ming.le portal
|   |   --> SAML Bearer Grant (reuse SSO token from Ming.le)
|   |-- Infor Data Lake / ETL extraction
|       --> Backend Service (Resource Owner) with .ionapi file
|-- Which credential file format?
|   |-- .ionapi file (JSON with ti, cn, ci, cs, iu, pu, oa, ot, saak, sask)
|   |   --> Parse file, extract endpoints and credentials
|   |-- Manual ClientID/Secret (registered but no .ionapi)
|       --> Obtain token endpoint from Infor OS admin portal
|-- Token refresh strategy?
    |-- Short jobs (<2h) --> No refresh needed, single token per job
    |-- Long jobs (>2h) --> Enable refresh tokens, handle rotation
    |-- Idle >30 days --> Must re-authenticate; refresh token auto-invalidated

Quick Reference

OperationEndpoint / ConfigValueNotes
Authorization endpoint{pu}/{oa}From .ionapi fileTenant-specific URL
Token endpoint{pu}/{ot}From .ionapi filePOST with form-encoded params
Token revocation{pu}/revoke_token.oauth2POSTSend refresh_token + token_type_hint=refresh_token
IFS Service API testGET /ifsservice/usermgt/v2/users/meReturns current userBest endpoint to verify auth works
.ionapi portal URLpuBase URL for auth endpointsTenant-specific, do not hardcode
.ionapi client IDciOAuth 2.0 client identifierFrom authorized app registration
.ionapi client secretcsOAuth 2.0 client secretSensitive, never commit to VCS
.ionapi service account keysaakService account access keyBackend Service grant only
.ionapi service account secretsaskService account secret keyBackend Service grant only
.ionapi tenant IDtiInfor tenant identifierMulti-tenant environment ID
.ionapi ION API URLiuBase URL for API callsTarget for all API requests after auth
Default access token TTL~2 hoursConfigurable by adminRe-request before expiry
Default refresh token rotation~8 hoursConfigurableStore latest rotated token

Step-by-Step Integration Guide

1. Register an authorized application in ION API Gateway

Navigate to Infor OS > API Gateway > Authorized Apps. Create a new authorized application. Select "Backend Service" for server-to-server integrations. Enable "Create Service Account" and associate it with an IFS user. Download the .ionapi credentials file. [src2, src6]

Infor OS steps:
1. Log in to Infor OS (your CloudSuite portal)
2. Navigate to Burger menu > API Gateway > Authorized Apps
3. Click "+" to create new authorized app
4. For Backend Service:
   a. Select type: "Backend Service"
   b. Enable "Create Service Account"
   c. Select user to associate with service account
   d. Optionally add scopes
5. Save and download the .ionapi credentials file
6. Store the .ionapi file securely

Verify: Open the .ionapi file and confirm it contains ci, cs, pu, oa, ot fields. For backend services, also confirm saak and sask are present.

2. Parse the .ionapi credentials file

The .ionapi file is a JSON file containing all endpoints and credentials needed for authentication. [src3]

import json

def parse_ionapi(filepath):
    with open(filepath, 'r') as f:
        creds = json.load(f)
    return {
        'client_id': creds['ci'],
        'client_secret': creds['cs'],
        'token_endpoint': f"{creds['pu']}/{creds['ot']}",
        'ionapi_url': creds.get('iu', ''),
        'saak': creds.get('saak', ''),
        'sask': creds.get('sask', ''),
    }

creds = parse_ionapi('/path/to/credentials.ionapi')

Verify: The parsed token_endpoint URL should be a valid HTTPS URL.

3. Acquire access token (Resource Owner grant)

For server-to-server integrations, POST to the token endpoint with all four credentials. [src2, src3]

import requests

response = requests.post(creds['token_endpoint'], data={
    'grant_type': 'password',
    'username': creds['saak'],
    'password': creds['sask'],
    'client_id': creds['client_id'],
    'client_secret': creds['client_secret'],
})
token_data = response.json()
print(f"Token: {token_data['access_token'][:20]}...")

Verify: Response contains access_token and token_type of Bearer. expires_in should be approximately 7200.

4. Call an ION API with the Bearer token

Include the access token as a Bearer token in the Authorization header. [src2]

headers = {
    'Authorization': f"Bearer {token_data['access_token']}",
    'Accept': 'application/json',
}
response = requests.get(
    f"{creds['ionapi_url']}/ifsservice/usermgt/v2/users/me",
    headers=headers
)
print(response.json())

Verify: GET /ifsservice/usermgt/v2/users/me returns JSON with user details. HTTP 200 confirms auth is working.

5. Handle token refresh

If refresh tokens are enabled, exchange the refresh token for a new access token before expiry. Always store the latest rotated refresh token. [src5]

new_tokens = requests.post(creds['token_endpoint'], data={
    'grant_type': 'refresh_token',
    'refresh_token': token_data['refresh_token'],
    'client_id': creds['client_id'],
    'client_secret': creds['client_secret'],
}).json()
# CRITICAL: Store the NEW refresh_token -- it rotates every ~8h
current_refresh = new_tokens.get('refresh_token', token_data['refresh_token'])

Verify: New access_token returned with fresh expires_in.

6. Revoke tokens on completion

Revoke refresh tokens at the end of integration sessions to prevent orphan grants. [src3]

requests.post(
    f"{creds['portal_url']}/revoke_token.oauth2",
    data={'token': refresh_token, 'token_type_hint': 'refresh_token'},
    auth=(creds['client_id'], creds['client_secret'])
)

Verify: HTTP 200. Subsequent API calls with revoked token return 401.

Code Examples

Python: Complete Backend Service Authentication

# Input:  .ionapi credentials file path
# Output: Authenticated API call to Infor ION API Gateway

import json, requests, time

class InforAuthClient:
    def __init__(self, ionapi_path):
        with open(ionapi_path, 'r') as f:
            creds = json.load(f)
        self.client_id = creds['ci']
        self.client_secret = creds['cs']
        self.token_url = f"{creds['pu']}/{creds['ot']}"
        self.ionapi_url = creds.get('iu', '')
        self.saak = creds.get('saak', '')
        self.sask = creds.get('sask', '')
        self._token = None
        self._token_expiry = 0

    def get_token(self):
        if self._token and time.time() < self._token_expiry - 300:
            return self._token
        resp = requests.post(self.token_url, data={
            'grant_type': 'password',
            'username': self.saak, 'password': self.sask,
            'client_id': self.client_id, 'client_secret': self.client_secret,
        })
        resp.raise_for_status()
        data = resp.json()
        self._token = data['access_token']
        self._token_expiry = time.time() + data.get('expires_in', 7200)
        return self._token

    def call_api(self, endpoint, method='GET', payload=None):
        headers = {'Authorization': f'Bearer {self.get_token()}', 'Accept': 'application/json'}
        resp = requests.request(method, f'{self.ionapi_url}{endpoint}', headers=headers, json=payload)
        resp.raise_for_status()
        return resp.json()

client = InforAuthClient('/path/to/credentials.ionapi')
user = client.call_api('/ifsservice/usermgt/v2/users/me')
print(f"Connected as: {user}")

JavaScript/Node.js: Backend Service Authentication

// Input:  Path to .ionapi credentials file
// Output: Authenticated API call to ION API Gateway
// npm install axios
const axios = require('axios');
const fs = require('fs');

class InforAuthClient {
  constructor(ionapiPath) {
    const creds = JSON.parse(fs.readFileSync(ionapiPath, 'utf8'));
    this.clientId = creds.ci;
    this.clientSecret = creds.cs;
    this.tokenUrl = `${creds.pu}/${creds.ot}`;
    this.ionapiUrl = creds.iu || '';
    this.saak = creds.saak || '';
    this.sask = creds.sask || '';
    this._token = null;
    this._tokenExpiry = 0;
  }

  async getToken() {
    if (this._token && Date.now() < this._tokenExpiry - 300000) return this._token;
    const params = new URLSearchParams({
      grant_type: 'password', username: this.saak, password: this.sask,
      client_id: this.clientId, client_secret: this.clientSecret,
    });
    const resp = await axios.post(this.tokenUrl, params.toString(),
      { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } });
    this._token = resp.data.access_token;
    this._tokenExpiry = Date.now() + (resp.data.expires_in || 7200) * 1000;
    return this._token;
  }

  async callApi(endpoint, method = 'GET', data = null) {
    const token = await this.getToken();
    const resp = await axios({ method, url: `${this.ionapiUrl}${endpoint}`,
      headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' }, data });
    return resp.data;
  }
}

(async () => {
  const client = new InforAuthClient('/path/to/credentials.ionapi');
  const user = await client.callApi('/ifsservice/usermgt/v2/users/me');
  console.log('Connected as:', user);
})();

cURL: Quick token acquisition and API test

# Input:  .ionapi file with ci, cs, saak, sask, pu, ot, iu
# Output: Access token + IFS user info

IONAPI_FILE="/path/to/credentials.ionapi"
CI=$(jq -r '.ci' "$IONAPI_FILE")
CS=$(jq -r '.cs' "$IONAPI_FILE")
SAAK=$(jq -r '.saak' "$IONAPI_FILE")
SASK=$(jq -r '.sask' "$IONAPI_FILE")
PU=$(jq -r '.pu' "$IONAPI_FILE")
OT=$(jq -r '.ot' "$IONAPI_FILE")
IU=$(jq -r '.iu' "$IONAPI_FILE")

# Acquire token
TOKEN=$(curl -s -X POST "${PU}/${OT}" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=password&username=${SAAK}&password=${SASK}&client_id=${CI}&client_secret=${CS}" \
  | jq -r '.access_token')

# Verify
curl -s "${IU}/ifsservice/usermgt/v2/users/me" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/json" | jq .

Data Mapping

.ionapi File Field Reference

FieldFull NameRequired ForTypeDescription
tiTenant IDAllStringIdentifies the Infor multi-tenant environment
cnCommon NameAllStringDisplay name of the authorized application
ciClient IDAllStringOAuth 2.0 client identifier
csClient SecretAllStringOAuth 2.0 client secret (sensitive)
iuION API URLAllURLBase URL for all API calls after authentication
puPortal URLAllURLBase URL for IFS/OAuth endpoints
oaOAuth AuthorizationAuth Code, ImplicitPathAuthorization endpoint path (append to pu)
otOAuth TokenAllPathToken endpoint path (append to pu)
orOAuth RevokeAllPathToken revocation endpoint path
saakService Account Access KeyBackend ServiceStringUsername equivalent for Resource Owner grant
saskService Account Secret KeyBackend ServiceStringPassword equivalent for Resource Owner grant
vVersionAllStringCredentials file format version

Data Type Gotchas

Error Handling & Failure Points

Common Error Codes

CodeMeaningCauseResolution
401UnauthorizedAccess token expired, malformed, or revokedRe-acquire token; check token_endpoint URL matches tenant [src1]
403Forbidden / Scope mismatchAuthorized app lacks required scopes, or service account user has insufficient IFS rolesAdd required scopes to authorized app; verify IFS user security roles [src2]
400Invalid grantWrong grant_type, expired/rotated refresh token, or incorrect saak/saskVerify all 4 credentials from .ionapi file; check refresh token rotation [src3]
429Too Many RequestsExceeded configured quota or spike arrest thresholdImplement exponential backoff; check endpoint policy quotas [src4]
504Gateway TimeoutAPI call exceeded gateway timeout (default 1 min, max 5 min)Reduce payload/query size; request timeout extension via admin [src7]
invalid_grantToken exchange failureRefresh token rotated and old token reused, or service account disabledUse latest rotated refresh_token; verify service account is active [src5]

Failure Points in Production

Anti-Patterns

Wrong: Hardcoding token endpoint URLs

# BAD -- endpoint URL hardcoded, breaks when switching tenants
TOKEN_URL = "https://mingle-ionapi.inforcloudsuite.com/TENANT_ID/as/token.oauth2"
response = requests.post(TOKEN_URL, data=payload)

Correct: Read endpoints from .ionapi file

# GOOD -- endpoints parsed from .ionapi file, portable across environments
with open('credentials.ionapi', 'r') as f:
    creds = json.load(f)
token_url = f"{creds['pu']}/{creds['ot']}"
response = requests.post(token_url, data=payload)

Wrong: Ignoring refresh token rotation

# BAD -- caches initial refresh_token, ignores rotated token
initial_refresh = token_data['refresh_token']
for batch in batches:
    new_tokens = refresh(initial_refresh)  # Fails after ~8h
    process(batch, new_tokens['access_token'])

Correct: Always store the latest rotated refresh token

# GOOD -- updates refresh_token from every response
current_refresh = token_data['refresh_token']
for batch in batches:
    new_tokens = refresh(current_refresh)
    current_refresh = new_tokens.get('refresh_token', current_refresh)
    save_to_disk(current_refresh)
    process(batch, new_tokens['access_token'])

Wrong: Using same .ionapi file across tenants

# BAD -- one .ionapi file reused for dev, test, and prod
client = InforAuthClient('shared-credentials.ionapi')

Correct: Separate .ionapi files per environment

# GOOD -- environment-specific credentials
import os
env = os.environ.get('INFOR_ENV', 'dev')
client = InforAuthClient(f'/secrets/infor-{env}.ionapi')

Common Pitfalls

Diagnostic Commands

# Parse .ionapi file and display endpoints (mask secrets)
IONAPI_FILE="/path/to/credentials.ionapi"
jq '{tenant: .ti, name: .cn, portal_url: .pu, ionapi_url: .iu, has_saak: (.saak != null)}' "$IONAPI_FILE"

# Acquire token and display metadata
CI=$(jq -r '.ci' "$IONAPI_FILE"); CS=$(jq -r '.cs' "$IONAPI_FILE")
SAAK=$(jq -r '.saak' "$IONAPI_FILE"); SASK=$(jq -r '.sask' "$IONAPI_FILE")
PU=$(jq -r '.pu' "$IONAPI_FILE"); OT=$(jq -r '.ot' "$IONAPI_FILE")

TOKEN_RESPONSE=$(curl -s -X POST "${PU}/${OT}" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=password&username=${SAAK}&password=${SASK}&client_id=${CI}&client_secret=${CS}")

echo "$TOKEN_RESPONSE" | jq '{token_type, expires_in, scope, has_refresh: (.refresh_token != null)}'

# Test API access
IU=$(jq -r '.iu' "$IONAPI_FILE")
TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r '.access_token')
curl -s "${IU}/ifsservice/usermgt/v2/users/me" -H "Authorization: Bearer $TOKEN" -H "Accept: application/json" | jq .

# Check if specific API scope is accessible
curl -s -o /dev/null -w "%{http_code}" "${IU}/YOUR_API_ENDPOINT" -H "Authorization: Bearer $TOKEN"
# 200 = OK, 403 = scope missing

Version History & Compatibility

VersionReleaseStatusKey ChangesNotes
Infor OS 2026.x2026-01CurrentRefresh token grant lifetime configuration GALatest version
Infor OS 2025.x2025-01SupportedRefresh token rotation enabled by defaultBreaking: apps caching old refresh tokens fail
Infor OS 2024.x2024-01SupportedEndpoint policies (quota, throttling, cache)New rate limiting capabilities
Infor OS 2023.x2023-01LegacyOAuth 2.0 scopes enforcement expandedMore APIs require explicit scope assignment

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
Building server-to-server integrations with any Infor CloudSuite product via ION API GatewayIntegrating with on-premises Infor OS (ADFS-based)IFS with ADFS claims-based authentication
Need unattended batch/ETL access to Infor Data Lake or CloudSuite APIsEmbedding a widget inside Infor Ming.le that already has SSOSAML Bearer grant (reuse existing SSO token)
Want portable, environment-agnostic auth using .ionapi credential filesUsing IFS Cloud from IFS AB (different vendor entirely)IFS Cloud OAuth 2.0 documentation (IFS AB)
Integrating third-party iPaaS (MuleSoft, Boomi, Workato) with InforAccessing Infor APIs from within Infor Process Automation (IPA)IPA internal service mesh (no external OAuth needed)

Important Caveats

Related Units