nopSolutions/nopCommerce · error · NopException

Failed to obtain user credentials for the authorization serv

Error message

Failed to obtain user credentials for the authorization server. Check the client secrets and allow the application to perform required operations.

What it means

Thrown by SmtpBuilder.GetGmailCredentialsAsync after calling AuthorizationCodeWebApp.AuthorizeAsync. If the returned Credential is null, the OAuth handshake did not yield usable credentials, so the SASL mechanism cannot be built. Unlike the empty-field checks, this is a runtime OAuth failure: credentials were configured but authorization/token retrieval failed.

Source

Thrown at src/Libraries/Nop.Services/Messages/SmtpBuilder.cs:77

        var credentialRoot = _fileProvider.Combine(tokenFilePath, emailAccount.Email);

        var codeFlow = new GoogleAuthorizationCodeFlow(new GoogleAuthorizationCodeFlow.Initializer
        {
            ClientSecrets = new ClientSecrets
            {
                ClientId = emailAccount.ClientId,
                ClientSecret = emailAccount.ClientSecret
            },
            Scopes = NopMessageDefaults.GmailScopes,
            DataStore = new FileDataStore(credentialRoot, true)
        });

        var authCode = new AuthorizationCodeWebApp(codeFlow, null, null);

        var authResult = await authCode.AuthorizeAsync(emailAccount.Email, CancellationToken.None);

        if (authResult.Credential is null)
            throw new NopException("Failed to obtain user credentials for the authorization server. Check the client secrets and allow the application to perform required operations.");

        if (authResult.Credential.Token?.IsStale == true)
            await authResult.Credential.RefreshTokenAsync(CancellationToken.None);

        return new SaslMechanismOAuth2(authResult.Credential.UserId, authResult.Credential.Token.AccessToken);
    }

    protected virtual async Task<SaslMechanism> GetExchangeCredentialsAsync(EmailAccount emailAccount)
    {
        ArgumentNullException.ThrowIfNull(emailAccount);

        if (string.IsNullOrEmpty(emailAccount.ClientId))
            throw new NopException(await _localizationService.GetResourceAsync("Admin.Configuration.EmailAccounts.Fields.ClientId.Required"));

        if (string.IsNullOrEmpty(emailAccount.ClientSecret))
            throw new NopException(await _localizationService.GetResourceAsync("Admin.Configuration.EmailAccounts.Fields.ClientSecret.Required"));

        if (string.IsNullOrEmpty(emailAccount.TenantId))

View on GitHub (pinned to 64bdf2ff08)

Solutions

  1. Re-run the OAuth consent flow so a fresh token is stored (delete the GmailAuthStore folder if corrupt).
  2. Confirm the Google Cloud app is published or the test user is added under OAuth consent.
  3. Verify ClientId/ClientSecret are correct and the Gmail scopes are authorized.
  4. Check server clock synchronization and outbound HTTPS access to accounts.google.com.

Example fix

// before
var authResult = await authCode.AuthorizeAsync(emailAccount.Email, CancellationToken.None);
if (authResult.Credential is null)
    throw new NopException("Failed to obtain user credentials...");

// after - retry consent with a hint
var authResult = await authCode.AuthorizeAsync(emailAccount.Email, CancellationToken.None);
if (authResult.Credential is null)
    throw new NopException("Gmail OAuth failed. Re-authorize the account in admin and retry.");
Defensive patterns

Strategy: retry

Validate before calling

// Surface a clear pre-check: presence of a stored token indicates prior consent
var tokenDir = _fileProvider.Combine(_fileProvider.MapPath(NopMessageDefaults.GmailAuthStorePath), emailAccount.Email);
if (!_fileProvider.GetFiles(tokenDir, "*.token").Any())
    return Error("Complete Gmail OAuth consent for this account before sending.");

Type guard

// Cannot type-guard an external OAuth result; check the stored credential existence instead
static bool HasStoredGmailToken(IFileProvider fp, string email)
    => fp.GetFiles(fp.Combine(fp.MapPath(NopMessageDefaults.GmailAuthStorePath), email), "*").Any();

Try / catch

try { var sasl = await smtpBuilder.GetGmailCredentialsAsync(account); }
catch (NopException ex) when (ex.Message.StartsWith("Failed to obtain user credentials"))
{ /* prompt admin to re-authorize; optionally retry once after clearing the token store */ }

Prevention

When it happens

Trigger: Gmail OAuth where ClientId/Secret are set but AuthorizeAsync returns null Credential — e.g. consent not granted, token store corrupt/expired, revoked access, clock skew, or network failure contacting Google.

Common situations: User never completed the browser consent step; refresh token expired/revoked; FileDataStore token files deleted or for a different user; app not verified/published in Google Cloud; offline/network issues during token exchange.

Related errors


AI-assisted analysis of nopSolutions/nopCommerce@64bdf2ff08 (2026-08-13). Data as JSON: /api/errors/e1b2bba5c8f594ef. Report an issue: GitHub.