1

I've followed the steps at this link to setup a custom signin page for Azure AD B2C:

https://learn.microsoft.com/en-us/azure/active-directory-b2c/customize-ui-with-html?pivots=b2c-user-flow

Which are essentially:

  1. Create a custom HTML file:
<!DOCTYPE html>
<html>
<head>
    <title>My Product Brand Name</title>
</head>
<body>
    <div id="api"></div>
</body>
</html>
  1. Upload it to a file hosting service

  2. In Azure AD B2C > User Flows > [ your_sign_in_flow ], set the following:

Use custom page content: YES  
  
Custom page URI:  [ paste the URL of your hosted file here ]
  1. Click Save

  2. Click Run User Flow to test the flow

That produces this login page:

enter image description here

Where the HTML of the api div is:

<div id="api" data-name="Unified">
      <div class="heading">
        <h1 role="heading">Sign in</h1>
      </div>
        <div class="claims-provider-list-buttons social" aria-label="Sign in with your social account" role="form">
          <div class="intro">
            <h2 aria-level="1">Sign in with your social account</h2>
          </div>
          <div class="options">
              <div>
                  <button class="accountButton firstButton claims-provider-selection" id="AzureADPeople" role="link" autofocus="">Azure AD People</button>
              </div>
                      </div>
        </div>

        <div class="divider">
          <h2>OR</h2>
        </div>
      <form id="localAccountForm" action="JavaScript:void(0);" class="localAccount" aria-label="Sign in with your email address">
        <div class="intro">
          <h2 aria-level="1">
            Sign in with your email address
          </h2>
        </div>
        <div class="error pageLevel" aria-hidden="true" role="alert" style="display: none;">
          <p></p>
        </div>
        <div class="entry">
          <div class="entry-item">
            <label for="email">
              Email Address
            </label>
            <div class="error itemLevel" aria-hidden="true" role="alert" style="display: none;">
              <p></p>
            </div>
            <input type="email" id="email" name="Email Address" title="Please enter a valid Email Address" pattern="^[a-zA-Z0-9!#$%&amp;'+^_`{}~-]+(?:\.[a-zA-Z0-9!#$%&amp;'+^_`{}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?$" autofocus="" placeholder="Email Address" aria-label="Email Address">
          </div>
            <div class="entry-item">
              <div class="password-label">
                <label for="password">Password</label>
                    <u><a id="forgotPassword" href="/<my-tenant-name>.onmicrosoft.com/B2C_1_signin1/api/CombinedSigninAndSignup/forgotPassword?csrf_token=****==&amp;tx=StateProperties=****&amp;p=B2C_1_signin1">Forgot your password?</a></u>
                              </div>
              <div class="error itemLevel" aria-hidden="true" style="display: none;">
                <p role="alert"></p>
              </div>
              <input type="password" id="password" name="Password" placeholder="Password" aria-label="Password" autocomplete="current-password" aria-required="true">
                          </div>
          <div class="working"></div>


          <div class="buttons">
            <button id="next" type="submit" form="localAccountForm">Sign in</button>
          </div>
        </div>
      </form>
  </div>

In order to be able to customise this page, I need to know how to do the following:

  1. Remove text elements if I don't want them displayed
  2. Modify text elements if I want to change the text strings
  3. Style text elements if I want them to have different font stylings
  4. Wrap various elements in divs with particular classes to be able to apply a design
    (eg display the different sign in options as inline-blocks side by side etc)

My question is:

Is it permissible to do all of this via JavaScript (and CSS to hide/style text elements)?

Technically I think I could do it, but I'm not sure if it is allowed or best practice.

This page has a list of guidelines for using JavaScript in Azure AD B2C custom pages:

https://learn.microsoft.com/en-us/azure/active-directory-b2c/javascript-and-page-layout?pivots=b2c-user-flow#guidelines-for-using-javascript

One of them is:

Don't take a dependency on Azure AD B2C code or comments.

But I'm not sure how elements can be customised without being able to reference the code within the api div.

For reference, the Page Layouts page does have a Version setting, which makes me think that, if we keep the same version selected, the contents of the api div would be unlikely to change (and therefore not break any JavaScript referencing particular divs etc).

enter image description here

Proof of Concept

I was able to create this custom signin page:

enter image description here

With this custom HTML page:

<!DOCTYPE html>
<html>
<head>
    <title>My Custom Login Page</title>

    <!-- links to CSS framework -->
    <script src="https://cdn.jsdelivr.net/npm/uikit@3.15.19/dist/js/uikit.min.js" integrity="sha256-gHEvLzvpjU93VFTgu5myAgmc+0I1CzBzYe0YhTJ8uqI=" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/uikit@3.15.19/dist/js/uikit-icons.min.js" integrity="sha256-PxrcCuBlFkPVlPsGxNzt59W3EKYOH79z+v/tq9uFS2M=" crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.15.19/dist/css/uikit.min.css" integrity="sha256-f3SVFXP30MgahZ0Z7Lp1W02MuLgtiAtk8uFqTbGxNfY=" crossorigin="anonymous">

    <!-- links to google fonts -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Josefin+Sans:wght@200&family=Noto+Sans+Ethiopic:wght@300&display=swap" rel="stylesheet">

    <!-- CSS styles -->  
    <style>
    #api {
        width: 300px;
        padding: 25px 40px 40px 40px;
        border: 1px solid #000;
        margin: 0 auto;
        margin-top: 50px;
    }

    .heading {
        text-align: center;
    }

    h1 {
        font-family: 'Josefin Sans';
        color: #000;
        text-decoration: none;
        text-transform: uppercase;
        font-size: 45px;
        margin-bottom: 0px;
    }

    .tagline {
        margin: -5px 0px 25px 0px;
        padding: 0px;
        display: block;
        font-size: 14px;
        color: #727272;
    }

    h2 {
        font-size: 14px;
        line-height: 12px !important;
        margin-bottom: 10px;
    }

    .social {
        margin-top: 30px;
    }

    #forgotPassword {
        font-size: 14px;
        margin-left: 5px;
        text-transform: lowercase;
    }

    .uk-input:focus,
    .uk-select:focus,
    .uk-textarea:focus {
        border-color: #e5e5e5 !important;
    }
    </style>

    <!-- jQuery to get things looking the way I want -->
    <script>

    $(document).ready(function() {

    console.log("document is ready apparently"); 

    // hacky - put in a delay of 1 second to wait for the 'api' div content to be added
    setTimeout(() => {
        console.log("Delayed for 1 second.");
        $(".heading h1").text("APP");
        $( "<span class='tagline'>Tagline Goes Here</span>").insertAfter( ".heading h1" );  
        $("#localAccountForm .intro h2").text("External Users"); 
        $(".social h2").text("Internal Users"); 
        $("label[for='email'], label[for='password']").remove(); 
        $("input").addClass("uk-input"); 
        $("#email").addClass("uk-margin-small-bottom"); 
        $("button").addClass("uk-button uk-button-default uk-button-small uk-margin-small-top"); 
        $("#AzureADPeople").text("Single Sign-On"); 
        $('.divider').remove();
        $('.social').insertAfter('#localAccountForm');
        $('.password-label').insertAfter('#password');
    }, "1000"); 

    });
    </script>
</head>
<body>
    <!-- this is the only thing required for a custom page -->  
    <div id="api"></div>
</body>
</html>
user1063287
  • 10,265
  • 25
  • 122
  • 218

1 Answers1

2

Don't take a dependency on Azure AD B2C code or comments.

This moreso is referring to our JS hooks and low level functionality, not the DOM.

You’ll need CSS/JS to manipulate the DOM for your requirements.

Jas Suri - MSFT
  • 10,605
  • 2
  • 10
  • 20
  • Thank you for the confirmation, is there anyway to 'know' when the `api` div has finishing loading its content? So that we can apply our transformations after the `api` div has loaded all its content? As a workaround, as shown in my original post, I am currently using a 1 second `setTimeOut` after `document.ready`, but that is hacky and doesn't look very good. – user1063287 Jan 03 '23 at 06:15
  • Usually document.ready() is sufficient. – Jas Suri - MSFT Jan 03 '23 at 17:32
  • `$(document).ready(function() { // transformations here })` does not work. It only works if I add a `setTimeout` of 1 second within the document ready function. Are there any events available from the Azure injections into the `api` div? Eg when the process has started and when it has finished? If so, we could hide the `api` div with CSS, wait for the Azure injections to finish, and then run our transformation code and show the `api` div. – user1063287 Jan 04 '23 at 22:00
  • @user1063287 Not sure if you're still looking for an answer but the `defer` keyword worked for me: `` – Ikeem Wilson Jun 01 '23 at 21:01