M365 Show -  Microsoft 365 Digital Workplace Daily
M365 Show with Mirko Peters - Microsoft 365 Digital Workplace Daily
Nobody Explains Microsoft Graph Consent—Here’s What’s Missing
0:00
-22:33

Nobody Explains Microsoft Graph Consent—Here’s What’s Missing

Ever tried setting up app-only permissions with Microsoft Graph and ended up knee-deep in consent dialogs, confused about what just happened? You’re not alone. Most tutorials gloss over what ‘consent’ really means for non-interactive services—and where things can quietly go off the rails.
Today, we’re breaking down the end-to-end workflow: exactly what you must configure in Azure AD, how permissions actually work, and what happens behind the scenes when you grab that elusive access token. Ready to finally fill in the missing pieces?

The Consent Gap: Where Most Tutorials Get Microsoft Graph Wrong

If you've ever read a quick-start on Microsoft Graph, you've probably seen the moment where the author waves at ‘consent’ and then skips straight to the code. There’s always that implied idea: tick a few boxes in Azure AD, grant the permissions your app needs, and you’re good to go. But the reality is, what happens with consent—especially when there’s no user in the mix—gets glossed over in documentation and walkthroughs alike. It’s easy to assume app-only access is just a matter of flipping a switch, but this is one of those areas where the devil’s in the details, and that devil can sneak up on you in a real tenant.

A lot of admins and developers treat setting up app-only access in Microsoft Graph like they’re checking off a grocery list. Pick the permissions, hit “register,” and call it done. But as soon as something changes—a policy update, a new compliance review, or even an audit request from IT—those simple checkboxes start causing headaches. Suddenly, you’re hit with unexpected consent prompts, puzzled users, and security teams asking why an unattended app has sweeping admin-level access. What’s supposed to be non-interactive service access quickly turns into a permission soup that nobody quite remembers approving.

Let’s put this in a real-world Microsoft 365 context. Imagine you’ve got a background service—maybe an automation script collecting usage stats, or a tool syncing files from OneDrive to SharePoint. Everything seems to work. Then, one afternoon, a global admin flips a setting related to consent policies, maybe to comply with a security mandate. The next morning? Your automation fails, logs fill up with unhelpful errors, and people are left scratching their heads. The consent mechanics behind that app-only setup are suddenly impossible to ignore, but it’s too late for an easy fix.

Microsoft’s own documentation is dense, but here’s the bit that’s easy to miss: app-only permissions aren’t bound to a single user’s access, and they don’t ride along with a person’s security context. This means one consent event—one click, maybe months ago—can assign permissions that persist until someone manually revokes or alters them. If the app was granted ‘Sites.ReadWrite.All’ for SharePoint, that doesn’t just mean it can poke around your own mailbox. It could touch or expose every SharePoint site in the tenant, all because the consent scope was too broad. Most of the time, the tutorials don’t even pause to unpack this, so the risk quietly snowballs in the background.

That brings us to a pattern that shows up over and over again in the field. You’ve got delegated permissions—what users see when they log in to an app and consent to “read your profile” or “view your mail.” Then there’s app-only permissions, where a service account operates without a human at the keyboard. Research from tenant-breach post-mortems shows admins often confuse the two, granting overly broad app permissions thinking they’re just approving a narrow workflow. It’s the difference between letting someone peek into one room versus handing them a master key for the whole building. Most folks don’t realize that with application permissions, even one misstep in the Azure portal means that automation can reach far more data than expected, all while nobody’s watching.

Let’s take SharePoint as a tangent here, because that’s where things get really interesting—and sometimes risky. If you’re building a migrations tool or an analytics app and use SharePoint’s ‘Sites.ReadWrite.All’ permission, you’re not just scoping to one document library, even if that’s your intent. Instead, you’ve just given your process access to every site, personal drive, and team file in the tenant. A single consent event—maybe rushed through to resolve a support ticket—opens the door to data exposure. And the kicker? Unless someone goes out of their way to review the app’s permissions periodically, there’s rarely a visible audit trail. At best you get an entry in the sign-in logs, but that doesn’t tell you what was accessed, or if the app ever moved out of bounds.

On top of that, policies around who can grant consent in Azure AD are far from one-size-fits-all. Some tenants allow privileged users to grant permissions for certain apps. Others lock things down so tightly that only global admins can approve or review app access. This patchwork approach means you might have an automation script that’s been running quietly for months—then, a new consent policy blocks its next token request, bringing everything to a halt. Teams relying on that service suddenly find their automation broken, and the audit trail leads to an admin who doesn’t even remember granting access in the first place.

So, what’s actually on the line? Consent is more than a technicality—it’s a security posture, a compliance process, and an accountability framework rolled into one. If you don’t know who approved which permissions, when, and for what purpose, you’re flying blind. Silent security gaps are built into the workflow, not because someone was careless, but because the mechanics behind app-only consent rarely get explained in detail.

But once you truly understand the mechanics—who consents, how scopes work, and what happens when permissions change—you’re no longer just checking boxes. You’re closing security gaps before they turn into incident reports and making sure responsibility is clear, not buried in the fine print. Now, let’s actually tackle the registration process, because granting safe, non-interactive access is where most admins set themselves up for pain later on.

Registering Apps the Right Way: Avoiding Accidental Over-Permission

Let’s talk about the moment almost every admin glosses over—the actual Azure AD app registration. You’re rolling through the portal, naming your app, clicking “register,” maybe slapping your company name at the end so it feels official. You’re focused on the finish line, not the details buried in all those permission toggles. But here’s where things quietly go sideways. Everything in that interface looks the same: application and delegated permissions jumbled together, all sorts of checkboxes with similar sounding labels. It’s not always obvious that one small change dictates who can access what—and for how long. All the while the impact of these choices gets buried until you go back later and realize your so-called non-interactive app has more access than anything else in your tenant.

Picture this: you’re setting up an automation to process calendar invites for a project team. Nobody wants to sit around clicking consent popups, so you go with app-only permissions. In the portal, you’re looking for the fastest path—select “Calendars.Read” and move on. But then the UI nudges you with a list of “suggested” permissions, and it’s tempting to grab a few extras while you’re there. That’s how you end up with “Calendars.ReadWrite” instead of just “read.” The distinction is subtle in the interface but substantial in practice. Now your background task doesn’t just see events—it can create, edit, or delete across the board, for every mailbox the app can touch.

This pattern shows up everywhere, especially with tools, frameworks, and integrations. Let’s say you’re wiring up Power Platform to sync user profiles for a dashboard. You might see “User.Read.All” and “Directory.Read.All” sitting next to each other. One is scoped just to basic profile information, the other sweeps through your whole Azure AD directory. Most busy admins just grab both and move on, not realizing “Directory.Read.All” is often flagged in audits as high risk. And in an app-only context, you’ve granted that access to a background service, not to an individual with a role. So, even if you trust your users, an app using application permissions can perform actions they never would be able to approve themselves.

This would all be less risky if the admin experience in Azure AD made the difference between delegated and app-only permissions impossible to miss. But it’s not that simple. Application permissions live in their own tab, but if you’re not sure about what each permission does, it quickly becomes a buffet of options. You’re in a hurry, there’s a looming project deadline, and all you want is for the integration to work. So, you end up grabbing “Mail.ReadWrite” or even “Sites.ReadWrite.All”—the nuclear option of SharePoint permissions—because you don’t have time to chase down the exact permission mapping. That’s how tomorrow’s compliance headache gets baked into your environment without anyone noticing.

The problem gets worse as you scale up. Software vendors who want the easiest customer onboarding usually ask for the highest-level permissions so their apps don’t break on edge cases. You see this all over ISV and SaaS documentation: “in order for our app to work, please grant Directory.ReadWrite.All.” Even when your use case only needs to see whether users are active, not edit accounts or change passwords. Most folks don’t want to open a support ticket or block a vendor rollout, so they agree. Later, those permissions sit unused—or worse, become a target for attackers who discover unused but over-permissioned service principals in your tenant.

Microsoft’s guidance doesn’t make things easier, either. Even though the docs spell out why you should use least privilege, the specifics are often hidden several links deep, under headings you wouldn’t find unless you’re searching for a post-breach audit checklist. You’ve got to dig for practical examples of minimal sets of permissions, or how to restrict an app so it only touches what’s truly needed. And after a long day bouncing between compliance meetings and Teams calls, who has the energy to read whitepapers on privilege boundaries? So, admins take shortcuts, clicking through without tuning scopes. The end result is an app registration that works, but with way more power than anyone intended.

Then we get to compliance. It’s not just about technical risk anymore—it’s about real-world consequences. When broad app-only permissions are assigned, you can trigger audit events, notifications, or even regulatory reviews, depending on your industry. Finance and healthcare tenants, for example, now have auditors who review every app registration and cross-check for “.All” permissions or unchecked consent scopes. Even if your intention was innocent, the audit looks at the end state, not the excuses. And when you’re under the microscope, explaining why your background process has full SharePoint access is not a conversation you want to have.

So what does it look like to do this right? Less is always more. Find the smallest permission set your process needs, and resist adding extras to “future-proof” the app. Review what you’ve assigned, and if you see the words “.All” at the end of a permission scope, pause and ask whether you’re opening up too much. Paring things back now saves you a lot of grief next quarter, and gives your compliance team a fighting chance at keeping your tenant secure. Now, let’s get into how the consent process actually shapes all of this in practice, and why it plays by a different set of rules when it’s non-interactive.

Admin Consent, Consent Flows, and Why Service Access Isn’t Just a Checkbox

You’ve just mapped out your permissions during app registration and you’re feeling pretty confident. The next step is supposed to be simple: grant the app what it needs to run. But what actually happens when you hit that consent button? That’s where things get unpredictable. Some permissions slide right through, the tenant is happy, and the app connects fine. Other times, Azure throws a full-stop admin consent dialog in your face, sometimes out of nowhere, and you’re left wondering what tipped the balance. It’s not random, but the logic hides under layers of Microsoft documentation, and most tutorials don’t mention it. The difference always comes down to who can approve what—and, as odd as it sounds, why some permissions officially require a higher degree of trust.

When you’re dealing with app-only permissions, the stakes are higher because these apps don’t rely on a user context. Nobody logs in interactively when your service account runs a report at midnight. Because of this, Microsoft builds in a stricter consent flow for app-only scenarios—no average user is allowed to grant those permissions. That power sits strictly with admins, and in most environments, it’s only the global or privileged ones. The admin consent dialog you see? That’s Azure AD flagging permissions it considers sensitive or broad. If you’re trying to let an app read mailboxes, write to SharePoint sites, or pull profiles out of the directory, that’s guaranteed admin territory. The reasoning is simple: the wider the scope, the bigger the potential impact, so only people with oversight of your tenant can unlock those doors.

Here’s where it gets slippery. That single click in the admin consent dialog is more than just a routine step. It’s a pivotal moment, because whoever clicks “Consent” is vouching that the app truly needs those permissions. In practice, though, the person hitting that button might be an IT manager under a time crunch, a developer with temporary admin rights, or even a support contractor tasked with “just getting the app working.” When you’re in a rush to unblock a stalled deployment, it’s tempting to approve more scopes than an app actually needs—just to solve the problem quickly. Those extra permissions often aren’t revoked, even after the crisis passes, and months later, the app still has access well beyond its original purpose.

That’s a compliance and monitoring headache in the making. Let’s say the initial consent happens and everyone moves on, project delivered. But what happens as your app matures? Maybe it now needs to access new APIs, or a business process changes, or regulatory pressure means a permissions review gets scheduled. Consent isn’t a one-time task. Every time you add a new permission—especially app-only permissions—someone has to go back, reapprove, and leave a trail. In a perfect world, each of these approval events is logged, reviewed, and cross-checked against policy. In reality, few organizations keep up with this, and gaps appear in your compliance records. If you ever need to answer who, when, and why a certain permission got granted, you’ll be rooting around in Azure AD’s audit logs, looking for entries nested under “Enterprise Applications,” cross-referencing object IDs and obscure timestamps that aren’t exactly user-friendly.

Now, about that persistent access. The moment admin consent is granted for app-only permissions, that app holds those keys until you manually intervene—either by revoking consent, disabling the app registration, or changing what it’s allowed to do. There’s no built-in automatic review or expiry. That means a single, maybe long-forgotten, consent action could leave a service principal with broad, ongoing access for years. Attackers know this too—it’s literally become its own cloud security sub-specialty. Old, unused app registrations with lingering permissions are low-hanging fruit for anyone looking to move laterally inside a compromised tenant.

Microsoft does have audit logs and consent review tools, but they’re buried enough that most admins don’t visit them regularly. If you know where to look, you can pull reports showing who consented to which permissions, when, and for what apps. But there’s no notification built in for when a particularly risky permission call is approved. You have to be intentional with after-the-fact reviews or set up your own alerting. In large organizations, this is the gap where incidents happen. Someone fixes a broken workflow after hours, grants broad permissions “just to get by,” and forgets to dial back after the fact.

There’s also the reality that consent flows aren’t set-and-forget even in steady-state operations. For instance, if your Azure AD policies shift or you introduce Conditional Access that changes access controls, previously granted consents can become invalidated, or new admin approval might suddenly be required. Your background job that’s been humming along suddenly throws authentication errors, users get cut off from automations, and you’re left firefighting.

Understanding the full admin consent flow—who can grant, what can be granted, and which events are tracked—isn’t just for passing an audit. It’s your leverage point to keep service access within real boundaries, spot over-permissioning before it’s a problem, and have answers ready when the next compliance review lands in your inbox. And since consent is never truly one-and-done, staying on top of these flows is what separates a well-managed integration from an unmonitored security risk. Now, once consent is squared away, the next battle is how your app actually gets—and uses—those tokens in the wild. That’s where things get even more interesting.

Access Tokens: The Final Hurdle—and the Hidden Risks in Service-to-Service Auth

You’ve clicked every consent box, picked the right permissions, and convinced your admin to rubber-stamp approval. So the app’s job is done—at least on paper. Now comes the part that actually decides whether your automation lives or dies: the access token. Your non-interactive script wakes up, tries to authenticate against Microsoft Graph, and then you’re staring at the logs. Will you see rows of successful requests or start hunting through error messages about invalid_grant, missing roles, or cryptic 401s? Here’s where the reality undercuts every “it just works” tutorial. Getting an access token seems easy when you’re running a sample in the portal, but it’s a very different story once things go into production, where nothing is interactive and tokens aren’t handed out on a silver platter.

For starters, there’s the small matter of how many places things can break down. Did the admin consent apply to the right tenant? Did the app registration pick up all the roles it needs, or were they assigned under an old config? Is the certificate expired, or has a secret quietly rolled over? Even with app-only permissions, any one of these cracks will stop your workflow flat, and most failure messages are about as clear as mud. A service-to-service script might fail for days before anyone even notices, especially when it’s something that normally hums along without supervision—think scheduled reports, nightly data syncs, or file migrations. Nobody is watching that closely, not until something business critical grinds to a halt.

This is what happens in the wild: you have an Azure Function scheduled to do the same job every night. One week, out of nowhere, it starts failing—always at the step where it grabs a token. The logs show a simple “unauthorized” message. Only after a painful deep dive does someone realize the app secret expired last weekend, and there was no notification in place. The automation relies on a token every time it runs, but token acquisition is fragile. Whether you’re using a client secret, certificate, or managed identity, the moment anything changes, authentication can break. This isn’t just a technical nuisance; it balloons into a business problem pretty quickly, especially when downstream processes depend on that seamless background job.

But let’s say your token request succeeds. Your app is off to the races, using its access to hit the Microsoft Graph API as intended. It’s not just about the “happy path”—how do you actually guarantee the token isn’t granting too much access? Once the access token is issued, it reflects every permission (and mispermission) sitting inside that app registration. If that registration picked up “Sites.ReadWrite.All” to get going fast, that token can touch SharePoint data across your entire estate, not just the site or library you were targeting. And Microsoft’s API doesn’t know whether you meant to do that. Tokens don’t stop to ask, “are you sure?” They give access exactly as requested—nothing more, nothing less. In a busy environment, it takes discipline and tooling to double-check you haven’t over-permissioned every background job.

One of the least discussed issues in service-to-service auth is how infrequently teams review or rotate their secrets and certificates. You see it everywhere: an admin configures the app with a five-year secret so nobody has to worry about it soon. Maybe it works for a while, and people forget it exists. Then one morning, half your automation fails because the long-lived secret expired. Worse, if you’re using certificates, sometimes nobody remembers who owns the private key, or if it’s being used elsewhere. This is where tokens can slip through the cracks—either dangling with expired credentials or being quietly misused because nobody’s checking which apps are requesting tokens, for what scopes, and how often.

There’s another side to this: compliance scrutiny. Even if things are humming along technically, those same background jobs with broad tokens can pop up in compliance audits. Regulators are getting wise to service principals with all-encompassing permissions, and recurring findings now flag long-lived secrets, unused app registrations, and infrequent reviews. You don’t want to be answering questions in a compliance review about why your reporting script has the ability to edit mailboxes or purge SharePoint sites tenant-wide.

Real-world reliability isn’t about just grabbing a token once and ignoring it. It’s made in the routines—setting calendar reminders to rotate secrets before expiry, using Azure Key Vault-managed certificate rollovers, and putting alerting in place when authentication errors start creeping in. People tend to set and forget their service-to-service auth. That complacency is exactly how silent failures and risky exposures build up. You need to treat every token like an entry ticket that expires, needs validation, and should always be assessed against the smallest scope possible. Teams that do well here will even set up periodic reviews—pulling app registrations, auditing which ones are in use, reviewing their permissions, and pruning what’s not needed. In some regulated industries, this isn’t even optional anymore; it’s required if you want to keep your cloud certifications or pass an external assessment.

Tokens are powerful, because they turn app registrations into actors capable of doing real work across your environment. But they’re also ephemeral—a small window of authentication that closes fast, and, ideally, with checks around every corner. If you treat service-to-service tokens as a living part of your M365 security model, you catch mistakes before they become incidents. Routine validation and regular secret rotation aren’t glamorous. Setting it up takes up a few extra hours, but it’s those habits that mean your automations stay running and audit-ready, not waiting for a late-night incident call because something silently expired.

Handling access tokens well closes the loop on security, reliability, and compliance. If you’ve made it this far, you’re already ahead of most teams who see app registration as a one-and-done step. But there’s still the big picture to address—what’s the playbook for ensuring your app-only permissions actually protect your environment, not just enable workflows? That’s where the true line lies between secure, compliant automation and risky, forgotten service principals.

Conclusion

If you’ve ever skimmed past permissions screens or clicked “consent” just to keep a project moving, you’re not alone. The real difference between a secure, future-proof app integration and a risky one always comes down to understanding consent, right-sizing permissions, and building habits around monitoring tokens. Clicking through setup screens gets things working—at least at first—but it rarely covers your bases long-term. If today’s deep dive flagged holes you’ve missed, you’re already ahead of most. Keep the discussion going. Share your app-only wins—or fails—in the comments and subscribe for more real-world Microsoft 365 survival stories. The next workflow might surprise you.

Discussion about this episode

User's avatar