CORS Errror with loading dropin ui components

works ok on localhost.
CORS errors when running on stage environment.

Access to fetch at ‘https://api-sandbox.dwolla.com/customers/’ from origin ‘http://www.mysite.com’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

@stasevich , Would you be able to share some of your code in relation to drop-in components? Both server side and client side code where relevant. (Removing any sensitive information like names or API credentials)

is there a way we can do it over the email?

This is an entire script block.
Looks like its an error loading components prior to server side call.
Ui does load up, but beneficial owner is stuck

<script>
    dwolla.configure({
        environment: "sandbox",
        styles: "/Content/Dwolla.css",
        success: (result) => Promise.resolve(saveDwollaCustomer(result)),
        error: (err) => Promise.resolve(alert("Something went wrong. Please contact support. Error:" + err.response.message)),
        tokenUrl: "/Dwolla/GenerateToken"
    });

    function saveDwollaCustomer(result) {
        if (result.response.status != null && result.response.status == "certified")
            return;

        if (result.response.location.includes("beneficial-owners"))
            return;

        var link = result.response.location;

        var actualTabHeader = document.getElementById("DwollaCustomerTabHeader");
        actualTabHeader.classList.remove("active");
        var actualTabContent = document.getElementById("DwollaCustomer");
        actualTabContent.classList.remove("active");
        var nextTabHeader = document.getElementById("DwollaFundingSourceTabHeader");
        nextTabHeader.classList.add("active");
        var nextTabContent = document.getElementById("DwollaFundingSource");
        nextTabContent.classList.add("active");

        const guid = link.split('/').pop();

        const elem = document.querySelector('dwolla-beneficial-owners');
        elem.setAttribute('customerid', guid);

        $.get("/Dwolla/SaveBrokerVerifiedCustomer?customerLink=" + link, function(data) {
        }, "json");
    }
</script>

image

Hi @stasevich, is the “/Dwolla/GenerateToken” endpoint on the client-side of your app? Would you be able to share the implementation of that endpoint? That would help us investigate the issue as I suspect that that might be what’s causing the CORs error.

For context, when loading the Beneficial Owners component, the library makes a call to your "/Dwolla/GenerateToken" endpoint to generate a client-token. This client-token API call needs to be done via the server-side of your app, otherwise it will return a CORs error. After the token is generated, the drop-in can then use it to make calls to the API to POST the beneficial owner data from the client-side.

Hi @stasevich, is the “/Dwolla/GenerateToken” endpoint on the client-side of your app? Would you be able to share the implementation of that endpoint? That would help us investigate the issue as I suspect that that might be what’s causing the CORs error.
backEnd C# code

public ActionResult GenerateToken()
{
var token = new TokenService().Generate();

return Json(new { token }, JsonRequestBehavior.AllowGet);
}


public string Generate()
{
// TODO: Dwolla - move these constants to web.config
string clientId = ConfigurationManager.AppSettings[“DwollaApiKey”];
string clientSecret = ConfigurationManager.AppSettings[“DwollaApiSecret”];
string environmentLink = $“{ConfigurationManager.AppSettings[“DwollaEnvironment”]}/token”;

var client = new RestClient(environmentLink)
{
Authenticator = new HttpBasicAuthenticator(clientId, clientSecret)
};

var request = new RestRequest(Method.POST);
request.AddHeader(“Content-Type”, “application/x-www-form-urlencoded”);
request.AddParameter(“grant_type”, “client_credentials”);

var response = client.Execute(request);

if (response.StatusCode != HttpStatusCode.OK)
throw new Exception(“Dwolla failed to generate access token!”);

return response.Data.AccessToken;
}

Thanks for sharing the backend code, @stasevich.

I see. The Generate() function is currently requesting for an “access-token” from the API which is used in all subsequent API calls as the value of the Authorization header. This access-token can only make calls to the API from the server side.

What the drop-in component requires is a “client-token” in order to be able to make API requests from the client-side. So, to accomplish this, you can add another request within the function to make calls to the /client-token endpoint, and returning back that token instead of the access-token. The documentation I’ve linked above goes into more detail about clien-token generation.

What I would also recommend is separating the access-token generation logic from the Generate function and implement a mechanism to manage token expiration and renewal.

Since we don’t have an example in C# for the /tokenUrl implementation and GenerateClientTokenWithBody function, I leveraged the help of an AI language model (ChatGPT) to develop the following example and customized it to fit our requirements. It is just for reference to demonstrate the implementation and is not a working example. Hopefully this helps in aiding the implementation in your project. If you have any questions or need further details, please let us know!

using Microsoft.AspNetCore.Mvc;
using System;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;

// DwollaController class, to handle HTTP requests for your Dwolla integration.
[ApiController]
[Route("[controller]")]
public class DwollaController : ControllerBase
{
    private readonly HttpClient _httpClient;

    // Constructor for DwollaController that initializes an HttpClient for making API requests.
    public DwollaController(IHttpClientFactory httpClientFactory)
    {
        // Creating an HttpClient instance using the injected IHttpClientFactory.
        _httpClient = httpClientFactory.CreateClient();

        // Setting the base address for the HttpClient to the Dwolla API endpoint. You likely have all of this set up already.
        _httpClient.BaseAddress = new Uri("https://api-sandbox.dwolla.com/"); // Adjust base URL based on environment
    }

    // Endpoint for receiving POST requests at /tokenUrl.
    [HttpPost("tokenUrl")]
    public async Task < ActionResult > TokenUrl([FromBody] TokenRequestModel model)
    {
        try {
            // Generate a client token by calling the GenerateClientTokenWithBody method.
            var clientTokenRes = await GenerateClientTokenWithBody(model);

            // Return an Ok response with the generated client token.
            return Ok(new { token = clientTokenRes.Token });
        }
        catch (Exception ex)
        {
            // Handle errors and return a BadRequest response with an error message.
            return BadRequest(new { error = ex.Message });
        }
    }

    // Private method to generate a client token by sending a POST request to Dwolla's API.
    private async Task < ClientTokenResponse > GenerateClientTokenWithBody(TokenRequestModel body)
    {
        // Define the endpoint for generating client tokens.
        var tokenEndpoint = "client-tokens";

        // Serialize the provided TokenRequestModel into JSON and create a StringContent.
        var content = new StringContent(JsonSerializer.Serialize(body), System.Text.Encoding.UTF8, "application/json");

        // Send a POST request to the Dwolla API's client-tokens endpoint.
        var response = await _httpClient.PostAsync(tokenEndpoint, content);

        // Check if the response indicates success; if not, throw an exception.
        if (!response.IsSuccessStatusCode) {
            throw new Exception("Failed to generate client token");
        }

        // Read the response content and deserialize it into a ClientTokenResponse object.
        var responseContent = await response.Content.ReadAsStringAsync();
        var clientTokenRes = JsonSerializer.Deserialize<ClientTokenResponse>(responseContent);

        // Return the deserialized ClientTokenResponse object.
        return clientTokenRes;
    }
}

// Data model classes used for JSON serialization and deserialization.

// Represents the request payload for generating a client token.
public class TokenRequestModel {
    public string Action { get; set; }
    public Links Links { get; set; }
}

// Represents the "Links" property in the request payload.
public class Links {
    public Customer Customer { get; set; }
}

// Represents the "Customer" property within the "Links" property.
public class Customer {
    public string Href { get; set; }
}

// Represents the response structure for a generated client token.
public class ClientTokenResponse {
    public string Token { get; set; }
}

Implemented, but still same error, as it looks like the CORS ERROR before

the call to token

we’re calling from domain carshipit.com and getting CORS error, the error is not present on localhost.
CAN WE call from any domain other than localhost, sand box environment, do we have to register/set/preset safe url or something else domain related in order to do that?

updated code
dwolla.configure({
environment: “sandbox”,
styles: “/Content/Dwolla.css”,
success: (result) => Promise.resolve(saveDwollaCustomer(result)),
error: (err) => Promise.resolve(alert(“Something went wrong. Please contact support. Error:” + err.response.message)),
tokenUrl: “/Dwolla/TokenUrl”
});

[HttpPost]
public async Task TokenUrl(TokenRequestModel body)
{
try
{
// Serialize the provided TokenRequestModel into JSON and create a StringContent.
// var content = new StringContent(JsonConvert.SerializeObject(body), System.Text.Encoding.UTF8, “application/json”);
// Generate a client token by calling the GenerateClientTokenWithBody method.
var clientTokenRes = await _tokenService.GenerateClientTokenWithBody(body);

// Return an Ok response with the generated client token.
return Json(new { clientTokenRes.Token }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
// Handle errors and return a BadRequest response with an error message.
return Json(new { error = ex.Message });
}
}

public async Task GenerateClientTokenWithBody(TokenRequestModel body)
{

RestClient client = new RestClient(clientTokens)
{
//Authenticator = new HttpBasicAuthenticator(clientId, clientSecret)
};

var request = new RestRequest(Method.POST);
request.AddHeader(“Content-Type”, “application/json”);
request.AddJsonBody(body);

var response = await client.ExecuteTaskAsync(request);

if (response.StatusCode != System.Net.HttpStatusCode.OK)
{
throw new Exception(“Failed to generate client token”);
}

if (response.StatusCode != HttpStatusCode.OK)
throw new Exception(“Dwolla failed to generate client access token!”);
return response.Data;
}

You should be able to call from any domain and don’t need to register/set/preset a safe url.

Are you able to make if the client-token is being generated using the following endpoint?

https://api-sandbox.dwolla.com/client-tokens

Another thing I noticed is that the Customer ID is missing from the the URL that the component is trying to call -

/customers//funding-sources?removed=false....

Is there a customerId field in the component that you are not populating?

Can you please provide a working example with authorization header included.
As well as CustomerID where does it need to be included.
Specific example please?

While we don’t have examples in C#, we do have working examples in Express.js and Next.js. The latter example also has an accompanying video walkthrough.

I’d recommend checking out either one of those for reference. If you come across any questions, do let us know!

I guess what i’m missing is any reference to any authorization in all of these requests.
Unauthorized, video or site examples do not show sending any sort of authorization headers, are they not needed?

public ClientTokenResponse GenerateClientTokenWithBody(TokenRequestModel body)
{

RestClient client = new RestClient(clientTokens);

var request = new RestRequest(Method.POST);
request.AddHeader(“Accept”, “application/json”);
request.AddHeader(“Content-Type”, “application/json”);
request.AddHeader(“X-Requested-With”, “Dwolla-Drop-Ins-Library”);

var requestBody = new
{
action = body.Action,
_links = body.Links
};

request.AddJsonBody(requestBody);

var response = client.Execute(request);

with https://api-sandbox.dwolla.com/client-tokens as clientToken value, we’re getting
image.png