| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 | 
							- /*
 
-  * Copyright 2002-2024 the original author or authors.
 
-  *
 
-  * Licensed under the Apache License, Version 2.0 (the "License");
 
-  * you may not use this file except in compliance with the License.
 
-  * You may obtain a copy of the License at
 
-  *
 
-  *      https://www.apache.org/licenses/LICENSE-2.0
 
-  *
 
-  * Unless required by applicable law or agreed to in writing, software
 
-  * distributed under the License is distributed on an "AS IS" BASIS,
 
-  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
-  * See the License for the specific language governing permissions and
 
-  * limitations under the License.
 
-  */
 
- "use strict";
 
- import base64url from "./base64url.js";
 
- import http from "./http.js";
 
- import abortController from "./abort-controller.js";
 
- async function isConditionalMediationAvailable() {
 
-   return !!(
 
-     window.PublicKeyCredential &&
 
-     window.PublicKeyCredential.isConditionalMediationAvailable &&
 
-     (await window.PublicKeyCredential.isConditionalMediationAvailable())
 
-   );
 
- }
 
- async function authenticate(headers, contextPath, useConditionalMediation) {
 
-   let options;
 
-   try {
 
-     const optionsResponse = await http.post(`${contextPath}/webauthn/authenticate/options`, headers);
 
-     if (!optionsResponse.ok) {
 
-       throw new Error(`HTTP ${optionsResponse.status}`);
 
-     }
 
-     options = await optionsResponse.json();
 
-   } catch (err) {
 
-     throw new Error(`Authentication failed. Could not fetch authentication options: ${err.message}`, { cause: err });
 
-   }
 
-   // FIXME: Use https://www.w3.org/TR/webauthn-3/#sctn-parseRequestOptionsFromJSON
 
-   const decodedAllowCredentials = !options.allowCredentials
 
-     ? []
 
-     : options.allowCredentials.map((cred) => ({
 
-         ...cred,
 
-         id: base64url.decode(cred.id),
 
-       }));
 
-   const decodedOptions = {
 
-     ...options,
 
-     allowCredentials: decodedAllowCredentials,
 
-     challenge: base64url.decode(options.challenge),
 
-   };
 
-   // Invoke the WebAuthn get() method.
 
-   const credentialOptions = {
 
-     publicKey: decodedOptions,
 
-     signal: abortController.newSignal(),
 
-   };
 
-   if (useConditionalMediation) {
 
-     // Request a conditional UI
 
-     credentialOptions.mediation = "conditional";
 
-   }
 
-   let cred;
 
-   try {
 
-     cred = await navigator.credentials.get(credentialOptions);
 
-   } catch (err) {
 
-     throw new Error(`Authentication failed. Call to navigator.credentials.get failed: ${err.message}`, { cause: err });
 
-   }
 
-   const { response, type: credType } = cred;
 
-   let userHandle;
 
-   if (response.userHandle) {
 
-     userHandle = base64url.encode(response.userHandle);
 
-   }
 
-   const body = {
 
-     id: cred.id,
 
-     rawId: base64url.encode(cred.rawId),
 
-     response: {
 
-       authenticatorData: base64url.encode(response.authenticatorData),
 
-       clientDataJSON: base64url.encode(response.clientDataJSON),
 
-       signature: base64url.encode(response.signature),
 
-       userHandle,
 
-     },
 
-     credType,
 
-     clientExtensionResults: cred.getClientExtensionResults(),
 
-     authenticatorAttachment: cred.authenticatorAttachment,
 
-   };
 
-   let authenticationResponse;
 
-   try {
 
-     const authenticationCallResponse = await http.post(`${contextPath}/login/webauthn`, headers, body);
 
-     if (!authenticationCallResponse.ok) {
 
-       throw new Error(`HTTP ${authenticationCallResponse.status}`);
 
-     }
 
-     authenticationResponse = await authenticationCallResponse.json();
 
-     //   if (authenticationResponse && authenticationResponse.authenticated) {
 
-   } catch (err) {
 
-     throw new Error(`Authentication failed. Could not process the authentication request: ${err.message}`, {
 
-       cause: err,
 
-     });
 
-   }
 
-   if (!(authenticationResponse && authenticationResponse.authenticated && authenticationResponse.redirectUrl)) {
 
-     throw new Error(
 
-       `Authentication failed. Expected {"authenticated": true, "redirectUrl": "..."}, server responded with: ${JSON.stringify(authenticationResponse)}`,
 
-     );
 
-   }
 
-   return authenticationResponse.redirectUrl;
 
- }
 
- async function register(headers, contextPath, label) {
 
-   if (!label) {
 
-     throw new Error("Error: Passkey Label is required");
 
-   }
 
-   let options;
 
-   try {
 
-     const optionsResponse = await http.post(`${contextPath}/webauthn/register/options`, headers);
 
-     if (!optionsResponse.ok) {
 
-       throw new Error(`Server responded with HTTP ${optionsResponse.status}`);
 
-     }
 
-     options = await optionsResponse.json();
 
-   } catch (e) {
 
-     throw new Error(`Registration failed. Could not fetch registration options: ${e.message}`, { cause: e });
 
-   }
 
-   // FIXME: Use https://www.w3.org/TR/webauthn-3/#sctn-parseCreationOptionsFromJSON
 
-   const decodedExcludeCredentials = !options.excludeCredentials
 
-     ? []
 
-     : options.excludeCredentials.map((cred) => ({
 
-         ...cred,
 
-         id: base64url.decode(cred.id),
 
-       }));
 
-   const decodedOptions = {
 
-     ...options,
 
-     user: {
 
-       ...options.user,
 
-       id: base64url.decode(options.user.id),
 
-     },
 
-     challenge: base64url.decode(options.challenge),
 
-     excludeCredentials: decodedExcludeCredentials,
 
-   };
 
-   let credentialsContainer;
 
-   try {
 
-     credentialsContainer = await navigator.credentials.create({
 
-       publicKey: decodedOptions,
 
-       signal: abortController.newSignal(),
 
-     });
 
-   } catch (e) {
 
-     throw new Error(`Registration failed. Call to navigator.credentials.create failed: ${e.message}`, { cause: e });
 
-   }
 
-   // FIXME: Let response be credential.response. If response is not an instance of AuthenticatorAttestationResponse, abort the ceremony with a user-visible error. https://www.w3.org/TR/webauthn-3/#sctn-registering-a-new-credential
 
-   const { response } = credentialsContainer;
 
-   const credential = {
 
-     id: credentialsContainer.id,
 
-     rawId: base64url.encode(credentialsContainer.rawId),
 
-     response: {
 
-       attestationObject: base64url.encode(response.attestationObject),
 
-       clientDataJSON: base64url.encode(response.clientDataJSON),
 
-       transports: response.getTransports ? response.getTransports() : [],
 
-     },
 
-     type: credentialsContainer.type,
 
-     clientExtensionResults: credentialsContainer.getClientExtensionResults(),
 
-     authenticatorAttachment: credentialsContainer.authenticatorAttachment,
 
-   };
 
-   const registrationRequest = {
 
-     publicKey: {
 
-       credential: credential,
 
-       label: label,
 
-     },
 
-   };
 
-   let verificationJSON;
 
-   try {
 
-     const verificationResp = await http.post(`${contextPath}/webauthn/register`, headers, registrationRequest);
 
-     if (!verificationResp.ok) {
 
-       throw new Error(`HTTP ${verificationResp.status}`);
 
-     }
 
-     verificationJSON = await verificationResp.json();
 
-   } catch (e) {
 
-     throw new Error(`Registration failed. Could not process the registration request: ${e.message}`, { cause: e });
 
-   }
 
-   if (!(verificationJSON && verificationJSON.success)) {
 
-     throw new Error(`Registration failed. Server responded with: ${JSON.stringify(verificationJSON)}`);
 
-   }
 
- }
 
- export default {
 
-   authenticate,
 
-   register,
 
-   isConditionalMediationAvailable,
 
- };
 
 
  |