I’m curious about the best way to handle credentials like OAuth tokens for Google Apps when working with clients. Since credentials need to be configured for every node, the first time I did it, it felt quite tedious and time-consuming. Should we walk clients through this process over a call? What’s the recommended best practice for this?"
Hey @Maxwell45, while you wait for a response, here are some things that might help:
Suggested resources
Automatically matched to your question.
Docs:
Forum:
PurveshGandhi, mje, Quantum_Jake - you’ve helped with similar issues before, can you take a look?
Automatically suggested by n8n’s community bot. It’s a pilot - please share feedback here.
I’ve been thinking about this same question.
My instinct is that for technical users you could probably walk them through generating and entering their own credentials directly, but for non-technical clients that doesn’t seem realistic, you’d likely end up handling it yourself.
That raises something I don’t have an answer for either: if I’m the one holding a client’s credentials on their behalf, is there a privacy or compliance standard I should be following?
I haven’t worked with clients on this yet, so I’m asking alongside you rather than answering, curious if anyone here has a clear line on where that responsibility sits.
These threads might help
@Maxwell45 never handle raw client credentials yourself.
For OAuth (Google, etc.): have the client click through “Sign in with Google” themselves using their own account, even if you’re screen-sharing and guiding them. Never authorize with your own account “temporarily” you’ll just have to redo it later.
For API keys: get them via a secure one-time channel (password manager share, not email/Slack), store in n8n credentials, and never store them elsewhere.
Scale tip: for many clients, use n8n’s external secrets or a project-based setup so each client’s credentials stay isolated instead of one shared credential store.
Onboarding: send a short numbered checklist per integration (“go to X, click Sign in, approve access”) instead of a live call, cuts your time down a lot once you’ve written it once.
One thing worth adding on top of what’s already here: n8n’s Sharing tab and Projects handle who can use a credential, but they’re not a tenant-isolation boundary. Users added to a project can execute workflows using credentials created in that project even if you never explicitly shared them 1:1 - it works more like “available to the project” than strict per-user scoping.
So for a few clients on one instance, project-based separation + the shared-credential approach above works fine. But if you’re managing many external clients, or have compliance/data-residency requirements, the safer pattern is one n8n instance (or at least separate projects with no cross-membership) per client, rather than relying on roles alone within a single shared instance.
Hi @Maxwell45 Welcome!
Credentials are per service, not per node. You create one Google credential and every node of that type picks it from the credential dropdown, so a workflow with ten Gmail nodes still needs one authorisation, not ten.
On Cloud, Managed OAuth2 covers Gmail, Google Sheets, Drive, Calendar, Docs, Slides, Contacts and Tasks with nothing to configure in the Google Cloud Console at all, which removes the part that made the first run slow.
On self-hosted that option does not exist, so build one Google Cloud project of your own and reuse the same Client ID and Secret across every client instead of making each of them create a project. Set the consent screen publishing status to “In production” before you onboard anyone. An app left on “Testing” only works for accounts you add as test users, and its refresh tokens expire after 7 days, which is the usual reason a client integration silently dies a week after handover.
If you are weighing where to run client instances, this covers the tradeoffs:
This is the exact question that pushed me down a rabbit hole, so a few things I’ve landed on from doing it for real:
For technical clients, walking them through entering their own credentials works. For non-technical ones it’s not realistic — but “handling it yourself” doesn’t have to mean you hold their raw passwords. The cleaner path is OAuth where the client authorizes inside n8n directly, so you never take custody of the actual login. You get a working connection, they keep ownership, and the liability question mostly goes away.
On TeSldrah’s compliance point — the thing nobody warns you about isn’t the setup, it’s what happens *after*. OAuth tokens expire silently. The client’s automation just stops producing output, n8n still reports the run as “success,” and you find out days later when the client asks where their data went. So the real responsibility that sits with you isn’t just storing creds safely — it’s noticing when a client’s connection has quietly died before they do.
Curious how others here handle that second part — do you actively monitor each client credential’s state, or only find out when something visibly breaks?
@ASHIM_DOLEY s point about tokens expiring silently while n8n still reports success is the same root problem showing up somewhere else — a run finishing without an error only proves the call didn’t error, not that the intended outcome actually happened. Same pattern with wrong-record writes, wrong-amount payments, garbage data that ‘completes’ cleanly. Fix is the same regardless of what’s actually failing silently: don’t trust success status alone for anything consequential, verify the real outcome before calling it done. Cheap to add per integration, expensive to explain to a client why their automation died three weeks ago and nobody noticed.
Agreed on all of it — “the call didn’t error” and “the thing you wanted actually happened” being different claims is the cleanest way I’ve seen it put.
One place I’d split from “the fix is the same regardless”: outcome verification tells you it broke, but not who broke it, and those need different responses. A dead OAuth token, a client renaming a column in their sheet, and a bug in my own expression can all produce the identical empty-but-green run. Verification catches all three equally well. Two of them are a message to the client, one is my evening.
The other thing about per-integration verification being cheap to add: it’s cheap to add once. What “correct output” looks like is defined by the client’s data, and clients move things. So the verification layer drifts out of date the same way the workflow does, and now there are two things to maintain instead of one.
On your last line — when you’ve had one of those “this died three weeks ago” conversations, what actually made it expensive? The lost time, or not being able to say quickly whether it was your side or theirs? I ask because I keep assuming the second one is the worse part, and I’d like to know whether that’s actually true or just true for me.
Fair split — I’ll take the correction. Verification catches all three cases equally, but it only tells you that the run lied, not whose lie it was. Different problems.
On your question: the second one, and it’s not close. Lost time is a cost — you eat it and move on. Not being able to say whose side it broke on is a trust event. The client doesn’t experience three weeks of silent failure as three weeks; they experience the thirty seconds where they ask what happened and you don’t have an answer. “Your OAuth token expired on the 4th, here’s the trail” — the relationship survives, maybe gets stronger. “I’m not sure yet” converts a technical failure into doubt about whether you’re in control. That doubt outlives the fix.
On drift — partially agree. What drifts is verifying output correctness against the client’s data. But there’s a narrower check that doesn’t: the action’s own receipt. “The API returned a message ID” stays stable no matter what the client renames in their sheet. Receipt-checking is mine and cheap to keep; output-correctness is shared with the client.
Where my bias comes from: I’ve been building an approval/audit layer for exactly this class of problem in n8n, and the most uncomfortable lesson was finding the same gap in my own system — my “executed” status proved the call was made, not that the downstream action verifiably completed. So that distinction is me having stepped on the rake, not theory.
How do you handle the “whose side” moment today — keep evidence at the boundary, or reconstruct from n8n execution logs after the fact?