I am experiencing a 401 unauthorized_client error when setting up Google Service Account credentials in n8n and enabling the Impersonate a User option.
Despite having the Google Service Account and relevant APIs enabled, and confirming that the setup works via direct API calls (e.g., using a custom script), the credential validation in n8n fails.
{ “error”: “unauthorized_client”, “error_description”: “Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.” }
Steps to Reproduce:
Create a Google Service Account and enable Domain-Wide Delegation in the Google Workspace Admin Console.
Configure the required Scopes for the Service Account (see the list below).
In n8n, create new Google API credentials using the Service Account JSON key.
Enable Impersonate a User and provide a valid user email from the domain.
Attempt to save/test the credential, resulting in the 401 error.
Scopes Configured (as shown in the attached image): I have already added the following scopes to the Domain-Wide Delegation settings:
https://www.googleapis.com/auth/gmail.readonly
https://www.googleapis.com/auth/gmail.send
https://www.googleapis.com/auth/gmail.modify
https://www.googleapis.com/auth/spreadsheets
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/calendar
https://www.googleapis.com/auth/drive.metadata
https://www.googleapis.com/auth/presentations
Debug Info:
n8n Version: 2.1.4
Installation: Self-hosted (Docker)
Additional Context: I have verified that the credentials and impersonation work correctly using external tools/scripts, which suggests the issue might be related to how n8n validates the scopes or constructs the JWT token during the credential setup phase.
Is your google app is in testing or production envrionment? have you tried adding that user specifically as a test user to see if it works? and make sure your app is set to external if the email which you are connecting is outside your domain level otherwise Internal is also fine.
I am working in a production environment, and the email account for DWD (Domain-Wide Delegation) is within the same organization. I have already verified the Service Account and DWD settings using a Python script, and they work correctly. However, I’m specifically encountering a 401 error when enabling the credentials in n8n. I’m not sure which OAuth scopes are mandatory for n8n to function properly.
After struggling, I’ve tried creating an AppScript (in GCP) and found out that my service account is well confingured with DWD (domain-wide delegation)
I can use the saved credentials in HTTP Request node (able to retrieve email and querying gmail api) , but not with Gmail Trigger nor other Gmail nodes.
Obviously the issue is into n8n nodes and not into credentials itself
I’m using the Community edition (self-hosted with version 2.2.6)
Unfortunately, I haven’t been able to resolve this yet.
I want to highlight that I’ve confirmed my credentials and Scopes work perfectly when I trigger them via a custom script/code. However, the 401 Unauthorized error only persists when using the n8n Google Service Account node with the “Impersonate a User” option enabled. This suggests there might be a specific issue or limitation in how n8n handles the impersonation logic compared to a standard implementation.
Currently, I’m at a standstill because applying for additional Google Scopes or making configuration changes requires a tedious administrative process within my organization. Since the code-based approach works but n8n doesn’t, it’s been difficult to justify these internal hurdles.
@Nicolas_KLEIN thank you for sharing your findings! This confirms exactly what I’ve been experiencing.
Like you, I’ve tested my credentials via custom code/scripts, and they work perfectly. This proves that the Google Cloud side (Service Account, DWD, and Scopes) is configured correctly.
It seems clear now that the issue lies within the n8n Google nodes (specifically when “Impersonate a User” is active), rather than the credentials themselves.
For me, this is particularly frustrating because I’m currently stuck in a tedious administrative/bureaucratic process within my organization to request any further changes to Google Scopes. Since the code-based approach works but the n8n node doesn’t, it’s difficult for me to justify these internal hurdles to my IT department.
I hope the n8n team can look into why the dedicated nodes are failing while manual HTTP requests and external scripts are succeeding.
@minshyh16 You can use the n8n “HTTP Request” node with your credentials, just don’t forget to specify the requested scopes for HTTP Request in your credentials.
I also hope a fix to make our workflows lighter and simpler
Regarding Gmail, from testing, I think the problem is in the Gmail node itself. HTTP node request worked for me. @Nicolas_KLEIN Thanks for the extra thing to check.
IF using an HTTP request, you need to add scopes in n8n credentials as well. I add only one:
Your discovery about n8n potentially checking a “bundled” set of Scopes (Drive, Sheets, etc.) even when only using one product is a huge revelation. It explains why a manual HTTP request (which only sends the specific scope we define) works, while the dedicated n8n nodes fail.
This puts me in a tough spot, though. As I mentioned, my organization has a very strict and tedious administrative process for approving Google Scopes. If I have to request a long list of “extra” Scopes (like Drive and Presentations) just to get a Gmail node working, I will need to provide a very strong technical justification to our IT security team.
This definitely feels like a bug or a rigid design in the n8n Google node implementation. It shouldn’t require unrelated Scopes to function.
I will share your findings with my team to see if we can push through a request for these additional Scopes as a workaround, but I really hope the n8n team can look into decoupling these requirements in the node itself.
I think it’s a security breach to always provide a whole set of unexpected scopes. Besides, the credentials configuration should use the request scope for HTTP requests when this is provided as the default set of scopes instead of a bunch of useless ones. I mean, users can then create different credentials depending on their needs without being asked for a predefined scope they don’t want.