Bug 1880216 - Migrate Fenix docs into Sphinx. r=owlish,geckoview-reviewers,android...
[gecko.git] / dom / webauthn / PublicKeyCredential.cpp
blob444d9742b3193c4750be4b2f150bb57fccbe2e03
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/Base64.h"
8 #include "mozilla/HoldDropJSObjects.h"
9 #include "mozilla/Preferences.h"
10 #include "mozilla/StaticPrefs_security.h"
11 #include "mozilla/dom/AuthenticatorResponse.h"
12 #include "mozilla/dom/CredentialsContainer.h"
13 #include "mozilla/dom/ChromeUtils.h"
14 #include "mozilla/dom/Navigator.h"
15 #include "mozilla/dom/Promise.h"
16 #include "mozilla/dom/PublicKeyCredential.h"
17 #include "mozilla/dom/WebAuthenticationBinding.h"
18 #include "mozilla/dom/WebAuthnManager.h"
19 #include "nsCycleCollectionParticipant.h"
21 #ifdef MOZ_WIDGET_ANDROID
22 # include "mozilla/MozPromise.h"
23 # include "mozilla/java/GeckoResultNatives.h"
24 # include "mozilla/java/WebAuthnTokenManagerWrappers.h"
25 #endif
27 namespace mozilla::dom {
29 NS_IMPL_CYCLE_COLLECTION_CLASS(PublicKeyCredential)
30 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PublicKeyCredential, Credential)
31 tmp->mRawIdCachedObj = nullptr;
32 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
34 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PublicKeyCredential, Credential)
35 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
36 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mRawIdCachedObj)
37 NS_IMPL_CYCLE_COLLECTION_TRACE_END
39 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PublicKeyCredential,
40 Credential)
41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAttestationResponse)
42 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAssertionResponse)
43 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
45 NS_IMPL_ADDREF_INHERITED(PublicKeyCredential, Credential)
46 NS_IMPL_RELEASE_INHERITED(PublicKeyCredential, Credential)
48 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PublicKeyCredential)
49 NS_INTERFACE_MAP_END_INHERITING(Credential)
51 PublicKeyCredential::PublicKeyCredential(nsPIDOMWindowInner* aParent)
52 : Credential(aParent), mRawIdCachedObj(nullptr) {
53 mozilla::HoldJSObjects(this);
56 PublicKeyCredential::~PublicKeyCredential() { mozilla::DropJSObjects(this); }
58 JSObject* PublicKeyCredential::WrapObject(JSContext* aCx,
59 JS::Handle<JSObject*> aGivenProto) {
60 return PublicKeyCredential_Binding::Wrap(aCx, this, aGivenProto);
63 void PublicKeyCredential::GetRawId(JSContext* aCx,
64 JS::MutableHandle<JSObject*> aValue,
65 ErrorResult& aRv) {
66 if (!mRawIdCachedObj) {
67 mRawIdCachedObj = ArrayBuffer::Create(aCx, mRawId, aRv);
68 if (aRv.Failed()) {
69 return;
72 aValue.set(mRawIdCachedObj);
75 void PublicKeyCredential::GetAuthenticatorAttachment(
76 DOMString& aAuthenticatorAttachment) {
77 if (mAuthenticatorAttachment.isSome()) {
78 aAuthenticatorAttachment.SetKnownLiveString(mAuthenticatorAttachment.ref());
79 } else {
80 aAuthenticatorAttachment.SetNull();
84 already_AddRefed<AuthenticatorResponse> PublicKeyCredential::Response() const {
85 if (mAttestationResponse) {
86 return do_AddRef(mAttestationResponse);
88 if (mAssertionResponse) {
89 return do_AddRef(mAssertionResponse);
91 return nullptr;
94 void PublicKeyCredential::SetRawId(const nsTArray<uint8_t>& aBuffer) {
95 mRawId.Assign(aBuffer);
98 void PublicKeyCredential::SetAuthenticatorAttachment(
99 const Maybe<nsString>& aAuthenticatorAttachment) {
100 mAuthenticatorAttachment = aAuthenticatorAttachment;
103 void PublicKeyCredential::SetAttestationResponse(
104 const RefPtr<AuthenticatorAttestationResponse>& aAttestationResponse) {
105 mAttestationResponse = aAttestationResponse;
108 void PublicKeyCredential::SetAssertionResponse(
109 const RefPtr<AuthenticatorAssertionResponse>& aAssertionResponse) {
110 mAssertionResponse = aAssertionResponse;
113 /* static */
114 already_AddRefed<Promise>
115 PublicKeyCredential::IsUserVerifyingPlatformAuthenticatorAvailable(
116 GlobalObject& aGlobal, ErrorResult& aError) {
117 nsCOMPtr<nsPIDOMWindowInner> window =
118 do_QueryInterface(aGlobal.GetAsSupports());
119 if (!window) {
120 aError.Throw(NS_ERROR_FAILURE);
121 return nullptr;
124 RefPtr<WebAuthnManager> manager =
125 window->Navigator()->Credentials()->GetWebAuthnManager();
126 return manager->IsUVPAA(aGlobal, aError);
129 /* static */
130 already_AddRefed<Promise> PublicKeyCredential::IsConditionalMediationAvailable(
131 GlobalObject& aGlobal, ErrorResult& aError) {
132 RefPtr<Promise> promise =
133 Promise::Create(xpc::CurrentNativeGlobal(aGlobal.Context()), aError);
134 if (aError.Failed()) {
135 return nullptr;
137 #if defined(MOZ_WIDGET_ANDROID)
138 promise->MaybeResolve(false);
139 #else
140 promise->MaybeResolve(
141 StaticPrefs::security_webauthn_enable_conditional_mediation());
142 #endif
143 return promise.forget();
146 void PublicKeyCredential::GetClientExtensionResults(
147 AuthenticationExtensionsClientOutputs& aResult) {
148 aResult = mClientExtensionOutputs;
151 void PublicKeyCredential::ToJSON(JSContext* aCx,
152 JS::MutableHandle<JSObject*> aRetval,
153 ErrorResult& aError) {
154 JS::Rooted<JS::Value> value(aCx);
155 if (mAttestationResponse) {
156 RegistrationResponseJSON json;
157 GetId(json.mId);
158 GetId(json.mRawId);
159 mAttestationResponse->ToJSON(json.mResponse, aError);
160 if (aError.Failed()) {
161 return;
163 if (mAuthenticatorAttachment.isSome()) {
164 json.mAuthenticatorAttachment.Construct();
165 json.mAuthenticatorAttachment.Value() = mAuthenticatorAttachment.ref();
167 if (mClientExtensionOutputs.mCredProps.WasPassed()) {
168 json.mClientExtensionResults.mCredProps.Construct(
169 mClientExtensionOutputs.mCredProps.Value());
171 if (mClientExtensionOutputs.mHmacCreateSecret.WasPassed()) {
172 json.mClientExtensionResults.mHmacCreateSecret.Construct(
173 mClientExtensionOutputs.mHmacCreateSecret.Value());
175 json.mType.Assign(u"public-key"_ns);
176 if (!ToJSValue(aCx, json, &value)) {
177 aError.StealExceptionFromJSContext(aCx);
178 return;
180 } else if (mAssertionResponse) {
181 AuthenticationResponseJSON json;
182 GetId(json.mId);
183 GetId(json.mRawId);
184 mAssertionResponse->ToJSON(json.mResponse, aError);
185 if (aError.Failed()) {
186 return;
188 if (mAuthenticatorAttachment.isSome()) {
189 json.mAuthenticatorAttachment.Construct();
190 json.mAuthenticatorAttachment.Value() = mAuthenticatorAttachment.ref();
192 if (mClientExtensionOutputs.mAppid.WasPassed()) {
193 json.mClientExtensionResults.mAppid.Construct(
194 mClientExtensionOutputs.mAppid.Value());
196 json.mType.Assign(u"public-key"_ns);
197 if (!ToJSValue(aCx, json, &value)) {
198 aError.StealExceptionFromJSContext(aCx);
199 return;
201 } else {
202 MOZ_ASSERT_UNREACHABLE(
203 "either mAttestationResponse or mAssertionResponse should be set");
205 JS::Rooted<JSObject*> result(aCx, &value.toObject());
206 aRetval.set(result);
209 void PublicKeyCredential::SetClientExtensionResultAppId(bool aResult) {
210 mClientExtensionOutputs.mAppid.Construct();
211 mClientExtensionOutputs.mAppid.Value() = aResult;
214 void PublicKeyCredential::SetClientExtensionResultCredPropsRk(bool aResult) {
215 mClientExtensionOutputs.mCredProps.Construct();
216 mClientExtensionOutputs.mCredProps.Value().mRk.Construct();
217 mClientExtensionOutputs.mCredProps.Value().mRk.Value() = aResult;
220 void PublicKeyCredential::SetClientExtensionResultHmacSecret(
221 bool aHmacCreateSecret) {
222 mClientExtensionOutputs.mHmacCreateSecret.Construct();
223 mClientExtensionOutputs.mHmacCreateSecret.Value() = aHmacCreateSecret;
226 bool Base64DecodeToArrayBuffer(GlobalObject& aGlobal, const nsAString& aString,
227 ArrayBuffer& aArrayBuffer, ErrorResult& aRv) {
228 JSContext* cx = aGlobal.Context();
229 JS::Rooted<JSObject*> result(cx);
230 Base64URLDecodeOptions options;
231 options.mPadding = Base64URLDecodePadding::Ignore;
232 mozilla::dom::ChromeUtils::Base64URLDecode(
233 aGlobal, NS_ConvertUTF16toUTF8(aString), options, &result, aRv);
234 if (aRv.Failed()) {
235 return false;
237 return aArrayBuffer.Init(result);
240 void PublicKeyCredential::ParseCreationOptionsFromJSON(
241 GlobalObject& aGlobal,
242 const PublicKeyCredentialCreationOptionsJSON& aOptions,
243 PublicKeyCredentialCreationOptions& aResult, ErrorResult& aRv) {
244 if (aOptions.mRp.mId.WasPassed()) {
245 aResult.mRp.mId.Construct(aOptions.mRp.mId.Value());
247 aResult.mRp.mName.Assign(aOptions.mRp.mName);
249 aResult.mUser.mName.Assign(aOptions.mUser.mName);
250 if (!Base64DecodeToArrayBuffer(aGlobal, aOptions.mUser.mId,
251 aResult.mUser.mId.SetAsArrayBuffer(), aRv)) {
252 aRv.ThrowEncodingError("could not decode user ID as urlsafe base64");
253 return;
255 aResult.mUser.mDisplayName.Assign(aOptions.mUser.mDisplayName);
257 if (!Base64DecodeToArrayBuffer(aGlobal, aOptions.mChallenge,
258 aResult.mChallenge.SetAsArrayBuffer(), aRv)) {
259 aRv.ThrowEncodingError("could not decode challenge as urlsafe base64");
260 return;
263 aResult.mPubKeyCredParams = aOptions.mPubKeyCredParams;
265 if (aOptions.mTimeout.WasPassed()) {
266 aResult.mTimeout.Construct(aOptions.mTimeout.Value());
269 for (const auto& excludeCredentialJSON : aOptions.mExcludeCredentials) {
270 PublicKeyCredentialDescriptor* excludeCredential =
271 aResult.mExcludeCredentials.AppendElement(fallible);
272 if (!excludeCredential) {
273 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
274 return;
276 excludeCredential->mType = excludeCredentialJSON.mType;
277 if (!Base64DecodeToArrayBuffer(aGlobal, excludeCredentialJSON.mId,
278 excludeCredential->mId.SetAsArrayBuffer(),
279 aRv)) {
280 aRv.ThrowEncodingError(
281 "could not decode excluded credential ID as urlsafe base64");
282 return;
284 if (excludeCredentialJSON.mTransports.WasPassed()) {
285 excludeCredential->mTransports.Construct(
286 excludeCredentialJSON.mTransports.Value());
290 if (aOptions.mAuthenticatorSelection.WasPassed()) {
291 aResult.mAuthenticatorSelection = aOptions.mAuthenticatorSelection.Value();
294 aResult.mAttestation = aOptions.mAttestation;
296 if (aOptions.mExtensions.WasPassed()) {
297 if (aOptions.mExtensions.Value().mAppid.WasPassed()) {
298 aResult.mExtensions.mAppid.Construct(
299 aOptions.mExtensions.Value().mAppid.Value());
301 if (aOptions.mExtensions.Value().mCredProps.WasPassed()) {
302 aResult.mExtensions.mCredProps.Construct(
303 aOptions.mExtensions.Value().mCredProps.Value());
305 if (aOptions.mExtensions.Value().mHmacCreateSecret.WasPassed()) {
306 aResult.mExtensions.mHmacCreateSecret.Construct(
307 aOptions.mExtensions.Value().mHmacCreateSecret.Value());
309 if (aOptions.mExtensions.Value().mMinPinLength.WasPassed()) {
310 aResult.mExtensions.mMinPinLength.Construct(
311 aOptions.mExtensions.Value().mMinPinLength.Value());
316 void PublicKeyCredential::ParseRequestOptionsFromJSON(
317 GlobalObject& aGlobal,
318 const PublicKeyCredentialRequestOptionsJSON& aOptions,
319 PublicKeyCredentialRequestOptions& aResult, ErrorResult& aRv) {
320 if (!Base64DecodeToArrayBuffer(aGlobal, aOptions.mChallenge,
321 aResult.mChallenge.SetAsArrayBuffer(), aRv)) {
322 aRv.ThrowEncodingError("could not decode challenge as urlsafe base64");
323 return;
326 if (aOptions.mTimeout.WasPassed()) {
327 aResult.mTimeout.Construct(aOptions.mTimeout.Value());
330 if (aOptions.mRpId.WasPassed()) {
331 aResult.mRpId.Construct(aOptions.mRpId.Value());
334 for (const auto& allowCredentialJSON : aOptions.mAllowCredentials) {
335 PublicKeyCredentialDescriptor* allowCredential =
336 aResult.mAllowCredentials.AppendElement(fallible);
337 if (!allowCredential) {
338 aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
339 return;
341 allowCredential->mType = allowCredentialJSON.mType;
342 if (!Base64DecodeToArrayBuffer(aGlobal, allowCredentialJSON.mId,
343 allowCredential->mId.SetAsArrayBuffer(),
344 aRv)) {
345 aRv.ThrowEncodingError(
346 "could not decode allowed credential ID as urlsafe base64");
347 return;
349 if (allowCredentialJSON.mTransports.WasPassed()) {
350 allowCredential->mTransports.Construct(
351 allowCredentialJSON.mTransports.Value());
355 aResult.mUserVerification = aOptions.mUserVerification;
357 if (aOptions.mExtensions.WasPassed()) {
358 if (aOptions.mExtensions.Value().mAppid.WasPassed()) {
359 aResult.mExtensions.mAppid.Construct(
360 aOptions.mExtensions.Value().mAppid.Value());
362 if (aOptions.mExtensions.Value().mCredProps.WasPassed()) {
363 aResult.mExtensions.mCredProps.Construct(
364 aOptions.mExtensions.Value().mCredProps.Value());
366 if (aOptions.mExtensions.Value().mHmacCreateSecret.WasPassed()) {
367 aResult.mExtensions.mHmacCreateSecret.Construct(
368 aOptions.mExtensions.Value().mHmacCreateSecret.Value());
370 if (aOptions.mExtensions.Value().mMinPinLength.WasPassed()) {
371 aResult.mExtensions.mMinPinLength.Construct(
372 aOptions.mExtensions.Value().mMinPinLength.Value());
377 } // namespace mozilla::dom