Skip to main content

AutoFlow Credentials

AutoFlow Credentials are a central, encrypted store for the authentication details your flows need when they call external systems. The HTTP Request step is the first consumer; future external-call steps will plug into the same store.

Configure each external service once, then reference it by Code from any step that talks to it. Rotate a key, swap an environment, or change a secret in one place — every flow that uses the credential picks up the change immediately. Secrets never appear in step configurations or execution logs.

What you get

CapabilityWhat it means
No secrets in flowsTokens, passwords, and client secrets live in encrypted storage, not in step configurations or logs.
One change, every flowWhen an API key rotates, update the credential and every flow using it picks up the new value.
Consistent authThe right Authorization header (or query parameter) is produced for you per auth type.
Cleaner execution logsAuth-injected headers are filtered out of the execution log automatically.
Allow-list URL guardWhen base URLs are configured, requests are checked when the step runs — credentials can't be exfiltrated to a spy URL by a misconfigured or malicious step.
Per-companyEach Business Central company has its own credentials. The same Code in different companies can point to different accounts.

Setting up a credential

Open AutoFlow Credentials from Tell-Me, then choose New. The card is split into General, Authentication, Default Headers, and Base URLs.

General

FieldWhat it does
CodeUp to 20 characters. Use a convention like MYAPI-PROD or SHOP-DE-TEST. This is what the HTTP-Request step shows in its lookup.
DescriptionFree text shown in lookups.
Test URL (optional)URL the Test action calls. If empty, Test falls back to the first base URL. Set this to a low-impact health endpoint when no base URL is appropriate for a probe.

Authentication

Pick the type that matches the target system. The card shows only the fields relevant to the chosen type.

A soft inline warning appears if a required secret hasn't been stored yet — saving the credential is still allowed, but flow runs that need it will fail clearly until the secret is set.

No authentication

For public endpoints. No secret is stored.

API key

The API issues a static key sent on every request. Configure:

  • API key name — the header name or query-parameter name to use.
  • API key locationHeader (most common) or Query parameter.
  • API key value — entered once, then masked. Stored encrypted.

Basic (username + password)

HTTP Basic authentication. Configure:

  • Username
  • Password — entered once, then masked.

AutoFlow sends Authorization: Basic <base64(user:pass)>.

Bearer token

A static bearer token. Configure:

  • Bearer token — entered once, then masked.

AutoFlow sends Authorization: Bearer <token>. Tokens of any size are supported (the storage layer chunks transparently).

OAuth 2.0 client credentials

Server-to-server OAuth. AutoFlow fetches a token from the authorization server, caches it, and re-uses it until shortly before expiry. Configure:

  • OAuth token URL — the token endpoint of the authorization server.
  • OAuth client ID
  • Client secret — entered once, then masked.
  • OAuth scope (optional) — space-separated list of scopes.
  • OAuth grant type — currently only Client credentials.
  • OAuth client-credential transport — choose how the client ID and secret are sent to the token endpoint:
    • Request body (form-encoded) — sent as client_id=…&client_secret=… in the body. Default.
    • Authorization header (HTTP Basic) — sent as Authorization: Basic <base64(client_id:client_secret)>. Required by some IdPs (e.g. older ADFS).

Tokens are cached encrypted and refreshed automatically when they're within 60 seconds of expiry. The expires_in field is parsed permissively — both JSON integer (3600) and JSON string ("3600") shapes are accepted, with a 3600-second fallback if the value is missing or unparseable.

If the token endpoint returns a non-success status, the error includes the first 200 characters of the response body (per RFC 6749 the failure body is error + error_description, never tokens) so you can see what the IdP is actually saying without leaking secrets.

Base URLs

A list of allowed URL prefixes for this credential. Two roles, both important:

  1. Convenience prefix. When the HTTP-Request step uses a relative URL like /widgets, AutoFlow prefixes it with the first entry in this list.
  2. Security allow-list. When the HTTP-Request step uses an absolute URL, AutoFlow checks that the URL starts with one of the configured base URLs. If it doesn't match, the request is rejected before fire — the credential's secret is never sent. This prevents an attacker (or a misconfigured flow) from routing a credential's token to a spy URL like https://attacker.test/exfil.

Examples (with base URL https://api.example/v1):

Step URLOutcome
/widgetsPrefixed → https://api.example/v1/widgets
https://api.example/v1/widgets?page=2Allowed (matches prefix) ✅
https://api.example/v1Allowed (exact match) ✅
https://api.example/v123/widgetsRejected/v1 boundary check
https://attacker.test/exfilRejected — host doesn't match

Multiple entries are supported — useful when one credential authorises an API with regional or environment variants (api-eu.example.com, api-us.example.com). The first one wins for relative-URL prefixing; all of them are valid prefixes for absolute-URL validation.

The HTTP-Request step's config page also runs the allow-list check at design time — if you type a URL that won't pass, you'll see a soft warning right under the field. The check that runs when the flow fires stays the hard guard.

Leaving the list empty allows any URL — no prefixing, no validation. Use this only when you really want a wide-open credential, accepting the risk that a flow misconfiguration could send the credential's secret anywhere.

Plain-HTTP warning

http:// base URLs are still allowed (on-prem and localhost workflows need them) but the card surfaces a soft warning when any base URL uses plain HTTP, since secrets sent over http:// can be intercepted on the network path. Use https:// for production endpoints.

Default headers

Headers that should always be sent with this credential — for example User-Agent, X-Tenant, Accept. Applied to every request that uses the credential.

Two restrictions:

  • You can't add Authorization as a default header — that's reserved for the auth type.
  • When the credential's API-key location is Header, you can't add a default header with the same name as the configured API-key header — the auth would overwrite it anyway.

Manual headers configured on the HTTP-Request step override default headers with the same name.

Header precedence and execution log

When a step uses a credential, AutoFlow composes the outgoing request headers in this exact order:

  1. Default Headers from the credential (lowest priority).
  2. Manual headers typed on the HTTP-Request step (override defaults with the same name).
  3. Auth headers computed by the auth type (always win, even over manual headers).

Auth headers are also excluded from the execution log, so secrets never appear there. Default and manual headers are logged normally.

For ApiKey-in-Query auth, the URL after prefix resolution but before the query-parameter append is what the log shows — so the secret never lands in the log even though it's on the wire URL.

The Test action

Use Test on the credential card to send a GET to the Test URL (or first base URL if Test URL is empty). The result message shows the full HTTP status and full response body.

Useful to verify that:

  • The Base URL / Test URL is reachable.
  • Authentication actually succeeds (a 200 or 401 is far more informative than a misconfigured flow).

Lifecycle

Rotate a secret

Open the card, type the new value into the secret field, save. The old value is overwritten in encrypted storage. Cached OAuth tokens are not invalidated automatically — call Clear stored secrets if you want to force a fresh fetch.

Clear all secrets

The Clear stored secrets action permanently deletes every secret stored for this credential (API key, password, bearer token, OAuth client secret, cached OAuth tokens). The credential record itself stays.

Rename a credential

Code can be renamed via the standard BC rename. The framework moves the encrypted secrets and child rows (Default Headers, Base URLs) to the new code, then calls the auth type's OnCredentialRenamed hook so extension auth types can move their own custom-keyed secrets too.

Delete a credential

Deleting the credential record also deletes all stored secrets, child rows, and runs the auth type's OnCredentialDeleted cleanup hook. Flows that still reference the deleted credential by code will fail with a clear error the next time they run — fix the step by selecting a different credential or clearing the reference.

Multi-company

Credentials are per-company by default. To re-use the same code across multiple companies, create the credential in each company. Each company can point to a different account if needed.

Permissions

Three permission sets ship with the feature:

Permission setPurpose
mse365 AF Cred. ReadRead access to credentials and their child tables (used while flows run). Anyone running flows that use credentials needs this.
mse365 AF Cred. EditRead + write. Includes Cred. Read. Required to create, modify, or delete credentials.
mse365 AF Cred. RTWraps Cred. Read. Intended to be included by the customer's runtime / flow-execution permset, so adding flow-execution rights to a user automatically grants the read access needed to resolve credentials. Doesn't include the Edit grant.

How it's stored

Secrets are AES-encrypted by the BC platform's tenant encryption key and held in IsolatedStorage with DataScope::Company. Each secret is split into ≤200-character chunks because BC's encryption helpers cap input at 215 characters; reads reassemble the chunks transparently. Plaintext values never touch the database.

Tenant encryption prerequisite

The platform's Cryptography Management.EncryptText requires the BC tenant to have data encryption enabled.

  • BC SaaS: encryption is on by default. Nothing to do.
  • On-prem / sandbox / dev containers: a tenant admin must enable encryption once. Either via the Encryption Management page in the BC web client, or — if PowerShell Enable-NavEncryptionKey hangs (some containerised setups) — by publishing a one-shot OnPrem-target bootstrap app whose Install codeunit calls Cryptography Management.EnableEncryption(true).

When encryption is missing, every credential read or write fails with the platform message An encryption key is required to complete the request. — preferred over silent plaintext fallback.

Extending: custom auth types

The auth-type list is extensible. Use this when the target system uses an authentication scheme that isn't covered by the five built-in types — for example a legacy webshop that builds headers from a configurable secret + timestamp + HMAC.

What to implement

Add a new value to the mse365 AF Credential Auth Type enum, pointing at a codeunit that implements the mse365 AF ICredentialAuth interface:

enumextension 50100 "Acme Hmac Auth" extends "mse365 AF Credential Auth Type"
{
value(50100; AcmeHmac)
{
Caption = 'Acme HMAC';
Implementation = "mse365 AF ICredentialAuth" = "Acme Hmac Auth Impl.";
}
}

codeunit 50100 "Acme Hmac Auth Impl." implements "mse365 AF ICredentialAuth"
{
procedure BuildAuthHeaders(CredentialCode: Code[20]; RequestCtx: Interface "mse365 AF IHttpRequest Ctx."; var Headers: Dictionary of [Text[50], Text[4096]])
begin
// Compute headers from your stored secret + the current timestamp, etc.
end;

procedure RewriteUrl(CredentialCode: Code[20]; var Url: Text)
begin
// No-op for header-based auth; ApiKey-in-Query overrides this.
end;

procedure OnCredentialDeleted(CredentialCode: Code[20])
begin
// Clean up any IsolatedStorage entries you wrote under custom keys.
end;

procedure OnCredentialRenamed(OldCode: Code[20]; NewCode: Code[20])
begin
// Move IsolatedStorage entries from OldCode to NewCode under your custom keys.
// Built-in secrets and child tables are migrated by the framework.
end;
}

Custom configuration fields

If your auth type needs more than a single secret, extend the credential table and card page:

tableextension 50100 "Acme Hmac Cred Fields" extends "mse365 AF Credential"
{
fields
{
field(50100; "Acme Hmac Algorithm"; Code[10]) { Caption = 'HMAC algorithm'; }
}
}

pageextension 50100 "Acme Hmac Cred Card" extends "mse365 AF Credential Card"
{
layout
{
addlast(Authentication)
{
group(AcmeHmac)
{
Visible = Rec."Auth Type" = Rec."Auth Type"::AcmeHmac;
field("Acme Hmac Algorithm"; Rec."Acme Hmac Algorithm") { }
}
}
}
}

Store secrets via mse365 AF Credential Storage to inherit the same chunked encrypted storage — don't roll your own:

var
Storage: Codeunit "mse365 AF Credential Storage";
begin
Storage.SetSecret(CredentialCode, 'AcmeHmacSigningKey', SigningKey);
end;

Storage.DeleteAllSecrets and MoveAllSecrets only know about the framework's six built-in secret names — that's why OnCredentialDeleted and OnCredentialRenamed exist for extensions to clean up or migrate their own keys.

Interface contract

MethodCalled whenWhat to do
BuildAuthHeadersEvery outgoing requestAdd auth headers to the dictionary. Headers added here are stripped from execution logs automatically.
RewriteUrlEvery outgoing requestMutate the URL if needed (e.g. append query). The pre-rewrite URL is what gets logged.
OnCredentialDeletedCredential row deletedWipe custom IsolatedStorage entries or auxiliary table rows.
OnCredentialRenamedCredential Code renamedMove custom IsolatedStorage entries from OldCode to NewCode.

The five built-in auth types (None / ApiKey / Basic / Bearer / OAuth2CC) implement empty stubs for OnCredentialDeleted and OnCredentialRenamed — the framework's DeleteAllSecrets / MoveAllSecrets already covers them.