Skip to content

OAuth Migration Runbook from Classic Token #31

Description

@mverbraak-t4e

AFAS OAuth Migration Runbook (Target Users)

Purpose

This document helps migrate an existing AFAS Profit target connector for users from classic token authentication to OAuth client credentials.

Use targeted changes only. Do not replace complete scripts, because customer-specific logic may already be present.

Scope

Apply these changes to:

  • create.ps1
  • update.ps1
  • delete.ps1
  • disable.ps1
  • enable.ps1
  • import.ps1
  • configuration.json

Preconditions In AFAS

Before changing the connector, make sure the customer has arranged the following in AFAS Profit:

  1. An AFAS App Connector exists.
  2. The App Connector is configured for OAuth client credentials as OAuth-only (no hybrid setup with classic token + OAuth).
  3. The following values are available:
    • ClientId
    • ClientSecret
  4. The App Connector has access to the same Get/Update connectors as the old setup.

Important: advise customers to use an OAuth-only App Connector. AFAS states that in September 2026 existing Classic tokens will automatically get an end date of 15 February 2027. From that date those tokens no longer work. New Classic tokens can still be created with another end date until 31 August 2027. App connectors that have not been used for 6 months or longer are blocked.

See AFAS documentation for more information: Eigen app connector inrichten in vogelvlucht and Eigen app connector beheren.

Required connectors:

  • GetConnector: T4E_HelloID_Users_v2 (or customer-specific equivalent)
  • UpdateConnector: KnUser

Summary Of The Change

Classic flow:

  • reads Configuration.Token
  • Base64-encodes the token
  • sends Authorization: AfasToken ...

OAuth flow:

  • reads Configuration.ClientId and Configuration.ClientSecret
  • requests an access token from https://.../ProfitRestServices/oauth/token
  • sends Authorization: Bearer ...

Recommended Working Method

  1. Make a backup copy of create.ps1, update.ps1, delete.ps1, disable.ps1, enable.ps1, import.ps1, and configuration.json.
  2. Apply each change below: replace the Before block with the After block, or insert the block at the indicated anchor.
  3. Do not remove unrelated customer customizations.
  4. Validate all lifecycle actions and import after the update.

Step 1: Update configuration.json

Remove (located after the BaseUri object):

  {
    "key": "Token",
    "type": "input",
    "defaultValue": "",
    "templateOptions": {
      "label": "Token in XML format",
      "type": "password",
      "placeholder": "<token><version>1</version><data>D5R324DD5F4TRD945E530ED3CDD70D94BBDEC4C732B43F285ECB12345678</data></token>",
      "description": "",
      "required": true
    }
  },

Insert after the BaseUri object:

  {
    "key": "ClientId",
    "type": "input",
    "templateOptions": {
      "label": "ClientId",
      "placeholder": "Example: 2f8a2f4d-9a5f-41d8-aec8-6ab123456789",
      "description": "The OAuth client id of the AFAS AppConnector.",
      "required": true
    }
  },
  {
    "key": "ClientSecret",
    "type": "input",
    "templateOptions": {
      "label": "ClientSecret",
      "type": "password",
      "placeholder": "Example: 1234567890abcdef1234567890abcdef12345678",
      "description": "The OAuth client secret of the AFAS AppConnector.",
      "required": true
    }
  },

Step 2: Update create/update/delete/disable/enable/import scripts

All scripts (create.ps1, update.ps1, delete.ps1, disable.ps1, enable.ps1, import.ps1) — replace the classic header block:

Before:

$encodedToken = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($actionContext.Configuration.Token))
$authValue = "AfasToken $encodedToken"
$Headers = @{ Authorization = $authValue }
$Headers.Add("IntegrationId", "45963_140664")

After:

$tokenUri = "$($actionContext.Configuration.BaseUri)/oauth/token"
Write-Verbose "Requesting OAuth access token from [$tokenUri]"

$tokenRequestBody = @{
    grant_type    = 'client_credentials'
    client_id     = $actionContext.Configuration.ClientId
    client_secret = $actionContext.Configuration.ClientSecret
}

$tokenResponse = Invoke-RestMethod -Method Post -Uri $tokenUri -Body $tokenRequestBody -ContentType 'application/x-www-form-urlencoded' -UseBasicParsing -Verbose:$false

if ([String]::IsNullOrWhiteSpace([String]$tokenResponse.access_token)) {
    throw "OAuth token endpoint did not return an access_token."
}

if ([String]::IsNullOrWhiteSpace([String]$tokenResponse.token_type)) {
    throw "OAuth token endpoint did not return a token_type."
}

$Headers = @{ Authorization = "$($tokenResponse.token_type) $($tokenResponse.access_token)" }
$Headers.Add("IntegrationId", "45963_140664")

Step 3: Search terms to confirm classic auth is fully removed

Search all scripts for:

  • Configuration.Token
  • AfasToken
  • ToBase64String
  • ASCII.GetBytes

Expected result: no active matches remain.

Validation

After the migration, run a Preview/Test in HelloID for:

  • create.ps1
  • update.ps1
  • delete.ps1
  • disable.ps1
  • enable.ps1
  • import.ps1

Common Failure Causes

  1. The AFAS App Connector is not configured for OAuth client credentials.
  2. ClientId or ClientSecret was copied incorrectly.
  3. The App Connector lacks required Get/Update connector permissions.
  4. One script was missed and still uses classic token authentication.

Short Checklist

  1. Arrange OAuth client credentials in AFAS.
  2. Update configuration.json: add ClientId and ClientSecret, remove Token.
  3. Replace all AfasToken logic with OAuth Bearer headers in create/update/delete/disable/enable/import.
  4. Search for leftover classic-auth keywords.
  5. Run Preview/Test for all actions and import.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions