UNPKG

@azure/static-web-apps-cli

Version:
375 lines (354 loc) 15.5 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <title>Azure Static Web Apps Emulator - Authentication Portal</title> <link rel="shortcut icon" href="https://appservice.azureedge.net/images/static-apps/v3/favicon.svg" type="image/x-icon" /> <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/4.1.1/css/bootstrap.min.css" crossorigin="anonymous" /> <style> html, body { height: 100%; } body { display: flex; flex-direction: column; } .container-height { flex: 1 0 auto; } footer { flex-shrink: 0; width: 100%; background-color: #f5f5f5; left: -1px; padding: 10px; display: flex; font-size: 0.8em; justify-content: space-between; } footer nav { display: flex; } footer ul { list-style: none; display: flex; } footer li { margin: 0 10px; } </style> </head> <body> <nav class="navbar navbar-light"> <div class="navbar-brand"> <div class="container pl-4 ml-5"> <img src=https://appservice.azureedge.net/images/static-apps/v3/microsoft_azure_logo.svg width="270" height="108" alt /> </div> </div> </nav> <div class="container-fluid container-height mr-1"> <div class="row"> <div class="row col-xs-12 col-sm-12 d-block d-lg-none d-xl-none d-md-block d-sm-block d-xs-block"> <div class="text-center"><img src=https://appservice.azureedge.net/images/static-apps/v3/staticapps.svg /></div> </div> <div class=" extra-pl-small-scr offset-xl-1 offset-lg-1 offset-md-2 offset-sm-2 offset-xs-4 col-xl-5 col-lg-5 col-md-10 col-sm-11 col-xs-11 div-vertical-center " > <div class="container-fluid"> <h1 class="pb-4">Azure Static Web Apps Auth</h1> <form action="" name="auth"> <div class="form-group row"> <label for="identityProvider" class="col-4 col-form-label">Provider</label> <div class="col-8"> <input disabled id="identityProvider" name="identityProvider" list="providers-list" placeholder="Choose an auth provider" type="text" required="required" class="form-control" /> <small id="identityProviderHelpBlock" class="form-text text-muted">Name of the identity provider</small> </div> <datalist id="providers-list"> <option value="aad"></option> <option value="google"></option> <option value="github"></option> <option value="twitter"></option> <option value="facebook"></option> <option value="openid"></option> </datalist> </div> <div class="form-group row"> <label for="userId" class="col-4 col-form-label">User ID</label> <div class="col-8"> <input id="userId" name="userId" autocomplete="off" placeholder="Choose a user ID" type="text" aria-describedby="userIdHelpBlock" required="required" class="form-control" /> <small id="userIdHelpBlock" class="form-text text-muted">An Azure Static Web Apps-specific unique ID for the user</small> </div> </div> <div class="form-group row"> <label for="userDetails" class="col-4 col-form-label">Username</label> <div class="col-8"> <input id="userDetails" name="userDetails" autocomplete="off" placeholder="Choose a username" type="text" aria-describedby="userDetailsHelpBlock" required="required" class="form-control" /> <small id="userDetailsHelpBlock" class="form-text text-muted">Username or email address of the user</small> </div> </div> <div class="form-group row"> <label for="userRoles" class="col-4 col-form-label">User's roles</label> <div class="col-8"> <textarea id="userRoles" name="userRoles" cols="40" rows="3" class="form-control" aria-describedby="userRolesHelpBlock"></textarea> <small id="userRolesHelpBlock" class="form-text text-muted">Roles used during authorization. One role per line.</small> <small id="userRolesHelpBlock" class="form-text text-muted" >Note: roles "authenticated" and "anonymous" will be added automatically if not provided.</small > </div> </div> <div class="form-group row"> <label for="claims" class="col-4 col-form-label">User's claims</label> <div class="col-8"> <textarea id="claims" name="claims" cols="40" rows="3" class="form-control" aria-describedby="userClaimsHelpBlock" placeholder="[{'typ': 'name','val': 'Azure Static Web Apps'}]" ></textarea> <small id="userClaimsHelpBlockError" class="d-none form-text text-muted alert alert-danger">Invalid JSON value!</small> <small id="userClaimsHelpBlock" class="form-text text-muted" >Claims from the identity provider. JSON array of claims. See <a href="https://docs.microsoft.com/azure/static-web-apps/user-information">documentation</a> for example claims.</small > </div> </div> <div class="form-group row"> <div class="col-4"></div> <div class="col-8 text-right"> <button name="submit" id="submit" type="submit" class="btn btn-primary">Login</button> <button name="clear" id="clear" type="button" class="btn btn-outline-danger">Clear</button> </div> </div> </form> </div> </div> <div class="col-xl-5 col-lg-5 col-md-12 d-none d-lg-block"> <div class="text-center"> <img src="https://appservice.azureedge.net/images/static-apps/v3/staticapps.svg" /> </div> </div> </div> </div> <footer> <div>Azure Static Web Apps emulator (<a rel="noopener noreferrer" target="_blank" href="https://github.com/Azure/static-web-apps-cli/commit/11fe14d">main+sha.11fe14d</a>)</div> <nav> <ul> <li><a href="https://github.com/azure/static-web-apps-cli/blob/master/CONTRIBUTING.md">Contribute</a></li> <li><a href="https://docs.microsoft.com/azure/static-web-apps/">Documentation</a></li> <li><a href="https://privacy.microsoft.com/privacystatement">Privacy & Cookies</a></li> <li><a href="https://www.microsoft.com/legal/intellectualproperty/copyright">Terms of Use</a></li> </ul> </nav> <div class="copyright">© Microsoft 2023</div> </footer> <script src="https://ajax.aspnetcdn.com/ajax/jquery/jquery-3.2.1.min.js" crossorigin="anonymous"></script> <script src="https://ajax.aspnetcdn.com/ajax/bootstrap/4.1.1/bootstrap.min.js" crossorigin="anonymous"></script> <script> const defaultRoles = ["anonymous", "authenticated"]; const defaultClaims = "[]"; // this should be a valid JSON string function saveCookie(formElement) { const data = localStorage[hashStorageKey(formElement)]; document.cookie = `StaticWebAppsAuthCookie=${btoa(data)}; path=/`; } function redirectToApp() { const defaultRedirectPath = "/"; try { const metaSearch = document.querySelector(`meta[name="swa:originalSearch"]`)?.content || ""; const redirectPath = metaSearch || window.location.search || defaultRedirectPath; const urlSearch = (metaSearch || location.search).replace("?", ""); const urlQuery = urlSearch && Object.fromEntries(new Map(urlSearch.split("&").map((query) => query.split("=")))); const postLoginRedirectUri = urlQuery ? urlQuery["post_login_redirect_uri"] : redirectPath; window.location.href = decodeURIComponent(postLoginRedirectUri) || defaultRedirectPath; } catch (error) { console.warn(error); window.location.href = defaultRedirectPath; } } function parseIdentityProviderFromUrl() { const pathname = document.querySelector(`meta[name="swa:originalPath"]`)?.content || window.location.pathname; if (pathname.includes("/.auth/login")) { return pathname.split("/").pop(); } else { return null; } } function syncIdentityProviderFromUrl(formElement) { // @TODO handle unknown providers const identityProviderName = parseIdentityProviderFromUrl(); const providerElement = $("#identityProvider"); if (providerElement.val() === "") { providerElement.val(identityProviderName); providerElement.saveToLocalStorage(); } else { // custom routes: let the user enter their provider providerElement.removeAttr("disabled"); } } function hashStorageKey(element) { const formElement = element.closest("form"); const providerName = parseIdentityProviderFromUrl(); const formName = formElement.attr("name"); return `${formName}@${providerName}`; } function generateUserId() { return [...Array(32)].map(() => Math.floor(Math.random() * 16).toString(16)).join(""); } $.fn.saveToLocalStorage = function saveToLocalStorage(options) { const settings = $.extend({}, options); if (typeof Storage == "undefined") { console.warn("localStorage is not available. Auth data will not be cached!"); return false; } return this.each(function () { const element = $(this); const storageKey = hashStorageKey(element); if (!localStorage[storageKey]) { localStorage[storageKey] = "{}"; } const inputName = element.attr("name"); const inputValue = element.val(); let data = JSON.parse(localStorage[storageKey]); if (inputName === "userRoles") { // ensure default roles are included data[inputName] = [...new Set([...inputValue.trim().split("\n"), ...defaultRoles])]; } else if (inputName === "claims") { const claimsErrorBlock = $("#userClaimsHelpBlockError"); const formSubmitBtn = $("#submit"); try { claimsErrorBlock.addClass("d-none"); formSubmitBtn.prop("disabled", false); data[inputName] = JSON.parse(inputValue); } catch (error) { claimsErrorBlock.removeClass("d-none"); formSubmitBtn.prop("disabled", true); data[inputName] = JSON.parse(defaultClaims); } } else { data[inputName] = inputValue; } localStorage[storageKey] = JSON.stringify(data); }); }; $.fn.syncFromLocalStorage = function syncFromLocalStorage(options) { const settings = $.extend({}, options); return this.each(function () { const element = $(this); const storageKey = hashStorageKey(element); let data = {}; if (typeof Storage == "undefined") { console.warn("localStorage is not available. Auth data will not be cached!"); return false; } if (localStorage[storageKey]) { data = JSON.parse(localStorage[storageKey]); if (!data) { // initialize localStorage to empty object localStorage[storageKey] = JSON.stringify({}); data = JSON.parse(localStorage[storageKey]); } } element.find("input, textarea").each(function () { const input = $(this); const inputName = input.attr("name"); if (inputName === "userRoles") { const userRoles = data[inputName]; if (Array.isArray(userRoles)) { input.val(userRoles.join("\n")); } else if (input.val() === "") { // set default values and cache entry input.val(settings.defaultRoles.join("\n")); input.saveToLocalStorage(); } } else if (inputName === "identityProvider") { // don't restore provider name from storage. // this value will be taken from the URL } else if (inputName === "userId") { const userId = data[inputName] || generateUserId(); input.val(userId); input.saveToLocalStorage(); } else if (inputName === "claims") { const userClaims = data[inputName]; if (Array.isArray(userClaims)) { input.val(JSON.stringify(userClaims, null, 2)); } else if (!userClaims || input.val() === "") { // set default values and cache entry input.val(settings.defaultClaims); input.saveToLocalStorage(); } } else if (input.attr("type") !== "submit") { const value = data[inputName]; input.val(value); } }); }); }; </script> <script> $(function () { // setup default values const form = $("form[name='auth']"); // trigger on first call form.syncFromLocalStorage({ defaultRoles, defaultClaims, }); // get default provider name from URL syncIdentityProviderFromUrl(form); // delegate storing auth info to each input form.on("keyup", "input, textarea", (event) => $(event.currentTarget).saveToLocalStorage()); // clear form $("#clear").click(() => { form.find("input[type=text]:not(#identityProvider)").val(""); form.find("textarea#userRoles").val(defaultRoles.join("\n")); form.find("textarea#claims").val(defaultClaims); form.find("#userId").val(generateUserId()); form.find("input, textarea").each((idx, element) => $(element).saveToLocalStorage()); }); // store cookie info as base64 form.submit(() => { event.preventDefault(); saveCookie(form); redirectToApp(); }); }); </script> </body> </html>