Select credentials via expression

Jumping in here because I ran into this exact same scaling issue!

@Phillip_Glau To answer your specific question: Yes, the “while-loop” style execution is possible, but you can’t do it using n8n’s native service nodes since they lock the credentials.

To achieve the loop you described:

  1. Trigger via Webhook: Pass your array of user credentials (API keys, OAuth tokens, etc.) into the workflow dynamically. You can send this array directly from your frontend client dashboard or your database into an n8n Webhook node.

  2. Iterate: Use a Loop / Split In Batches node to iterate through that array of users.

  3. Make the API Call: Swap out the native service node for an HTTP Request node. These allow you to map the credential from your current loop iteration using ‘expressions’

There were actually a lot of people asking for a blog of this exact workaround over in this other multi-tenant OAuth thread so we have published one recently: Dynamic Credential Management in n8n: One Workflow, Many Clients | Five
It walks you through exactly how to set up these HTTP nodes, using step-by-step examples for OpenAI, OpenRouter, and Google Sheets. Hope this helps.

1 Like

+1     ⇒ I also need the ability to select credentials dynamically.

I have several complex SQL queries spread across multiple workflows that I need to run on one of several databases based on the input.

It seems that currently the only option is to duplicate all SQL nodes and select the correct predefined credentials for each one. If changes are made to the SQL queries, this must then be done manually for all duplicates.

This is all well and good for bearer/tokens that persist/are known before-hand. But for flows like MCP OAuth and DCR, I don’t think this is going to work as there is no “token” to exchange / use automatically, without reimplementing the entire OAuth + DCR as nodes… right?

I actually was able to connect to Google Sheets this exact way using the HTTP Request node, which obviously requires a Google OAuth token

Your frontend should handle the Google SSO completely, grabs the short-lived access token and passes it directly into the n8n webhook payload.
From there, n8n just acts as a consumer. You map that token straight into the Authorization header of your HTTP Request node, just like you would with a standard API key.

I actually already have an example of Google Sheets in the blog

Hey, thanks for replying. What I mean, is a DCR oauth credential. This means there is no pre-shared token. From trying your solution, it doesn’t quite work for this. fyi the DCR (dynamic client registration) is a feature commonly used in MCP.

@zeeb0t That’s a good point. Since DCR requires the client to provision its client_id and secret on the fly, we actually can adapt this architecture to support DCR by using any database. Instead of the frontend holding a static ID, we just shift the DCR handshake to the backend before n8n ever gets triggered.

For example, the backend intercepts the creation and fires a POST /register request to the Identity Provider (Auth0, Okta, custom MCP server, etc.). We then receive the client_id and secret and save them to the DB. The backend then simply uses those dynamic credentials to grab the access_token, and fires that into the n8n webhook payload.

That being said, I haven’t gone too deep down the DCR rabbit hole just yet! If you happen to have a dummy n8n workflow or a sample DCR payload you’d be willing to share, I’d love to take a look. I’m actively developing this app and it would be awesome to try and build out a working prototype of this exact flow.

From what I understand, the MCP DCR oAuth is something built-in to n8n currently. It is a credential type, whose automation is part of the n8n codebase. I don’t see any obvious location the tokens are stored. Likely as hidden fields of the credential.

@zeeb0t n8n does handle the DCR handshake natively as part of the MCP credential type, and those tokens are almost certainly stored as encrypted hidden fields in n8n’s credential store. But I think the core problem from the original thread still applies, even with MCP credentials handled natively, n8n locks each node to a single credential. So if you’re running a workflow that needs to serve multiple MCP agents each dynamically registered with their own client_id/secret you still can’t select which MCP credential to use at runtime via an expression.

So the HTTP node workaround isn’t about reimplementing what n8n already does for MCP auth it’s about getting around the credential-locking problem for multi-tenant scenarios.

Thanks @Pranoy123 - I understand the workaround, but I think that’s exactly the line I’m not comfortable crossing.

Passing an already-valid bearer token into an HTTP Request node is easy enough, but with MCP OAuth/DCR the hard part is everything before and around that token: discovery, dynamic client registration, redirect URI handling, PKCE/state, token exchange, refresh behaviour, expiry handling, secure storage, provider-specific quirks, and keeping up with a spec/implementation area that is still moving quickly.

That is the part n8n is already starting to own through the native credential/node implementation. Recreating that externally just so I can work around static credential selection feels risky and brittle, especially for MCP where the auth model is more than “paste this API key into a header”.

So I think we’re talking about two different things:

  1. For simple bearer/API-key style integrations, yes, HTTP Request + dynamic header injection is a sensible workaround.

  2. For native n8n MCP OAuth/DCR credentials, the problem remains: the workflow/node still needs a way to select the correct saved credential at runtime, instead of requiring duplicated workflows or reimplementing the whole OAuth/DCR lifecycle outside n8n.

That’s why I still think expression-based credential selection is the cleaner and safer fix here.

I was following this thread for the same general reason as the rest of you, and found a workaround that may help some people here.

It does not dynamically select a different saved credential instance by ID. That still appears to be the missing feature.

However, for credential types where the important value is just an API key/token value, you can do it the other way around:

Use one fixed credential instance, but make the credential field itself dynamic using an expression.

So instead of:

Dynamically choose Credential A, B, or C

you do:

Use one credential, but dynamically resolve the API key/token inside that credential at runtime

For example, imagine you have an Edit Fields / Set node earlier in the workflow called:

Runtime Config

and that node outputs an apiKey field.

Option 1: Use a runtime key, otherwise fall back to a default key

In the credential’s API key field, you could use:

{{ $('Runtime Config').first().json.apiKey || 'your-default-api-key-here' }}

That gives you:

apiKey has a value → use that value
apiKey is missing or empty → use the default key

This is useful where you want a normal/default account to be used unless the workflow provides something else.

Option 2: Use a runtime key only, otherwise let the workflow fail

If you do not want any fallback at all, you can simply use:

{{ $('Runtime Config').first().json.apiKey }}

That gives you:

apiKey has a value → use that value
apiKey is missing or empty → the credential resolves to empty/undefined and the node should fail

That may actually be preferable in some cases. If the workflow is supposed to provide a runtime credential value, failing loudly is often better than silently using the wrong/default account.

One thing worth noting: when you are editing the credential itself, the expression may show undefined or fail to preview. That makes sense, because the credential does not inherently know which workflow, execution, or node it will be attached to.

The important part is that once the credential is actually used by a node inside a workflow, the expression is evaluated in that workflow execution context. So references such as:

$('Runtime Config').first().json.apiKey

can resolve correctly at runtime, even if they appear as undefined while viewing or testing the credential in isolation.

This works because n8n supports expressions inside credential fields. So the node still uses a normal, fixed credential reference, but the value inside that credential is resolved dynamically at execution time.

This is effectively an inverse workaround for this feature request:

Not dynamic credential selection:
"Choose which credential ID to use"

But dynamic credential value resolution:
"Use this credential, but resolve the API key/token from the workflow execution"

Obviously there are caveats:

  1. This is mostly useful for simple API-key/token-style credentials.

  2. I would not expect this to solve OAuth-style credential switching cleanly.

  3. If you hardcode a fallback key inside the expression, that fallback key is readable to anyone who can view/edit that credential.

  4. If the runtime key comes from workflow data, be careful with execution-data saving/redaction so you don’t store secrets in execution history.

But for cases where you just need a credential-backed node to use a runtime-provided API key, this seems to work very well and avoids creating temporary credentials, updating workflow JSON, executing a second workflow, then deleting the credential afterwards.

Or, it avoids creating many duplicates of the same workflow to handle different client keys.

This is a MAJOR win as I use nodes that REQUIRE credential instance selection!

It is not the same as dynamically selecting credentials by ID, but for many API-key-based integrations it achieves the practical outcome in a much cleaner way.