webauthn-register.ftl 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. <#import "template.ftl" as layout>
  2. <@layout.registrationLayout; section>
  3. <#if section = "title">
  4. title
  5. <#elseif section = "header">
  6. <span class="${properties.kcWebAuthnKeyIcon}"></span>
  7. ${kcSanitize(msg("webauthn-registration-title"))?no_esc}
  8. <#elseif section = "form">
  9. <form id="register" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
  10. <div class="${properties.kcFormGroupClass!}">
  11. <input type="hidden" id="clientDataJSON" name="clientDataJSON"/>
  12. <input type="hidden" id="attestationObject" name="attestationObject"/>
  13. <input type="hidden" id="publicKeyCredentialId" name="publicKeyCredentialId"/>
  14. <input type="hidden" id="authenticatorLabel" name="authenticatorLabel"/>
  15. <input type="hidden" id="error" name="error"/>
  16. </div>
  17. </form>
  18. <script type="text/javascript" src="${url.resourcesCommonPath}/node_modules/jquery/dist/jquery.min.js"></script>
  19. <script type="text/javascript" src="${url.resourcesPath}/js/base64url.js"></script>
  20. <script type="text/javascript">
  21. function registerSecurityKey() {
  22. // Check if WebAuthn is supported by this browser
  23. if (!window.PublicKeyCredential) {
  24. $("#error").val("${msg("webauthn-unsupported-browser-text")?no_esc}");
  25. $("#register").submit();
  26. return;
  27. }
  28. // mandatory parameters
  29. let challenge = "${challenge}";
  30. let userid = "${userid}";
  31. let username = "${username}";
  32. let signatureAlgorithms = "${signatureAlgorithms}";
  33. let pubKeyCredParams = getPubKeyCredParams(signatureAlgorithms);
  34. let rpEntityName = "${rpEntityName}";
  35. let rp = {name: rpEntityName};
  36. let publicKey = {
  37. challenge: base64url.decode(challenge, {loose: true}),
  38. rp: rp,
  39. user: {
  40. id: base64url.decode(userid, {loose: true}),
  41. name: username,
  42. displayName: username
  43. },
  44. pubKeyCredParams: pubKeyCredParams,
  45. };
  46. // optional parameters
  47. let rpId = "${rpId}";
  48. publicKey.rp.id = rpId;
  49. let attestationConveyancePreference = "${attestationConveyancePreference}";
  50. if (attestationConveyancePreference !== 'not specified') publicKey.attestation = attestationConveyancePreference;
  51. let authenticatorSelection = {};
  52. let isAuthenticatorSelectionSpecified = false;
  53. let authenticatorAttachment = "${authenticatorAttachment}";
  54. if (authenticatorAttachment !== 'not specified') {
  55. authenticatorSelection.authenticatorAttachment = authenticatorAttachment;
  56. isAuthenticatorSelectionSpecified = true;
  57. }
  58. let requireResidentKey = "${requireResidentKey}";
  59. if (requireResidentKey !== 'not specified') {
  60. if (requireResidentKey === 'Yes')
  61. authenticatorSelection.requireResidentKey = true;
  62. else
  63. authenticatorSelection.requireResidentKey = false;
  64. isAuthenticatorSelectionSpecified = true;
  65. }
  66. let userVerificationRequirement = "${userVerificationRequirement}";
  67. if (userVerificationRequirement !== 'not specified') {
  68. authenticatorSelection.userVerification = userVerificationRequirement;
  69. isAuthenticatorSelectionSpecified = true;
  70. }
  71. if (isAuthenticatorSelectionSpecified) publicKey.authenticatorSelection = authenticatorSelection;
  72. let createTimeout = ${createTimeout};
  73. if (createTimeout !== 0) publicKey.timeout = createTimeout * 1000;
  74. let excludeCredentialIds = "${excludeCredentialIds}";
  75. let excludeCredentials = getExcludeCredentials(excludeCredentialIds);
  76. if (excludeCredentials.length > 0) publicKey.excludeCredentials = excludeCredentials;
  77. navigator.credentials.create({publicKey})
  78. .then(function (result) {
  79. window.result = result;
  80. let clientDataJSON = result.response.clientDataJSON;
  81. let attestationObject = result.response.attestationObject;
  82. let publicKeyCredentialId = result.rawId;
  83. $("#clientDataJSON").val(base64url.encode(new Uint8Array(clientDataJSON), {pad: false}));
  84. $("#attestationObject").val(base64url.encode(new Uint8Array(attestationObject), {pad: false}));
  85. $("#publicKeyCredentialId").val(base64url.encode(new Uint8Array(publicKeyCredentialId), {pad: false}));
  86. let initLabel = "WebAuthn Authenticator (Default Label)";
  87. let labelResult = window.prompt("Please input your registered authenticator's label", initLabel);
  88. if (labelResult === null) labelResult = initLabel;
  89. $("#authenticatorLabel").val(labelResult);
  90. $("#register").submit();
  91. })
  92. .catch(function (err) {
  93. $("#error").val(err);
  94. $("#register").submit();
  95. });
  96. }
  97. function getPubKeyCredParams(signatureAlgorithms) {
  98. let pubKeyCredParams = [];
  99. if (signatureAlgorithms === "") {
  100. pubKeyCredParams.push({type: "public-key", alg: -7});
  101. return pubKeyCredParams;
  102. }
  103. let signatureAlgorithmsList = signatureAlgorithms.split(',');
  104. for (let i = 0; i < signatureAlgorithmsList.length; i++) {
  105. pubKeyCredParams.push({
  106. type: "public-key",
  107. alg: signatureAlgorithmsList[i]
  108. });
  109. }
  110. return pubKeyCredParams;
  111. }
  112. function getExcludeCredentials(excludeCredentialIds) {
  113. let excludeCredentials = [];
  114. if (excludeCredentialIds === "") return excludeCredentials;
  115. let excludeCredentialIdsList = excludeCredentialIds.split(',');
  116. for (let i = 0; i < excludeCredentialIdsList.length; i++) {
  117. excludeCredentials.push({
  118. type: "public-key",
  119. id: base64url.decode(excludeCredentialIdsList[i],
  120. {loose: true})
  121. });
  122. }
  123. return excludeCredentials;
  124. }
  125. </script>
  126. <input type="submit"
  127. class="${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
  128. id="registerWebAuthn" value="${msg("doRegister")}" onclick="registerSecurityKey()"/>
  129. <#if !isSetRetry?has_content && isAppInitiatedAction?has_content>
  130. <form action="${url.loginAction}" class="${properties.kcFormClass!}" id="kc-webauthn-settings-form"
  131. method="post">
  132. <button type="submit"
  133. class="${properties.kcButtonClass!} ${properties.kcButtonDefaultClass!} ${properties.kcButtonBlockClass!} ${properties.kcButtonLargeClass!}"
  134. id="cancelWebAuthnAIA" name="cancel-aia" value="true">${msg("doCancel")}
  135. </button>
  136. </form>
  137. </#if>
  138. </#if>
  139. </@layout.registrationLayout>