本文へ移動
cccskills
無料GitHub で公開

connected-app-troubleshooting

Troubleshooting Connected App OAuth flows — IP relaxation vs IP restriction, refresh token policy traps (default kills the connection on first refresh), session-revocation semantics, the OAuth error-code catalog (`invalid_grant`, `invalid_client_id`, `unsupported_grant_type`), per-user vs admin-pre-approved flows, and the user-policy check (Connected App must be assigned to the user via profile / permset). Covers the Login History debug trail. NOT for designing the OAuth flow itself — use integration/oauth-flows-and-connected-apps. NOT for a SAML / SSO login failure — use security/sso-saml-troubleshooting. Trigger keywords: 'invalid_grant', 'OAUTH_APP_BLOCKED', 'redirect_uri_mismatch', 'LoginHistory Status', 'LoginSubType', 'OauthRefreshToken', 'ForwardedForIp', 'SetupAuditTrail connected app', 'refreshTokenPolicy zero', 'isSecretRequiredForRefreshToken', 'works once then fails', 'diagnosis record'.

インストール方法を見る

含まれるファイル(9)

  • SKILL.md24.3 KB
  • references/examples.md5.8 KB
  • references/gotchas.md13.2 KB
  • references/llm-anti-patterns.md6.6 KB
  • references/metadata-examples.md19.8 KB
  • references/well-architected.md5.5 KB
  • scripts/check_connected_app_troubleshooting.py20.3 KB
  • templates/connected-app-diagnosis-record.yaml4.1 KB
  • templates/connected-app-troubleshooting-template.md3.6 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Connected App Troubleshooting

OAuth via Connected Apps has a consistent set of failure modes that admins hit repeatedly: refresh tokens silently revoked, users not assigned to the app, IP restrictions blocking the caller, callback URL mismatches, and the error catalog being opaque ("invalid_grant" can mean five different things).

This skill is the diagnostic playbook. It assumes a Connected App exists and an OAuth flow is failing; the input is the error symptom, the output is the next action.

What this skill is NOT. Designing the OAuth architecture (which flow to pick, JWT setup, certificate management) is integration/oauth-flows-and-connected-apps, and authoring the app file itself is admin/connected-apps-and-auth. SAML / SSO debugging is security/sso-saml-troubleshooting. This skill is for the "OAuth-via-Connected-App is failing; what now" moment.


Before Starting

  • Capture the error code verbatim. invalid_grant vs invalid_client_id vs unsupported_grant_type mean different things.
  • Identify the flow. Web Server, JWT Bearer, User Agent, Device, Refresh Token, Username-Password (deprecated for most uses).
  • Identify the user. Known interactive user, dedicated integration user, Connected App pre-approved user.
  • Pull Login History. Setup → Login History filtered by the user / time window. The Salesforce side captures every login attempt with status code; many failures show here even when the client only sees a generic OAuth error.

Questions to Ask Before Configuring

Ask these before changing a single Connected App field. A failing OAuth flow has one true cause and four plausible ones; every question below eliminates a branch of the decision table using evidence that already exists in the org.

AskWhy it mattersWhat a good answer adds
"Paste the response body, not your summary of it."The token endpoint's error string and the HTTP status are different signals. REST API documents 401 as "The session ID or OAuth token used has expired or is invalid", and 403 as a refusal to check permissions againstThe row of the runbook table to start on, and whether this is an auth failure or a post-auth authorization failure
"Which grant is failing — the first authorization, or a later refresh?"LoginSubType separates OauthWebServer from OauthRefreshToken, so Salesforce already recorded which half brokeWhether to inspect oauthConfig (initial exchange) or oauthPolicy (renewal) — they are different fields
"Did this ever work, and when did it last succeed?"A working-then-broken integration points at a policy change or a token lifetime, not at configuration; a never-worked one points at pre-authorization or callback URLThe SetupAuditTrail window to search and whether a LoginHistory success row exists to compare against
"Who is the authenticating user, by Id?"UserId is one of only twelve filterable LoginHistory fields, so it is the anchor of every evidence queryA query that compiles, instead of one rejected on WHERE Application or WHERE Status
"Can the caller enumerate its egress IPs, and does anything proxy the request?"ipRelaxation ENFORCE is evaluated against the org's IP restrictions, and SourceIp records the first proxy rather than the originWhether the IP branch is even testable from Salesforce-side evidence (gotcha 12)
"Who will run the verification query, and do they hold Customize Application?"OauthToken shows an under-privileged caller only their own rows, so a clean result can be an artefact of the reviewer's permissionsA named runner for the evidence step, so an empty result means something
"Is there a sandbox with the same policy values, and has My Domain been deployed in it?"The reproduction is only valid if the login host and the policy block match; callback URLs are host-specificA safe place to test the fix before touching the production app

What a proper configuration adds over just doing it: the fix is attached to a written diagnosis record — symptom, verbatim error, the evidence rows that ruled the other causes out, and a root-cause category — so the next person who sees invalid_grant on this app does not restart the same five-branch search from zero.


Core Concepts

The OAuth error catalog

UNVERIFIED (2026-09-04): the token-endpoint error strings below are not documented in this skill's source set — the REST API Developer Guide covers the HTTP layer (401 as an expired or invalid session/OAuth token, 403 as a refusal to check permissions against) but not the OAuth vocabulary. Treat each string as a hint and confirm the diagnosis with the evidence columns in references/metadata-examples.md § 1, which are grounded.

Error codeCommon causeFix
invalid_grantRefresh token revoked / expired / not issued for this clientVerify Refresh Token Policy on the Connected App; re-authorize the user
invalid_client_idWrong Consumer KeyCheck the Consumer Key matches the Connected App in the right org
invalid_client_credentialsWrong Consumer SecretReset / fetch the secret in Setup
unsupported_grant_typeConnected App's "Permitted Users" or "OAuth Policies" don't permit this flowEdit the Connected App; ensure the flow's grant type is enabled
redirect_uri_mismatchCallback URL in app doesn't match the request's redirect_uriUpdate the Connected App's callback URL list
inactive_userUser is deactivatedActivate the user OR use a different user
IP_RESTRICTEDConnected App's IP relaxation = "Enforce IP restrictions" + caller IP not on user's profile login rangeEither: relax to "Relax IP restrictions for activated devices", OR add IP to user's profile range
error=login_required (silent)Session expired but Connected App is configured for SSO bypassUser must re-authenticate; auto-refresh isn't applicable
OAUTH_APP_BLOCKEDSetup → Connected Apps Usage → admin blocked the appUnblock or pick a different app

Refresh Token Policy: the silent-killer setting

On the Connected App, Refresh Token Policy has four values:

ValueBehavior
Refresh token is valid until revokedToken works indefinitely until explicitly revoked.
Immediately expire refresh tokenRefresh token expires after one use.
Expire refresh token if not used for N daysSliding window; inactive refresh tokens expire.
Expire refresh token after N daysHard expiry from issuance.

In metadata the field is refreshTokenPolicy on ConnectedAppOauthPolicy, it is Required, and its documented values are zero, infinite, specific_lifetime:number:HOURS|DAYS|MONTHS and specific_inactivity:number:HOURS|DAYS|MONTHS (Metadata API Developer Guide, ConnectedAppOauthPolicy).

Correction to the row above, grounded: infinite is the documented default — "the refresh token is used indefinitely, unless revoked by the user or Salesforce admin. Default setting." The Setup UI in an older org may have presented a different pre-selected option. UNVERIFIED (2026-09-04): the claim that older orgs defaulted to "Immediately expire" is not in the Metadata API guide, which documents infinite as the default; treat it as a field observation and read the deployed value rather than assuming either default.

zero is the value that produces the classic "works once, then dies" report: "The refresh token is invalid immediately. The user can use the current session (access token) already issued, but can't obtain a new session when the access token expires." The failure therefore surfaces one access-token lifetime after the change that caused it — which is why the SetupAuditTrail search window has to start before the first failure, not at it.

Right answer for server-to-server: an explicit value you chose. infinite only when a revocation runbook exists; specific_inactivity: when the integration has a predictable cadence. The policy-vs-kill-switch distinction and the revoke procedure belong to admin/integration-admin-connected-apps.

IP Relaxation

SettingBehavior
Enforce IP restrictionsApply user's profile IP range; calls from outside fail with IP_RESTRICTED.
Relax IP restrictions for activated devicesFirst use prompts a device-verification email; subsequent calls from the verified device skip IP check.
Relax IP restrictionsSkip IP check for this Connected App.

For server-to-server integrations from cloud infrastructure (AWS, Azure, etc.) where the IP set is large or rotating, "Relax IP restrictions" plus a tightly-scoped integration user is the standard pattern.

User assignment to the Connected App

The user authenticating via the Connected App must be authorized to use it. Two models:

  • All users may self-authorize. Anyone with API access can use the app; the user sees a consent screen on first use.
  • Admin-pre-approved users only. The app must be assigned to the user via a profile or permission set's "Connected App Access" entries. Without assignment, the user gets OAUTH_APP_BLOCKED or similar.

Server-to-server integrations should use admin-pre-approved + a dedicated integration user. The integration user has only the permsets needed; the Connected App is assigned to those permsets specifically.

Login History as the diagnostic source

Setup → Login History (or LoginHistory SOQL):

  • Status column tells you "Success" / "Failed: User not assigned to Connected App" / "Failed: Restricted IP" / "Invalid Password" / etc.
  • SourceIp verifies the caller's IP.
  • Application confirms the Connected App in use.
  • AuthenticationServiceId identifies the auth method.

Status is the fastest discriminator, because it "[d]isplays the status of the attempted login. Status is either success or a reason for failure" — a sentence the Object Reference does not expand into a value list, so read the strings your org actually emits rather than matching against a remembered catalog.

LoginSubType is the field that names the grant: OauthWebServer, OauthRefreshToken, OauthClientCredentials, OauthUserAgent, OauthTokenExchange, OAuthDevice, OauthUsernamePassword and the hybrid variants. It tells you which half of the dance failed before you open a single Setup page.

Neither is filterable. LoginHistory publishes a closed list of twelve filterable fields, and Status, LoginSubType and SourceIp are outside it (gotcha 11). Anchor every evidence query on UserId and LoginTime, then read the rest off the returned rows. The ready-made queries are in references/metadata-examples.md.

Failures often show in Login History with a clearer cause than the client receives. Always check Login History before guessing.


Common Patterns

Pattern A — JWT Bearer Flow setup that "works once, then dies"

Symptom. First call succeeds; subsequent calls fail with invalid_grant.

Cause. Refresh Token Policy isn't relevant for JWT Bearer (JWT itself is the credential, no refresh token), but the JWT signature validation may fail if:

  • Certificate expired.
  • Wrong certificate referenced (Connected App keyed to one cert, client signing with another).
  • Username mismatch (sub claim must be the username, exactly).

Right answer. Check certificate expiry. Match the certificate in the Connected App's Use digital signatures setting against the cert the client signs with. Test with a fresh JWT generated manually to isolate variables.

Pattern B — Server-to-server integration's refresh token dies on day 2

Symptom. Web Server flow integration works on Monday; fails Tuesday with invalid_grant.

Cause. Refresh Token Policy is "Immediately expire refresh token" (default in older orgs) or a short window.

Right answer. Set Refresh Token Policy = "Refresh token is valid until revoked." Re-authorize the integration user (initial authorization captures a new refresh token under the new policy).

Pattern C — User can't access the Connected App

Symptom. User OAuth flow fails with OAUTH_APP_BLOCKED or a silent fall-through.

Diagnosis. Setup → Apps → Connected Apps → click the app → "OAuth Policies" → "Permitted Users":

  • All users may self-authorize — user gets consent prompt.
  • Admin approved users are pre-authorized — user must be assigned via profile or permset.

For "Admin approved" model: Setup → Profiles or Permission Sets → edit the user's profile/permset → "Connected App Access" → enable the app.

Pattern D — Cloud-hosted integration hits IP restriction

Symptom. Integration on AWS Lambda / Heroku fails with IP_RESTRICTED from a different IP each time.

Cause. Connected App IP Relaxation = "Enforce IP restrictions"

  • user's profile has a tight IP range that doesn't match the cloud IP pool.

Right answer. Connected App IP Relaxation = "Relax IP restrictions" (cloud IPs are too dynamic for IP-range pinning). Compensate by tightening the integration user's permission scope and rotating credentials regularly.

Pattern E — Connected App admin-blocked for security review

Symptom. All flows for the app fail with OAUTH_APP_BLOCKED.

Cause. Setup → Connected Apps OAuth Usage → admin set the app to "Block" while reviewing or revoking access.

Fix. Unblock the app, OR (if the block was intentional) migrate the integration to a different Connected App that's been approved.


Decision Guidance

SymptomDiagnosisFix
invalid_grant after first dayRefresh token policy too aggressiveSet to "Valid until revoked"
invalid_grant immediately on first callRefresh token never issued (wrong scope, wrong user)Add refresh_token scope; re-auth
invalid_client_idWrong Consumer KeyVerify against Connected App
IP_RESTRICTED from cloud infraTight IP enforcementRelax IP restrictions
OAUTH_APP_BLOCKEDUser not assigned OR app admin-blockedProfile / permset assignment OR unblock
redirect_uri_mismatchCallback URL not in app's listAdd the URL to the app
Silent failure / popup blockedOAuth Web Server flow + popup blockerTest in incognito; or use device flow
unsupported_grant_typeFlow not enabled in Connected App's OAuth PoliciesEdit app; enable the grant type
Inactive user errorIntegration user deactivatedReactivate or migrate to new user
First call works, every subsequent call failsRefresh token policy = "Immediately expire"Set to "Valid until revoked"

Recommended Workflow

  1. Open a diagnosis record and paste the raw failure into it. Copy the YAML skeleton from templates/connected-app-diagnosis-record.yaml. Fill symptom, error (verbatim response body, secrets replaced with [REDACTED]), app, user-id and first-failure. An error you have paraphrased is an error you have already started guessing about.
  2. Run the evidence query set in references/metadata-examples.md § 2 and record every result. LoginHistory for the user over the failure window, OauthToken for the app, SetupAuditTrail for recent Setup changes. Each evidence entry needs both a query and a result — including "0 rows", which is itself a finding. Run the OauthToken query as a user holding Customize Application, or the empty result proves nothing.
  3. Walk the runbook table in references/metadata-examples.md § 1 top to bottom. It goes symptom → error string → the LoginHistory Status / LoginSubType to look for → the exact ConnectedApp XML path to inspect → the fix. Stop at the first row whose evidence column matches what step 2 actually returned; assign the matching root-cause category.
  4. Inspect the deployed policy, not the Setup screen. Retrieve the app and compare its oauthConfig / oauthPolicy against the annotated excerpt in references/metadata-examples.md § 3. admin/connected-apps-and-auth owns the full file; this step only reads the four fields the runbook named.
  5. Reproduce in a sandbox before changing production using the procedure in references/metadata-examples.md § 4 — matching policy block, the sandbox login host, and the sandbox-suffixed username. Skip this only when the diagnosis is a user assignment, which is reversible in one click.
  6. Apply the fix, re-authorize from a clean state, and re-run the same queries from step 2. Paste the post-fix rows into verified-by. A refresh-policy fix is not verified until a LoginSubType = OauthRefreshToken row returns Success, because the initial authorization would have succeeded either way.
  7. Lint the record and the metadata. python3 scripts/check_connected_app_troubleshooting.py --manifest-dir force-app/main/default fails on an incomplete diagnosis record and on the three policy combinations that produce the classic failures. Commit the record next to the change.

Review Checklist

  • Error code captured exactly from the client.
  • Login History checked for the user / time window.
  • Connected App Refresh Token Policy is "Valid until revoked" for server-to-server.
  • User is assigned to the Connected App via profile or permset.
  • Callback URL list includes every URL the client uses.
  • IP Relaxation matches the integration's IP source (relax for cloud, enforce for on-prem).
  • Cert (for JWT Bearer) is current and matches client.
  • Test post-fix verifies Success in Login History.

Salesforce-Specific Gotchas

  1. refreshTokenPolicy zero works for one access-token lifetime, then dies — and the documented default is infinite, not "immediately expire". (See references/gotchas.md § 1.)
  2. invalid_grant is overloaded — five different causes; check Login History to disambiguate. (See references/gotchas.md § 2.)
  3. User assignment to Connected App is required for "Admin pre-approved" mode. Profile or permset entry. (See references/gotchas.md § 3.)
  4. redirect_uri mismatch is character-exact — trailing slashes matter. (See references/gotchas.md § 4.)
  5. JWT sub claim must be the user's Username, not Email. (See references/gotchas.md § 5.)
  6. Connected App admin-block is a separate setting from disabling OAuth. (See references/gotchas.md § 6.)
  7. IP Relaxation interacts with the user's profile IP range — both must permit the caller. (See references/gotchas.md § 7.)
  8. LoginSubType names the failing grant but cannot anchor a WHERE clause — it is outside the twelve filterable fields. (See references/gotchas.md § 11.)
  9. ForwardedForIp is never populated for an OAuth login, so the documented way to see past a proxy does not exist for this flow. (See references/gotchas.md § 12.)
  10. SetupAuditTrail.Section and Display are not filterable — the "who changed the policy" query has to filter on Action. (See references/gotchas.md § 13.)
  11. Secret-required is two independent switches, so an app can pass its first token exchange and fail every refresh. (See references/gotchas.md § 14.)

Output Artifacts

ArtifactDescription
Error → Cause → Fix mappingTranslated explanation for the specific OAuth error
Connected App settings changeRefresh Token Policy, IP Relaxation, Permitted Users, Callback URL
User assignment updateProfile / permset Connected App Access entry
Verification via Login HistoryConfirms the test post-fix shows Success

Reference Files

FileRead it when
references/metadata-examples.mdYou are running a live diagnosis — the symptom-to-fix runbook table, the three evidence queries, the oauthPolicy excerpt to compare against, the sandbox reproduction procedure, and the diagnosis-record shape all live here
references/gotchas.mdAn evidence query returned an answer you don't trust, a fix didn't take, or the failure timestamp doesn't line up with the change that caused it
references/examples.mdYou want the worked narratives end to end, including a completed diagnosis record
references/well-architected.mdYou need the Security / Reliability framing for a post-incident write-up, or the official source behind a claim here
references/llm-anti-patterns.mdYou are reviewing AI-generated OAuth triage advice before acting on it
templates/connected-app-troubleshooting-template.mdAt the start of a triage, to capture the symptom, flow, user and evidence in one place
templates/connected-app-diagnosis-record.yamlThe record you fill during the triage and commit alongside the fix — linted by the checker
scripts/check_connected_app_troubleshooting.pyBefore deploying a policy fix, and again once the diagnosis record is written

Related Skills

  • integration/oauth-flows-and-connected-apps — choosing and designing the OAuth grant flow; this skill is the troubleshooting half.
  • admin/connected-apps-and-auth — authoring the full connectedApp / External Client App / Named Credential file, and the artefact decision.
  • admin/integration-admin-connected-apps — the operations half: hardening, revocation, key and secret rotation, monitoring, and the periodic review.
  • security/oauth-token-management — token lifecycle and revocation semantics beyond a single failing flow.
  • security/login-forensics — when the question is "who logged in and from where", not "why did this grant fail".
  • security/sso-saml-troubleshooting — SAML-specific debugging (different runtime, different evidence).
  • apex/apex-jwt-bearer-flow — JWT-specific setup including certificate management.
  • integration/named-credentials-setup — External Credentials and Named Credentials, which interact with these OAuth flows.
  • admin/org-setup-and-configuration — My Domain, trusted IP ranges and session settings, which the login host and the IP branch both depend on.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Use this skill when writing test-first, behavior-driven acceptance criteria in Given/When/Then format for a Salesforce user story. Covers happy path, edge cases, negative paths, permission boundaries, and data-state preconditions so the AC block can drive UAT scripts and Apex test design downstream. Trigger keywords: given when then, gherkin, behavior driven AC, test first acceptance criteria, scenario outline, BDD acceptance criteria. NOT for the user-story format itself (use admin/user-story-writing-for-salesforce). NOT for UAT script writing (use admin/uat-test-case-design). NOT for Apex test method generation (use agents/test-class-generator). NOT for high-level UAT planning (use admin/uat-and-acceptance-criteria). More triggers: acceptance criteria record, ac_id, req_id on a criterion, artefact under test, negative case for a rule-type requirement, no-match fall-through criterion, oracle for a Then clause, test type apex flow manual, criteria linter, check_ac_format.py, --manifest-dir.

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

Task and Event objects: polymorphic WhatId/WhoId, Activity object model, ActivityHistory vs OpenActivity, activity timeline customization, bulk task creation, Einstein Activity Capture boundaries. NOT for turning on EAC or calendar sync — use admin/einstein-activity-capture-setup. NOT for Email-to-Case — use admin/case-management-setup. Covers ActivitiesSettings metadata, enableActivities, shared Task/Event custom fields and FLS, TaskStatus value sets, Shared Activities and TaskRelation/EventRelation, recurring and child activities, and partial-success bulk DML on Task.

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

Design Invocable Apex actions that return deterministic, agent-friendly errors instead of surfacing raw exceptions to the LLM. NOT for authoring the action itself — @InvocableMethod schema, labels, security context — use agentforce/custom-agent-actions-apex. NOT for the Apex tests that force each error branch — use agentforce/agent-action-unit-tests.

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

Use when designing how an Agentforce agent extracts structured input parameters (slots) from a user's natural-language utterance to invoke an action. Triggers: 'agent extract date from user', 'agent action argument extraction', 'agentforce slot filling', 'agent invocable input mapping', 'agent fails to fill required parameter'. NOT for action authoring (use agentforce/agent-actions) or for prompt-template variable binding (use agentforce/prompt-builder-templates).

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

Apex test patterns for @InvocableMethod agent actions: per-reason-code branch coverage, bulk safety, callout mocking, deterministic assertions. NOT for testing whether the agent routes to the right topic or answers well — use agentforce/agent-testing-and-evaluation. NOT for designing the error envelope the tests assert on — use agentforce/agent-action-error-handling.

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

Designing or reviewing Agentforce actions: Flow actions, Apex invocable actions, prompt-template actions, action naming, input and output contracts, confirmation requirements, and safe error behavior. Triggers: 'agent actions', 'build an agent that can look up an order', 'make the agent able to check order status', 'give the agent a capability', 'agent that can retrieve a record', 'flow action for agent', 'agent invocable action', 'action schema design'. NOT for writing the Apex class itself — use agentforce/custom-agent-actions-apex. NOT for the reason_code error envelope that stops an agent retry loop — use agentforce/agent-action-error-handling.

日本語の概要は準備中です。原文の説明を表示しています。

PranavNagrecha/AwesomeSalesforceSkills192026年10月4日 更新

PranavNagrecha のスキルをすべて見る

このスキルの問題を報告する