Repository navigation
Add the OAuth2 password grant for tokens without a refresh token (Datto RMM) - #1075
Merged
Merged
Conversation
Datto RMM issues 100-hour tokens with no refresh token, so a connector set up with the authorization-code flow stopped working after 100 hours. The password grant asks the token URL again with the stored username and password. The grant can now be chosen in the connector forms, and the REST and GraphQL engines retry a 401 for machine grants too.
helpcodeai
enabled auto-merge (squash)
October 13, 2026 13:32
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Datto RMM issues 100-hour tokens with no refresh token. A connector set up with the authorization-code flow therefore stopped working after 100 hours, and could only be revived by authorizing again. This adds the OAuth2 password grant (RFC 6749 §4.3): the connector asks the token URL again with the stored username and password when the token is about to expire or the API answers 401. The grant can now be chosen in the connector forms. Part 1 of the design in #979.
Changes
OAuth2TokenService:grant: 'password'sendsusername/password(Datto: API key / API secret) with the client as HTTP Basic by default (public-client:public), in the body withclient_secret_post, orclient_idonly for a public client. Scopes typed in the form (scopes) are now sent for the machine grants too.oauth-grant.util.ts(isMachineGrant,canRenewToken). REST and GraphQL engines retry a 401 when the grant can renew by itself; before, the retry required a stored refresh token, soclient_credentialsconnectors never retried either.GET/PATCH /api/connectors/:id/oauth-configtake and returngrant;username/passwordare write-only (hasUsername/hasPassword). The authorization-code flow is stored as no grant, as every authorized connector has today.client_credentials(no browser authorization).docs/connectors/rest.md: the Datto example used the authorization-code flow, which is exactly the setup that breaks after 100 hours; replaced with the password grant.Type
Testing
Commands and results:
packages/backend:npx tsc --noEmit -p tsconfig.jsonclean;npm test552 suites / 14188 tests passed;npm run lint0 errors (two unrelated files rewritten by--fixwere reverted).packages/frontend:npx tsc --noEmit -p tsconfig.jsonclean; eslint on the changed files clean;npx playwright test tests/e2e/connector-oauth-grant.spec.ts tests/e2e/connector-oauth1-edit.spec.ts tests/e2e/connector-oauth-complete.spec.ts9 passed.npm cihoistedreact-dom19.2.7 to the root next toreact19.3.0, sonext devrefused to start for Playwright; react-dom was aligned innode_modulesfor the run only.Cloud and self-hosted behave the same: the grant is per connector, no deployment-mode branch.
Checklist
npm test)Related Issues
Relates to #979