max 2 weeks ago
commit 8d57c28675

@ -0,0 +1,7 @@
<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/20/solid/arrow-top-right-on-square.svg -->
<#macro kw>
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path clip-rule="evenodd" d="M4.25 5.5C3.83579 5.5 3.5 5.83579 3.5 6.25V14.75C3.5 15.1642 3.83579 15.5 4.25 15.5H12.75C13.1642 15.5 13.5 15.1642 13.5 14.75V10.75C13.5 10.3358 13.8358 10 14.25 10C14.6642 10 15 10.3358 15 10.75V14.75C15 15.9926 13.9926 17 12.75 17H4.25C3.00736 17 2 15.9926 2 14.75V6.25C2 5.00736 3.00736 4 4.25 4H9.25C9.66421 4 10 4.33579 10 4.75C10 5.16421 9.66421 5.5 9.25 5.5H4.25Z" fill-rule="evenodd" />
<path clip-rule="evenodd" d="M6.19385 12.7532C6.47175 13.0603 6.94603 13.0841 7.25319 12.8062L16.5 4.43999V7.25C16.5 7.66421 16.8358 8 17.25 8C17.6642 8 18 7.66421 18 7.25V2.75C18 2.33579 17.6642 2 17.25 2H12.75C12.3358 2 12 2.33579 12 2.75C12 3.16421 12.3358 3.5 12.75 3.5H15.3032L6.24682 11.6938C5.93966 11.9717 5.91595 12.446 6.19385 12.7532Z" fill-rule="evenodd" />
</svg>
</#macro>

@ -0,0 +1,6 @@
<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/20/solid/chevron-down.svg -->
<#macro kw>
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path clip-rule="evenodd" d="M5.23017 7.20938C5.52875 6.92228 6.00353 6.93159 6.29063 7.23017L10 11.1679L13.7094 7.23017C13.9965 6.93159 14.4713 6.92228 14.7698 7.20938C15.0684 7.49647 15.0777 7.97125 14.7906 8.26983L10.5406 12.7698C10.3992 12.9169 10.204 13 10 13C9.79599 13 9.60078 12.9169 9.45938 12.7698L5.20938 8.26983C4.92228 7.97125 4.93159 7.49647 5.23017 7.20938Z" fill-rule="evenodd" />
</svg>
</#macro>

@ -0,0 +1,7 @@
<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/20/solid/eye.svg -->
<#macro kw>
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path clip-rule="evenodd" d="M3.28033 2.21967C2.98744 1.92678 2.51256 1.92678 2.21967 2.21967C1.92678 2.51256 1.92678 2.98744 2.21967 3.28033L16.7197 17.7803C17.0126 18.0732 17.4874 18.0732 17.7803 17.7803C18.0732 17.4874 18.0732 17.0126 17.7803 16.7197L16.0352 14.9745C17.5064 13.8594 18.6595 12.3465 19.3344 10.5959C19.4814 10.2144 19.4816 9.79127 19.3347 9.40962C17.892 5.66051 14.256 3 9.99859 3C8.28207 3 6.66657 3.43249 5.2551 4.19444L3.28033 2.21967ZM7.75194 6.69128L8.84367 7.78301C9.18951 7.60223 9.58291 7.5 10.0002 7.5C11.3809 7.5 12.5002 8.61929 12.5002 10C12.5002 10.4173 12.398 10.8107 12.2172 11.1565L13.3091 12.2484C13.7454 11.6077 14.0004 10.8336 14.0004 10C14.0004 7.79086 12.2095 6 10.0004 6C9.16675 6 8.39268 6.25501 7.75194 6.69128Z" fill-rule="evenodd" />
<path d="M10.7484 13.9302L13.2711 16.4529C12.2462 16.8074 11.1458 17 10.0004 17C5.74298 17 2.10698 14.3395 0.664255 10.5904C0.517392 10.2087 0.517518 9.78563 0.66461 9.40408C1.15603 8.12932 1.90108 6.98057 2.83791 6.01969L6.0702 9.25198C6.02436 9.4943 6.00037 9.74435 6.00037 10C6.00037 12.2091 7.79123 14 10.0004 14C10.256 14 10.5061 13.976 10.7484 13.9302Z" />
</svg>
</#macro>

@ -0,0 +1,7 @@
<#-- https://github.com/tailwindlabs/heroicons/blob/master/src/20/solid/eye.svg -->
<#macro kw>
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<path d="M10 12.5C11.3807 12.5 12.5 11.3807 12.5 10C12.5 8.61929 11.3807 7.5 10 7.5C8.61929 7.5 7.5 8.61929 7.5 10C7.5 11.3807 8.61929 12.5 10 12.5Z" />
<path clip-rule="evenodd" d="M0.664255 10.5904C0.517392 10.2087 0.517518 9.78563 0.66461 9.40408C2.10878 5.65788 5.7433 3 9.99859 3C14.256 3 17.892 5.66051 19.3347 9.40962C19.4816 9.79127 19.4814 10.2144 19.3344 10.5959C17.8902 14.3421 14.2557 17 10.0004 17C5.74298 17 2.10698 14.3395 0.664255 10.5904ZM14.0004 10C14.0004 12.2091 12.2095 14 10.0004 14C7.79123 14 6.00037 12.2091 6.00037 10C6.00037 7.79086 7.79123 6 10.0004 6C12.2095 6 14.0004 7.79086 14.0004 10Z" fill-rule="evenodd" />
</svg>
</#macro>

@ -0,0 +1,7 @@
<#-- https://apple.com -->
<#macro kw name="Apple">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M12.7549 5.53846C13.8059 5.53846 15.1234 4.8061 15.9079 3.82962C16.6184 2.94469 17.1365 1.70884 17.1365 0.472981C17.1365 0.305149 17.1217 0.137317 17.0921 0C15.9227 0.0457724 14.5164 0.808645 13.6727 1.8309C13.0066 2.60903 12.3997 3.82963 12.3997 5.08074C12.3997 5.26383 12.4293 5.44692 12.4441 5.50795C12.5181 5.5232 12.6365 5.53846 12.7549 5.53846ZM9.05428 24C10.4901 24 11.1266 23.0083 12.9178 23.0083C14.7385 23.0083 15.1382 23.9695 16.7368 23.9695C18.3059 23.9695 19.3569 22.4743 20.3487 21.0095C21.4589 19.3312 21.9178 17.6834 21.9474 17.6071C21.8438 17.5766 18.8388 16.3102 18.8388 12.7552C18.8388 9.67324 21.2072 8.28481 21.3405 8.178C19.7714 5.85887 17.3882 5.79784 16.7368 5.79784C14.9753 5.79784 13.5395 6.89638 12.6365 6.89638C11.6595 6.89638 10.3717 5.85887 8.84704 5.85887C5.94572 5.85887 3 8.33058 3 12.9994C3 15.8983 4.09539 18.965 5.44243 20.9485C6.59704 22.6268 7.60362 24 9.05428 24Z" fill="#000000" />
</svg>
</#macro>

@ -0,0 +1,14 @@
<#-- https://atlassian.design/resources/logo-library -->
<#macro kw name="Bitbucket">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M0.770456 1.21005C0.290906 1.21005 -0.0687541 1.64964 0.0111659 2.08923L3.24816 21.9108C3.32808 22.4304 3.76768 22.79 4.28719 22.79H19.9526C20.3123 22.79 20.632 22.5102 20.7119 22.1505L23.9888 2.1292C24.0688 1.64964 23.7091 1.25002 23.2296 1.25002L0.770456 1.21005ZM14.5177 15.5167H9.52232L8.20352 8.44335H15.7565L14.5177 15.5167Z" fill="#2684FF" />
<path d="M22.9098 8.44335H15.7165L14.5177 15.5167H9.52232L3.64773 22.5102C3.64773 22.5102 3.92746 22.7501 4.32709 22.7501H19.9925C20.3522 22.7501 20.6719 22.4702 20.7518 22.1105L22.9098 8.44335Z" fill="url(#bitbucket)" />
<defs>
<linearGradient id="bitbucket" x1="24.5925" y1="10.4366" x2="12.672" y2="19.7417" gradientUnits="userSpaceOnUse">
<stop offset="0.176" stop-color="#0052CC" />
<stop offset="1" stop-color="#2684FF" />
</linearGradient>
</defs>
</svg>
</#macro>

@ -0,0 +1,7 @@
<#-- https://discord.com/branding -->
<#macro kw name="Discord">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M20.3303 4.42852C18.7535 3.70661 17.0888 3.19446 15.3789 2.90516C15.1449 3.32345 14.9332 3.75381 14.7446 4.19445C12.9232 3.91998 11.071 3.91998 9.24961 4.19445C9.06095 3.75386 8.84924 3.3235 8.61535 2.90516C6.90433 3.19691 5.2386 3.71027 3.66019 4.43229C0.526644 9.06843 -0.322811 13.5894 0.101917 18.0462C1.937 19.4021 3.99098 20.4332 6.17458 21.0948C6.66626 20.4335 7.10134 19.732 7.47519 18.9976C6.76511 18.7324 6.07975 18.4052 5.42706 18.0198C5.59884 17.8952 5.76684 17.7669 5.92918 17.6423C7.82837 18.5354 9.90124 18.9985 12 18.9985C14.0987 18.9985 16.1715 18.5354 18.0707 17.6423C18.235 17.7763 18.403 17.9047 18.5729 18.0198C17.9189 18.4058 17.2323 18.7337 16.5209 18.9995C16.8943 19.7335 17.3294 20.4345 17.8216 21.0948C20.007 20.4359 22.0626 19.4052 23.898 18.0481C24.3963 12.8797 23.0467 8.4002 20.3303 4.42852ZM8.01318 15.3053C6.82961 15.3053 5.85179 14.2312 5.85179 12.9099C5.85179 11.5885 6.79563 10.505 8.0094 10.505C9.22318 10.505 10.1934 11.5885 10.1727 12.9099C10.1519 14.2312 9.21941 15.3053 8.01318 15.3053ZM15.9867 15.3053C14.8013 15.3053 13.8272 14.2312 13.8272 12.9099C13.8272 11.5885 14.7711 10.505 15.9867 10.505C17.2024 10.505 18.1651 11.5885 18.1444 12.9099C18.1236 14.2312 17.193 15.3053 15.9867 15.3053Z" fill="#5865F2" />
</svg>
</#macro>

@ -0,0 +1,8 @@
<#-- https://www.facebook.com/brand/resources/facebookapp/logo -->
<#macro kw name="Facebook">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M24 12C24 5.37258 18.6274 0 12 0C5.37258 0 0 5.37258 0 12C0 17.9895 4.38822 22.954 10.125 23.8542V15.4688H7.07813V12H10.125V9.35626C10.125 6.34875 11.9165 4.68751 14.6576 4.68751C15.9705 4.68751 17.3438 4.92188 17.3438 4.92188V7.875H15.8306C14.3399 7.875 13.875 8.80002 13.875 9.74901V12H17.2031L16.6711 15.4688H13.875V23.8542C19.6118 22.954 24 17.9895 24 12Z" fill="#1877F2" />
<path d="M16.6711 15.4688L17.2031 12H13.875V9.74901C13.875 8.80002 14.3399 7.875 15.8306 7.875H17.3438V4.92188C17.3438 4.92188 15.9705 4.68751 14.6576 4.68751C11.9165 4.68751 10.125 6.34875 10.125 9.35626V12H7.07813V15.4688H10.125V23.8542C10.7453 23.9514 11.3722 24.0002 12 24C12.6379 24 13.2641 23.9501 13.875 23.8542V15.4688H16.6711Z" fill="white" />
</svg>
</#macro>

@ -0,0 +1,7 @@
<#-- https://github.com/logos -->
<#macro kw name="GitHub">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M11.8452 0C5.13387 0 0 5.09516 0 11.8065C0 17.1726 3.37742 21.7645 8.20161 23.3806C8.82097 23.4919 9.03871 23.1097 9.03871 22.7952C9.03871 22.4952 9.02419 20.8403 9.02419 19.8242C9.02419 19.8242 5.6371 20.55 4.92581 18.3823C4.92581 18.3823 4.37419 16.9742 3.58065 16.6113C3.58065 16.6113 2.47258 15.8516 3.65806 15.8661C3.65806 15.8661 4.8629 15.9629 5.52581 17.1145C6.58548 18.9823 8.36129 18.4452 9.05323 18.1258C9.16452 17.3516 9.47903 16.8145 9.82742 16.4952C7.12258 16.1952 4.39355 15.8032 4.39355 11.1484C4.39355 9.81774 4.76129 9.15 5.53548 8.29839C5.40968 7.98387 4.99839 6.6871 5.66129 5.0129C6.67258 4.69839 9 6.31936 9 6.31936C9.96774 6.04839 11.0081 5.90806 12.0387 5.90806C13.0694 5.90806 14.1097 6.04839 15.0774 6.31936C15.0774 6.31936 17.4048 4.69355 18.4161 5.0129C19.079 6.69194 18.6677 7.98387 18.5419 8.29839C19.3161 9.15484 19.7903 9.82258 19.7903 11.1484C19.7903 15.8177 16.9403 16.1903 14.2355 16.4952C14.6806 16.8774 15.0581 17.6032 15.0581 18.7403C15.0581 20.371 15.0435 22.3887 15.0435 22.7855C15.0435 23.1 15.2661 23.4823 15.8806 23.371C20.7194 21.7645 24 17.1726 24 11.8065C24 5.09516 18.5565 0 11.8452 0Z" fill="#181717" />
</svg>
</#macro>

@ -0,0 +1,10 @@
<#-- https://about.gitlab.com/press/press-kit -->
<#macro kw name="GitLab">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M23.6005 9.59068L23.5668 9.50445L20.3002 0.979328C20.2337 0.81224 20.116 0.670499 19.964 0.574441C19.8119 0.480015 19.6345 0.434537 19.4557 0.444146C19.2769 0.453756 19.1054 0.51799 18.9643 0.628176C18.8248 0.741526 18.7235 0.895119 18.6744 1.06805L16.4688 7.81617H7.53749L5.33186 1.06805C5.28402 0.894177 5.18257 0.739814 5.04194 0.626926C4.90083 0.51674 4.7293 0.452506 4.55053 0.442896C4.37175 0.433287 4.19433 0.478766 4.04222 0.573191C3.89054 0.669636 3.77297 0.811254 3.70606 0.978078L0.433225 9.49945L0.400734 9.58568C-0.0695071 10.8143 -0.127551 12.1626 0.235354 13.4271C0.598259 14.6916 1.36244 15.8039 2.41267 16.5962L2.42392 16.605L2.45391 16.6262L7.43002 20.3527L9.89184 22.2159L11.3914 23.3481C11.5668 23.4813 11.781 23.5534 12.0012 23.5534C12.2215 23.5534 12.4357 23.4813 12.6111 23.3481L14.1107 22.2159L16.5725 20.3527L21.5786 16.6037L21.5911 16.5937C22.639 15.8013 23.4014 14.6901 23.7637 13.4273C24.1261 12.1645 24.0688 10.8182 23.6005 9.59068Z" fill="#E24329" />
<path d="M23.6005 9.59068L23.5668 9.50445C21.9751 9.83116 20.4752 10.5054 19.1742 11.4789L12 16.9036C14.4431 18.7519 16.57 20.3577 16.57 20.3577L21.5761 16.6087L21.5886 16.5987C22.638 15.8063 23.4015 14.6945 23.7644 13.4305C24.1272 12.1666 24.0697 10.8191 23.6005 9.59068Z" fill="#FC6D26" />
<path d="M7.43002 20.3577L9.89184 22.2209L11.3914 23.3531C11.5668 23.4863 11.781 23.5584 12.0012 23.5584C12.2215 23.5584 12.4357 23.4863 12.6111 23.3531L14.1107 22.2209L16.5725 20.3577C16.5725 20.3577 14.4431 18.7469 12 16.9036C9.55693 18.7469 7.43002 20.3577 7.43002 20.3577Z" fill="#FCA326" />
<path d="M4.8245 11.4789C3.5246 10.5034 2.02503 9.8274 0.433225 9.49945L0.400734 9.58568C-0.0695071 10.8143 -0.127551 12.1626 0.235354 13.4271C0.598259 14.6916 1.36244 15.8039 2.41267 16.5962L2.42392 16.605L2.45391 16.6262L7.43002 20.3527C7.43002 20.3527 9.55443 18.7469 12 16.8986L4.8245 11.4789Z" fill="#FC6D26" />
</svg>
</#macro>

@ -0,0 +1,10 @@
<#-- https://developers.google.com/identity/branding-guidelines -->
<#macro kw name="Google">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M23.76 12.2727C23.76 11.4218 23.6836 10.6036 23.5418 9.81818H12.24V14.46H18.6982C18.42 15.96 17.5745 17.2309 16.3036 18.0818L18.2427 19.5873L20.1818 21.0927C22.4509 19.0036 23.76 15.9273 23.76 12.2727Z" fill="#4285F4" />
<path d="M12.24 24C15.48 24 18.1964 22.9255 20.1818 21.0927L16.3036 18.0818C15.2291 18.8018 13.8545 19.2273 12.24 19.2273C9.11455 19.2273 6.46909 17.1164 5.52545 14.28L3.52091 15.8345L1.51636 17.3891C3.49091 21.3109 7.54909 24 12.24 24Z" fill="#34A853" />
<path d="M5.52545 14.28C5.28545 13.56 5.14909 12.7909 5.14909 12C5.14909 11.2091 5.28545 10.44 5.52545 9.72L3.52091 8.16546L1.51636 6.61091C0.703637 8.23091 0.240001 10.0636 0.240001 12C0.240001 13.9364 0.703637 15.7691 1.51636 17.3891L5.52545 14.28Z" fill="#FBBC05" />
<path d="M12.24 4.77273C14.0018 4.77273 15.5836 5.37818 16.8273 6.56727L20.2691 3.12545C18.1909 1.18909 15.4745 0 12.24 0C7.54909 0 3.49091 2.68909 1.51636 6.61091L5.52545 9.72C6.46909 6.88364 9.11455 4.77273 12.24 4.77273Z" fill="#EA4335" />
</svg>
</#macro>

@ -0,0 +1,35 @@
<#-- https://www.facebook.com/brand/resources/instagram/instagram-brand -->
<#macro kw name="Instagram">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M19.8463 5.59481C19.8463 4.79718 19.1998 4.15313 18.4052 4.15313C17.6105 4.15313 16.9635 4.79718 16.9635 5.59481C16.9635 6.38951 17.6105 7.03356 18.4052 7.03356C19.1998 7.03356 19.8463 6.38951 19.8463 5.59481Z" fill="url(#instagram_0)" />
<path d="M21.7666 16.8484C21.7132 18.0185 21.5175 18.6543 21.355 19.0765C21.1367 19.6364 20.8764 20.0367 20.4542 20.4565C20.0367 20.8764 19.6364 21.1362 19.0765 21.352C18.6543 21.5169 18.0161 21.7132 16.8461 21.769C15.5811 21.8247 15.2063 21.8366 11.9985 21.8366C8.7937 21.8366 8.4159 21.8247 7.15094 21.769C5.98089 21.7132 5.34573 21.5169 4.92345 21.352C4.36067 21.1362 3.96334 20.8764 3.54347 20.4565C3.12061 20.0367 2.86028 19.6364 2.645 19.0765C2.48248 18.6543 2.28384 18.0185 2.2334 16.8484C2.17175 15.5835 2.16045 15.2027 2.16045 12.0015C2.16045 8.7937 2.17175 8.4159 2.2334 7.15094C2.28384 5.98089 2.48248 5.34573 2.645 4.9199C2.86028 4.36067 3.12061 3.96272 3.54347 3.54284C3.96334 3.12359 4.36067 2.86321 4.92345 2.645C5.34573 2.47954 5.98089 2.28619 7.15094 2.23046C8.4159 2.17469 8.7937 2.16045 11.9985 2.16045C15.2063 2.16045 15.5811 2.17469 16.8461 2.23046C18.0161 2.28619 18.6543 2.47954 19.0765 2.645C19.6364 2.86321 20.0367 3.12359 20.4542 3.54284C20.8764 3.96272 21.1367 4.36067 21.355 4.9199C21.5175 5.34573 21.7132 5.98089 21.7666 7.15094C21.8253 8.4159 21.8395 8.7937 21.8395 12.0015C21.8395 15.2027 21.8253 15.5835 21.7666 16.8484ZM23.9271 7.05251C23.8683 5.77388 23.6667 4.90033 23.3672 4.13948C23.0624 3.35012 22.6538 2.68116 21.9848 2.01221C21.3188 1.34623 20.6499 0.937607 19.8605 0.629238C19.0967 0.3327 18.2261 0.128677 16.9469 0.0729453C15.6677 0.0112537 15.2591 0 11.9985 0C8.74091 0 8.32935 0.0112537 7.05015 0.0729453C5.77388 0.128677 4.90388 0.3327 4.1365 0.629238C3.35012 0.937607 2.68116 1.34623 2.01519 2.01221C1.34623 2.68116 0.937606 3.35012 0.629815 4.13948C0.333277 4.90033 0.131656 5.77388 0.0699646 7.05251C0.0142331 8.33171 0 8.74091 0 12.0015C0 15.2591 0.0142331 15.6677 0.0699646 16.9469C0.131656 18.2231 0.333277 19.0961 0.629815 19.8605C0.937606 20.6469 1.34623 21.3188 2.01519 21.9848C2.68116 22.6508 3.35012 23.0624 4.1365 23.3702C4.90388 23.6667 5.77388 23.8683 7.05015 23.9271C8.32935 23.9858 8.74091 24 11.9985 24C15.2591 24 15.6677 23.9858 16.9469 23.9271C18.2261 23.8683 19.0967 23.6667 19.8605 23.3702C20.6499 23.0624 21.3188 22.6508 21.9848 21.9848C22.6538 21.3188 23.0624 20.6469 23.3672 19.8605C23.6667 19.0961 23.8683 18.2231 23.9271 16.9469C23.9858 15.6677 24 15.2591 24 12.0015C24 8.74091 23.9858 8.33171 23.9271 7.05251Z" fill="url(#instagram_1)" />
<path d="M11.9985 15.998C9.7906 15.998 7.99901 14.2094 7.99901 12.0014C7.99901 9.78998 9.7906 7.99901 11.9985 7.99901C14.207 7.99901 16.001 9.78998 16.001 12.0014C16.001 14.2094 14.207 15.998 11.9985 15.998ZM11.9985 5.83558C8.59502 5.83558 5.83855 8.59796 5.83855 12.0014C5.83855 15.402 8.59502 18.1614 11.9985 18.1614C15.402 18.1614 18.1614 15.402 18.1614 12.0014C18.1614 8.59796 15.402 5.83558 11.9985 5.83558Z" fill="url(#instagram_2)" />
<defs>
<linearGradient id="instagram_0" x1="-139.421" y1="-139.299" x2="42.0899" y2="42.0606" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFD521" />
<stop offset="0.05" stop-color="#FFD521" />
<stop offset="0.501119" stop-color="#F50000" />
<stop offset="0.95" stop-color="#B900B4" />
<stop offset="0.950079" stop-color="#B900B4" />
<stop offset="1" stop-color="#B900B4" />
</linearGradient>
<linearGradient id="instagram_1" x1="0.216477" y1="0.218256" x2="22.0189" y2="22.0207" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFD521" />
<stop offset="0.05" stop-color="#FFD521" />
<stop offset="0.501119" stop-color="#F50000" />
<stop offset="0.95" stop-color="#B900B4" />
<stop offset="0.950079" stop-color="#B900B4" />
<stop offset="1" stop-color="#B900B4" />
</linearGradient>
<linearGradient id="instagram_2" x1="0.222127" y1="23.7823" x2="22.0193" y2="1.98511" gradientUnits="userSpaceOnUse">
<stop stop-color="#FFD521" />
<stop offset="0.05" stop-color="#FFD521" />
<stop offset="0.501119" stop-color="#F50000" />
<stop offset="0.95" stop-color="#B900B4" />
<stop offset="0.950079" stop-color="#B900B4" />
<stop offset="1" stop-color="#B900B4" />
</linearGradient>
</defs>
</svg>
</#macro>

@ -0,0 +1,7 @@
<#-- https://brand.linkedin.com/downloads -->
<#macro kw name="LinkedIn">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M20.4491 20.4489H16.8931V14.88C16.8931 13.552 16.8694 11.8425 15.0436 11.8425C13.1915 11.8425 12.9081 13.2894 12.9081 14.7833V20.4485H9.35204V8.99659H12.7658V10.5616H12.8136C13.1553 9.97748 13.649 9.49694 14.2421 9.17118C14.8352 8.84542 15.5056 8.68663 16.1819 8.71173C19.7861 8.71173 20.4506 11.0824 20.4506 14.1666L20.4491 20.4489ZM5.33963 7.43118C4.93148 7.43126 4.53248 7.3103 4.19308 7.08361C3.85368 6.85692 3.58912 6.53467 3.43287 6.15762C3.27661 5.78057 3.23567 5.36565 3.31522 4.96534C3.39477 4.56502 3.59125 4.19728 3.8798 3.90863C4.16835 3.61997 4.53602 3.42337 4.9363 3.34367C5.33659 3.26397 5.75153 3.30476 6.12863 3.46089C6.50574 3.61701 6.82808 3.88145 7.05489 4.22077C7.28171 4.56009 7.40281 4.95905 7.40288 5.36719C7.40293 5.63819 7.3496 5.90655 7.24594 6.15694C7.14228 6.40732 6.99031 6.63484 6.79872 6.8265C6.60713 7.01816 6.37967 7.17021 6.12931 7.27396C5.87896 7.37771 5.61063 7.43114 5.33963 7.43118ZM7.11765 20.4489H3.5579V8.99659H7.11765V20.4489ZM22.222 0.0016345H1.77099C1.30681 -0.00360376 0.859536 0.175657 0.527458 0.500024C0.195381 0.824392 0.00566506 1.26733 0 1.7315V22.2673C0.00547116 22.7317 0.195065 23.175 0.527132 23.4997C0.859198 23.8244 1.30658 24.004 1.77099 23.999H22.222C22.6873 24.0049 23.1359 23.8258 23.4693 23.5011C23.8027 23.1764 23.9936 22.7326 24 22.2673V1.73002C23.9934 1.26493 23.8024 0.821487 23.469 0.497127C23.1356 0.172767 22.6871 -0.00598123 22.222 0.000152816" fill="#0A66C2" />
</svg>
</#macro>

@ -0,0 +1,10 @@
<#-- https://learn.microsoft.com/azure/active-directory/develop/howto-add-branding-in-azure-ad-apps -->
<#macro kw name="Microsoft">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M11.3684 0H0V11.3684H11.3684V0Z" fill="#F25022" />
<path d="M11.3684 12.6316H0V24H11.3684V12.6316Z" fill="#00A4EF" />
<path d="M24 0H12.6316V11.3684H24V0Z" fill="#7FBA00" />
<path d="M24 12.6316H12.6316V24H24V12.6316Z" fill="#FFB900" />
</svg>
</#macro>

@ -0,0 +1,9 @@
<#-- https://openid.net/add-openid/logos -->
<#macro kw name="OpenID">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M10.9071 2.6627V20.8342V23.1134L14.5427 21.4023V0.886559L10.9071 2.6627Z" fill="#F8941C" />
<path d="M23.4982 8.66156L24 13.8724L16.9691 12.3427" fill="#BCBEC0" />
<path d="M3.63569 15.3299C3.63569 12.7538 6.44278 10.5843 10.2689 9.92206V7.61282C4.41656 8.32019 0 11.5061 0 15.3299C0 19.2916 4.74059 22.5682 10.9071 23.1134V20.8342C6.75829 20.3141 3.63569 18.0475 3.63569 15.3299ZM15.1809 7.61373V9.92206C16.7033 10.1855 18.065 10.6863 19.1519 11.3582L21.7227 9.76936C19.9707 8.68649 17.7095 7.91936 15.1809 7.61373Z" fill="#BCBEC0" />
</svg>
</#macro>

@ -0,0 +1,11 @@
<#-- https://www.redhat.com/technologies/cloud-computing/openshift -->
<#macro kw name="Red Hat OpenShift">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M24 6.60227C23.7314 6.04752 23.4206 5.51216 23.06 5.00724L19.2073 6.40936C19.6553 6.86793 20.0318 7.38336 20.3396 7.93459L24 6.60227ZM6.96939 10.8635L3.11552 12.2656C3.16478 12.8837 3.2715 13.4942 3.42101 14.0923L7.08198 12.7594C6.96292 12.1401 6.9207 11.5021 6.96939 10.8635Z" fill="#C22133" />
<path d="M15.5221 5.51084C16.3237 5.88497 17.0181 6.39515 17.6039 6.99328L21.4566 5.5912C20.3893 4.09352 18.9356 2.84212 17.1547 2.01116C11.6465 -0.557309 5.07525 1.83406 2.50739 7.34161C1.67586 9.12373 1.3668 11.0167 1.51223 12.8498L5.36554 11.4476C5.42944 10.6126 5.63294 9.77582 6.00646 8.97361C7.67543 5.39535 11.9439 3.84258 15.5221 5.51084ZM19.6315 12.5515C19.5699 13.386 19.3594 14.2228 18.9847 15.0256C17.3164 18.6044 13.0473 20.1573 9.46965 18.489C8.66683 18.1142 7.96727 17.6082 7.38377 17.0089L3.53872 18.408C4.60363 19.9057 6.05558 21.1577 7.83765 21.9892C13.3458 24.5571 19.9159 22.1657 22.4844 16.6576C23.3165 14.8767 23.6232 12.9837 23.4766 11.1524L19.6315 12.5515Z" fill="#DB212E" />
<path d="M20.579 7.84756L16.918 9.17988C17.5983 10.3984 17.9196 11.8011 17.814 13.212L21.6591 11.8135C21.549 10.4358 21.1828 9.09077 20.579 7.84756ZM3.66097 14.0044L0 15.3379C0.337597 16.6778 0.921419 17.9433 1.72169 19.0698L5.56619 17.67C4.57929 16.6567 3.92307 15.3742 3.66097 14.0044Z" fill="#EB2126" />
<path d="M23.442 5.58726C23.3206 5.39023 23.1945 5.19611 23.0602 5.00728L19.2075 6.40941C19.377 6.58299 19.533 6.76712 19.6819 6.95594L23.442 5.58726ZM6.95466 11.6373C6.9449 11.3794 6.94979 11.1212 6.9693 10.8638L3.11542 12.2659C3.13536 12.5128 3.16642 12.7579 3.20338 13.0025L6.95466 11.6373Z" fill="#AD213B" />
<path d="M23.4764 11.1523L19.6313 12.5515C19.5908 13.1051 19.4829 13.6598 19.3064 14.2052L23.4916 12.6793C23.5217 12.1706 23.5166 11.6604 23.4764 11.1523ZM3.53905 18.4085C3.83724 18.8278 4.16469 19.2254 4.51896 19.5984L8.70474 18.0719C8.21567 17.7658 7.77352 17.4087 7.38354 17.0088L3.53905 18.4085Z" fill="#BA2133" />
</svg>
</#macro>

@ -0,0 +1,9 @@
<#-- https://www.paypal.com -->
<#macro kw name="PayPal">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M20.1597 6.09762C20.1374 6.23998 20.112 6.38552 20.0833 6.53504C19.0995 11.586 15.7338 13.3309 11.4352 13.3309H9.24646C8.72077 13.3309 8.27778 13.7127 8.19586 14.2312L7.07527 21.3381L6.75795 23.3526C6.74531 23.4325 6.75013 23.5142 6.77209 23.5921C6.79405 23.67 6.83263 23.7421 6.88516 23.8037C6.93769 23.8652 7.00293 23.9146 7.07639 23.9485C7.14985 23.9824 7.22978 24 7.31069 24H11.1926C11.6523 24 12.0428 23.666 12.1151 23.2126L12.1533 23.0154L12.8842 18.3772L12.9311 18.1227C13.0027 17.6678 13.394 17.3337 13.8537 17.3337H14.4343C18.1953 17.3337 21.1395 15.8067 22 11.388C22.3595 9.5421 22.1734 8.00079 21.2222 6.91679C20.9207 6.58146 20.5607 6.30387 20.1597 6.09762Z" fill="#179BD7" />
<path d="M19.1305 5.68724C18.8164 5.59637 18.4969 5.52545 18.1738 5.4749C17.5354 5.37679 16.8902 5.32972 16.2444 5.33413H10.3973C10.1748 5.33396 9.95951 5.4134 9.79045 5.55809C9.6214 5.70279 9.50969 5.90319 9.47551 6.12307L8.23165 14.0014L8.19586 14.2312C8.23461 13.9804 8.36185 13.7518 8.55455 13.5867C8.74725 13.4215 8.99269 13.3308 9.24646 13.3309H11.4352C15.7338 13.3309 19.0995 11.5852 20.0833 6.53504C20.1128 6.38552 20.1374 6.23998 20.1597 6.09762C19.9001 5.96149 19.6295 5.84736 19.3508 5.75644C19.2778 5.73219 19.2043 5.70913 19.1305 5.68724Z" fill="#222D65" />
<path d="M9.47551 6.12307C9.5094 5.90313 9.62105 5.70263 9.79019 5.55801C9.95933 5.41339 10.1747 5.33423 10.3973 5.33492H16.2444C16.9371 5.33492 17.5837 5.38026 18.1738 5.47569C18.5731 5.53845 18.9669 5.63232 19.3516 5.75644C19.6419 5.85267 19.9115 5.9664 20.1605 6.09762C20.4531 4.23104 20.1581 2.96014 19.1488 1.80933C18.0362 0.5424 16.0281 0 13.4584 0H5.99843C5.47352 0 5.02577 0.381748 4.94464 0.901084L1.83738 20.5969C1.8229 20.6883 1.82841 20.7818 1.85352 20.8709C1.87863 20.96 1.92274 21.0427 1.98282 21.1131C2.0429 21.1835 2.11753 21.2401 2.20157 21.279C2.28561 21.3178 2.37707 21.338 2.46965 21.3381L7.07527 21.3381L8.23165 14.0014L9.47551 6.12307Z" fill="#253B80" />
</svg>
</#macro>

@ -0,0 +1,84 @@
<#import "./apple.ftl" as appleIcon>
<#import "./bitbucket.ftl" as bitbucketIcon>
<#import "./discord.ftl" as discordIcon>
<#import "./facebook.ftl" as facebookIcon>
<#import "./github.ftl" as githubIcon>
<#import "./gitlab.ftl" as gitlabIcon>
<#import "./google.ftl" as googleIcon>
<#import "./instagram.ftl" as instagramIcon>
<#import "./linkedin.ftl" as linkedinIcon>
<#import "./microsoft.ftl" as microsoftIcon>
<#import "./oidc.ftl" as oidcIcon>
<#import "./openshift.ftl" as openshiftIcon>
<#import "./paypal.ftl" as paypalIcon>
<#import "./slack.ftl" as slackIcon>
<#import "./stackoverflow.ftl" as stackoverflowIcon>
<#import "./twitter.ftl" as twitterIcon>
<#macro apple>
<@appleIcon.kw />
</#macro>
<#macro bitbucket>
<@bitbucketIcon.kw />
</#macro>
<#macro discord>
<@discordIcon.kw />
</#macro>
<#macro facebook>
<@facebookIcon.kw />
</#macro>
<#macro github>
<@githubIcon.kw />
</#macro>
<#macro gitlab>
<@gitlabIcon.kw />
</#macro>
<#macro google>
<@googleIcon.kw />
</#macro>
<#macro instagram>
<@instagramIcon.kw />
</#macro>
<#macro "linkedin-openid-connect">
<@linkedinIcon.kw />
</#macro>
<#macro microsoft>
<@microsoftIcon.kw />
</#macro>
<#macro oidc>
<@oidcIcon.kw />
</#macro>
<#macro "openshift-v3">
<@openshiftIcon.kw />
</#macro>
<#macro "openshift-v4">
<@openshiftIcon.kw />
</#macro>
<#macro paypal>
<@paypalIcon.kw />
</#macro>
<#macro slack>
<@slackIcon.kw />
</#macro>
<#macro stackoverflow>
<@stackoverflowIcon.kw />
</#macro>
<#macro twitter>
<@twitterIcon.kw />
</#macro>

@ -0,0 +1,14 @@
<#-- https://slack.com/media-kit -->
<#macro kw name="Slack">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M5.04235 15.1661C5.04235 16.5537 3.9088 17.6873 2.52117 17.6873C1.13355 17.6873 0 16.5537 0 15.1661C0 13.7785 1.13355 12.645 2.52117 12.645H5.04235V15.1661Z" fill="#E01E5A" />
<path d="M6.3127 15.1661C6.3127 13.7785 7.44626 12.645 8.83388 12.645C10.2215 12.645 11.355 13.7785 11.355 15.1661V21.4788C11.355 22.8664 10.2215 24 8.83388 24C7.44626 24 6.3127 22.8664 6.3127 21.4788V15.1661Z" fill="#E01E5A" />
<path d="M8.83388 5.04235C7.44626 5.04235 6.3127 3.9088 6.3127 2.52117C6.3127 1.13355 7.44626 0 8.83388 0C10.2215 0 11.355 1.13355 11.355 2.52117V5.04235H8.83388Z" fill="#36C5F0" />
<path d="M8.83388 6.3127C10.2215 6.3127 11.355 7.44626 11.355 8.83388C11.355 10.2215 10.2215 11.355 8.83388 11.355H2.52117C1.13355 11.355 0 10.2215 0 8.83388C0 7.44626 1.13355 6.3127 2.52117 6.3127H8.83388Z" fill="#36C5F0" />
<path d="M18.9577 8.83388C18.9577 7.44626 20.0912 6.3127 21.4788 6.3127C22.8664 6.3127 24 7.44626 24 8.83388C24 10.2215 22.8664 11.355 21.4788 11.355H18.9577V8.83388Z" fill="#2EB67D" />
<path d="M17.6873 8.83388C17.6873 10.2215 16.5537 11.355 15.1661 11.355C13.7785 11.355 12.645 10.2215 12.645 8.83388V2.52117C12.645 1.13355 13.7785 0 15.1661 0C16.5537 0 17.6873 1.13355 17.6873 2.52117V8.83388Z" fill="#2EB67D" />
<path d="M15.1661 18.9577C16.5537 18.9577 17.6873 20.0912 17.6873 21.4788C17.6873 22.8664 16.5537 24 15.1661 24C13.7785 24 12.645 22.8664 12.645 21.4788V18.9577H15.1661Z" fill="#ECB22E" />
<path d="M15.1661 17.6873C13.7785 17.6873 12.645 16.5537 12.645 15.1661C12.645 13.7785 13.7785 12.645 15.1661 12.645H21.4788C22.8664 12.645 24 13.7785 24 15.1661C24 16.5537 22.8664 17.6873 21.4788 17.6873H15.1661Z" fill="#ECB22E" />
</svg>
</#macro>

@ -0,0 +1,8 @@
<#-- https://stackoverflow.design/brand/logo -->
<#macro kw name="Stack Overflow">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M18.9475 15.4707H21.0799V24H1.88887V15.4707H4.0212V21.8677H18.9475V15.4707Z" fill="#BBBBBB" />
<path d="M6.34504 14.8368L16.8177 17.0379L17.2579 14.9444L6.78527 12.7422L6.34504 14.8368ZM7.73086 9.82211L17.4322 14.3403L18.3359 12.4001L8.63439 7.88188L7.73086 9.82211ZM10.4153 5.06255L18.6399 11.9114L20.0096 10.2666L11.785 3.41794L10.4153 5.06255ZM15.7242 0L14.0067 1.27746L20.3936 9.86495L22.1111 8.58768L15.7242 0ZM6.15354 19.7353H16.8152V17.603H6.15354V19.7353Z" fill="#F58025" />
</svg>
</#macro>

@ -0,0 +1,7 @@
<#-- https://about.twitter.com/en/who-we-are/brand-toolkit -->
<#macro kw name="Twitter">
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<title>${name}</title>
<path d="M21.543 7.10409C21.5576 7.31567 21.5576 7.52725 21.5576 7.74077C21.5576 14.2471 16.6045 21.7508 7.54759 21.7508V21.7469C4.87215 21.7508 2.25229 20.9844 0 19.5394C0.389031 19.5862 0.780012 19.6096 1.17197 19.6106C3.38915 19.6126 5.54296 18.8686 7.28726 17.4987C5.18026 17.4588 3.3326 16.085 2.68714 14.0793C3.42523 14.2217 4.18574 14.1924 4.91018 13.9945C2.61304 13.5304 0.96039 11.5121 0.96039 9.1682C0.96039 9.14675 0.96039 9.12627 0.96039 9.1058C1.64485 9.48703 2.41121 9.6986 3.19512 9.722C1.03157 8.27606 0.364656 5.39781 1.67118 3.14748C4.17112 6.22365 7.8596 8.09373 11.8191 8.29166C11.4223 6.58148 11.9644 4.7894 13.2436 3.58721C15.2268 1.72298 18.3459 1.81853 20.2101 3.80074C21.3129 3.58331 22.3698 3.17868 23.337 2.60537C22.9694 3.74516 22.2001 4.71335 21.1725 5.32859C22.1484 5.21353 23.102 4.95223 24 4.55345C23.3389 5.54406 22.5063 6.40695 21.543 7.10409Z" fill="#1D9BF0" />
</svg>
</#macro>

@ -0,0 +1,22 @@
<#macro kw color="">
<#switch color>
<#case "error">
<#assign colorClass="bg-red-100 text-red-600">
<#break>
<#case "info">
<#assign colorClass="bg-blue-100 text-blue-600">
<#break>
<#case "success">
<#assign colorClass="bg-green-100 text-green-600">
<#break>
<#case "warning">
<#assign colorClass="bg-orange-100 text-orange-600">
<#break>
<#default>
<#assign colorClass="bg-blue-100 text-blue-600">
</#switch>
<div class="${colorClass} p-4 rounded-lg text-sm" role="alert">
<#nested>
</div>
</#macro>

@ -0,0 +1,5 @@
<#macro kw>
<body class="bg-secondary-100 flex flex-col items-center justify-center min-h-screen sm:py-16">
<#nested>
</body>
</#macro>

@ -0,0 +1,5 @@
<#macro kw>
<div class="flex flex-col pt-4 space-y-2">
<#nested>
</div>
</#macro>

@ -0,0 +1,33 @@
<#macro kw color="" component="button" size="" rest...>
<#switch color>
<#case "primary">
<#assign colorClass="bg-primary-600 text-white focus:ring-primary-600 hover:bg-primary-700">
<#break>
<#case "secondary">
<#assign colorClass="bg-secondary-100 text-secondary-600 focus:ring-secondary-600 hover:bg-secondary-200 hover:text-secondary-900">
<#break>
<#default>
<#assign colorClass="bg-primary-600 text-white focus:ring-primary-600 hover:bg-primary-700">
</#switch>
<#switch size>
<#case "medium">
<#assign sizeClass="px-4 py-2 text-sm">
<#break>
<#case "small">
<#assign sizeClass="px-2 py-1 text-xs">
<#break>
<#default>
<#assign sizeClass="px-4 py-2 text-sm">
</#switch>
<${component}
class="${colorClass} ${sizeClass} flex justify-center relative rounded-lg w-full focus:outline-none focus:ring-2 focus:ring-offset-2"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<#nested>
</${component}>
</#macro>

@ -0,0 +1,19 @@
<#macro kw content="" footer="" header="">
<div class="bg-white p-8 rounded-lg space-y-6">
<#if header?has_content>
<div class="space-y-4">
${header}
</div>
</#if>
<#if content?has_content>
<div class="space-y-4">
${content}
</div>
</#if>
<#if footer?has_content>
<div class="space-y-4">
${footer}
</div>
</#if>
</div>
</#macro>

@ -0,0 +1,19 @@
<#macro kw checked=false label="" name="" rest...>
<div class="flex items-center">
<input
<#if checked>checked</#if>
class="border-secondary-200 h-4 rounded text-primary-600 w-4 focus:ring-primary-200 focus:ring-opacity-50"
id="${name}"
name="${name}"
type="checkbox"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<label class="ml-2 text-secondary-600 text-sm" for="${name}">
${label}
</label>
</div>
</#macro>

@ -0,0 +1,5 @@
<#macro kw>
<div class="max-w-md space-y-6 w-full">
<#nested>
</div>
</#macro>

@ -0,0 +1,11 @@
<#macro kw rest...>
<form
class="m-0 space-y-4"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<#nested>
</form>
</#macro>

@ -0,0 +1,5 @@
<#macro kw>
<h1 class="text-center text-xl">
<#nested>
</h1>
</#macro>

@ -0,0 +1,78 @@
<#import "/assets/icons/eye.ftl" as iconEye>
<#import "/assets/icons/eye-slash.ftl" as iconEyeSlash>
<#macro
kw
autofocus=false
class="block border-secondary-200 mt-1 rounded-md w-full focus:border-primary-300 focus:ring focus:ring-primary-200 focus:ring-opacity-50 sm:text-sm"
disabled=false
invalid=false
label=""
message=""
name=""
required=true
type="text"
rest...
>
<div>
<label class="sr-only" for="${name}">
${label}
</label>
<#if type == "password">
<div class="relative" x-data="{ show: false }">
<input
<#if autofocus>autofocus</#if>
<#if disabled>disabled</#if>
<#if required>required</#if>
aria-invalid="${invalid?c}"
class="${class}"
id="${name}"
name="${name}"
placeholder="${label}"
:type="show ? 'text' : 'password'"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<button
@click="show = !show"
aria-controls="${name}"
:aria-expanded="show"
class="absolute text-secondary-400 right-3 top-3 sm:top-2"
type="button"
>
<div x-show="!show">
<@iconEye.kw />
</div>
<div x-cloak x-show="show">
<@iconEyeSlash.kw />
</div>
</button>
</div>
<#else>
<input
<#if autofocus>autofocus</#if>
<#if disabled>disabled</#if>
<#if required>required</#if>
aria-invalid="${invalid?c}"
class="${class}"
id="${name}"
name="${name}"
placeholder="${label}"
type="${type}"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
</#if>
<#if invalid?? && message??>
<div class="mt-2 text-red-600 text-sm">
${message?no_esc}
</div>
</#if>
</div>
</#macro>

@ -0,0 +1,30 @@
<#macro kw color="" component="a" size="" rest...>
<#switch color>
<#case "primary">
<#assign colorClass="text-primary-600 hover:text-primary-500">
<#break>
<#case "secondary">
<#assign colorClass="text-secondary-600 hover:text-secondary-900">
<#break>
<#default>
<#assign colorClass="text-primary-600 hover:text-primary-500">
</#switch>
<#switch size>
<#case "small">
<#assign sizeClass="text-sm">
<#break>
<#default>
<#assign sizeClass="">
</#switch>
<${component}
class="<#compress>${colorClass} ${sizeClass} inline-flex</#compress>"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<#nested>
</${component}>
</#macro>

@ -0,0 +1,5 @@
<#macro kw>
<div class="font-bold text-center text-2xl">
<#nested>
</div>
</#macro>

@ -0,0 +1,5 @@
<#macro kw>
<div class="flex justify-around">
<#nested>
</div>
</#macro>

@ -0,0 +1,18 @@
<#macro kw checked=false id="" label="" rest...>
<div>
<input
<#if checked>checked</#if>
class="border-secondary-200 focus:ring-primary-600"
id="${id}"
type="radio"
<#list rest as attrName, attrValue>
${attrName}="${attrValue}"
</#list>
>
<label class="ml-2 text-secondary-600 text-sm" for="${id}">
${label}
</label>
</div>
</#macro>

@ -0,0 +1,81 @@
<#import "/assets/providers/providers.ftl" as providerIcons>
<#macro kw providers=[]>
<div class="pt-4 separate text-secondary-600 text-sm">
${msg("identity-provider-login-label")}
</div>
<div class="gap-4 grid grid-cols-3">
<#list providers as provider>
<#switch provider.alias>
<#case "apple">
<#assign colorClass="hover:bg-provider-apple/10">
<#break>
<#case "bitbucket">
<#assign colorClass="hover:bg-provider-bitbucket/10">
<#break>
<#case "discord">
<#assign colorClass="hover:bg-provider-discord/10">
<#break>
<#case "facebook">
<#assign colorClass="hover:bg-provider-facebook/10">
<#break>
<#case "github">
<#assign colorClass="hover:bg-provider-github/10">
<#break>
<#case "gitlab">
<#assign colorClass="hover:bg-provider-gitlab/10">
<#break>
<#case "google">
<#assign colorClass="hover:bg-provider-google/10">
<#break>
<#case "instagram">
<#assign colorClass="hover:bg-provider-instagram/10">
<#break>
<#case "linkedin-openid-connect">
<#assign colorClass="hover:bg-provider-linkedin/10">
<#break>
<#case "microsoft">
<#assign colorClass="hover:bg-provider-microsoft/10">
<#break>
<#case "oidc">
<#assign colorClass="hover:bg-provider-oidc/10">
<#break>
<#case "openshift-v3">
<#assign colorClass="hover:bg-provider-openshift/10">
<#break>
<#case "openshift-v4">
<#assign colorClass="hover:bg-provider-openshift/10">
<#break>
<#case "paypal">
<#assign colorClass="hover:bg-provider-paypal/10">
<#break>
<#case "slack">
<#assign colorClass="hover:bg-provider-slack/10">
<#break>
<#case "stackoverflow">
<#assign colorClass="hover:bg-provider-stackoverflow/10">
<#break>
<#case "twitter">
<#assign colorClass="hover:bg-provider-twitter/10">
<#break>
<#default>
<#assign colorClass="hover:bg-secondary-100">
</#switch>
<a
class="${colorClass} border border-secondary-200 flex justify-center py-2 rounded-lg hover:border-transparent"
data-provider="${provider.alias}"
href="${provider.loginUrl}"
type="button"
>
<#if providerIcons[provider.alias]??>
<div class="h-6 w-6">
<@providerIcons[provider.alias] />
</div>
<#else>
${provider.displayName!}
</#if>
</a>
</#list>
</div>
</#macro>

@ -0,0 +1,29 @@
<#import "/assets/icons/chevron-down.ftl" as icon>
<#import "/components/atoms/link.ftl" as link>
<#macro kw currentLocale="" locales=[]>
<div class="relative" x-data="{ open: false }">
<@link.kw @click="open = true" color="secondary" component="button" type="button">
<div class="flex items-center">
<span class="mr-1 text-sm">${currentLocale}</span>
<@icon.kw />
</div>
</@link.kw>
<div
@click.away="open = false"
class="absolute bg-white bottom-0 -left-4 max-h-80 mb-6 overflow-y-scroll rounded-lg shadow-lg"
x-cloak
x-show="open"
>
<#list locales as locale>
<#if currentLocale != locale.label>
<div class="px-4 py-2">
<@link.kw color="secondary" href=locale.url size="small">
${locale.label}
</@link.kw>
</div>
</#if>
</#list>
</div>
</div>
</#macro>

@ -0,0 +1,15 @@
<#import "/assets/icons/arrow-top-right-on-square.ftl" as icon>
<#import "/components/atoms/link.ftl" as link>
<#macro kw linkHref="" linkTitle="" name="">
<div class="flex items-center justify-center mb-4 space-x-2">
<span class="font-medium">${name}</span>
<@link.kw
color="primary"
href=linkHref
title=linkTitle
>
<@icon.kw />
</@link.kw>
</div>
</#macro>

@ -0,0 +1,35 @@
<#macro kw script="">
<title>${msg("loginTitle", (realm.displayName!""))}</title>
<meta charset="utf-8">
<meta name="robots" content="noindex, nofollow">
<meta name="viewport" content="width=device-width, initial-scale=1">
<#if properties.meta?has_content>
<#list properties.meta?split(" ") as meta>
<meta name="${meta?split('==')[0]}" content="${meta?split('==')[1]}">
</#list>
</#if>
<#if properties.favicons?has_content>
<#list properties.favicons?split(" ") as favicon>
<link href="${url.resourcesPath}/${favicon?split('==')[0]}" rel="${favicon?split('==')[1]}">
</#list>
</#if>
<#if properties.styles?has_content>
<#list properties.styles?split(" ") as style>
<link href="${url.resourcesPath}/${style}" rel="stylesheet">
</#list>
</#if>
<#if script?has_content>
<script defer src="${url.resourcesPath}/${script}" type="module"></script>
</#if>
<#if properties.scripts?has_content>
<#list properties.scripts?split(" ") as script>
<script defer src="${url.resourcesPath}/${script}" type="module"></script>
</#list>
</#if>
</#macro>

@ -0,0 +1,18 @@
<#import "template.ftl" as layout>
<#import "components/atoms/alert.ftl" as alert>
<#import "components/atoms/link.ftl" as link>
<@layout.registrationLayout displayMessage=false; section>
<#if section="header">
${kcSanitize(msg("errorTitle"))?no_esc}
<#elseif section="form">
<@alert.kw color="error">${kcSanitize(message.summary)?no_esc}</@alert.kw>
<#if !skipLink??>
<#if client?? && client.baseUrl?has_content>
<@link.kw color="secondary" href=client.baseUrl size="small">
${kcSanitize(msg("backToApplication"))?no_esc}
</@link.kw>
</#if>
</#if>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,5 @@
<#macro kw>
<#compress>
${msg("loginTotpDeviceName")} <#if totp.otpCredentials?size gte 1>*</#if>
</#compress>
</#macro>

@ -0,0 +1,5 @@
<#macro kw>
<#compress>
${msg("authenticatorCode")} *
</#compress>
</#macro>

@ -0,0 +1,11 @@
<#macro kw>
<#compress>
<#if !realm.loginWithEmailAllowed>
${msg("username")}
<#elseif !realm.registrationEmailAsUsername>
${msg("usernameOrEmail")}
<#else>
${msg("email")}
</#if>
</#compress>
</#macro>

@ -0,0 +1,110 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<#import "components/atoms/link.ftl" as link>
<#import "features/labels/totp.ftl" as totpLabel>
<#import "features/labels/totp-device.ftl" as totpDeviceLabel>
<#assign totpLabel><@totpLabel.kw /></#assign>
<#assign totpDeviceLabel><@totpDeviceLabel.kw /></#assign>
<@layout.registrationLayout
displayMessage=!messagesPerField.existsError("totp", "userLabel")
displayRequiredFields=false
;
section
>
<#if section="header">
${msg("loginTotpTitle")}
<#elseif section="form">
<ol class="list-decimal pl-4 space-y-2">
<li class="space-y-2">
<p>${msg("loginTotpStep1")}</p>
<ul class="list-disc pl-4">
<#list totp.supportedApplications as app>
<li>${msg(app)}</li>
</#list>
</ul>
</li>
<#if mode?? && mode="manual">
<li>
<p>${msg("loginTotpManualStep2")}</p>
<p class="font-medium text-xl">${totp.totpSecretEncoded}</p>
</li>
<li>
<@link.kw color="primary" href=totp.qrUrl>
${msg("loginTotpScanBarcode")}
</@link.kw>
</li>
<li class="space-y-2">
<p>${msg("loginTotpManualStep3")}</p>
<ul class="list-disc pl-4">
<li>${msg("loginTotpType")}: ${msg("loginTotp." + totp.policy.type)}</li>
<li>${msg("loginTotpAlgorithm")}: ${totp.policy.getAlgorithmKey()}</li>
<li>${msg("loginTotpDigits")}: ${totp.policy.digits}</li>
<#if totp.policy.type="totp">
<li>${msg("loginTotpInterval")}: ${totp.policy.period}</li>
<#elseif totp.policy.type="hotp">
<li>${msg("loginTotpCounter")}: ${totp.policy.initialCounter}</li>
</#if>
</ul>
</li>
<#else>
<li>
<p>${msg("loginTotpStep2")}</p>
<img
alt="Figure: Barcode"
class="mx-auto"
src="data:image/png;base64, ${totp.totpSecretQrCode}"
>
<@link.kw color="primary" href=totp.manualUrl>
${msg("loginTotpUnableToScan")}
</@link.kw>
</li>
</#if>
<li>${msg("loginTotpStep3")}</li>
<li>${msg("loginTotpStep3DeviceName")}</li>
</ol>
<@form.kw action=url.loginAction method="post">
<input name="totpSecret" type="hidden" value="${totp.totpSecret}">
<#if mode??>
<input name="mode" type="hidden" value="${mode}">
</#if>
<@input.kw
autocomplete="off"
autofocus=true
invalid=messagesPerField.existsError("totp")
label=totpLabel
message=kcSanitize(messagesPerField.get("totp"))
name="totp"
required=false
type="text"
/>
<@input.kw
autocomplete="off"
invalid=messagesPerField.existsError("userLabel")
label=totpDeviceLabel
message=kcSanitize(messagesPerField.get("userLabel"))
name="userLabel"
required=false
type="text"
/>
<@buttonGroup.kw>
<#if isAppInitiatedAction??>
<@button.kw color="primary" type="submit">
${msg("doSubmit")}
</@button.kw>
<@button.kw color="secondary" name="cancel-aia" type="submit" value="true">
${msg("doCancel")}
</@button.kw>
<#else>
<@button.kw color="primary" type="submit">
${msg("doSubmit")}
</@button.kw>
</#if>
</@buttonGroup.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,18 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/form.ftl" as form>
<@layout.registrationLayout; section>
<#if section="header">
${msg("confirmLinkIdpTitle")}
<#elseif section="form">
<@form.kw action=url.loginAction method="post">
<@button.kw color="primary" name="submitAction" type="submit" value="updateProfile">
${msg("confirmLinkIdpReviewProfile")}
</@button.kw>
<@button.kw color="primary" name="submitAction" type="submit" value="linkAccount">
${msg("confirmLinkIdpContinue", idpDisplayName)}
</@button.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,62 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<@layout.registrationLayout; section>
<#if section="header">
<#if client.attributes.logoUri??>
<img class="mb-4 mx-auto" src="${client.attributes.logoUri}"/>
</#if>
<p>
<#if client.name?has_content>
${msg("oauthGrantTitle", advancedMsg(client.name))}
<#else>
${msg("oauthGrantTitle", client.clientId)}
</#if>
</p>
<#elseif section="form">
<h3>${msg("oauthGrantRequest")}</h3>
<ul class="list-disc pl-4">
<#if oauth.clientScopesRequested??>
<#list oauth.clientScopesRequested as clientScope>
<li>
<#if !clientScope.dynamicScopeParameter??>
${advancedMsg(clientScope.consentScreenText)}
<#else>
${advancedMsg(clientScope.consentScreenText)}: <b>${clientScope.dynamicScopeParameter}</b>
</#if>
</li>
</#list>
</#if>
</ul>
<#if client.attributes.policyUri?? || client.attributes.tosUri??>
<h3>
<#if client.name?has_content>
${msg("oauthGrantInformation",advancedMsg(client.name))}
<#else>
${msg("oauthGrantInformation",client.clientId)}
</#if>
<#if client.attributes.tosUri??>
${msg("oauthGrantReview")}
<a href="${client.attributes.tosUri}" target="_blank">${msg("oauthGrantTos")}</a>
</#if>
<#if client.attributes.policyUri??>
${msg("oauthGrantReview")}
<a href="${client.attributes.policyUri}" target="_blank">${msg("oauthGrantPolicy")}</a>
</#if>
</h3>
</#if>
<@form.kw action=url.oauthAction method="post">
<input name="code" type="hidden" value="${oauth.code}">
<@buttonGroup.kw>
<@button.kw color="primary" name="accept" type="submit">
${msg("doYes")}
</@button.kw>
<@button.kw color="secondary" name="cancel" type="submit">
${msg("doNo")}
</@button.kw>
</@buttonGroup.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,50 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<#import "components/atoms/radio.ftl" as radio>
<#import "features/labels/totp.ftl" as totpLabel>
<#assign totpLabel><@totpLabel.kw /></#assign>
<@layout.registrationLayout
displayMessage=!messagesPerField.existsError("totp")
;
section
>
<#if section="header">
${msg("doLogIn")}
<#elseif section="form">
<@form.kw action=url.loginAction method="post">
<#if otpLogin.userOtpCredentials?size gt 1>
<div class="flex items-center space-x-4">
<#list otpLogin.userOtpCredentials as otpCredential>
<@radio.kw
checked=(otpCredential.id == otpLogin.selectedCredentialId)
id="kw-otp-credential-${otpCredential?index}"
label=otpCredential.userLabel
name="selectedCredentialId"
tabindex=otpCredential?index
value=otpCredential.id
/>
</#list>
</div>
</#if>
<@input.kw
autocomplete="off"
autofocus=true
invalid=messagesPerField.existsError("totp")
label=totpLabel
message=kcSanitize(messagesPerField.get("totp"))
name="otp"
type="text"
/>
<@buttonGroup.kw>
<@button.kw color="primary" name="submitAction" type="submit">
${msg("doLogIn")}
</@button.kw>
</@buttonGroup.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,18 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<@layout.registrationLayout; section>
<#if section="header">
${msg("pageExpiredTitle")}
<#elseif section="form">
<@buttonGroup.kw>
<@button.kw color="primary" component="a" href=url.loginRestartFlowUrl>
${msg("doTryAgain")}
</@button.kw>
<@button.kw color="secondary" component="a" href=url.loginAction>
${msg("doContinue")}
</@button.kw>
</@buttonGroup.kw>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,39 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<#import "components/atoms/link.ftl" as link>
<@layout.registrationLayout displayMessage=!messagesPerField.existsError("password"); section>
<#if section="header">
${msg("doLogIn")}
<#elseif section="form">
<@form.kw
action=url.loginAction
method="post"
onsubmit="login.disabled = true; return true;"
>
<@input.kw
autofocus=true
invalid=messagesPerField.existsError("password")
label=msg("password")
message=kcSanitize(messagesPerField.get("password"))?no_esc
name="password"
type="password"
/>
<#if realm.resetPasswordAllowed>
<div class="flex items-center justify-between">
<@link.kw color="primary" href=url.loginResetCredentialsUrl size="small">
${msg("doForgotPassword")}
</@link.kw>
</div>
</#if>
<@buttonGroup.kw>
<@button.kw color="primary" name="login" type="submit">
${msg("doLogIn")}
</@button.kw>
</@buttonGroup.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,91 @@
<#import "template.ftl" as layout>
<#import "components/atoms/alert.ftl" as alert>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/checkbox.ftl" as checkbox>
<#import "components/atoms/form.ftl" as form>
<@layout.registrationLayout script="dist/recoveryCodes.js"; section>
<#if section="header">
${msg("recovery-code-config-header")}
<#elseif section="form">
<div class="space-y-6" x-data="recoveryCodes">
<@alert.kw color="warning">
<div class="space-y-2">
<h4 class="font-medium">${msg("recovery-code-config-warning-title")}</h4>
<p>${msg("recovery-code-config-warning-message")}</p>
</div>
</@alert.kw>
<ul class="columns-2 font-mono text-center" x-ref="codeList">
<#list recoveryAuthnCodesConfigBean.generatedRecoveryAuthnCodesList as code>
<li>${code[0..3]}-${code[4..7]}-${code[8..]}</li>
</#list>
</ul>
<div class="flex items-stretch space-x-4 mb-4">
<@button.kw @click="print" color="secondary" size="small" type="button">
${msg("recovery-codes-print")}
</@button.kw>
<@button.kw @click="download" color="secondary" size="small" type="button">
${msg("recovery-codes-download")}
</@button.kw>
<@button.kw @click="copy" color="secondary" size="small" type="button">
${msg("recovery-codes-copy")}
</@button.kw>
</div>
<@form.kw action=url.loginAction method="post">
<input
name="generatedRecoveryAuthnCodes"
type="hidden"
value="${recoveryAuthnCodesConfigBean.generatedRecoveryAuthnCodesAsString}"
/>
<input
name="generatedAt"
type="hidden"
value="${recoveryAuthnCodesConfigBean.generatedAt?c}"
/>
<input
name="userLabel"
type="hidden"
value="${msg('recovery-codes-label-default')}"
/>
<@checkbox.kw
label=msg("recovery-codes-confirmation-message")
name="kcRecoveryCodesConfirmationCheck"
required="required"
x\-ref="confirmationCheck"
/>
<@buttonGroup.kw>
<#if isAppInitiatedAction??>
<@button.kw color="primary" type="submit">
${msg("recovery-codes-action-complete")}
</@button.kw>
<@button.kw
@click="$refs.confirmationCheck.required = false"
color="secondary"
name="cancel-aia"
type="submit"
value="true"
>
${msg("recovery-codes-action-cancel")}
</@button.kw>
<#else>
<@button.kw color="primary" type="submit">
${msg("recovery-codes-action-complete")}
</@button.kw>
</#if>
</@buttonGroup.kw>
</@form.kw>
</div>
</#if>
</@layout.registrationLayout>
<script>
document.addEventListener('alpine:init', () => {
Alpine.store('recoveryCodes', {
downloadFileDate: '${msg("recovery-codes-download-file-date")}',
downloadFileDescription: '${msg("recovery-codes-download-file-description")}',
downloadFileHeader: '${msg("recovery-codes-download-file-header")}',
downloadFileName: 'kc-download-recovery-codes',
})
})
</script>

@ -0,0 +1,26 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<@layout.registrationLayout; section>
<#if section="header">
${msg("auth-recovery-code-header")}
<#elseif section="form">
<@form.kw action=url.loginAction method="post">
<@input.kw
autocomplete="off"
autofocus=true
label=msg("auth-recovery-code-prompt", recoveryAuthnCodesInputBean.codeNumber?c)
name="recoveryCodeInput"
type="text"
/>
<@buttonGroup.kw>
<@button.kw color="primary" name="login" type="submit">
${msg("doLogIn")}
</@button.kw>
</@buttonGroup.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,44 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<#import "components/atoms/link.ftl" as link>
<#import "features/labels/username.ftl" as usernameLabel>
<#assign usernameLabel><@usernameLabel.kw /></#assign>
<@layout.registrationLayout
displayInfo=true
displayMessage=!messagesPerField.existsError("username")
;
section
>
<#if section="header">
${msg("emailForgotTitle")}
<#elseif section="form">
<@form.kw action=url.loginAction method="post">
<@input.kw
autocomplete=realm.loginWithEmailAllowed?string("email", "username")
autofocus=true
invalid=messagesPerField.existsError("username")
label=usernameLabel
message=kcSanitize(messagesPerField.get("username"))
name="username"
type="text"
value=(auth?has_content && auth.showUsername())?then(auth.attemptedUsername, '')
/>
<@buttonGroup.kw>
<@button.kw color="primary" type="submit">
${msg("doSubmit")}
</@button.kw>
</@buttonGroup.kw>
</@form.kw>
<#elseif section="info">
${msg("emailInstruction")}
<#elseif section="nav">
<@link.kw color="secondary" href=url.loginUrl size="small">
${kcSanitize(msg("backToLogin"))?no_esc}
</@link.kw>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,64 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/checkbox.ftl" as checkbox>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<@layout.registrationLayout
displayMessage=!messagesPerField.existsError("password", "password-confirm")
;
section
>
<#if section="header">
${msg("updatePasswordTitle")}
<#elseif section="form">
<@form.kw action=url.loginAction method="post">
<input
autocomplete="username"
name="username"
type="hidden"
value="${username}"
>
<input autocomplete="current-password" name="password" type="hidden">
<@input.kw
autocomplete="new-password"
autofocus=true
invalid=messagesPerField.existsError("password", "password-confirm")
label=msg("passwordNew")
name="password-new"
type="password"
/>
<@input.kw
autocomplete="new-password"
invalid=messagesPerField.existsError("password-confirm")
label=msg("passwordConfirm")
message=kcSanitize(messagesPerField.get("password-confirm"))
name="password-confirm"
type="password"
/>
<#if isAppInitiatedAction??>
<@checkbox.kw
checked=true
label=msg("logoutOtherSessions")
name="logout-sessions"
value="on"
/>
</#if>
<@buttonGroup.kw>
<#if isAppInitiatedAction??>
<@button.kw color="primary" type="submit">
${msg("doSubmit")}
</@button.kw>
<@button.kw color="secondary" name="cancel-aia" type="submit" value="true">
${msg("doCancel")}
</@button.kw>
<#else>
<@button.kw color="primary" type="submit">
${msg("doSubmit")}
</@button.kw>
</#if>
</@buttonGroup.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,71 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<@layout.registrationLayout
displayMessage=!messagesPerField.existsError("email", "firstName", "lastName", "username")
;
section
>
<#if section="header">
${msg("loginProfileTitle")}
<#elseif section="form">
<@form.kw action=url.loginAction method="post">
<#if user.editUsernameAllowed>
<@input.kw
autocomplete="username"
autofocus=true
invalid=messagesPerField.existsError("username")
label=msg("username")
message=kcSanitize(messagesPerField.get("username"))
name="username"
type="text"
value=(user.username)!''
/>
</#if>
<@input.kw
autocomplete="email"
invalid=messagesPerField.existsError("email")
label=msg("email")
message=kcSanitize(messagesPerField.get("email"))
name="email"
type="email"
value=(user.email)!''
/>
<@input.kw
autocomplete="given-name"
invalid=messagesPerField.existsError("firstName")
label=msg("firstName")
message=kcSanitize(messagesPerField.get("firstName"))
name="firstName"
type="text"
value=(user.firstName)!''
/>
<@input.kw
autocomplete="family-name"
invalid=messagesPerField.existsError("lastName")
label=msg("lastName")
message=kcSanitize(messagesPerField.get("lastName"))
name="lastName"
type="text"
value=(user.lastName)!''
/>
<@buttonGroup.kw>
<#if isAppInitiatedAction??>
<@button.kw color="primary" type="submit">
${msg("doSubmit")}
</@button.kw>
<@button.kw color="secondary" name="cancel-aia" type="submit" value="true">
${msg("doCancel")}
</@button.kw>
<#else>
<@button.kw color="primary" type="submit">
${msg("doSubmit")}
</@button.kw>
</#if>
</@buttonGroup.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,71 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/checkbox.ftl" as checkbox>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<#import "components/atoms/link.ftl" as link>
<#import "components/molecules/identity-provider.ftl" as identityProvider>
<#import "features/labels/username.ftl" as usernameLabel>
<#assign usernameLabel><@usernameLabel.kw /></#assign>
<@layout.registrationLayout
displayInfo=realm.password && realm.registrationAllowed && !registrationDisabled??
displayMessage=!messagesPerField.existsError("username")
;
section
>
<#if section="header">
${msg("loginAccountTitle")}
<#elseif section="form">
<#if realm.password>
<@form.kw
action=url.loginAction
method="post"
onsubmit="login.disabled = true; return true;"
>
<#if !usernameHidden??>
<@input.kw
autocomplete=realm.loginWithEmailAllowed?string("email", "username")
autofocus=true
disabled=usernameEditDisabled??
invalid=messagesPerField.existsError("username")
label=usernameLabel
message=kcSanitize(messagesPerField.get("username"))?no_esc
name="username"
type="text"
value=(login.username)!''
/>
</#if>
<#if realm.rememberMe && !usernameHidden??>
<div class="flex items-center justify-between">
<@checkbox.kw
checked=login.rememberMe??
label=msg("rememberMe")
name="rememberMe"
/>
</div>
</#if>
<@buttonGroup.kw>
<@button.kw color="primary" name="login" type="submit">
${msg("doLogIn")}
</@button.kw>
</@buttonGroup.kw>
</@form.kw>
</#if>
<#elseif section="info">
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
<div class="text-center">
${msg("noAccount")}
<@link.kw color="primary" href=url.registrationUrl>
${msg("doRegister")}
</@link.kw>
</div>
</#if>
<#elseif section="socialProviders">
<#if realm.password && social.providers??>
<@identityProvider.kw providers=social.providers />
</#if>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,40 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/link.ftl" as link>
<@layout.registrationLayout; section>
<#if section = "header">
${msg("doLogIn")}
<#elseif section = "form">
<div>
<div>${msg("clientCertificate")}</div>
<div class="text-secondary-600">
<#if x509.formData.subjectDN??>
${(x509.formData.subjectDN!"")}
<#else>
${msg("noCertificate")}
</#if>
</div>
</div>
<#if x509.formData.isUserEnabled??>
<div>
<span>${msg("doX509Login")}</span>
<b>${(x509.formData.username!'')}</b>
</div>
</#if>
<@form.kw action=url.loginAction method="post">
<@buttonGroup.kw>
<@button.kw color="primary" name="login" type="submit">
${msg("doContinue")}
</@button.kw>
<#if x509.formData.isUserEnabled??>
<@button.kw color="secondary" name="cancel" type="submit">
${msg("doIgnore")}
</@button.kw>
</#if>
</@buttonGroup.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,87 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/checkbox.ftl" as checkbox>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<#import "components/atoms/link.ftl" as link>
<#import "components/molecules/identity-provider.ftl" as identityProvider>
<#import "features/labels/username.ftl" as usernameLabel>
<#assign usernameLabel><@usernameLabel.kw /></#assign>
<@layout.registrationLayout
displayInfo=realm.password && realm.registrationAllowed && !registrationDisabled??
displayMessage=!messagesPerField.existsError("username", "password")
;
section
>
<#if section="header">
${msg("loginAccountTitle")}
<#elseif section="form">
<#if realm.password>
<@form.kw
action=url.loginAction
method="post"
onsubmit="login.disabled = true; return true;"
>
<input
name="credentialId"
type="hidden"
value="<#if auth.selectedCredential?has_content>${auth.selectedCredential}</#if>"
>
<@input.kw
autocomplete=realm.loginWithEmailAllowed?string("email", "username")
autofocus=true
disabled=usernameEditDisabled??
invalid=messagesPerField.existsError("username", "password")
label=usernameLabel
message=kcSanitize(messagesPerField.getFirstError("username", "password"))
name="username"
type="text"
value=(login.username)!''
/>
<@input.kw
invalid=messagesPerField.existsError("username", "password")
label=msg("password")
name="password"
type="password"
/>
<#if realm.rememberMe && !usernameEditDisabled?? || realm.resetPasswordAllowed>
<div class="flex items-center justify-between">
<#if realm.rememberMe && !usernameEditDisabled??>
<@checkbox.kw
checked=login.rememberMe??
label=msg("rememberMe")
name="rememberMe"
/>
</#if>
<#if realm.resetPasswordAllowed>
<@link.kw color="primary" href=url.loginResetCredentialsUrl size="small">
${msg("doForgotPassword")}
</@link.kw>
</#if>
</div>
</#if>
<@buttonGroup.kw>
<@button.kw color="primary" name="login" type="submit">
${msg("doLogIn")}
</@button.kw>
</@buttonGroup.kw>
</@form.kw>
</#if>
<#elseif section="info">
<#if realm.password && realm.registrationAllowed && !registrationDisabled??>
<div class="text-center">
${msg("noAccount")}
<@link.kw color="primary" href=url.registrationUrl>
${msg("doRegister")}
</@link.kw>
</div>
</#if>
<#elseif section="socialProviders">
<#if realm.password && social.providers??>
<@identityProvider.kw providers=social.providers />
</#if>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,25 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/link.ftl" as link>
<@layout.registrationLayout; section>
<#if section="header">
${msg("logoutConfirmTitle")}
<#elseif section="form">
<p>${msg("logoutConfirmHeader")}</p>
<@form.kw action=url.logoutConfirmAction method="post">
<input name="session_code" type="hidden" value="${logoutConfirm.code}">
<@button.kw color="primary" name="confirmLogout" type="submit" value=msg('doLogout')>
${msg("doLogout")}
</@button.kw>
</@form.kw>
<#if !logoutConfirm.skipLink>
<#if (client.baseUrl)?has_content>
<@link.kw color="secondary" href=client.baseUrl size="small">
${kcSanitize(msg("backToApplication"))?no_esc}
</@link.kw>
</#if>
</#if>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,88 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/input.ftl" as input>
<#import "components/atoms/link.ftl" as link>
<@layout.registrationLayout
displayMessage=!messagesPerField.existsError("firstName", "lastName", "email", "username", "password", "password-confirm")
;
section
>
<#if section="header">
${msg("registerTitle")}
<#elseif section="form">
<@form.kw action=url.registrationAction method="post">
<@input.kw
autocomplete="given-name"
autofocus=true
invalid=messagesPerField.existsError("firstName")
label=msg("firstName")
message=kcSanitize(messagesPerField.get("firstName"))
name="firstName"
type="text"
value=(register.formData.firstName)!''
/>
<@input.kw
autocomplete="family-name"
invalid=messagesPerField.existsError("lastName")
label=msg("lastName")
message=kcSanitize(messagesPerField.get("lastName"))
name="lastName"
type="text"
value=(register.formData.lastName)!''
/>
<@input.kw
autocomplete="email"
invalid=messagesPerField.existsError("email")
label=msg("email")
message=kcSanitize(messagesPerField.get("email"))
name="email"
type="email"
value=(register.formData.email)!''
/>
<#if !realm.registrationEmailAsUsername>
<@input.kw
autocomplete="username"
invalid=messagesPerField.existsError("username")
label=msg("username")
message=kcSanitize(messagesPerField.get("username"))
name="username"
type="text"
value=(register.formData.username)!''
/>
</#if>
<#if passwordRequired??>
<@input.kw
autocomplete="new-password"
invalid=messagesPerField.existsError("password", "password-confirm")
label=msg("password")
message=kcSanitize(messagesPerField.getFirstError("password", "password-confirm"))
name="password"
type="password"
/>
<@input.kw
autocomplete="new-password"
invalid=messagesPerField.existsError("password-confirm")
label=msg("passwordConfirm")
message=kcSanitize(messagesPerField.get("password-confirm"))
name="password-confirm"
type="password"
/>
</#if>
<#if recaptchaRequired??>
<div class="g-recaptcha" data-sitekey="${recaptchaSiteKey}" data-size="compact"></div>
</#if>
<@buttonGroup.kw>
<@button.kw color="primary" type="submit">
${msg("doRegister")}
</@button.kw>
</@buttonGroup.kw>
</@form.kw>
<#elseif section="nav">
<@link.kw color="secondary" href=url.loginUrl size="small">
${kcSanitize(msg("backToLogin"))?no_esc}
</@link.kw>
</#if>
</@layout.registrationLayout>

@ -0,0 +1 @@
var s={};Object.defineProperty(s,"__esModule",{value:!0});function v(e,r,a){var l;if(a===void 0&&(a={}),!r.codes){r.codes={};for(var n=0;n<r.chars.length;++n)r.codes[r.chars[n]]=n}if(!a.loose&&e.length*r.bits&7)throw new SyntaxError("Invalid padding");for(var b=e.length;e[b-1]==="=";)if(--b,!a.loose&&!((e.length-b)*r.bits&7))throw new SyntaxError("Invalid padding");for(var c=new((l=a.out)!=null?l:Uint8Array)(b*r.bits/8|0),t=0,i=0,u=0,f=0;f<b;++f){var h=r.codes[e[f]];if(h===void 0)throw new SyntaxError("Invalid character "+e[f]);i=i<<r.bits|h,t+=r.bits,t>=8&&(t-=8,c[u++]=255&i>>t)}if(t>=r.bits||255&i<<8-t)throw new SyntaxError("Unexpected end of data");return c}function o(e,r,a){a===void 0&&(a={});for(var l=a,n=l.pad,b=n===void 0?!0:n,c=(1<<r.bits)-1,t="",i=0,u=0,f=0;f<e.length;++f)for(u=u<<8|255&e[f],i+=8;i>r.bits;)i-=r.bits,t+=r.chars[c&u>>i];if(i&&(t+=r.chars[c&u<<r.bits-i]),b)for(;t.length*r.bits&7;)t+="=";return t}var p={chars:"0123456789ABCDEF",bits:4},y={chars:"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",bits:5},d={chars:"0123456789ABCDEFGHIJKLMNOPQRSTUV",bits:5},w={chars:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",bits:6},x={chars:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",bits:6},E={parse:function(r,a){return v(r.toUpperCase(),p,a)},stringify:function(r,a){return o(r,p,a)}},$={parse:function(r,a){return a===void 0&&(a={}),v(a.loose?r.toUpperCase().replace(/0/g,"O").replace(/1/g,"L").replace(/8/g,"B"):r,y,a)},stringify:function(r,a){return o(r,y,a)}},U={parse:function(r,a){return v(r,d,a)},stringify:function(r,a){return o(r,d,a)}},S={parse:function(r,a){return v(r,w,a)},stringify:function(r,a){return o(r,w,a)}},g={parse:function(r,a){return v(r,x,a)},stringify:function(r,a){return o(r,x,a)}},C={parse:v,stringify:o};s.base16=E;s.base32=$;s.base32hex=U;s.base64=S;s.base64url=g;s.codec=C;s.base16;s.base32;s.base32hex;s.base64;const I=s.base64url;s.codec;export{I as b};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

@ -0,0 +1 @@
import{m as o}from"./assets/module.esm-9a996e1c.js";window.Alpine=o;o.start();

@ -0,0 +1,8 @@
import{m as p}from"./assets/module.esm-9a996e1c.js";document.addEventListener("alpine:init",()=>{p.data("recoveryCodes",function(){const{codeList:o}=this.$refs,{downloadFileDate:n,downloadFileDescription:i,downloadFileHeader:d,downloadFileName:s}=this.$store.recoveryCodes,a=new Date().toLocaleString(navigator.language),r=o.getElementsByTagName("li"),l=Array.from(r).map(t=>t.innerText).join(`
`);return{copy:()=>navigator.clipboard.writeText(l),download:()=>{const t=document.createElement("a"),c=`${d}
${l}
${i}
${n} ${a}`;t.setAttribute("href","data:text/plain;charset=utf-8,"+encodeURIComponent(c)),t.setAttribute("download",`${s}.txt`),t.click()},print:()=>{const t=o.innerHTML,m=`<html><style>div { font-family: monospace; list-style-type: none }</style><body><title>${s}</title><p>${d}</p><div>${t}</div><p>${i}</p><p>${n} ${a}</p></body></html>`,e=window.open();e&&(e.document.write(m),e.print(),e.close())}}})});

@ -0,0 +1 @@
import{m as g}from"./assets/module.esm-9a996e1c.js";import{b as n}from"./assets/index-a7b84447.js";document.addEventListener("alpine:init",()=>{g.data("webAuthnAuthenticate",function(){const{authenticatorDataInput:l,authnSelectForm:r,clientDataJSONInput:p,credentialIdInput:f,errorInput:s,signatureInput:d,userHandleInput:h,webAuthnForm:a}=this.$refs,{challenge:m,createTimeout:o,isUserIdentified:A,rpId:w,unsupportedBrowserText:y,userVerification:u}=this.$store.webAuthnAuthenticate,c=i=>{if(!window.PublicKeyCredential){s.value=y,a.submit();return}const t={challenge:n.parse(m,{loose:!0}),rpId:w};i.length&&(t.allowCredentials=i),parseInt(o)!==0&&(t.timeout=parseInt(o)*1e3),u!=="not specified"&&(t.userVerification=u),navigator.credentials.get({publicKey:t}).then(e=>{e instanceof PublicKeyCredential&&e.response instanceof AuthenticatorAssertionResponse&&(window.result=e,l.value=n.stringify(new Uint8Array(e.response.authenticatorData),{pad:!1}),p.value=n.stringify(new Uint8Array(e.response.clientDataJSON),{pad:!1}),d.value=n.stringify(new Uint8Array(e.response.signature),{pad:!1}),f.value=e.id,e.response.userHandle&&(h.value=n.stringify(new Uint8Array(e.response.userHandle),{pad:!1})),a.submit())}).catch(e=>{s.value=e,a.submit()})},b=()=>{const i=[];if(r){const t=Array.from(r.elements);t.length&&t.forEach(e=>{e instanceof HTMLInputElement&&i.push({id:n.parse(e.value,{loose:!0}),type:"public-key"})})}c(i)};return{webAuthnAuthenticate:()=>{if(!A){c([]);return}b()}}})});

@ -0,0 +1 @@
import{m as T}from"./assets/module.esm-9a996e1c.js";import{b as a}from"./assets/index-a7b84447.js";document.addEventListener("alpine:init",()=>{T.data("webAuthnRegister",function(){const{attestationObjectInput:m,authenticatorLabelInput:g,clientDataJSONInput:A,errorInput:I,publicKeyCredentialIdInput:w,registerForm:s,transportsInput:C}=this.$refs,{attestationConveyancePreference:u,authenticatorAttachment:c,challenge:K,createTimeout:l,excludeCredentialIds:v,requireResidentKey:p,rpEntityName:S,rpId:P,signatureAlgorithms:R,unsupportedBrowserText:x,userId:E,userVerificationRequirement:d,username:f}=this.$store.webAuthnRegister,L=t=>{const e=[];return t===""?(e.push({alg:-7,type:"public-key"}),e):(t.split(",").forEach(r=>{e.push({alg:parseInt(r),type:"public-key"})}),e)},q=t=>{const e=[];return t===""||t.split(",").forEach(r=>{e.push({id:a.parse(r,{loose:!0}),type:"public-key"})}),e},N=t=>{if(t===""||t.constructor!==Array)return"";let e="";return t.forEach(i=>{e+=i+","}),e.slice(0,-1)};return{registerSecurityKey:()=>{if(!window.PublicKeyCredential){I.value=x,s.submit();return}const t={challenge:a.parse(K,{loose:!0}),pubKeyCredParams:L(R),rp:{id:P,name:S},user:{displayName:f,id:a.parse(E,{loose:!0}),name:f}};u!=="not specified"&&(t.attestation=u);const e={};let i=!1;c!=="not specified"&&(e.authenticatorAttachment=c,i=!0),p!=="not specified"&&(p==="Yes"?e.requireResidentKey=!0:e.requireResidentKey=!1,i=!0),d!=="not specified"&&(e.userVerification=d,i=!0),i&&(t.authenticatorSelection=e);const r=q(v);r.length>0&&(t.excludeCredentials=r),parseInt(l)!==0&&(t.timeout=parseInt(l)*1e3),navigator.credentials.create({publicKey:t}).then(n=>{if(n instanceof PublicKeyCredential&&n.response instanceof AuthenticatorAttestationResponse){const{getTransports:h}=n.response;window.result=n;const O=n.rawId;if(m.value=a.stringify(new Uint8Array(n.response.attestationObject),{pad:!1}),A.value=a.stringify(new Uint8Array(n.response.clientDataJSON),{pad:!1}),w.value=a.stringify(new Uint8Array(O),{pad:!1}),typeof h=="function"){const b=h();b&&(C.value=N(b))}else console.log("Your browser is not able to recognize supported transport media for the authenticator.");const y="WebAuthn Authenticator (Default Label)";let o=window.prompt("Please input your registered authenticator's label",y);o===null&&(o=y),g.value=o,s.submit()}}).catch(function(n){n.value=n,s.submit()})}}})});

@ -0,0 +1,28 @@
<#import "template.ftl" as layout>
<#import "components/atoms/form.ftl" as form>
<#import "components/atoms/link.ftl" as link>
<@layout.registrationLayout displayInfo=false; section>
<#if section="header">
${msg("loginChooseAuthenticator")}
<#elseif section="form">
<div x-data>
<@form.kw action=url.loginAction method="post" x\-ref="selectCredentialForm">
<input name="authenticationExecution" type="hidden" x-ref="authExecInput" />
<#list auth.authenticationSelections as authenticationSelection>
<div>
<@link.kw
@click="$refs.authExecInput.value = '${authenticationSelection.authExecId}'; $refs.selectCredentialForm.submit()"
color="primary"
component="button"
type="button"
>
${msg("${authenticationSelection.displayName}")}
</@link.kw>
<div class="text-sm">${msg("${authenticationSelection.helpText}")}</div>
</div>
</#list>
</@form.kw>
</div>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,84 @@
<#import "document.ftl" as document>
<#import "components/atoms/alert.ftl" as alert>
<#import "components/atoms/body.ftl" as body>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/card.ftl" as card>
<#import "components/atoms/container.ftl" as container>
<#import "components/atoms/heading.ftl" as heading>
<#import "components/atoms/logo.ftl" as logo>
<#import "components/atoms/nav.ftl" as nav>
<#import "components/molecules/locale-provider.ftl" as localeProvider>
<#import "components/molecules/username.ftl" as username>
<#macro
registrationLayout
displayInfo=false
displayMessage=true
displayRequiredFields=false
script=""
showAnotherWayIfPresent=true
>
<#assign cardHeader>
<@logo.kw>
${kcSanitize(msg("loginTitleHtml", (realm.displayNameHtml!"")))?no_esc}
</@logo.kw>
<#if !(auth?has_content && auth.showUsername() && !auth.showResetCredentials())>
<@heading.kw>
<#nested "header">
</@heading.kw>
<#else>
<#nested "show-username">
<@username.kw
linkHref=url.loginRestartFlowUrl
linkTitle=msg("restartLoginTooltip")
name=auth.attemptedUsername
/>
</#if>
</#assign>
<#assign cardContent>
<#if displayMessage && message?has_content && (message.type != "warning" || !isAppInitiatedAction??)>
<@alert.kw color=message.type>
${kcSanitize(message.summary)?no_esc}
</@alert.kw>
</#if>
<#nested "form">
<#if displayRequiredFields>
<p class="text-secondary-600 text-sm">
* ${msg("requiredFields")}
</p>
</#if>
<#if auth?has_content && auth.showTryAnotherWayLink() && showAnotherWayIfPresent>
<form action="${url.loginAction}" method="post">
<input name="tryAnotherWay" type="hidden" value="on" />
<@button.kw color="secondary" type="submit">
${msg("doTryAnotherWay")}
</@button.kw>
</form>
</#if>
<#nested "socialProviders">
</#assign>
<#assign cardFooter>
<#if displayInfo>
<#nested "info">
</#if>
</#assign>
<html<#if realm.internationalizationEnabled> lang="${locale.currentLanguageTag}"</#if>>
<head>
<@document.kw script=script />
</head>
<@body.kw>
<@container.kw>
<@card.kw content=cardContent footer=cardFooter header=cardHeader />
<@nav.kw>
<#nested "nav">
<#if realm.internationalizationEnabled && locale.supported?size gt 1>
<@localeProvider.kw currentLocale=locale.current locales=locale.supported />
</#if>
</@nav.kw>
</@container.kw>
</@body.kw>
</html>
</#macro>

@ -0,0 +1,22 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<#import "components/atoms/form.ftl" as form>
<@layout.registrationLayout displayMessage=false; section>
<#if section="header">
${msg("termsTitle")}
<#elseif section="form">
${kcSanitize(msg("termsText"))?no_esc}
<@form.kw action=url.loginAction method="post">
<@buttonGroup.kw>
<@button.kw color="primary" name="accept" type="submit">
${msg("doAccept")}
</@button.kw>
<@button.kw color="secondary" name="cancel" type="submit">
${msg("doDecline")}
</@button.kw>
</@buttonGroup.kw>
</@form.kw>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,4 @@
parent=base
styles=dist/index.css
scripts=dist/index.js

@ -0,0 +1,71 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<@layout.registrationLayout script="dist/webAuthnAuthenticate.js"; section>
<#if section="title">
title
<#elseif section="header">
${kcSanitize(msg("webauthn-login-title"))?no_esc}
<#elseif section="form">
<div x-data="webAuthnAuthenticate">
<form action="${url.loginAction}" method="post" x-ref="webAuthnForm">
<input name="authenticatorData" type="hidden" x-ref="authenticatorDataInput" />
<input name="clientDataJSON" type="hidden" x-ref="clientDataJSONInput" />
<input name="credentialId" type="hidden" x-ref="credentialIdInput" />
<input name="error" type="hidden" x-ref="errorInput" />
<input name="signature" type="hidden" x-ref="signatureInput" />
<input name="userHandle" type="hidden" x-ref="userHandleInput" />
</form>
<#if authenticators??>
<form x-ref="authnSelectForm">
<#list authenticators.authenticators as authenticator>
<input value="${authenticator.credentialId}" type="hidden" />
</#list>
</form>
<#if shouldDisplayAuthenticators?? && shouldDisplayAuthenticators>
<#if authenticators.authenticators?size gt 1>
<p>${kcSanitize(msg("webauthn-available-authenticators"))?no_esc}</p>
</#if>
<#list authenticators.authenticators as authenticator>
<div>
<div class="font-medium">${kcSanitize(msg("${authenticator.label}"))?no_esc}</div>
<#if authenticator.transports?? && authenticator.transports.displayNameProperties?has_content>
<div>
<#list authenticator.transports.displayNameProperties as nameProperty>
<span>${kcSanitize(msg("${nameProperty!}"))?no_esc}</span>
<#if nameProperty?has_next>
<span>, </span>
</#if>
</#list>
</div>
</#if>
<div class="text-sm">
<span>${kcSanitize(msg("webauthn-createdAt-label"))?no_esc}</span>
<span>${kcSanitize(authenticator.createdAt)?no_esc}</span>
</div>
</div>
</#list>
</#if>
</#if>
<@buttonGroup.kw>
<@button.kw @click="webAuthnAuthenticate" color="primary" type="button">
${kcSanitize(msg("webauthn-doAuthenticate"))}
</@button.kw>
</@buttonGroup.kw>
</div>
</#if>
</@layout.registrationLayout>
<script>
document.addEventListener('alpine:init', () => {
Alpine.store('webAuthnAuthenticate', {
challenge: '${challenge}',
createTimeout: '${createTimeout}',
isUserIdentified: '${isUserIdentified}',
rpId: '${rpId}',
unsupportedBrowserText: '${msg("webauthn-unsupported-browser-text")?no_esc}',
userVerification: '${userVerification}',
})
})
</script>

@ -0,0 +1,34 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<@layout.registrationLayout displayMessage=true; section>
<#if section="header">
${kcSanitize(msg("webauthn-error-title"))?no_esc}
<#elseif section="form">
<div x-data>
<form action="${url.loginAction}" method="post" x-ref="errorCredentialForm">
<input name="authenticationExecution" type="hidden" x-ref="executionValueInput" />
<input name="isSetRetry" type="hidden" x-ref="isSetRetryInput" />
</form>
<@buttonGroup.kw>
<@button.kw
@click="$refs.executionValueInput.value = '${execution}'; $refs.isSetRetryInput.value = 'retry'; $refs.errorCredentialForm.submit()"
color="primary"
name="try-again"
tabindex="4"
type="button"
>
${kcSanitize(msg("doTryAgain"))?no_esc}
</@button.kw>
<#if isAppInitiatedAction??>
<form action="${url.loginAction}" method="post">
<@button.kw color="secondary" name="cancel-aia" type="submit" value="true">
${msg("doCancel")}
</@button.kw>
</form>
</#if>
</@buttonGroup.kw>
</div>
</#if>
</@layout.registrationLayout>

@ -0,0 +1,54 @@
<#import "template.ftl" as layout>
<#import "components/atoms/button.ftl" as button>
<#import "components/atoms/button-group.ftl" as buttonGroup>
<@layout.registrationLayout script="dist/webAuthnRegister.js"; section>
<#if section="title">
title
<#elseif section="header">
${kcSanitize(msg("webauthn-registration-title"))?no_esc}
<#elseif section="form">
<div x-data="webAuthnRegister">
<form action="${url.loginAction}" method="post" x-ref="registerForm">
<input name="attestationObject" type="hidden" x-ref="attestationObjectInput" />
<input name="authenticatorLabel" type="hidden" x-ref="authenticatorLabelInput" />
<input name="clientDataJSON" type="hidden" x-ref="clientDataJSONInput" />
<input name="error" type="hidden" x-ref="errorInput" />
<input name="publicKeyCredentialId" type="hidden" x-ref="publicKeyCredentialIdInput" />
<input name="transports" type="hidden" x-ref="transportsInput" />
</form>
<@buttonGroup.kw>
<@button.kw @click="registerSecurityKey" color="primary" type="submit">
${msg("doRegister")}
</@button.kw>
<#if !isSetRetry?has_content && isAppInitiatedAction?has_content>
<form action="${url.loginAction}" method="post">
<@button.kw color="secondary" name="cancel-aia" type="submit" value="true">
${msg("doCancel")}
</@button.kw>
</form>
</#if>
</@buttonGroup.kw>
</div>
</#if>
</@layout.registrationLayout>
<script>
document.addEventListener('alpine:init', () => {
Alpine.store('webAuthnRegister', {
attestationConveyancePreference: '${attestationConveyancePreference}',
authenticatorAttachment: '${authenticatorAttachment}',
challenge: '${challenge}',
createTimeout: '${createTimeout}',
excludeCredentialIds: '${excludeCredentialIds}',
requireResidentKey: '${requireResidentKey}',
rpEntityName: '${rpEntityName}',
rpId: '${rpId}',
signatureAlgorithms: '${signatureAlgorithms}',
unsupportedBrowserText: '${msg("webauthn-unsupported-browser-text")?no_esc}',
userId: '${userid}',
userVerificationRequirement: '${userVerificationRequirement}',
username: '${username}',
})
})
</script>
Loading…
Cancel
Save