static-web-apps-cli icon indicating copy to clipboard operation
static-web-apps-cli copied to clipboard

Auth Emulator - Indicate valid Username characters when invalid character(s) submitted

Open snerks opened this issue 4 years ago • 4 comments

Is your feature request related to a problem? Please describe.

Using the Auth Emulator page for GitHub (may be the same for other Identity Providers): http://localhost:4280/.auth/login/github

If the user enters invalid characters for Username, an attempt to submit the form fails "silently" in the UI.

Example value for Username:

یونیکد

The console gives additional information:

The string to be encoded contains characters outside of the Latin1 range.

Uncaught DOMException: Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range. at saveCookie (http://localhost:4280/.auth/login/github:168:54) at HTMLFormElement.<anonymous> (http://localhost:4280/.auth/login/github:316:11) at HTMLFormElement.dispatch (https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.2.1.min.js:3:10316) at HTMLFormElement.q.handle (https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.2.1.min.js:3:8343)

Describe the solution you'd like If it is required that Username characters be in the Latin1 range, add a validation message near the Username field in the UI. If the full range of Unicode characters is allowed, adjust logic, to allow such characters.

Describe alternatives you've considered Open the DevTools Console while running, to view run-time exceptions

Additional context Exception is thrown in this source code file: https://github.com/Azure/static-web-apps-cli/blob/main/src/public/auth.html

      function saveCookie(formElement) {
        const data = localStorage[hashStorageKey(formElement)];
        document.cookie = `StaticWebAppsAuthCookie=${btoa(data)}; path=/`;
      }

snerks avatar Aug 14 '21 13:08 snerks

This link may provide a workaround for the problem:

https://developer.mozilla.org/en-US/docs/Glossary/Base64

function utf8_to_b64( str ) {
  return window.btoa(unescape(encodeURIComponent( str )));
}

function b64_to_utf8( str ) {
  return decodeURIComponent(escape(window.atob( str )));
}

// Usage:
utf8_to_b64('✓ à la mode'); // "4pyTIMOgIGxhIG1vZGU="
b64_to_utf8('4pyTIMOgIGxhIG1vZGU='); // "✓ à la mode"

snerks avatar Aug 16 '21 09:08 snerks

I found that, if I change the Javascript in auth.html:

      function utf8_to_b64( str ) {
        return window.btoa(unescape(encodeURIComponent( str )));
      }
      function saveCookie(formElement) {
        const data = localStorage[hashStorageKey(formElement)];
        // document.cookie = `StaticWebAppsAuthCookie=${btoa(data)}; path=/`;
	document.cookie = `StaticWebAppsAuthCookie=${utf8_to_b64(data)}; path=/`;
      }

The redirect occurs in the browser.

If a Function needs to inspect the x-ms-client-principal cookie, a small change is required (based on https://github.com/MicrosoftDocs/azure-docs/blob/master/articles/static-web-apps/user-information.md):

        public static ClaimsPrincipal Parse(HttpRequest req)
        {
            var principal = new ClientPrincipal();

            if (req.Headers.TryGetValue("x-ms-client-principal", out var header))
            {
                var data = header[0];
                var decoded = Convert.FromBase64String(data);
               
                // Use UTF-8 Encoding
                // var json = Encoding.ASCII.GetString(decoded);
                var json = Encoding.UTF8.GetString(decoded);
                principal = JsonSerializer.Deserialize<ClientPrincipal>(json, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });
            }

            principal.UserRoles = principal.UserRoles?.Except(new string[] { "anonymous" }, StringComparer.CurrentCultureIgnoreCase);

            if (!principal.UserRoles?.Any() ?? true)
            {
                return new ClaimsPrincipal();
            }

            var identity = new ClaimsIdentity(principal.IdentityProvider);
            identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, principal.UserId));
            identity.AddClaim(new Claim(ClaimTypes.Name, principal.UserDetails));
            identity.AddClaims(principal.UserRoles.Select(r => new Claim(ClaimTypes.Role, r)));

            return new ClaimsPrincipal(identity);
        }

snerks avatar Aug 16 '21 10:08 snerks

Thanks for pointing this out. Looks like Static Web Apps is using UTF8 to encode the client principal, so it's probably more correct for the emulator to encode as UTF8 and for the Functions code samples to use UTF8 when decoding.

anthonychu avatar Aug 16 '21 21:08 anthonychu

@anthonychu Thanks for investigating.

Do you think we should:

  1. Mark this issue as a CLI bug (for the saveCookie problem)?
  2. Create a docs issue for the ClaimsPrinicpal sample?

snerks avatar Aug 17 '21 13:08 snerks