validationsendgrid-api-v3sender

Is Single Sender Validation in Sendgrid possible without logging in?


Was just wondering if it's possible for Single Sender Validation to be completed without having to login to Sendgrid as part of the process (e.g. click-through without login). For context, sometimes the people who "own" a mail address that we want to use for sending don't have access to Sendgrid, and we'd like them to be able to validate it. I think they can't by design, but wanted to confirm.

Looking at the API documentation, it looks like you can use the token sent in the validation email to complete the validation process, but I'm not sure if there's any way to effectively make use of that to redirect the user back to a process we control. There's another post that mentions the same kind of challenge, but thought I'd ask again as there wasn't anything definitive.

Is there a simple way to have the user who receives the validation redirect back to something other than sendgrid directly?

Thanks in advance!


Solution

  • The only alternative to logging in is to use the SendGrid API.

    First, you either request the verification using the UI, or you use the Create Verified Sender Request API to start the verification for the single sender.

    Then, the verification email will be sent to the specified email address which contains the verification URL. Usually, this URL will redirect you the the actual URL containing the verification token, as mentioned in the SO post you linked.

    Once you get the verification token, you can use the Verify Sender Request API, passing in the verification token, to verify the single sender.

    Note: All these APIs require a SendGrid API key.


    So technically, you could have an application that prompts your user for their email address to verify, then uses the SendGrid API to start the verification which sends the verification email, then ask the user to go to their email inbox and copy in their verification link, then let the user paste in the URL from which you can extract the verification token, and use the API to verify. While the user didn't have to log in, it still requires some manual work.

    However, the inputting of the email address and the checking the email inbox can also be done programmatically, so this process can be 100% automated, although it takes quite a bit of programming.

    Here's a C# sample:

    using System.Net;
    using Microsoft.AspNetCore.WebUtilities;
    using SendGrid;
    
    namespace VerifySender;
    
    internal class Program
    {
        public static async Task Main(string[] args)
        {
            var configuration = new ConfigurationBuilder()
                .AddUserSecrets<Program>(optional: true)
                .Build();
    
            var apiKey = configuration["SendGrid:ApiKey"] 
                         ?? Environment.GetEnvironmentVariable("SENDGRID_API_KEY")
                         ?? throw new Exception("SendGrid API Key not configured.");
            
            var client = new SendGridClient(apiKey);
    
            // replace this JSON with your own values
            const string data = """
            {
                "nickname": "Orders",
                "from_email": "orders@example.com",
                "from_name": "Example Orders",
                "reply_to": "orders@example.com",
                "reply_to_name": "Example Orders",
                "address": "1234 Fake St",
                "address2": "PO Box 1234",
                "state": "CA",
                "city": "San Francisco",
                "country": "USA",
                "zip": "94105"
            }
            """;
    
            var response = await client.RequestAsync(
                method: SendGridClient.Method.POST,
                urlPath: "verified_senders",
                requestBody: data
            );
    
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine($"Failed to request sender verification. HTTP status code {response.StatusCode}.");
                Console.WriteLine(await response.Body.ReadAsStringAsync());
                Console.WriteLine(response.Headers.ToString());
            }
    
            Console.WriteLine("Enter verification URL:");
            var verificationUrl = Console.ReadLine();
    
            var token = await GetVerificationTokenFromUrl(verificationUrl);
    
            response = await client.RequestAsync(
                method: SendGridClient.Method.GET,
                urlPath: $"verified_senders/verify/{token}"
            );
    
            if (!response.IsSuccessStatusCode)
            {
                Console.WriteLine($"Failed to verify sender. HTTP status code {response.StatusCode}.");
                Console.WriteLine(await response.Body.ReadAsStringAsync());
                Console.WriteLine(response.Headers.ToString());
            }
        }
    
        private static async Task<string> GetVerificationTokenFromUrl(string url)
        {
            /*
             * url could be three different types:
             * 1. Click Tracking Link which responds with HTTP Found and Location header to url type 2.
             * 2. URL containing the verification token:
             *      https://app.sendgrid.com/settings/sender_auth/senders/verify?token=[VERIFICATION_TOKEN]&etc=etc
             * 3. URL prompting the user to login, but contains url 2. in the redirect_to parameter:
             *      https://app.sendgrid.com/login?redirect_to=[URL_TYPE_2_ENCODED]
            */
            const string verificationBaseUrl = "https://app.sendgrid.com/settings/sender_auth/senders/verify";
            const string loginBaseUrl = "https://app.sendgrid.com/login";
            if (url.StartsWith(verificationBaseUrl))
            {
                var uri = new Uri(url, UriKind.Absolute);
                var parameters = QueryHelpers.ParseQuery(uri.Query);
                if (parameters.ContainsKey("token"))
                {
                    return parameters["token"].ToString();
                }
    
                throw new Exception("Did not find token in verification URL.");
            }
    
            if (url.StartsWith(loginBaseUrl))
            {
                var uri = new Uri(url, UriKind.Absolute);
                var parameters = QueryHelpers.ParseQuery(uri.Query);
                if (parameters.ContainsKey("redirect_to"))
                {
                    url = $"https://app.sendgrid.com{parameters["redirect_to"]}";
                    return await GetVerificationTokenFromUrl(url);
                }
    
                throw new Exception("Did not find token in verification URL.");
            }
    
            var clientHandler = new HttpClientHandler();
            clientHandler.AllowAutoRedirect = false;
            using var httpClient = new HttpClient(clientHandler);
            var response = await httpClient.GetAsync(url);
            if (response.StatusCode == HttpStatusCode.Found)
            {
                var uri = response.Headers.Location;
                return await GetVerificationTokenFromUrl(uri.ToString());
            }
    
            throw new Exception("Did not find token in verification URL.");
        }
    }
    

    Take note of the comments inside of GetVerificationTokenFromUrl. Since I don't trust the user to copy the URL from the email without clicking on it, I added support for three types of URL:

    1. Click Tracking Link which responds with HTTP Found and Location header to url type 2.
    2. URL containing the verification token: https://app.sendgrid.com/settings/sender_auth/senders/verify?token=[VERIFICATION_TOKEN]&etc=etc
    3. URL prompting the user to login, but contains url 2. in the redirect_to parameter: https://app.sendgrid.com/login?redirect_to=[URL_TYPE_2_ENCODED]

    Here's the full source code on GitHub.