H, I'd be happy for help: In my flow, I have a wel...
# ask-a-descoper
r
H, I'd be happy for help: In my flow, I have a welcome screen where the user can authentication via a magic link. My setup is project-->tenants --> subtenants I created a custom attribute called "TenantDomain" and I assign each of my tenants a specific domain. ( multiple tenants can have the same tenantDomain value) I'd like to add a condition BEFORE proceeding to the authentication via magic link, that checks the following: is the login domain ( e.g - user tried to login through env1.platform.com , then - the domain name would be "env1") among one of the user's associated tenant's tenantDomain? Consider the following setup: Users: User1 - User@platform.com Tenants: Tenant1 - TenantDomain = "env1" Tenant2 - TenantDomain = "env2" Associations: User1 is associated with Tenant1 Use-case 1: User1 tries to log in via env1@platform.com --> The condition will be evaluated as True ( The domain is env1, and the user is associated with Tenant1 which has the TenantDomain as "env1") User-case2: User1 tries to log in via env2@platform.com --> The condition will be evaluated as False ( The domain is env2, but the user is associated only with Tenant1 which has a different TenantDomain which is "env1") How can I achieve this?
n
Hi @Rani Fischer You can achieve this by adding a small “gate” step in the flow before sending the magic link. At a high level: 1. Use the flow’s hostname (device.location.hostname) to extract the subdomain (e.g. env1 from env1.platform.com). 2. After the user enters their email (but before authentication), run a scriptlet. 3. In the scriptlet, look at the user’s associated tenants (unauthUser.tenantIds), load each tenant’s custom attributes, and check whether any tenant’s TenantDomain matches the login subdomain. 4. Return a boolean (and optionally the matching tenantId). 5. Add a flow condition on that result: ◦ true → proceed with the magic link ◦ false → route to an error screen This keeps the check pre-auth, avoids account/tenant enumeration, and works cleanly even when users belong to multiple tenants. If you return the matched tenantId, you can also pre-select the tenant for the magic link. Happy to dive deeper on this if you have any questions.
r
@Nick Rodriguez Thank you for the lead! Regarding (3) , how can I load the tenant in the scriplet? I've tried
await descopeClient.management.tenant.loadtenantId(tenandId)
but it fails , but I got two issues: (1) The test in the builder doesn't run, it says the script syntax error (on the line attached here) I have to initialize the descopeClient? if yes -> how? or is there any other alternative to achieve it? I've tried to
import DescopeClient from '@descope/node-sdk';
but it says I can't. Thank you
n
Yes, the issue is that Flow Scriptlets don’t run in a full Node runtime, so you can’t import @descope/node-sdk, and there’s no descopeClient available to initialize there. If you need to call the management API, the recommended approach is: create a Generic HTTP Connector that calls Descope’s Tenant Management API (using a Management Key stored in the connector), then in the flow/scriptlet use the connector response (connectors.<name>...) to read the tenant’s customAttributes.TenantDomain and compare it to device.location.hostname’s subdomain. Example scriptlet:
Copy code
// Get hostname, e.g. "<http://env1.platform.com|env1.platform.com>"
const hostname = context.device.location.hostname || "";

// Extract subdomain ("env1")
const loginSubdomain = hostname.split(".")[0];

// Get user-associated tenant IDs
const tenantIds = context.unauthUser?.tenantIds || [];

// Default result
let domainMatch = false;
let matchedTenantId = null;

// Iterate through user tenants
for (const tenantId of tenantIds) {
  // Call the Generic HTTP connector
  const tenantResp = await context.connectors.loadTenant({
    tenantId: tenantId
  });

  if (!tenantResp || !tenantResp.customAttributes) {
    continue;
  }

  const tenantDomain = tenantResp.customAttributes.TenantDomain;

  if (
    tenantDomain &&
    tenantDomain.toLowerCase() === loginSubdomain.toLowerCase()
  ) {
    domainMatch = true;
    matchedTenantId = tenantId;
    break;
  }
}

// Expose results to the flow
return {
  domainMatch,
  matchedTenantId
};