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"
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
,
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
,
66 if (!mRawIdCachedObj
) {
67 mRawIdCachedObj
= ArrayBuffer::Create(aCx
, mRawId
, aRv
);
72 aValue
.set(mRawIdCachedObj
);
75 void PublicKeyCredential::GetAuthenticatorAttachment(
76 DOMString
& aAuthenticatorAttachment
) {
77 if (mAuthenticatorAttachment
.isSome()) {
78 aAuthenticatorAttachment
.SetKnownLiveString(mAuthenticatorAttachment
.ref());
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
);
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
;
114 already_AddRefed
<Promise
>
115 PublicKeyCredential::IsUserVerifyingPlatformAuthenticatorAvailable(
116 GlobalObject
& aGlobal
, ErrorResult
& aError
) {
117 nsCOMPtr
<nsPIDOMWindowInner
> window
=
118 do_QueryInterface(aGlobal
.GetAsSupports());
120 aError
.Throw(NS_ERROR_FAILURE
);
124 RefPtr
<WebAuthnManager
> manager
=
125 window
->Navigator()->Credentials()->GetWebAuthnManager();
126 return manager
->IsUVPAA(aGlobal
, aError
);
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()) {
137 #if defined(MOZ_WIDGET_ANDROID)
138 promise
->MaybeResolve(false);
140 promise
->MaybeResolve(
141 StaticPrefs::security_webauthn_enable_conditional_mediation());
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
;
159 mAttestationResponse
->ToJSON(json
.mResponse
, aError
);
160 if (aError
.Failed()) {
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
);
180 } else if (mAssertionResponse
) {
181 AuthenticationResponseJSON json
;
184 mAssertionResponse
->ToJSON(json
.mResponse
, aError
);
185 if (aError
.Failed()) {
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
);
202 MOZ_ASSERT_UNREACHABLE(
203 "either mAttestationResponse or mAssertionResponse should be set");
205 JS::Rooted
<JSObject
*> result(aCx
, &value
.toObject());
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
);
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");
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");
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
);
276 excludeCredential
->mType
= excludeCredentialJSON
.mType
;
277 if (!Base64DecodeToArrayBuffer(aGlobal
, excludeCredentialJSON
.mId
,
278 excludeCredential
->mId
.SetAsArrayBuffer(),
280 aRv
.ThrowEncodingError(
281 "could not decode excluded credential ID as urlsafe base64");
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");
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
);
341 allowCredential
->mType
= allowCredentialJSON
.mType
;
342 if (!Base64DecodeToArrayBuffer(aGlobal
, allowCredentialJSON
.mId
,
343 allowCredential
->mId
.SetAsArrayBuffer(),
345 aRv
.ThrowEncodingError(
346 "could not decode allowed credential ID as urlsafe base64");
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