LibWeb: Stub for Credential Management API

Stub out basic Credential Management APIs and import IDL tests.

Spec: https://w3c.github.io/webappsec-credential-management/
This commit is contained in:
devgianlu 2025-01-30 22:29:26 +01:00 committed by Andrew Kaster
parent e14511468f
commit da9eaf8788
Notes: github-actions[bot] 2025-02-05 20:19:50 +00:00
20 changed files with 534 additions and 0 deletions

View file

@ -36,6 +36,10 @@ set(SOURCES
Clipboard/ClipboardItem.cpp
Compression/CompressionStream.cpp
Compression/DecompressionStream.cpp
CredentialManagement/Credential.cpp
CredentialManagement/CredentialsContainer.cpp
CredentialManagement/FederatedCredential.cpp
CredentialManagement/PasswordCredential.cpp
Crypto/Crypto.cpp
Crypto/CryptoAlgorithms.cpp
Crypto/CryptoBindings.cpp

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CredentialManagement/Credential.h>
namespace Web::CredentialManagement {
// https://www.w3.org/TR/credential-management-1/#dom-credential-isconditionalmediationavailable
GC::Ref<WebIDL::Promise> Credential::is_conditional_mediation_available(JS::VM& vm)
{
auto* realm = vm.current_realm();
return WebIDL::create_rejected_promise_from_exception(*realm, vm.throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "is conditional mediation available"sv));
}
// https://www.w3.org/TR/credential-management-1/#dom-credential-willrequestconditionalcreation
GC::Ref<WebIDL::Promise> Credential::will_request_conditional_creation(JS::VM& vm)
{
auto* realm = vm.current_realm();
return WebIDL::create_rejected_promise_from_exception(*realm, vm.throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "will request conditional creation"sv));
}
Credential::~Credential() { }
Credential::Credential(JS::Realm& realm)
: PlatformObject(realm)
{
}
void Credential::initialize(JS::Realm& realm)
{
Base::initialize(realm);
WEB_SET_PROTOTYPE_FOR_INTERFACE(Credential);
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Forward.h>
#include <LibWeb/Bindings/CredentialPrototype.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/WebIDL/Promise.h>
namespace Web::CredentialManagement {
class Credential : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(Credential, Bindings::PlatformObject);
GC_DECLARE_ALLOCATOR(Credential);
public:
[[nodiscard]] static GC::Ref<Credential> create(JS::Realm&);
static GC::Ref<WebIDL::Promise> is_conditional_mediation_available(JS::VM&);
static GC::Ref<WebIDL::Promise> will_request_conditional_creation(JS::VM&);
virtual ~Credential() override;
String const& id() { return m_id; }
String const& name() { return m_name; }
String const& icon_url() { return m_icon_url; }
virtual String type() = 0;
protected:
explicit Credential(JS::Realm&);
virtual void initialize(JS::Realm&) override;
String m_id;
String m_name;
String m_icon_url;
};
struct CredentialData {
String id;
};
}

View file

@ -0,0 +1,24 @@
[Exposed=Window, SecureContext]
interface Credential {
readonly attribute USVString id;
readonly attribute DOMString type;
static Promise<boolean> isConditionalMediationAvailable();
static Promise<undefined> willRequestConditionalCreation();
};
[SecureContext]
interface mixin CredentialUserData {
readonly attribute USVString name;
readonly attribute USVString iconURL;
};
dictionary CredentialData {
required USVString id;
};
enum CredentialMediationRequirement {
"silent",
"optional",
"conditional",
"required"
};

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/CredentialManagement/CredentialsContainer.h>
namespace Web::CredentialManagement {
GC_DEFINE_ALLOCATOR(CredentialsContainer);
GC::Ref<CredentialsContainer> CredentialsContainer::create(JS::Realm& realm)
{
return realm.create<CredentialsContainer>(realm);
}
CredentialsContainer::~CredentialsContainer() { }
// https://www.w3.org/TR/credential-management-1/#dom-credentialscontainer-get
GC::Ref<WebIDL::Promise> CredentialsContainer::get(CredentialRequestOptions const&)
{
auto* realm = vm().current_realm();
return WebIDL::create_rejected_promise_from_exception(*realm, vm().throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "get"sv));
}
// https://www.w3.org/TR/credential-management-1/#dom-credentialscontainer-store
GC::Ref<WebIDL::Promise> CredentialsContainer::store(Credential const&)
{
auto* realm = vm().current_realm();
return WebIDL::create_rejected_promise_from_exception(*realm, vm().throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "store"sv));
}
// https://www.w3.org/TR/credential-management-1/#dom-credentialscontainer-create
GC::Ref<WebIDL::Promise> CredentialsContainer::create(CredentialCreationOptions const&)
{
auto* realm = vm().current_realm();
return WebIDL::create_rejected_promise_from_exception(*realm, vm().throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "create"sv));
}
// https://www.w3.org/TR/credential-management-1/#dom-credentialscontainer-preventsilentaccess
GC::Ref<WebIDL::Promise> CredentialsContainer::prevent_silent_access()
{
auto* realm = vm().current_realm();
return WebIDL::create_rejected_promise_from_exception(*realm, vm().throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "prevent silent access"sv));
}
CredentialsContainer::CredentialsContainer(JS::Realm& realm)
: PlatformObject(realm)
{
}
void CredentialsContainer::initialize(JS::Realm& realm)
{
Base::initialize(realm);
WEB_SET_PROTOTYPE_FOR_INTERFACE(CredentialsContainer);
}
}

View file

@ -0,0 +1,55 @@
/*
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Optional.h>
#include <LibJS/Forward.h>
#include <LibWeb/Bindings/CredentialsContainerPrototype.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/CredentialManagement/Credential.h>
#include <LibWeb/CredentialManagement/FederatedCredential.h>
#include <LibWeb/CredentialManagement/PasswordCredential.h>
#include <LibWeb/DOM/AbortSignal.h>
namespace Web::CredentialManagement {
class CredentialsContainer final : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(CredentialsContainer, Bindings::PlatformObject);
GC_DECLARE_ALLOCATOR(CredentialsContainer);
public:
[[nodiscard]] static GC::Ref<CredentialsContainer> create(JS::Realm&);
virtual ~CredentialsContainer() override;
GC::Ref<WebIDL::Promise> get(CredentialRequestOptions const& options);
GC::Ref<WebIDL::Promise> store(Credential const& credential);
GC::Ref<WebIDL::Promise> create(CredentialCreationOptions const& options);
GC::Ref<WebIDL::Promise> prevent_silent_access();
private:
explicit CredentialsContainer(JS::Realm&);
virtual void initialize(JS::Realm&) override;
};
struct CredentialRequestOptions {
Bindings::CredentialMediationRequirement mediation { Bindings::CredentialMediationRequirement::Optional };
GC::Ptr<DOM::AbortSignal> signal;
Optional<bool> password;
Optional<FederatedCredentialRequestOptions> federated;
};
struct CredentialCreationOptions {
Bindings::CredentialMediationRequirement mediation { Bindings::CredentialMediationRequirement::Optional };
GC::Ptr<DOM::AbortSignal> signal;
Optional<PasswordCredentialInit> password;
Optional<FederatedCredentialInit> federated;
};
}

View file

@ -0,0 +1,21 @@
#import <CredentialManagement/Credential.idl>
#import <CredentialManagement/FederatedCredential.idl>
#import <CredentialManagement/PasswordCredential.idl>
[Exposed=Window, SecureContext]
interface CredentialsContainer {
Promise<Credential?> get(optional CredentialRequestOptions options = {});
Promise<undefined> store(Credential credential);
Promise<Credential?> create(optional CredentialCreationOptions options = {});
Promise<undefined> preventSilentAccess();
};
dictionary CredentialRequestOptions {
CredentialMediationRequirement mediation = "optional";
AbortSignal signal;
};
dictionary CredentialCreationOptions {
CredentialMediationRequirement mediation = "optional";
AbortSignal signal;
};

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CredentialManagement/FederatedCredential.h>
namespace Web::CredentialManagement {
GC_DEFINE_ALLOCATOR(FederatedCredential);
GC::Ref<FederatedCredential> FederatedCredential::create(JS::Realm& realm)
{
return realm.create<FederatedCredential>(realm);
}
// https://www.w3.org/TR/credential-management-1/#dom-federatedcredential-federatedcredential
WebIDL::ExceptionOr<GC::Ref<FederatedCredential>> FederatedCredential::construct_impl(JS::Realm& realm, FederatedCredentialInit const&)
{
return realm.vm().throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "construct"sv);
}
FederatedCredential::~FederatedCredential()
{
}
FederatedCredential::FederatedCredential(JS::Realm& realm)
: Credential(realm)
{
}
void FederatedCredential::initialize(JS::Realm& realm)
{
Base::initialize(realm);
WEB_SET_PROTOTYPE_FOR_INTERFACE(FederatedCredential);
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Forward.h>
#include <LibWeb/Bindings/FederatedCredentialPrototype.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/CredentialManagement/Credential.h>
namespace Web::CredentialManagement {
class FederatedCredential final : public Credential {
WEB_PLATFORM_OBJECT(FederatedCredential, Credential);
GC_DECLARE_ALLOCATOR(FederatedCredential);
public:
[[nodiscard]] static GC::Ref<FederatedCredential> create(JS::Realm&);
static WebIDL::ExceptionOr<GC::Ref<FederatedCredential>> construct_impl(JS::Realm&, FederatedCredentialInit const&);
virtual ~FederatedCredential() override;
String const& provider() { return m_provider; }
Optional<String> const& protocol() { return m_protocol; }
String type() override { return "federated"_string; }
private:
explicit FederatedCredential(JS::Realm&);
virtual void initialize(JS::Realm&) override;
String m_provider;
Optional<String> m_protocol;
};
struct FederatedCredentialRequestOptions {
Optional<Vector<String>> providers;
Optional<Vector<String>> protocols;
};
struct FederatedCredentialInit : CredentialData {
Optional<String> name;
Optional<String> icon_url;
String origin;
String provider;
Optional<String> protocol;
};
}

View file

@ -0,0 +1,30 @@
#import <CredentialManagement/Credential.idl>
[Exposed=Window, SecureContext]
interface FederatedCredential : Credential {
constructor(FederatedCredentialInit data);
readonly attribute USVString provider;
readonly attribute DOMString? protocol;
};
FederatedCredential includes CredentialUserData;
dictionary FederatedCredentialRequestOptions {
sequence<USVString> providers;
sequence<DOMString> protocols;
};
partial dictionary CredentialRequestOptions {
FederatedCredentialRequestOptions federated;
};
dictionary FederatedCredentialInit : CredentialData {
USVString name;
USVString iconURL;
required USVString origin;
required USVString provider;
DOMString protocol;
};
partial dictionary CredentialCreationOptions {
FederatedCredentialInit federated;
};

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/CredentialManagement/PasswordCredential.h>
namespace Web::CredentialManagement {
GC_DEFINE_ALLOCATOR(PasswordCredential);
GC::Ref<PasswordCredential> PasswordCredential::create(JS::Realm& realm)
{
return realm.create<PasswordCredential>(realm);
}
// https://www.w3.org/TR/credential-management-1/#dom-passwordcredential-passwordcredential
WebIDL::ExceptionOr<GC::Ref<PasswordCredential>> PasswordCredential::construct_impl(JS::Realm& realm, HTML::HTMLFormElement const&)
{
return realm.vm().throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "construct"sv);
}
// https://www.w3.org/TR/credential-management-1/#dom-passwordcredential-passwordcredential-data
WebIDL::ExceptionOr<GC::Ref<PasswordCredential>> PasswordCredential::construct_impl(JS::Realm& realm, PasswordCredentialData const&)
{
return realm.vm().throw_completion<JS::InternalError>(JS::ErrorType::NotImplemented, "construct"sv);
}
PasswordCredential::~PasswordCredential()
{
}
PasswordCredential::PasswordCredential(JS::Realm& realm)
: Credential(realm)
{
}
void PasswordCredential::initialize(JS::Realm& realm)
{
Base::initialize(realm);
WEB_SET_PROTOTYPE_FOR_INTERFACE(PasswordCredential);
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2025, Altomani Gianluca <altomanigianluca@gmail.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibJS/Forward.h>
#include <LibWeb/Bindings/PasswordCredentialPrototype.h>
#include <LibWeb/Bindings/PlatformObject.h>
#include <LibWeb/CredentialManagement/Credential.h>
#include <LibWeb/HTML/HTMLFormElement.h>
namespace Web::CredentialManagement {
class PasswordCredential final : public Credential {
WEB_PLATFORM_OBJECT(PasswordCredential, Credential);
GC_DECLARE_ALLOCATOR(PasswordCredential);
public:
[[nodiscard]] static GC::Ref<PasswordCredential> create(JS::Realm&);
static WebIDL::ExceptionOr<GC::Ref<PasswordCredential>> construct_impl(JS::Realm&, HTML::HTMLFormElement const&);
static WebIDL::ExceptionOr<GC::Ref<PasswordCredential>> construct_impl(JS::Realm&, PasswordCredentialData const&);
virtual ~PasswordCredential() override;
String const& password() { return m_password; }
String type() override { return "password"_string; }
private:
explicit PasswordCredential(JS::Realm&);
virtual void initialize(JS::Realm&) override;
// TODO: Use Core::SecretString when it comes back
String m_password;
};
struct PasswordCredentialData : CredentialData {
Optional<String> name;
Optional<String> icon_url;
String origin;
String password;
};
using PasswordCredentialInit = Variant<PasswordCredentialData, GC::Root<HTML::HTMLFormElement>>;
}

View file

@ -0,0 +1,26 @@
#import <CredentialManagement/Credential.idl>
[Exposed=Window, SecureContext]
interface PasswordCredential : Credential {
constructor(HTMLFormElement form);
constructor(PasswordCredentialData data);
readonly attribute USVString password;
};
PasswordCredential includes CredentialUserData;
partial dictionary CredentialRequestOptions {
boolean password = false;
};
dictionary PasswordCredentialData : CredentialData {
USVString name;
USVString iconURL;
required USVString origin;
required USVString password;
};
typedef (PasswordCredentialData or HTMLFormElement) PasswordCredentialInit;
partial dictionary CredentialCreationOptions {
PasswordCredentialInit password;
};

View file

@ -99,6 +99,20 @@ struct ParsedCookie;
enum class Source;
}
namespace Web::CredentialManagement {
class Credential;
class CredentialsContainer;
class FederatedCredential;
class PasswordCredential;
struct CredentialData;
struct CredentialRequestOptions;
struct CredentialCreationOptions;
struct FederatedCredentialRequestOptions;
struct FederatedCredentialInit;
struct PasswordCredentialData;
}
namespace Web::Crypto {
class Crypto;
class SubtleCrypto;

View file

@ -11,6 +11,7 @@
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/NavigatorPrototype.h>
#include <LibWeb/Clipboard/Clipboard.h>
#include <LibWeb/CredentialManagement/CredentialsContainer.h>
#include <LibWeb/HTML/Navigator.h>
#include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/Window.h>
@ -68,6 +69,7 @@ void Navigator::visit_edges(Cell::Visitor& visitor)
visitor.visit(m_user_activation);
visitor.visit(m_service_worker_container);
visitor.visit(m_media_capabilities);
visitor.visit(m_credentials);
}
GC::Ref<MimeTypeArray> Navigator::mime_types()
@ -98,6 +100,13 @@ GC::Ref<UserActivation> Navigator::user_activation()
return *m_user_activation;
}
GC::Ref<CredentialManagement::CredentialsContainer> Navigator::credentials()
{
if (!m_credentials)
m_credentials = realm().create<CredentialManagement::CredentialsContainer>(realm());
return *m_credentials;
}
// https://w3c.github.io/pointerevents/#dom-navigator-maxtouchpoints
WebIDL::Long Navigator::max_touch_points()
{

View file

@ -54,6 +54,7 @@ public:
[[nodiscard]] GC::Ref<PluginArray> plugins();
[[nodiscard]] GC::Ref<Clipboard::Clipboard> clipboard();
[[nodiscard]] GC::Ref<UserActivation> user_activation();
[[nodiscard]] GC::Ref<CredentialManagement::CredentialsContainer> credentials();
Optional<FlyString> do_not_track() const;
@ -90,6 +91,9 @@ private:
// https://w3c.github.io/media-capabilities/#dom-navigator-mediacapabilities
GC::Ptr<MediaCapabilitiesAPI::MediaCapabilities> m_media_capabilities;
// https://w3c.github.io/webappsec-credential-management/#framework-credential-management
GC::Ptr<CredentialManagement::CredentialsContainer> m_credentials;
};
}

View file

@ -1,4 +1,5 @@
#import <Clipboard/Clipboard.idl>
#import <CredentialManagement/CredentialsContainer.idl>
#import <HTML/MimeTypeArray.idl>
#import <HTML/NavigatorBeacon.idl>
#import <HTML/NavigatorConcurrentHardware.idl>
@ -34,6 +35,9 @@ interface Navigator {
// https://w3c.github.io/media-capabilities/#dom-navigator-mediacapabilities
[SameObject] readonly attribute MediaCapabilities mediaCapabilities;
// https://w3c.github.io/webappsec-credential-management/#framework-credential-management
[SecureContext, SameObject] readonly attribute CredentialsContainer credentials;
};
// NOTE: As NavigatorContentUtils, NavigatorCookies, NavigatorPlugins, and NavigatorAutomationInformation

View file

@ -12,6 +12,10 @@ libweb_js_bindings(Clipboard/ClipboardEvent)
libweb_js_bindings(Clipboard/ClipboardItem)
libweb_js_bindings(Compression/CompressionStream)
libweb_js_bindings(Compression/DecompressionStream)
libweb_js_bindings(CredentialManagement/Credential)
libweb_js_bindings(CredentialManagement/CredentialsContainer)
libweb_js_bindings(CredentialManagement/FederatedCredential)
libweb_js_bindings(CredentialManagement/PasswordCredential)
libweb_js_bindings(Crypto/Crypto)
libweb_js_bindings(Crypto/CryptoKey)
libweb_js_bindings(Crypto/SubtleCrypto)

View file

@ -48,6 +48,8 @@ static bool is_platform_object(Type const& type)
"CanvasRenderingContext2D"sv,
"ClipboardItem"sv,
"CloseWatcher"sv,
"Credential"sv,
"CredentialsContainer"sv,
"CryptoKey"sv,
"DataTransfer"sv,
"Document"sv,
@ -56,6 +58,7 @@ static bool is_platform_object(Type const& type)
"DynamicsCompressorNode"sv,
"ElementInternals"sv,
"EventTarget"sv,
"FederatedCredential"sv,
"File"sv,
"FileList"sv,
"FontFace"sv,
@ -80,6 +83,7 @@ static bool is_platform_object(Type const& type)
"NavigationDestination"sv,
"NavigationHistoryEntry"sv,
"Node"sv,
"PasswordCredential"sv,
"Path2D"sv,
"PerformanceEntry"sv,
"PerformanceMark"sv,
@ -4424,6 +4428,7 @@ static void generate_using_namespace_definitions(SourceGenerator& generator)
// FIXME: This is a total hack until we can figure out the namespace for a given type somehow.
using namespace Web::Animations;
using namespace Web::Clipboard;
using namespace Web::CredentialManagement;
using namespace Web::Crypto;
using namespace Web::CSS;
using namespace Web::DOM;

View file

@ -71,6 +71,8 @@ CompositionEvent
CompressionStream
ConstantSourceNode
CountQueuingStrategy
Credential
CredentialsContainer
Crypto
CryptoKey
CustomElementRegistry
@ -111,6 +113,7 @@ EvalError
Event
EventSource
EventTarget
FederatedCredential
File
FileList
FileReader
@ -269,6 +272,7 @@ Option
OscillatorNode
PageTransitionEvent
PannerNode
PasswordCredential
Path2D
Performance
PerformanceEntry