DEV Community

Warren Parad for Authress

Posted on

 

Adding Custom Domains to your SaaS

You're building out a SaaS solution and realize for one reason or another supporting custom domains for your customers is a must. There are some products out there that can do this for you in some way, but they either cost a lot, are a huge maintenance burden or aren't scalable. Here, we'll walk through using AWS to add custom domains to your solution.


What are Custom Domains?

First, let's discuss what a custom domain actually is. A custom domain, let's your customer brand your API, UI, service, or product with their domain on top of your service. This provides a huge amount of value for both parties. For customers they can provide your service to their third parties or hide the implementation detail of what they are using. No one wants to expose their dependencies to customers, it's unnecessary and dangerous.

For example if you have a product @ https://product-service.com and offer custom domains for your customers, they may look like:

https://customer-A.product-service.com

or

https://02c77286.product-service.com

This might be fine in a lot of situations, you can give the customer this identifier or url and call it a day. You might even have some logic which would associate this with their user/account to automatically redirect them. That's all good, but in the first case, let's say you want to include a Support Portal as part of your Product Service, you don't want to redirect your customers to https://customer-A.third-party-website.com, you want them to stay on the same domain.

As a concrete example, when you sign up for our product you get an account ID that looks like bwdlb5r89jwhy232, so the urls look like this:

Image description


So why are we doing this again?

Just like the support portal option, custom domains have two other important uses.

Internal benefit

One example is your benefit. We'll use our product Authress as an example. Authress provides an API for access control. That means every customer has their user identities, resources, roles, and permissions managed by the service. Since Authress optimizes permission queries as well as user credential security and caching, segregating each customer to their own subdomain has a lot of value for us. Authress charges by API call, that means tracking of these calls is critical, using the subdomain is a great way to separate calls.

When the service allocates a subdomain to an account, the subdomain matches the accountId:

https://accountId-A1.api.authress.io

And while this is easy for developers to use in a service, it isn't pretty. So we provide the capability to mask this domain with one the customer chooses.

Security implications

Security is improved via:

A) There are lots of restrictions that exist on a per domain basis, CORS/Cookies are one of them. Allowing your users to set their custom domain for your product to match their domain, allows them to consume these resources in a safe way. Having different domains may tempt you to store some things that you shouldn't.

B) User SSO and authentication. For SSO, every business customer will want to have their own login provider (IdP) utilized. That means Account 1 uses IdP A, Account 2 uses IdP B, and so on. There is no way to know before a user logs in which IdP to use, so we need to guess. We could guess by forcing the user to enter their email, and then using the domain of the email.

This is a terrible solution. Instead, let the customer use a custom domain on top of your subdomain to configure their IdP. Then you can just look at the domain to determine which IdP to you. (Spoiler: this is exactly what Authress does to automatically support SSO for B2B products, you don't need to do any extra work, if implemented through Authress connections).


Setting up a custom domain

So you want to let your customers, tell you which domain they want and then you map

https://custom.domain.com => https://customer-A.product-service.com

This is mostly a DNS record, and you are done. All they have to do is add CNAME added to their Domain Registrar's hosted zone.

Sounds easy doesn't it? So why do all these companies try to charge you a ton for this?


The Challenge

While the CNAME works out of the box, the real problem comes with TLS or HTTPS. In order for that CNAME to work and for their users to correctly have encrypted traffic, YOUR service needs to serve a TLS certificate that matches THEIR domain.

If this was your domain, you would add a DNS verification to your Hosted Zone, and your cert would work. Or You could AWS Certificate Manager and this would work without even any code.

But you don't own this domain, which means somehow you need them to give you a cert, and this cert has to be renewable, that means every 3 months~1 year you would need a new cert...For every customer...

This also means maintaining a database of these encrypted certs and safe-guarding them, because if you:

  • lose them, then all your customers can no longer access your site
  • expose access to them, then anyone can impersonate your customers

Both of these are really bad.


The Solution

(A small aside: If you actually wanted to pay for this, there is a discussion about all the different options available on IndieHackers)

As I mentioned there are tons of solutions to this out in the world, but none of them are great, they are costly to maintain, and worse, if there is something that goes wrong, it will go really wrong. So here I'm going to outline how you can do with this AWS Certificate (ACM) and AWS CloudFront (CF) and not have to worry about anything else.

1. Set up your service

Run your service like you would, wherever you would, this could be AWS Lambda or ECS (or if you are a masochist--EC2, or just want to burn a lot of money for no reason--EKS).

2. Support wildcard processing

Your service needs to work with wildcards or genericly specified domains. This means that https://*.product-service.com needs to be handled correctly by your stack. For APIGW, this means adding a custom domain for AWS APIGW that includes the *. For a static site running in S3, however, you don't need any extra magic.

3. Enable custom domain requests

Let your customer request "I want to setup a custom domain, my domain is example.com". Generally speaking, require them to use a subdomain, because you likely aren't hosting their main website. For Authress, we require subdomains lik login.example.com.

4. Deploy Customer Account specific resources

To do this we'll deploy a new CloudFront and a new Certificate, both in the US-EAST-1 region (this is important). Both resources should request resources with the custom domain specified.

  • Create a new CloudFront Distribution with No alternate domain name set. (If you try to use the cert before it is validated it will fail, so don't do this). This distribution should point to your existing infrastructure, and have custom headers set for the specific customer accountId/subdomain. The CF distribution will have a domain like:
dmv3o3npogaoea.cloudfront.net
Enter fullscreen mode Exit fullscreen mode
  • Request a new ACM certificate with the custom domain and get back the DNS validation options, they look like this:
Name: _85dd6f5e25429205f5ffa77.example.com.
Value: _caee56563101.aocomg.acm-validations.aws.
Enter fullscreen mode Exit fullscreen mode

5. Mask these values so you can make updates later, by creating two CNAME records in your hosted zone:

CNAME: validation-customer-A.product-service.com
Value: _caee56563101.aocomg.acm-validations.aws (value from above)
Enter fullscreen mode Exit fullscreen mode

AND

Name: customer-A.product-service.com
Value: dmv3o3npogaoea.cloudfront.net
Enter fullscreen mode Exit fullscreen mode

6. Return these two sets of DNS CNAME values:

Name: _85dd6f5e25429205f5ffa77.example.com (Name from above)
Value: validation-customer-A.product-service.com (our custom name)
Enter fullscreen mode Exit fullscreen mode

AND

Name: example.com (their custom domain)
Value: customer-A.product-service.com
Enter fullscreen mode Exit fullscreen mode

7. Wait for your customer to add these two CNAME records to their domain hosted zone.

Concretely, from our example it would looks like this:
Image description

Image description

8. Once verified update your CloudFront distribution alternate domain name and certificate that was just verified.

Image description

Image description

Done.


The Result

Now you should be successfully running the CloudFront distribution as a TLS proxy for your service, website, app, etc... It will allow the white labeling that your customers need and you don't need to pay a fortunate to managing anything. ACM and ACF are free, and all the traffic you would pay for anyway just gets migrated to a new CF.

Even better? you can attach protections to your infrastructure to monitor the usage of these proxies to know how they are being used. And further, all the tools exist at the CF level for rate limiting, DDoS protection, real-time logging, custom assets, and the list goes on.


Come join our Community and discuss this and other security related topics!

Top comments (11)

Collapse
 
frenchcooc profile image
Info Comment hidden by post author - thread only accessible via permalink
Corentin • Edited

Just sharing that CloudFlare also offers something similar within their CloudFlare for SaaS offering. It's now available with all plans (including their free plan). Price is 100 custom domains for free, then $0.10/month per additional custom domain. Definitely a no-brainer coming from such a trusted brand.

Collapse
 
wparad profile image
Warren Parad

Absolutely true, although Cloudflare used to charge $5000/month just to get access to the functionality. There are lots of providers for doing it, this is only one valid way and basically free.

Collapse
 
fabiomoretti profile image
Fabio Moretti

What exact offering of cloudflare are you referencing?

Collapse
 
frenchcooc profile image
Corentin
Thread Thread
 
_alexblokh profile image
Alex Blokh

it does seem like it starts from free

Thread Thread
 
frenchcooc profile image
Corentin

It does! Cloudflare for Saas has a free plan which includes up to 100 custom domains

Thread Thread
 
_alexblokh profile image
Alex Blokh

yeah, for some reason it is extremely unclear with their pricing page
I think they're in a hurry and have to spend some time and refactor it a bit

Collapse
 
amadeojimenez profile image
amadeo

Very nice article! thanks a lot for the clear explanation.

I am considering implementing this but I am concerned about the limit in distributions per account (200), what if I have thousands of customers? I have read about another solution with just one cname pointing to a load balancer which terminates the SSL connection. Any thoughts?

Thank you!!

Collapse
 
wparad profile image
Warren Parad

That's just the default limit: docs.aws.amazon.com/AmazonCloudFro...

You don't want to spin up a whole load balancer per customer that is super expensive, and actually the default limit for ALBs is much lower at 50: docs.aws.amazon.com/elasticloadbal...

Collapse
 
amadeojimenez profile image
amadeo

Hi Warren, thanks for the answer.

Let me clarify,
I don't mean one ELB per customer, but one for all customers. Here the limit is the ammount of certificates per rule in ELB (which is 25 default) although one could pack many in one (ELB supports SAN).
There is the possibility of doing this with nginx + certbot and have no limit.

Somehow (independenly of the service, Cfront, ELB, whatever) configuring and mantaining one per customer feels a bit too much for me.

May be I am just scared XD

Collapse
 
sreekanth850 profile image
sreekanth

We use bunny.net pullzone, works in same concept. Its little bit easy to handle.

Some comments have been hidden by the post's author - find out more