Universal Login Page Templates
You can customize the New Universal Login pages by providing a Page Template using the Liquid template language. You can update the ULP templates only using the Management API. This capability can only be used if the tenant has Custom Domains enabled.
Page Templates let you define the content that is displayed around the Universal Login widgets (e.g., the Login box, the MFA box). The same template is used for all pages, which helps you to implement a consistent login with minimum effort.
The simplest template you can write is:
<!DOCTYPE html><html>
<head>
{%- auth0:head -%}
</head>
<body>
{%- auth0:widget -%}
</body></html>
The following tags must be present in the template:
auth0:widget -
Includes the HTML for the widget that is displayed in every page (e.g., Login, Reset Password)auth0:head -
Includes tags that are required to render the widget
If you use the class="_widget-auto-layout"
in the <body>
element, the widget will be centered in the page. If you want to position it yourself, you can omit it.
Page Templates Variables
The Page Templates have a set of context variables that can be used to impact how the page is rendered. This allows you to implement scenarios like:
Render different content depending on the Application (for example, you own two brands that need a different page design).
Render different content depending on the Prompt (for example, in the Login page you want to add content explaining what the application does, but in the MFA flow, you prefer to only have the MFA box).
Add a footer with links to the tenant's support page or email.
Available Variables
The available variables are:
The login page application's settings:
application.id
application.name
application.logo_url
application.metadata
Universal Login branding settings:
branding.logo_url
branding.colors.primary
branding.colors.page_background
Tenant's settings:
tenant.friendly_name
tenant.support_email
tenant.support_url
When a user logs in through an Organization, organization information:
organization.id
organization.display_name
organization.name
organization.metadata
organization.branding.logo_url
organization.branding.colors.primary
organization.branding.colors.page_background
Information about the current Universal Login screen.
locale
: Locale used to render the login pages, matching one of the supported tenant languagesprompt.name
: Name of the Universal Login prompt being renderedprompt.screen.name
: Name of the Universal Login screen being rendered.prompt.screen.texts
: All the localized texts needed in the screen being rendered.
The term
prompt
is used to refer to a specific step in the login flow. Eachprompt
can have one or more screens. Below is the list of available prompts, and you can find all the screens for each one by following the links:
Information about the current user, for pages rendered after the user authenticates:
user.user_id
user.picture
user.email
user.email_verified
user.app_metadata
user.user_metadata
user.family_name
user.given_name
user.name
user.nickname
user.username
Examples
Login box + image layout
The following template will show the login box to the left, and an image to the right only for the login/signup pages. The rest of the pages will look like the default ones.
<!DOCTYPE html><html lang="{{locale}}">
<head>
{%- auth0:head -%}
<style>
body {
background-image: url("https://images.unsplash.com/photo-1592450865877-e3a318ec3522?ixlib=rb-1.2.1&auto=format&fit=crop&w=2255&q=80");
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.prompt-wrapper {
position: relative;
display: flex;
align-items: center;
width: 480px;
height: 100%;
justify-content: center;
background-color: rgb(60,60,60);
}
</style>
<title>{{ prompt.screen.texts.pageTitle }}</title>
</head>
<body>
{% if prompt.name == "login" or prompt.name == "signup" %}
<div class="prompt-wrapper">
{%- auth0:widget -%}
</div>
{% else %}
{%- auth0:widget -%}
{% endif %}
</body>
</html>

The example below adds a gray footer with links to Privacy Policy and Terms of Services:
<!DOCTYPE html><html lang="{{locale}}">
<head>
{%- auth0:head -%}
<style>
body {
background-image: radial-gradient(white, rgb(200, 200, 200));
}
.footer {
background-color: rgb(120, 120, 120);
position: absolute;
bottom: 0;
left: 0;
padding: 16px 0;
width: 100%;
color: white;
/* Use a high z-index for future-proofing */
z-index: 10;
}
.footer ul {
text-align: center;
}
.footer ul li {
display: inline-block;
margin: 0 4px;
}
.footer ul li:not(:first-of-type) {
margin-left: 0;
}
.footer ul li:not(:first-of-type)::before {
content: '';
display: inline-block;
vertical-align: middle;
width: 4px;
height: 4px;
margin-right: 4px;
background-color: white;
border-radius: 50%;
}
.footer a {
color: white;
}
</style>
<title>{{ prompt.screen.texts.pageTitle }}</title>
</head>
<body class="_widget-auto-layout">
{%- auth0:widget -%}
<footer class="footer">
<ul>
<li><a href="https://company.com/privacy">Privacy Policy</a></li>
<li><a href="https://company.com/terms">Terms of Service</a></li>
</ul>
</footer>
</body></html>

Page Templates API
To set the Page Template you need to use the Management API. You first need to get a Management API token with the update:branding
, read:branding
, delete:branding
scopes. If you are using the API Explorer Application
to generate tokens, make sure those scopes are enabled for the Auth0 Management API
.
To set the template, you need to use the following endpoint:
curl --request PUT \
--url 'https://YOUR_DOMAIN/api/v2/branding/templates/universal-login' \
--header 'authorization: Bearer MGMT_API_ACCESS_TOKEN' \
--header 'content-type: text/html' \
--data '<!DOCTYPE html><html><head>{%- auth0:head -%}</head><body>{%- auth0:widget -%}</body></html>'
var client = new RestClient("https://YOUR_DOMAIN/api/v2/branding/templates/universal-login");
var request = new RestRequest(Method.PUT);
request.AddHeader("authorization", "Bearer MGMT_API_ACCESS_TOKEN");
request.AddHeader("content-type", "text/html");
request.AddParameter("text/html", "<!DOCTYPE html><html><head>{%- auth0:head -%}</head><body>{%- auth0:widget -%}</body></html>", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "https://YOUR_DOMAIN/api/v2/branding/templates/universal-login"
payload := strings.NewReader("<!DOCTYPE html><html><head>{%- auth0:head -%}</head><body>{%- auth0:widget -%}</body></html>")
req, _ := http.NewRequest("PUT", url, payload)
req.Header.Add("authorization", "Bearer MGMT_API_ACCESS_TOKEN")
req.Header.Add("content-type", "text/html")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
HttpResponse<String> response = Unirest.put("https://YOUR_DOMAIN/api/v2/branding/templates/universal-login")
.header("authorization", "Bearer MGMT_API_ACCESS_TOKEN")
.header("content-type", "text/html")
.body("<!DOCTYPE html><html><head>{%- auth0:head -%}</head><body>{%- auth0:widget -%}</body></html>")
.asString();
var axios = require("axios").default;
var options = {
method: 'PUT',
url: 'https://YOUR_DOMAIN/api/v2/branding/templates/universal-login',
headers: {authorization: 'Bearer MGMT_API_ACCESS_TOKEN', 'content-type': 'text/html'},
data: '<!DOCTYPE html><html><head>{%- auth0:head -%}</head><body>{%- auth0:widget -%}</body></html>'
};
axios.request(options).then(function (response) {
console.log(response.data);
}).catch(function (error) {
console.error(error);
});
#import <Foundation/Foundation.h>
NSDictionary *headers = @{ @"authorization": @"Bearer MGMT_API_ACCESS_TOKEN",
@"content-type": @"text/html" };
NSData *postData = [[NSData alloc] initWithData:[@"<!DOCTYPE html><html><head>{%- auth0:head -%}</head><body>{%- auth0:widget -%}</body></html>" dataUsingEncoding:NSUTF8StringEncoding]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_DOMAIN/api/v2/branding/templates/universal-login"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
[request setHTTPMethod:@"PUT"];
[request setAllHTTPHeaderFields:headers];
[request setHTTPBody:postData];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"%@", error);
} else {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
NSLog(@"%@", httpResponse);
}
}];
[dataTask resume];
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://YOUR_DOMAIN/api/v2/branding/templates/universal-login",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "PUT",
CURLOPT_POSTFIELDS => "<!DOCTYPE html><html><head>{%- auth0:head -%}</head><body>{%- auth0:widget -%}</body></html>",
CURLOPT_HTTPHEADER => [
"authorization: Bearer MGMT_API_ACCESS_TOKEN",
"content-type: text/html"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
import http.client
conn = http.client.HTTPSConnection("")
payload = "<!DOCTYPE html><html><head>{%- auth0:head -%}</head><body>{%- auth0:widget -%}</body></html>"
headers = {
'authorization': "Bearer MGMT_API_ACCESS_TOKEN",
'content-type': "text/html"
}
conn.request("PUT", "/YOUR_DOMAIN/api/v2/branding/templates/universal-login", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
require 'uri'
require 'net/http'
require 'openssl'
url = URI("https://YOUR_DOMAIN/api/v2/branding/templates/universal-login")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Put.new(url)
request["authorization"] = 'Bearer MGMT_API_ACCESS_TOKEN'
request["content-type"] = 'text/html'
request.body = "<!DOCTYPE html><html><head>{%- auth0:head -%}</head><body>{%- auth0:widget -%}</body></html>"
response = http.request(request)
puts response.read_body
import Foundation
let headers = [
"authorization": "Bearer MGMT_API_ACCESS_TOKEN",
"content-type": "text/html"
]
let postData = NSData(data: "<!DOCTYPE html><html><head>{%- auth0:head -%}</head><body>{%- auth0:widget -%}</body></html>".data(using: String.Encoding.utf8)!)
let request = NSMutableURLRequest(url: NSURL(string: "https://YOUR_DOMAIN/api/v2/branding/templates/universal-login")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "PUT"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
To retrieve the template, you need to use the following endpoint:
curl --request GET \
--url 'https://YOUR_DOMAIN/api/v2/branding/templates/universal-login' \
--header 'authorization: Bearer MGMT_API_ACCESS_TOKEN'
var client = new RestClient("https://YOUR_DOMAIN/api/v2/branding/templates/universal-login");
var request = new RestRequest(Method.GET);
request.AddHeader("authorization", "Bearer MGMT_API_ACCESS_TOKEN");
IRestResponse response = client.Execute(request);
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
url := "https://YOUR_DOMAIN/api/v2/branding/templates/universal-login"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("authorization", "Bearer MGMT_API_ACCESS_TOKEN")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
HttpResponse<String> response = Unirest.get("https://YOUR_DOMAIN/api/v2/branding/templates/universal-login")
.header("authorization", "Bearer MGMT_API_ACCESS_TOKEN")
.asString();
var axios = require("axios").default;
var options = {
method: 'GET',
url: 'https://YOUR_DOMAIN/api/v2/branding/templates/universal-login',
headers: {authorization: 'Bearer MGMT_API_ACCESS_TOKEN'}
};
axios.request(options).then(function (response) {
console.log(response.data);
}).catch(function (error) {
console.error(error);
});
#import <Foundation/Foundation.h>
NSDictionary *headers = @{ @"authorization": @"Bearer MGMT_API_ACCESS_TOKEN" };
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_DOMAIN/api/v2/branding/templates/universal-login"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
[request setHTTPMethod:@"GET"];
[request setAllHTTPHeaderFields:headers];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"%@", error);
} else {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
NSLog(@"%@", httpResponse);
}
}];
[dataTask resume];
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://YOUR_DOMAIN/api/v2/branding/templates/universal-login",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"authorization: Bearer MGMT_API_ACCESS_TOKEN"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
import http.client
conn = http.client.HTTPSConnection("")
headers = { 'authorization': "Bearer MGMT_API_ACCESS_TOKEN" }
conn.request("GET", "/YOUR_DOMAIN/api/v2/branding/templates/universal-login", headers=headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
require 'uri'
require 'net/http'
require 'openssl'
url = URI("https://YOUR_DOMAIN/api/v2/branding/templates/universal-login")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Get.new(url)
request["authorization"] = 'Bearer MGMT_API_ACCESS_TOKEN'
response = http.request(request)
puts response.read_body
import Foundation
let headers = ["authorization": "Bearer MGMT_API_ACCESS_TOKEN"]
let request = NSMutableURLRequest(url: NSURL(string: "https://YOUR_DOMAIN/api/v2/branding/templates/universal-login")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
To delete the template, you need to use the following endpoint:
curl --request DELETE \
--url 'https://YOUR_DOMAIN/api/v2/branding/templates/universal-login' \
--header 'authorization: Bearer MGMT_API_ACCESS_TOKEN'
var client = new RestClient("https://YOUR_DOMAIN/api/v2/branding/templates/universal-login");
var request = new RestRequest(Method.DELETE);
request.AddHeader("authorization", "Bearer MGMT_API_ACCESS_TOKEN");
IRestResponse response = client.Execute(request);
package main
import (
"fmt"
"net/http"
"io/ioutil"
)
func main() {
url := "https://YOUR_DOMAIN/api/v2/branding/templates/universal-login"
req, _ := http.NewRequest("DELETE", url, nil)
req.Header.Add("authorization", "Bearer MGMT_API_ACCESS_TOKEN")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
}
HttpResponse<String> response = Unirest.delete("https://YOUR_DOMAIN/api/v2/branding/templates/universal-login")
.header("authorization", "Bearer MGMT_API_ACCESS_TOKEN")
.asString();
var axios = require("axios").default;
var options = {
method: 'DELETE',
url: 'https://YOUR_DOMAIN/api/v2/branding/templates/universal-login',
headers: {authorization: 'Bearer MGMT_API_ACCESS_TOKEN'}
};
axios.request(options).then(function (response) {
console.log(response.data);
}).catch(function (error) {
console.error(error);
});
#import <Foundation/Foundation.h>
NSDictionary *headers = @{ @"authorization": @"Bearer MGMT_API_ACCESS_TOKEN" };
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_DOMAIN/api/v2/branding/templates/universal-login"]
cachePolicy:NSURLRequestUseProtocolCachePolicy
timeoutInterval:10.0];
[request setHTTPMethod:@"DELETE"];
[request setAllHTTPHeaderFields:headers];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"%@", error);
} else {
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
NSLog(@"%@", httpResponse);
}
}];
[dataTask resume];
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://YOUR_DOMAIN/api/v2/branding/templates/universal-login",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "DELETE",
CURLOPT_HTTPHEADER => [
"authorization: Bearer MGMT_API_ACCESS_TOKEN"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
import http.client
conn = http.client.HTTPSConnection("")
headers = { 'authorization': "Bearer MGMT_API_ACCESS_TOKEN" }
conn.request("DELETE", "/YOUR_DOMAIN/api/v2/branding/templates/universal-login", headers=headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
require 'uri'
require 'net/http'
require 'openssl'
url = URI("https://YOUR_DOMAIN/api/v2/branding/templates/universal-login")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Delete.new(url)
request["authorization"] = 'Bearer MGMT_API_ACCESS_TOKEN'
response = http.request(request)
puts response.read_body
import Foundation
let headers = ["authorization": "Bearer MGMT_API_ACCESS_TOKEN"]
let request = NSMutableURLRequest(url: NSURL(string: "https://YOUR_DOMAIN/api/v2/branding/templates/universal-login")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "DELETE"
request.allHTTPHeaderFields = headers
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse)
}
})
dataTask.resume()
The maximum size for the Page Template is 100KB. If that is not big enough, consider moving images/css files outside of the Page Template code.
CSS customization
There are a few things that you can customize using CSS:
You can hide the tenant logo by adding
class="_hide-prompt-logo"
in the<body>
element.
You can specify a custom logo by adding
class="_use-custom-prompt-logo"
in the<body>
element. This would let you, for example, change the login page logo depending on the application:
<% if application.name === "website" %>
#custom-prompt-logo {
background-image: url('https://acme.com/website.png');
}
<% elsif application.name === "employee_portal" %>
#custom-prompt-logo {
background-image: url('https://acme.com/acme.png');
}
<% endif %>
The current implementation does not support further CSS customization. If you look at the HTML that is generated, you will see code like:
.c10d15918.c7b3b8672 {
background: #D00E17;
}
The CSS class names change each time we build the project. If you write CSS that targets those classes, it will break.
Migrating from Page Templates Beta
If you were using the Page Templates Beta, make sure you add the class="_widget-auto-layout"
in the <body>
or you adjust your template to include the <auth0:widget>
inside a container you position wherever you want.
If you were using CSS to target the internal structure of the Universal Login Widget, those customizations will no longer be applied.
Troubleshooting
If the template is not being applied, check that you are navigating to <custom_domain>/authorize
. When you navigate to YOUR_DOMAIN/authorize
Auth0 will not render the page template.