30-second answer

Because connecting proves who you are, and the next command asks what you may do. They are separate questions answered by separate systems, and PnP does not ask the second one at connection time.

What this proves

An application identity was registered against a live tenant with no permissions granted at all: no application roles, no consent, nothing.

StepResultVerified
Connect-PnPOnline with certificate, zero permissionssucceeded18 Aug 2026
Get-PnPWeb, still zero permissionsrefused18 Aug 2026
Connect-PnPOnline after granting Sites.Read.Allsucceeded18 Aug 2026
Get-PnPWeb with Sites.Read.Allsucceeded18 Aug 2026
Get-PnPTenantSite with Sites.Read.Allrefused18 Aug 2026

The connection behaved identically in the first and third rows. Nothing about authorisation changed between them from the connection's point of view, because the connection never consulted it. A successful connection is therefore not evidence that anything can be read.

What it does not prove

It does not say the certificate is wrong, and it does not say the tenant is empty. A certificate problem fails at connection time with a different message: if you got this far, the identity is fine and the grant is not.

And a command refused for want of a permission establishes nothing about the tenant: not that the site has no lists, not that the estate is empty. A refusal is a fact about the identity that asked, and a script that writes it down as "no data" has turned a permission gap into a clean bill of health.

PowerShell

PowerShell
Connect-PnPOnline -Url "https://<tenant>.sharepoint.com" `
  -ClientId $appId -Tenant $tenantId -CertificatePath ./app.pfx
# succeeds

Get-PnPTenantSite

Example output

Text
Attempted to perform an unauthorized operation.

Explanation

Connecting obtains a token from Entra ID for the application, using the certificate as proof of identity. Entra issues that token whether or not the application has been granted anything: the token says this is application X in tenant Y, and a token with no roles in it is a valid token.

The next command presents that token to SharePoint, which looks at the roles inside it and decides. With no roles, the answer is a refusal, and the message above is the one SharePoint returns for an authorisation failure.

The failure therefore arrives one command later than the cause, and with the wrong-looking word. Three habits follow from it:

  • A successful connection is not a smoke test. If a script checks that Connect-PnPOnline did not throw and then reports "connection verified", it is verifying authentication and reporting authorisation.
  • "Unauthorized" here means missing consent, not a bad certificate.
  • The refused command names the missing permission indirectly. Which operation failed tells you which permission to look at, so make the first real call the narrowest one you need rather than the widest.

Production considerations

  • Grant, consent and then run one narrow read as a genuine check.
  • In application-only flows, consent is an administrator action; it is not implied by the application existing or by the certificate being valid.
  • Consent takes a moment to propagate. A refusal seconds after granting is worth retrying once before it is believed.

None. This is about the identity doing the reading, not about a tenant setting.

References

Found something wrong? Suggest a correction. The article source is not public; the engine it cites is.

What this answer underwrites

The engine reads these Microsoft operations to collect evidence, so what is established here is what those collectors rest on.

  • activitywhen a person last changed something on one site
  • agentsthe Copilot agents in one site, and the sources each declares
  • classificationwhat a site records about the kind of content it holds
  • modernityhow one site is built: template, branding, publishing
  • ownerswho administers one site
  • sharingwhat one site permits, and its default link
  • sitesevery site this identity can enumerate