#5463: What Happens When You Click "Connect" on an AI Plugin

You click accept, the tool runs, and a credential you never saw is doing the work. Here's who actually owns it.

Featuring
Listen
0:00
0:00
Episode Details
Episode ID
MWP-5646
Published
Duration
20:47
Audio
Direct link
Pipeline
V5.2
TTS Engine
chatterbox-regular
Script Writing Agent
DeepSeek 4.1 Flash

AI-Generated Content: This podcast is created using AI personas. Please verify any important information independently.

Connecting an AI plugin looks like nothing. You click accept, tick a few scope boxes, and the tool starts sending email on your behalf. Underneath that click is OAuth 2.0's authorization code flow with PKCE (RFC 7636): the client redirects you to the identity provider with its client ID and requested scopes, you consent, and Google returns a short-lived authorization code to the client's redirect URI. PKCE keeps that code from being intercepted in transit — critical for public clients like desktop and mobile apps that can't hold a secret. The client then exchanges the code for a short-lived access token and usually a long-lived refresh token, which lets it silently mint new access tokens later. The access token rides along as a bearer credential on every subsequent call: whoever holds it is treated as you.

In a well-built integration, that token never touches the client's UI. The gateway stores it server-side and hands the client an opaque session handle instead, refreshing silently as needed. So when you can't find a credential in a settings panel, the first answer is that there's nothing to display — the architecture is designed that way. The second answer is more interesting: sometimes the credential was never yours. Client credentials and platform app-context credentials are available to non-users and never carry a user identity. A service account API key is provisioned to the gateway out of band during server onboarding, attached when a tool runs, and never transits the client. You're not the principal of that credential — you're a person who pressed a button that caused a tool to run under the application's identity. The effect resembles a dark pattern; the cause is entirely different.

Nothing legally requires an app to show you a raw token, and the standards argue against it — a bearer token is a secret, and displaying it is an anti-pattern. The transparency obligation that does exist is to show the connection and let you revoke it. Revocation operates at the authorization grant level, not the token level: killing the grant stops the refresh token from minting new access tokens, but any access token already issued stays live until it expires, typically minutes to an hour. That gap is real, and it's why short TTLs matter. Disconnecting on the app side and revoking at the identity provider are two different acts — the app just forgets, while the identity provider is the authority that actually kills the grant. A dormant grant is also what makes reconnecting frictionless in a way you never asked for.

Then there's the confused deputy, a documented attack class going back to Hardy in 1988. In a User to Service Account flow, a human triggers a tool that executes under a service account. If the gateway attaches that credential without an explicit entitlement check, any user who can reach the tool inherits the service account's full privileges. The Supabase MCP leak in July 2025 was exactly this: a credential with more access than the user's own turned a bug into a privilege escalation vector. Bearer tokens make it worse because they're inherently relayable — if Agent A presents its JWT to Skill B, Skill B can reuse it to call Skill C while pretending to be Agent A. Audience-scoped tokens, proof of possession via DPoP, short TTLs, and per-request tokens all shrink that window.

Downloads

Episode Audio

Download the full episode as an MP3 file

Download MP3
Transcript (TXT)

Plain text transcript file

Transcript (PDF)

Formatted PDF with styling

#5463: What Happens When You Click "Connect" on an AI Plugin

Corn
ChatGPT's email plugin now lets you connect multiple accounts. A workspace address, a Gmail address, one after the other, each with an alias, and you pick which one sends by naming it in the tool call. It looks like nothing. That's the whole point of it.
Herman
It's a very good integration. I want to be clear about that up front, because everything we're about to say sounds like an accusation and it isn't.
Corn
Daniel's been building MCP integrations for two years now, so he knows exactly what's behind a flow like that, because he's built the ugly version himself. Wrong token type, wrong scope, reauthenticating every few days because the refresh token rotation didn't survive a weekend. He wants to know what's actually happening under the hood when we click accept and tick the scope boxes. Why can we never see the service account or the API key? Do apps have to show us that? How does revocation work when we never saw the token in the first place? And is there some scenario, however far-fetched, where a credential gets issued that you can't see and can't revoke. Then the practical one: how do you keep control of your auth surface.
Herman
The far-fetched scenario is the one I'd flag. It's not far-fetched.
Corn
Let's start with the click. What actually happens in that moment.
Herman
The click is the smallest part of it. What's underneath is OAuth 2.0, authorization code flow with PKCE, which is RFC 7636. The client, so ChatGPT in this case, redirects you to the identity provider, Google, with its client ID and the list of scopes it's asking for. You authenticate, you consent, that's your click. Google hands back a short-lived authorization code to the client's redirect URI, and PKCE is what stops that code being intercepted in transit, which matters enormously for public clients like desktop and mobile apps that can't keep a secret. Then the client exchanges the code for an access token, short-lived, and usually a refresh token, long-lived, which is what lets it silently mint new access tokens later without bothering you again. The access token then rides along as a bearer credential on every API call after that.
Corn
Bearer meaning whoever holds it is treated as you.
Herman
Whoever holds it is you, as far as the downstream service is concerned. There's no further proof required. That's why the whole rest of this conversation is about where that thing lives.
Corn
And where it lives is the interesting part.
Herman
In anything well built, it never touches the client's UI at all. The gateway doesn't hand the raw OAuth token back to the client. It stores the token internally and returns an opaque session handle. The client presents the session, the gateway looks up the token it has mapped to that session and refreshes it silently when it needs to. So when Daniel asks why he never sees a credential, the first answer is that the architecture is designed so there's nothing to see. It's not hidden behind a menu somewhere. It's server-side and it stays there.
Corn
Which is the right design. If ChatGPT handed you a raw bearer token in a settings panel, that would be a bug, not a feature.
Herman
Correct, and that's the thing to hold onto, because the next question is where people usually go wrong. They assume invisible means either sloppy or sinister. Sometimes it's neither. Sometimes the credential was never yours to begin with.
Corn
Explain that.
Herman
There's a two-axis model that makes this clean. One axis is persona: are you an interactive user, or are you an automated non-user, a service account. The other axis is credential type: no auth, static API key, dynamic API key, authorization code with PKCE, client credentials, platform app-context. And here's the line that matters. Client credentials and app-context credentials are available to non-users, and they never carry a user identity. Ever. They're not your credentials wearing a costume. They're the application's credentials, full stop.
Corn
So in the service account flow, where does the API key come from?
Herman
It's provisioned to the gateway ahead of time, out of band, during server onboarding. Somebody at the vendor sets it up before any user ever touches the product. And when you trigger a tool, the gateway attaches that key when it forwards the request downstream. The key never transits the client. It never passes through your browser or your app. No user identity is involved at any point in the chain.
Corn
So the answer to why can't I see it is: it isn't mine.
Herman
It isn't yours. You're not the principal of that credential. You're a person who pressed a button that caused a tool to run under the application's identity. There is nothing user-facing to display, because the user isn't a party to that credential at all. And I want to be honest about the shape of this, because it produces the same effect as a dark pattern. A credential you can't see, doing things on your behalf. The effect is identical. The cause is not.
Corn
That's the distinction Daniel's poking at, I think. He's not asking whether it's evil. He's asking whether the architecture actually accounts for him.
Herman
And in the service account case, it accounts for him as a trigger, not as a principal. Which is a real thing to be uncomfortable about, and I don't think the discomfort is wrong.
Corn
Does anything require apps to show you the token?
Herman
Nothing I've ever seen. There's no legal or regulatory mandate that says display the raw token to the user, and the standards argue the other way. A bearer token is a secret. Displaying it is a security anti-pattern. The transparency obligation that actually exists is a different one: show the connection, and let the user revoke it. That's what Google does with its security dashboard and third-party access, that's what ChatGPT does in the connector area. The token is deliberately opaque. The relationship is what's surfaced.
Corn
And the relationship is the part you can actually act on.
Herman
The relationship is the only part you can act on. Which is fine, as long as revocation actually works. And it does, but not the way most people assume.
Corn
So if the credential was never yours, what does revocation even mean?
Herman
It operates at the authorization grant level, not the token level. That's the key. When you revoke the app's access on Google's side, you're invalidating the grant. The refresh token stops working. The app can no longer mint new access tokens. Any access token already issued is still live until it expires, which is typically minutes to an hour. On the ChatGPT side, disconnecting the connector deletes the stored token or session mapping server-side. Both ends, both doing their own version of the same thing.
Corn
Eventually consistent, then.
Herman
Eventually consistent is exactly right, and that gap is real. A live access token can keep working until it expires even after you've revoked. It's a short window, but it's a window. And the bigger issue is that revocation only works if both ends honor it. There's a failure mode in enterprise settings where revoking someone's access meant contacting every server team individually. There was no single switch. That's the entire reason centralized gateways exist. Central revoke versus email twelve people and hope.
Corn
Twelve people and a spreadsheet.
Herman
Twelve people, a spreadsheet, and one guy on leave.
Corn
Let me push on that, because I want to make sure I've got the mechanics straight. If I revoke on Google's side, the refresh token dies immediately, but the access token I already handed out is still valid for its remaining lifetime. So there's a period where the app has a working key to a door I just told it to stop opening.
Herman
And the length of that period is whatever the access token's TTL is, which is a design decision somebody made, not a law of nature. Fifteen minutes, an hour, whatever. The app can't get new keys, but the old key still turns. That's why short TTLs matter, and it's why the industry keeps pushing them down. The shorter the access token lifetime, the shorter that dead zone.
Corn
And on the ChatGPT side, disconnecting the connector, that's a different operation entirely.
Herman
Different operation, same goal. It deletes the stored token or the session mapping server-side, so the gateway no longer has anything to present downstream. But notice what it doesn't do. It doesn't reach into Google and revoke the grant. It just stops using it. So if you disconnect on the ChatGPT side and never touch Google's dashboard, the grant is still sitting there, dormant. Which is fine until you reconnect, and then it can silently come back to life without a fresh consent screen, because the grant was never killed.
Corn
That's a useful thing to know. Disconnecting the app and revoking the grant are two different acts, and people treat them as the same act.
Herman
People treat them as the same act because the UI makes them feel like the same act. And they're not. If you actually want the thing dead, you revoke at the identity provider. That's the source of truth for the grant. Everything else is just the app forgetting.
Corn
So the practical advice is: when you're done with an integration for good, go to the identity provider's dashboard, not just the app's settings.
Herman
Go to the source. The app's settings are a convenience. The identity provider is the authority. If you only do one, do the one that actually kills the grant. And if you're the kind of person who reconnects things later, this matters more, because that dormant grant is exactly what makes the reconnect frictionless in a way you didn't ask for.
Corn
That brings us to the confused deputy, and it's not as far-fetched as it sounds.
Herman
It's not far-fetched at all. It's a documented attack class. Hardy described it in 1988, and the User to Service Account flow is the exact scenario. A human triggers a tool, the tool executes under a service account. If the gateway forwards that service account credential without checking anything, then any user who can reach the tool inherits the service account's full privileges. That's a privilege escalation. The paper on enterprise MCP gateways puts it bluntly: letting a user trigger a service-account-backed tool without an explicit authorization check is the confused-deputy escalation in disguise. The mitigation is an entitlement check before the credential gets attached. Does this user have service account access. Not does this user have a session. Does this user have access to the thing the service account can do.
Corn
And there's a real case of this.
Herman
The Supabase MCP leak in July 2025. The comment that stuck with everyone was that if it's running with credentials that aren't yours, credentials that provide full access, and your own credentials don't, then the bug becomes a privilege escalation vector. Classic confused deputy. That's not a theoretical paper. That's a shipped product with a real hole in it.
Corn
So Daniel's far-fetched scenario is just the default failure pattern when nobody puts the check in.
Herman
When nobody puts the check in, yes. And bearer tokens make it worse, because they're inherently relayable. There was a good discussion around Agent Passport earlier this year making the point that if Agent A presents its JWT to Skill B, Skill B can reuse that token to call Skill C while pretending to be Agent A. Nothing stops it. The token doesn't know who's holding it. That's what bearer means.
Corn
Unless you scope it.
Herman
Audience-scoped tokens, the aud claim, so a token minted for one service can't be replayed against another. Proof of possession, DPoP, where holding the token isn't enough, you have to prove you hold the private key too. Short TTLs. Per-request tokens instead of one long-lived sixty-minute thing. All of those shrink the window.
Corn
Walk me through DPoP for a second, because I think people hear proof of possession and glaze over. What's the actual mechanism.
Herman
The client generates a key pair and keeps the private key. When it makes a request, it signs a little proof with that private key and attaches it alongside the token. The server checks that the proof matches the key that's bound to the token. So even if somebody steals the token in transit, they can't use it, because they don't have the private key that the token is bound to. Holding the token isn't enough anymore. You have to be the thing the token was issued to.
Corn
So it converts a bearer token into something closer to a key you actually have to possess.
Herman
It converts a bearer token into a proof-of-possession token, yes. The token stops being enough on its own. And that's the whole point. Bearer tokens are convenient precisely because they're dumb. DPoP makes them slightly less dumb at the cost of a little more machinery on both ends.
Corn
And if you want to keep control of your actual auth surface, what do you do?
Herman
Prefer delegated OAuth over shared service accounts whenever a human is present. RFC 8693 token exchange preserves both identities in one signed token. The subject is the user, the acting subject is the agent. So the audit trail shows who acted and on whose behalf. That's the difference between a log that says the service account did it and a log that says Daniel did it through this tool. Demand audience-scoped tokens. Prefer proof of possession where it's offered. Insist on short TTLs. And verify that revocation actually propagates on both ends, because checking one end and assuming the other is how people get surprised. Then the one that requires no technical knowledge at all: watch for the User to Service Account pattern in anything you approve. If a tool runs under a service account, ask whether the platform checks entitlement before attaching that credential. If nobody can answer that question, that's your answer.
Corn
The uncomfortable version of all this is that the polished flow is doing exactly what it's designed to do. It's hiding plumbing that would terrify most users if they saw it.
Herman
And the plumbing is hard. The quote from a practitioner discussion that stayed with me was that authentication alone is brutal when you've got dozens of different OAuth implementations, each with their own issues around scopes and refresh logic, and that's where most integrations get bogged down. Daniel has lived that. Wrong token type, wrong scope, brittle rotation. That's not him being bad at it. That's the terrain.
Corn
The MCP spec says implementations should support OAuth 2.0 and leaves every detail to the implementer. That's the root of it. Everyone builds their own version of the same hard thing.
Herman
The gateway paper's own conclusion is that the hard part was organizational, not cryptographic. The crypto is solved. Getting twelve teams to agree on one revocation path is not.
Corn
Which is a weird thing to sit with, right? The math is done. The primitives exist. We know how to do this correctly. And the thing that actually blocks it is a meeting.
Herman
It's a meeting, or it's a team that owns one service and doesn't want to change its auth, or it's a vendor who shipped something in 2019 and nobody wants to touch it. The crypto is the easy part. It's been the easy part for a decade. The hard part is that security is a coordination problem wearing a technical costume.

Hilbert: The key works until it doesn't, and then you're rekeying everything. That's what the general manager used to say. I was night auditor at a hotel that ran the whole place on keycards. Front desk could issue master keys to housekeeping. Those cards opened every room in the building. Housekeeping never saw a key. They swiped a card, the door opened, they moved on. And when a master key went missing, you didn't revoke one key. You rekeyed every lock in the building. Took days. Cost a fortune. We had a laminated sheet in the back office listing which cards were master cards, because you had to know.
Corn
You had to know which ones were the dangerous ones.

Hilbert: You had to know. That's the part. This is exactly what you're describing. The user gets a card that opens the door, but they don't see the key. And when something goes wrong you're not revoking one key, you're rekeying the whole building. Difference is, at the hotel we at least knew which cards were master keys. Here, you don't even know which integrations are running with service accounts. Nobody's got the laminated sheet.
Herman
The laminated sheet is the entitlement check.

Hilbert: We kept ours behind the desk. I don't know where yours is.
Corn
Probably nobody's desk.

Hilbert: Anyway. I'm due at the pharmacy before six and I'd rather be early in that queue than late in it.
Herman
The thing I keep circling is that the invisible credential problem isn't a bug waiting to be fixed. It's the shape the whole ecosystem is settling into. Every platform that adopts MCP is going to make the same trade: hide the plumbing, surface the relationship, and hope the entitlement checks are where they're supposed to be. The confused deputy isn't a rare edge case. It's becoming the default failure pattern, because the default architecture is a human triggering a tool that runs under somebody else's identity.
Corn
The reason it becomes the default is that the alternative is more work for the platform, more friction for the user, and a worse demo. Nobody ships the version with the extra consent screen if the version without it tests better.
Herman
Nobody ships it. And to be fair to the platforms, most of them probably do have the check. The problem is you can't tell from the outside. The whole architecture is opaque by design, which is correct for the token and terrible for the trust. You're asked to take the entitlement check on faith because you can't see it, and you can't see it because seeing it would mean exposing the thing you're not supposed to see.
Corn
Which leaves the question Daniel actually ended on. If the credential was never yours, what does revocation mean? You're not revoking a key. You're severing a relationship. And how do you audit an integration you can't inspect?
Herman
You audit the relationship, not the token. That's the only surface you get.
Corn
One more thing from the research that didn't make the main run. The gateway paper's Flow 3, the one where naive designs fail, has a line that basically says the confused deputy escalation is what happens when you skip one check. One if statement. That's the whole distance between a sound architecture and a privilege escalation.
Herman
One check, and almost nobody writes it, because in testing everything works.
Corn
In testing everything works because the tester is the person who should have access.
Herman
The tester is always the authorized user. That's the trap. You test it as yourself, everything passes, you ship it. The check you never wrote is the one that only matters when somebody who shouldn't have access shows up. And by then it's in production.
Corn
It's the same reason security bugs cluster at the boundaries. The happy path is the path you walk every day. The unhappy path is the one you never visit until someone else does.
Herman
The unhappy path is where the credential you can't see is doing the thing you can't audit on behalf of a user who never knew they were a principal in the first place. Which is a lot of invisible machinery for one click.
Corn
Thanks as always to Hilbert Flumingtop, who produces this show and who is currently in a queue somewhere.
Herman
This has been My Weird Prompts. If you want to argue with us about bearer tokens, email us at show at my weird prompts dot com.
Corn
The next time you click accept on an integration, ask whose credential is actually being used. Then ask whether you could revoke it if you had to.
Herman
We'll be back soon.

This episode was generated with AI assistance. Hosts Herman and Corn are AI personalities.