Skip to content

47700 abm token invalid errors#49770

Open
andymFleet wants to merge 23 commits into
mainfrom
47700-abm-token-invalid-errors
Open

47700 abm token invalid errors#49770
andymFleet wants to merge 23 commits into
mainfrom
47700-abm-token-invalid-errors

Conversation

@andymFleet

@andymFleet andymFleet commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Related issue: Resolves #47700

Checklist for submitter

  • Changes file added for user-visible changes in changes/, orbit/changes/ or ee/fleetd-chrome/changes.
    See Changes files for more information.

  • Input data is properly validated, SELECT * is avoided, SQL injection is prevented (using placeholders for values in statements), JS inline code is prevented especially for url redirects, and untrusted data interpolated into shell scripts/commands is validated against shell metacharacters.

  • Timeouts are implemented and retries are limited to avoid infinite loops

Testing

  • Added/updated automated tests

  • QA'd all new/changed functionality manually. Not able to do for all code paths yet

Summary by CodeRabbit

  • New Features
    • Added token_invalid status for Apple Business Manager tokens, updated automatically based on Apple responses.
    • Enhanced host DEP assignment responses with structured dep_error details (including an error classification and helpful messaging) for device lookup failures.
  • Bug Fixes
    • Improved handling of Apple-enrollment failures by distinguishing invalid/rejected tokens, expired terms, not-found devices, server/API errors, and unavailable states.
    • Added regression and unit test coverage for token invalidation and DEP device error classification.

@andymFleet
andymFleet requested a review from a team as a code owner July 22, 2026 18:54
Copilot AI review requested due to automatic review settings July 22, 2026 18:54
@andymFleet
andymFleet marked this pull request as draft July 22, 2026 18:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

This PR expands Apple Business Manager (ABM/ADE/DEP) error detection and reporting so Fleet can (a) persist a token_invalid flag on ABM tokens when Apple rejects/revokes the token, and (b) return a structured dep_error classification on the DEP assignment endpoint instead of surfacing Apple API errors directly.

Changes:

  • Add detection for DEP token_rejected and signature_invalid errors and persist token invalid state (abm_tokens.token_invalid).
  • Add dep_error to the host DEP assignment response and classify Apple DEP “device details” failures into a stable enum.
  • Add/extend unit and datastore tests to cover the new token-invalid and classification behavior.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated no comments.

Show a summary per file
File Description
server/service/hosts.go Extends DEP assignment endpoint response with dep_error and threads classification through service method.
server/service/apple_mdm_test.go Stubs new datastore method in service tests to satisfy updated ABM updater interface.
server/mock/service/service_mock.go Updates mock service signature for GetHostDEPAssignmentDetails to include DEPDeviceErrorType.
server/mock/datastore_mock.go Adds mocked datastore method for updating ABM token invalid state.
server/mdm/nanodep/godep/client.go Adds IsServerError helper to detect DEP 5xx HTTP errors.
server/mdm/nanodep/godep/account.go Adds IsTokenRejected and IsSignatureInvalid error classifiers.
server/mdm/nanodep/godep/account_test.go Adds unit tests for the new DEP error classifier helpers.
server/mdm/apple/apple_mdm.go Updates DEP client after-hook to set/clear token_invalid; adds ClassifyDEPDeviceError.
server/mdm/apple/apple_mdm_test.go Adds tests verifying token_invalid updates and DEP device error classification.
server/fleet/service.go Updates Fleet service interface to return (depError, err) for DEP assignment details.
server/fleet/mdm.go Adds TokenInvalid to ABMToken and expands ABMTermsUpdater with token-invalid setter.
server/fleet/mdm_test.go Extends DEP client tests to account for token_invalid bookkeeping behavior.
server/fleet/datastore.go Adds datastore interface method SetABMTokenInvalidForOrgName.
server/fleet/apple_mdm.go Introduces DEPDeviceErrorType enum used by dep_assignment response.
server/datastore/mysql/apple_mdm.go Selects token_invalid in ABM token queries and implements SetABMTokenInvalidForOrgName.
server/datastore/mysql/apple_mdm_test.go Adds datastore integration test coverage for ABM token invalid flag updates.
changes/47700-abm-token-invalid-dep-error User-visible changelog entry (content excluded from diff).
Files excluded by content exclusion policy (1)
  • changes/47700-abm-token-invalid-dep-error

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds token_invalid to ABM token data, database queries, datastore interfaces, and update operations. Apple DEP error predicates detect rejected tokens and invalid signatures, while the DEP client updates token status. DEP device errors are classified into typed Fleet constants, and the DEP assignment API returns structured dep_error values. Tests and mocks cover persistence, classification, client behavior, and updated service contracts.

Possibly related PRs

  • fleetdm/fleet#49896: Implements overlapping ABM token-invalid and DEP device-error plumbing, including API and UI wiring.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes implement token_invalid detection/clearing and add DEP error classification and response fields as requested in #47700.
Out of Scope Changes check ✅ Passed The added mocks, tests, and helper methods are supporting changes for the ABM/DEP error-handling work and appear in scope.
Title check ✅ Passed The title is concise and clearly reflects the main change: ABM token invalid error handling.
Description check ✅ Passed The description covers the issue link, checklist items, and testing, and the omitted sections appear mostly not applicable.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 47700-abm-token-invalid-errors

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
server/mdm/apple/apple_mdm.go (1)

1062-1062: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate IsTokenRejected || IsSignatureInvalid predicate.

This OR-condition is repeated verbatim in the after-hook and in ClassifyDEPDeviceError. Extracting a single godep.IsTokenInvalid(err) bool helper would avoid the two call sites drifting if a third "token invalid" cause is added later.

♻️ Proposed helper (in the godep package)
// IsTokenInvalid returns true if err indicates the ABM token itself is no
// longer usable (rejected, or its signature is invalid).
func IsTokenInvalid(err error) bool {
	return IsTokenRejected(err) || IsSignatureInvalid(err)
}

Also applies to: 1142-1142

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/mdm/apple/apple_mdm.go` at line 1062, Extract the repeated
token-invalid predicate into a godep.IsTokenInvalid(err) helper that combines
IsTokenRejected and IsSignatureInvalid, then replace the duplicate OR conditions
in the after-hook and ClassifyDEPDeviceError with this helper while preserving
the existing reqErr nil guard.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@server/mdm/apple/apple_mdm.go`:
- Line 1062: Extract the repeated token-invalid predicate into a
godep.IsTokenInvalid(err) helper that combines IsTokenRejected and
IsSignatureInvalid, then replace the duplicate OR conditions in the after-hook
and ClassifyDEPDeviceError with this helper while preserving the existing reqErr
nil guard.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6737e8d3-e03f-4e90-a7cc-0908ca9f7100

📥 Commits

Reviewing files that changed from the base of the PR and between 95b535a and f83e5db.

📒 Files selected for processing (17)
  • changes/47700-abm-token-invalid-dep-error
  • server/datastore/mysql/apple_mdm.go
  • server/datastore/mysql/apple_mdm_test.go
  • server/fleet/apple_mdm.go
  • server/fleet/datastore.go
  • server/fleet/mdm.go
  • server/fleet/mdm_test.go
  • server/fleet/service.go
  • server/mdm/apple/apple_mdm.go
  • server/mdm/apple/apple_mdm_test.go
  • server/mdm/nanodep/godep/account.go
  • server/mdm/nanodep/godep/account_test.go
  • server/mdm/nanodep/godep/client.go
  • server/mock/datastore_mock.go
  • server/mock/service/service_mock.go
  • server/service/apple_mdm_test.go
  • server/service/hosts.go

Comment thread server/mdm/nanodep/godep/account.go
Copilot AI review requested due to automatic review settings July 23, 2026 12:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 16 out of 17 changed files in this pull request and generated 1 comment.

Files excluded by content exclusion policy (1)
  • changes/47700-abm-token-invalid-dep-error

Comment thread server/mdm/nanodep/godep/client.go Outdated
Copilot AI review requested due to automatic review settings July 23, 2026 12:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 16 out of 17 changed files in this pull request and generated 1 comment.

Files excluded by content exclusion policy (1)
  • changes/47700-abm-token-invalid-dep-error
Comments suppressed due to low confidence (1)

server/mdm/apple/apple_mdm.go:1073

  • The token_invalid flag is only cleared on successful requests. If Apple stops returning token_rejected/signature_invalid but returns a different definitive error like T_C_NOT_SIGNED (terms expired), token_invalid will remain stuck true until a successful request happens (which may not occur while terms are expired). Consider also updating token_invalid when terms-expired errors occur so the flag reflects the current Apple response.
		tokenInvalid := reqErr != nil && (godep.IsTokenRejected(reqErr) || godep.IsSignatureInvalid(reqErr))
		if reqErr == nil || tokenInvalid {
			if _, err := updater.SetABMTokenInvalidForOrgName(ctx, orgName, tokenInvalid); err != nil {
				logger.ErrorContext(ctx, "Apple DEP client: failed to update token invalid status of ABM token", "err", err)
			}
		}

		// if the request failed due to terms not signed, or if it succeeded,
		// update the ABM token's (and possibly the app config's) flag accordingly.
		// If it failed for any other reason, do not update the flag.
		termsExpired := reqErr != nil && godep.IsTermsNotSigned(reqErr)
		if reqErr == nil || termsExpired {

Comment thread server/service/hosts.go
@codecov

codecov Bot commented Jul 23, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 80.00000% with 15 lines in your changes missing coverage. Please review.
✅ Project coverage is 67.91%. Comparing base (f75cd2e) to head (ff87e27).
⚠️ Report is 20 commits behind head on main.

Files with missing lines Patch % Lines
server/service/hosts.go 35.29% 10 Missing and 1 partial ⚠️
server/datastore/mysql/apple_mdm.go 84.61% 1 Missing and 1 partial ⚠️
server/mdm/apple/apple_mdm.go 88.23% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #49770      +/-   ##
==========================================
+ Coverage   67.85%   67.91%   +0.06%     
==========================================
  Files        3905     3905              
  Lines      249669   249499     -170     
  Branches    13142    13064      -78     
==========================================
+ Hits       169403   169449      +46     
+ Misses      65042    64766     -276     
- Partials    15224    15284      +60     
Flag Coverage Δ
backend 69.32% <80.00%> (+0.08%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI review requested due to automatic review settings July 23, 2026 15:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 22 out of 23 changed files in this pull request and generated no new comments.

Files excluded by content exclusion policy (1)
  • changes/47700-abm-token-invalid-dep-error
Comments suppressed due to low confidence (1)

server/mdm/apple/apple_mdm.go:1066

  • token_invalid is only cleared on successful DEP requests. If a token previously hit token_rejected/signature_invalid (setting the flag) and later Apple stops returning those errors but instead returns a different DEP error (notably T_C_NOT_SIGNED / terms expired), the flag will remain stuck true even though the token is no longer rejected. This conflicts with the requirement to clear the bit once Apple stops returning those errors.

Consider clearing token_invalid when the token is clearly accepted but the request failed for a different known reason (e.g. terms expired).

		tokenInvalid := reqErr != nil && (godep.IsTokenRejected(reqErr) || godep.IsSignatureInvalid(reqErr))
		if reqErr == nil || tokenInvalid {
			if _, err := updater.SetABMTokenInvalidForOrgName(ctx, orgName, tokenInvalid); err != nil {
				logger.ErrorContext(ctx, "Apple DEP client: failed to update token invalid status of ABM token", "err", err)
			}

Copilot AI review requested due to automatic review settings July 24, 2026 08:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 22 out of 23 changed files in this pull request and generated no new comments.

Files excluded by content exclusion policy (1)
  • changes/47700-abm-token-invalid-dep-error

Copilot AI review requested due to automatic review settings July 24, 2026 08:44

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 22 out of 23 changed files in this pull request and generated no new comments.

Files excluded by content exclusion policy (1)
  • changes/47700-abm-token-invalid-dep-error

Copilot AI review requested due to automatic review settings July 24, 2026 13:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 22 out of 23 changed files in this pull request and generated 1 comment.

Files excluded by content exclusion policy (1)
  • changes/47700-abm-token-invalid-dep-error

Comment thread server/service/hosts.go Outdated
Copilot AI review requested due to automatic review settings July 24, 2026 13:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 23 out of 24 changed files in this pull request and generated 2 comments.

Files excluded by content exclusion policy (1)
  • changes/47700-abm-token-invalid-dep-error
Comments suppressed due to low confidence (2)

server/service/hosts.go:2529

  • The related issue/PR description call out a structured dep_error attribute on the dep_assignment response (null when no error). The endpoint currently only returns the human-readable dep_device_error message, so API consumers can't reliably branch on error type without parsing text. Consider returning the typed error code as a separate JSON field (e.g. dep_error) alongside the message.
	ID                uint                     `json:"id"`
	HostDEPAssignment *fleet.HostDEPAssignment `json:"host_dep_assignment"`
	DEPDevice         *godep.DeviceDetails     `json:"dep_device"`
	DEPDeviceError    *string                  `json:"dep_device_error"`
	Err               error                    `json:"error,omitempty"`

server/service/hosts.go:2545

  • If you add a structured dep_error code to the response, the endpoint should also populate it (as null when depError=="") alongside the existing dep_device_error message. That keeps the API contract explicit and lets the frontend display a message while branching on a stable enum.
	var depErrorMessage *string
	if depError != "" {
		msg := depError.Message()
		depErrorMessage = &msg
	}

Comment thread server/fleet/apple_mdm.go
Comment thread server/fleet/apple_mdm.go
Copilot AI review requested due to automatic review settings July 24, 2026 15:01
@coderabbitai coderabbitai Bot mentioned this pull request Jul 24, 2026
2 tasks

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

  • Copilot's review of this pull request may be incomplete because some of the changed files are excluded by your Copilot content exclusion settings. See Excluding content from Copilot for details.

Pull request overview

Copilot reviewed 23 out of 24 changed files in this pull request and generated no new comments.

Files excluded by content exclusion policy (1)
  • changes/47700-abm-token-invalid-dep-error
Comments suppressed due to low confidence (2)

server/service/hosts.go:2600

  • The comment says errors are classified via dep_error, but the response field and related docs use dep_device_error. This mismatch is confusing for maintainers and API consumers; please align the comment with the actual JSON attribute name.
	// Call Apple's "Get Device Details" API. Per the issue spec: on error, log
	// and classify the error via dep_error rather than surfacing it to the
	// caller.

server/fleet/apple_mdm.go:780

  • This is user-facing text, and it appears to refer to "Apple Business" and an "AB token". The product name is Apple Business Manager (ABM) and the remediation is typically to upload/replace the ABM token (not "renew an AB token"). Clarifying this message will reduce user confusion.
	case DEPDeviceErrorTokenInvalid:
		return "Fleet can't connect to Apple Business. An admin needs to renew the AB token."

@andymFleet
andymFleet marked this pull request as ready for review July 24, 2026 15:52

@JordanMontgomery JordanMontgomery left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall I think this looks good, one tiny comment, two things that are mostly just me griping about this awesome API - the response status change seems a bit odd to me but I assume it's something that's there and just not documented

Comment thread server/service/hosts.go
return depAssignment, nil, apple_mdm.ClassifyDEPDeviceError(err), nil
}

// Apple returns an entry for every requested serial even when it isn't

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've said it so many times but I really hate this API 😂

tokenAccepted := reqErr == nil || godep.IsTermsNotSigned(reqErr)
if tokenAccepted || tokenInvalid {
if _, err := updater.SetABMTokenInvalidForOrgName(ctx, orgName, tokenInvalid); err != nil {
logger.ErrorContext(ctx, "Apple DEP client: failed to update token invalid status of ABM token", "err", err)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the below block does something like:

			// on API call success, if the global terms expired flag is not set and
			// the count is 0, no need to do anything else (it means this ABM token
			// already had the flag cleared).
			if reqErr == nil && count == 0 && !appCfg.MDM.AppleBMTermsExpired {
				return reqErr
			}

Does it make sense to do something like this here so this only executes against the writer when a change is needed? As best I can tell this will cause a query against the writer DB each time even though most times it won't be needed

case "FOUND123":
devices[serial] = map[string]any{
"serial_number": serial,
"response_status": "SUCCESS",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this an actual part of the Apple API? they don't list response_status on their API docs. https://developer.apple.com/documentation/devicemanagement/device-details but @MagnusHJensen just added it and seemed to think it is so I'm good with it if it's there.

Have I mentioned how I hate this API and its documentation? 😂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ABM errors: Backend/API work to flag more errors and return them on the DEP status API

3 participants