Passing a secret to custom HTML

What I’m trying to do:
I need to pass a license key from a secret to this custom HTML
What I’ve tried and what’s not working:
I built a function that gets this license key from the secret, but when I load a project, it loads, and then I have a pop-up that the license key is not available.
Code Sample:

import anvil.server
import anvil.secrets

@anvil.server.callable
def get_studio_license():
  
  return anvil.secrets.get_secret("STUDIO_LICENSE")

from ._anvil_designer import Form1Template
from anvil import *
import anvil.server
from anvil import js

class Form1(Form1Template):
  def __init__(self, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)
  # Funkcja dostępna w JS jako window.getStudioLicense()
    js.window.getStudioLicense = self._get_studio_license
    
    # Po wystawieniu funkcji od razu odpalamy JS inicjujący edytor


  def _get_studio_license(self):
    # Pobranie secreta z serwera
    return anvil.server.call("get_studio_license")
      # Any code you write here will run before the form opens.


Clone link:

<script>
  // Helper – czekamy aż Anvil podpiął window.getStudioLicense
  function waitForGetStudioLicense() {
    return new Promise((resolve) => {
      const check = () => {
        if (window.getStudioLicense) {
          resolve();
        } else {
          setTimeout(check, 50);
        }
      };
      check();
    });
  }

  (async function () {
    // Czekamy na funkcję z Anvila
    await waitForGetStudioLicense();

    // Pobieramy klucz licencji z Pythona (Anvil Form → server)
    const licenseKey = window.getStudioLicense();

    GrapesJsStudioSDK.createStudioEditor({
      root: '#studio-editor',
      licenseKey: licenseKey,
      project: {
        type: 'web',
        id: 'UNIQUE_PROJECT_ID'
      },
      identity: {
        id: 'UNIQUE_END_USER_ID'
      },
      assets: {
        storageType: 'cloud'
      },
      storage: {
        type: 'cloud',
        autosaveChanges: 100,
        autosaveIntervalMs: 10000
      }
    });
  })();
</script>

Hi,
I think I was having the same issue on a project I haven’t touched for about 6 months. I don’t know much HTML, but piecing together some answers on the forum helped me build this for Stripe checkout, hopefully this can help you:

<script>
      (async function startCheckout() {
        try {
          console.log("Fetching client secret from Anvil...");
          const clientSecret = await anvil.call(document.getElementById("checkout-container"), "checkout", 2000);

          console.log("Received client secret:", clientSecret);

          if (!clientSecret || !clientSecret.startsWith("cs_")) {
            console.error("Invalid client secret:", clientSecret);
            return;
          }

          // Inject client secret into checkout container
          document.getElementById("checkout-container").setAttribute("data-client-secret", clientSecret);
          // console.log("!!!!", document.getElementById("checkout-container"))

          // Initialize Stripe Embedded Checkout
          const stripe = Stripe(TEST_KEY_GOES_HERE);
          const checkout = await stripe.initEmbeddedCheckout({ clientSecret });

          console.log("Checkout instance created:", checkout);
          checkout.mount("#checkout-container");
        } catch (error) {
          console.error("Error initializing checkout:", error);
        }
      })(); // Immediately invokes the function on page load
    </script>

Another way to do it is like this:

HTML Code

<script>
  async function getSecret(secretName) {
    const secret = await window.get_secret(secretName)
    return secret
  }
</script>
<button onclick="getSecret('test_secret').then(secret => console.log(secret)).catch(err => console.error(err))">Click Me</button>

Anvil Code

from ._anvil_designer import Form1Template
import anvil.server
from anvil.js import window


class Form1(Form1Template):
  def __init__(self, **properties):
    self.init_components(**properties)
    window.get_secret = self.get_secret

  def get_secret(self, secret):
    return anvil.server.call("get_secret", secret)

Server Code

import anvil.server
import anvil.secrets


# Make sure you add code to secure the secrets for the right users.
@anvil.server.callable
def get_secret(secret_key):
  return anvil.secrets.get_secret(secret_key)
  1. I made an anvil method that calls the server function and set it as a method on window (this is the recommended way to call python from js Gotcha with calling Python from Javascript - #2)

Do note that secrets are no longer secret once they get returned from a server function. They’re available at that point in plain text in the client and available to anyone who is sufficiently technically capable. I wouldn’t expose any API key through this mechanism that allows someone to cost me money.

2 Likes

… or copy/damage someone’s data.

1 Like

Yes don’t know the use case but could be that customising Auth / anvil.user could be an option. Could be feasible as this allows options beyond just presenting a login form with email and password with customisation… > 1 server call will be slower but more secure.