Skip to content

External provider with no email returns generic 500 (unexpected_failure) instead of email_address_not_provided #2583

@PierreVieira

Description

@PierreVieira

Summary

When an external OAuth provider returns no email and the provider is not configured as email-optional, the OAuth callback rejects the sign-in with a generic internal server error:

https://github.com/supabase/auth/blob/master/internal/api/external.go#L175-L177

if len(userData.Emails) == 0 && !emailOptional {
    return apierrors.NewInternalServerError("Error getting user email from external provider")
}

NewInternalServerError hardcodes HTTP 500 with error_code = unexpected_failure. This has two downsides:

  1. It mis-classifies a non-server fault as a 500. The provider simply didn't return an email — the server is working fine. The redirect already carries error=server_error, which reads like an outage.
  2. Clients have no stable way to detect this specific case. Since the only distinguishing signal is the human-readable error_description string, clients (e.g. mobile SDKs) are forced to substring-match "Error getting user email from external provider" to tell "provider returned no email" apart from any other internal failure.

There is already a dedicated, unused error code

errorcode.go defines email_address_not_provided, but it is not referenced anywhere in the codebase:

https://github.com/supabase/auth/blob/master/internal/api/apierrors/errorcode.go#L105

ErrorCodeEmailAddressNotProvided ErrorCode = "email_address_not_provided"

Proposal

Use the existing code via NewUnprocessableEntityError (422), consistent with how nearby external-identity conditions are reported (e.g. ErrorCodeSignupDisabled uses 422):

return apierrors.NewUnprocessableEntityError(apierrors.ErrorCodeEmailAddressNotProvided, "Error getting user email from external provider")

For the OAuth redirect flow this keeps error=server_error and error_description unchanged (422 is not in oauthErrorMap, so it falls back to server_error), and only upgrades error_code from unexpected_failure to email_address_not_provided — so existing clients are unaffected while new clients gain a stable code to branch on.

Motivation

This surfaces with Sign in with Apple, where a user can withhold the email scope. With "Allow users without an email" disabled, the provider returns no email and Auth correctly rejects the sign-in — but downstream SDKs can't cleanly tell that apart from a real server error, so they fall back to a generic "something went wrong" message.

Side note / question (not part of the proposed fix)

The native grant_type=id_token path (token_oidc.go) passes emailOptional into createAccountFromExternalIdentity but does not seem to have the equivalent empty-email rejection that the web /callback path has. Is the email requirement intended to be enforced for the id_token grant too, or is that deliberately left to account-linking? Happy to open a separate issue if it's worth looking into.

I have a PR ready for the proposed change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions