⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 251645 in webkit


Ignore:
Timestamp:
Oct 27, 2019, 3:53:33 PM (7 years ago)
Author:
jiewen_tan@apple.com
Message:

[WebAuthn] Warn users when multiple NFC tags present
https://bugs.webkit.org/show_bug.cgi?id=200932
<rdar://problem/54890736>

Reviewed by Brent Fulgham.

Source/WebCore:

Covered by new tests in existing test file.

  • testing/MockWebAuthenticationConfiguration.h:

(WebCore::MockWebAuthenticationConfiguration::NfcConfiguration::encode const):
(WebCore::MockWebAuthenticationConfiguration::NfcConfiguration::decode):

  • testing/MockWebAuthenticationConfiguration.idl:

Adds a new test option.

Source/WebKit:

This patch utilizes -[_WKWebAuthenticationPanelDelegate panel:updateWebAuthenticationPanel:] to
inform clients about multiple physical tags are presenting such that clients can instruct users
to select only one of them physically. Given a physical tag could have multiple different
interfaces, which NearField will treat them into different NFTags, the tagID is then used to
identify if there are actually multiple physical tags.

This patch also adds the ability to restart polling of a partiuclar NFReaderSession to NfcConnection
and the ability to restart the whole session to NfcService. The former is used to recover from errors
in the discovery stages, and the latter is used to recover from errors returned from authenticators
in the request stages. For the latter, given NfcConnection is not awared of the syntax of FIDO2/U2F
protocol, and CtapAuthenticator/U2fAuthenticator are not awared the transport of the underneath driver.
A generic restartDiscovery process is added to each service and it is up to the actual service to
implement the actual process such that AuthenticatorManager can arbitrarily call it after exceptions
are returned to restart the whole NFC session. To achieve restartDiscovery, NfcConnection is made
RefCounted as well such that both the NfcService and the CtapNfcDriver could hold it at the same time.
CtapNfcDriver uses the connection to complete requests as before while NfcService has the new capability
to use it to stop the current session when restartDiscovery kicks off.

  • Platform/spi/Cocoa/NearFieldSPI.h:
  • UIProcess/WebAuthentication/AuthenticatorManager.cpp:

(WebKit::AuthenticatorManager::serviceStatusUpdated):
(WebKit::AuthenticatorManager::respondReceived):
(WebKit::AuthenticatorManager::restartDiscovery):

  • UIProcess/WebAuthentication/AuthenticatorManager.h:
  • UIProcess/WebAuthentication/AuthenticatorTransportService.cpp:

(WebKit::AuthenticatorTransportService::startDiscovery):
(WebKit::AuthenticatorTransportService::restartDiscovery):

  • UIProcess/WebAuthentication/AuthenticatorTransportService.h:

(WebKit::AuthenticatorTransportService::restartDiscoveryInternal):

  • UIProcess/WebAuthentication/Cocoa/NfcConnection.h:
  • UIProcess/WebAuthentication/Cocoa/NfcConnection.mm:

(WebKit::NfcConnection::create):
(WebKit::NfcConnection::NfcConnection):
(WebKit::NfcConnection::~NfcConnection):
(WebKit::NfcConnection::stop const):
(WebKit::NfcConnection::didDetectTags):
(WebKit::NfcConnection::restartPolling):
(WebKit::NfcConnection::startPolling):
(WebKit::NfcConnection::didDetectTags const): Deleted.

  • UIProcess/WebAuthentication/Cocoa/NfcService.h:
  • UIProcess/WebAuthentication/Cocoa/NfcService.mm:

(WebKit::NfcService::NfcService):
(WebKit::NfcService::didConnectTag):
(WebKit::NfcService::didDetectMultipleTags const):
(WebKit::NfcService::setConnection):
(WebKit::NfcService::restartDiscoveryInternal):
(WebKit::NfcService::platformStartDiscovery):
(WebKit::NfcService::setDriver): Deleted.

  • UIProcess/WebAuthentication/Mock/MockNfcService.h:
  • UIProcess/WebAuthentication/Mock/MockNfcService.mm:

(-[WKMockNFTag tagID]):
(-[WKMockNFTag initWithNFTag:]):
(-[WKMockNFTag dealloc]):
(-[WKMockNFTag initWithType:]):
(-[WKMockNFTag initWithType:tagID:]):
(WebKit::MockNfcService::receiveStopPolling):
(WebKit::MockNfcService::receiveStartPolling):
(WebKit::MockNfcService::platformStartDiscovery):
(WebKit::MockNfcService::detectTags):
(WebKit::MockNfcService::detectTags const): Deleted.

  • UIProcess/WebAuthentication/fido/CtapNfcDriver.cpp:

(WebKit::CtapNfcDriver::CtapNfcDriver):

  • UIProcess/WebAuthentication/fido/CtapNfcDriver.h:
  • UIProcess/WebAuthentication/fido/U2fAuthenticator.cpp:

Tools:

  • TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
  • TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm:

(-[TestWebAuthenticationPanelDelegate panel:updateWebAuthenticationPanel:]):
(TestWebKitAPI::TEST):
Adds a new test for -[_WKWebAuthenticationPanelDelegate panel:updateWebAuthenticationPanel:].

  • TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-nfc-multiple-tags.html: Added.

LayoutTests:

  • http/wpt/webauthn/public-key-credential-create-success-nfc.https-expected.txt:
  • http/wpt/webauthn/public-key-credential-create-success-nfc.https.html:

Adds new tests for multiple physical tags and service restart.

Location:
trunk
Files:
1 added
24 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r251641 r251645  
     12019-10-21  Jiewen Tan  <jiewen_tan@apple.com>
     2
     3        [WebAuthn] Warn users when multiple NFC tags present
     4        https://bugs.webkit.org/show_bug.cgi?id=200932
     5        <rdar://problem/54890736>
     6
     7        Reviewed by Brent Fulgham.
     8
     9        * http/wpt/webauthn/public-key-credential-create-success-nfc.https-expected.txt:
     10        * http/wpt/webauthn/public-key-credential-create-success-nfc.https.html:
     11        Adds new tests for multiple physical tags and service restart.
     12
    1132019-10-27  Simon Fraser  <simon.fraser@apple.com>
    214
  • trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-nfc.https-expected.txt

    r251602 r251645  
    44PASS PublicKeyCredential's [[create]] with multiple tags in a mock nfc authenticator.
    55PASS PublicKeyCredential's [[create]] with U2F in a mock nfc authenticator.
     6PASS PublicKeyCredential's [[create]] with multiple physical tags in a mock nfc authenticator.
     7PASS PublicKeyCredential's [[create]] with service restart in a mock nfc authenticator.
    68
  • trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-nfc.https.html

    r251602 r251645  
    2222                },
    2323                challenge: Base64URL.parse("MTIzNDU2"),
    24                 pubKeyCredParams: [{ type: "public-key", alg: -7 }],
    25                 timeout: 100
     24                pubKeyCredParams: [{ type: "public-key", alg: -7 }]
    2625            }
    2726        };
     
    4544                challenge: Base64URL.parse("MTIzNDU2"),
    4645                pubKeyCredParams: [{ type: "public-key", alg: -7 }],
    47                 authenticatorSelection: { authenticatorAttachment: "cross-platform" },
    48                 timeout: 100
     46                authenticatorSelection: { authenticatorAttachment: "cross-platform" }
    4947            }
    5048        };
     
    6765                },
    6866                challenge: Base64URL.parse("MTIzNDU2"),
    69                 pubKeyCredParams: [{ type: "public-key", alg: -7 }],
    70                 timeout: 100
     67                pubKeyCredParams: [{ type: "public-key", alg: -7 }]
    7168            }
    7269        };
     
    9188                },
    9289                challenge: Base64URL.parse("MTIzNDU2"),
    93                 pubKeyCredParams: [{ type: "public-key", alg: -7 }],
    94                 timeout: 100
     90                pubKeyCredParams: [{ type: "public-key", alg: -7 }]
    9591            }
    9692        };
     
    10298        });
    10399    }, "PublicKeyCredential's [[create]] with U2F in a mock nfc authenticator.");
     100
     101    promise_test(t => {
     102        const options = {
     103            publicKey: {
     104                rp: {
     105                    name: "localhost",
     106                },
     107                user: {
     108                    name: "John Appleseed",
     109                    id: Base64URL.parse(testUserhandleBase64),
     110                    displayName: "Appleseed",
     111                },
     112                challenge: Base64URL.parse("MTIzNDU2"),
     113                pubKeyCredParams: [{ type: "public-key", alg: -7 }]
     114            }
     115        };
     116
     117        if (window.internals)
     118            internals.setMockWebAuthenticationConfiguration({ nfc: { error: "success", payloadBase64: [testNfcCtapVersionBase64, testGetInfoResponseApduBase64, testCreationMessageApduBase64], multiplePhysicalTags: true } });
     119        return navigator.credentials.create(options).then(credential => {
     120            checkCtapMakeCredentialResult(credential);
     121        });
     122    }, "PublicKeyCredential's [[create]] with multiple physical tags in a mock nfc authenticator.");
     123
     124    promise_test(t => {
     125        const options = {
     126            publicKey: {
     127                rp: {
     128                    name: "localhost",
     129                },
     130                user: {
     131                    name: "John Appleseed",
     132                    id: Base64URL.parse(testUserhandleBase64),
     133                    displayName: "Appleseed",
     134                },
     135                challenge: Base64URL.parse("MTIzNDU2"),
     136                pubKeyCredParams: [{ type: "public-key", alg: -7 }]
     137            }
     138        };
     139
     140        if (window.internals)
     141            internals.setMockWebAuthenticationConfiguration({ silentFailure: true, nfc: { error: "malicious-payload", payloadBase64: [testDummyMessagePayloadBase64, testNfcCtapVersionBase64, testGetInfoResponseApduBase64, testCreationMessageApduBase64] } });
     142        return navigator.credentials.create(options).then(credential => {
     143            checkCtapMakeCredentialResult(credential);
     144        });
     145    }, "PublicKeyCredential's [[create]] with service restart in a mock nfc authenticator.");
    104146</script>
  • trunk/Source/WebCore/ChangeLog

    r251644 r251645  
     12019-10-21  Jiewen Tan  <jiewen_tan@apple.com>
     2
     3        [WebAuthn] Warn users when multiple NFC tags present
     4        https://bugs.webkit.org/show_bug.cgi?id=200932
     5        <rdar://problem/54890736>
     6
     7        Reviewed by Brent Fulgham.
     8
     9        Covered by new tests in existing test file.
     10
     11        * testing/MockWebAuthenticationConfiguration.h:
     12        (WebCore::MockWebAuthenticationConfiguration::NfcConfiguration::encode const):
     13        (WebCore::MockWebAuthenticationConfiguration::NfcConfiguration::decode):
     14        * testing/MockWebAuthenticationConfiguration.idl:
     15        Adds a new test option.
     16
    1172019-10-27  Antti Koivisto  <antti@apple.com>
    218
  • trunk/Source/WebCore/testing/MockWebAuthenticationConfiguration.h

    r251602 r251645  
    9494        Vector<String> payloadBase64;
    9595        bool multipleTags { false };
     96        bool multiplePhysicalTags { false };
    9697
    9798        template<class Encoder> void encode(Encoder&) const;
     
    194195void MockWebAuthenticationConfiguration::NfcConfiguration::encode(Encoder& encoder) const
    195196{
    196     encoder << error << payloadBase64 << multipleTags;
     197    encoder << error << payloadBase64 << multipleTags << multiplePhysicalTags;
    197198}
    198199
     
    206207        return WTF::nullopt;
    207208    if (!decoder.decode(result.multipleTags))
     209        return WTF::nullopt;
     210    if (!decoder.decode(result.multiplePhysicalTags))
    208211        return WTF::nullopt;
    209212    return result;
  • trunk/Source/WebCore/testing/MockWebAuthenticationConfiguration.idl

    r251602 r251645  
    101101    sequence<DOMString> payloadBase64;
    102102    boolean multipleTags = false;
     103    boolean multiplePhysicalTags = false;
    103104};
  • trunk/Source/WebKit/ChangeLog

    r251639 r251645  
     12019-10-21  Jiewen Tan  <jiewen_tan@apple.com>
     2
     3        [WebAuthn] Warn users when multiple NFC tags present
     4        https://bugs.webkit.org/show_bug.cgi?id=200932
     5        <rdar://problem/54890736>
     6
     7        Reviewed by Brent Fulgham.
     8
     9        This patch utilizes -[_WKWebAuthenticationPanelDelegate panel:updateWebAuthenticationPanel:] to
     10        inform clients about multiple physical tags are presenting such that clients can instruct users
     11        to select only one of them physically. Given a physical tag could have multiple different
     12        interfaces, which NearField will treat them into different NFTags, the tagID is then used to
     13        identify if there are actually multiple physical tags.
     14
     15        This patch also adds the ability to restart polling of a partiuclar NFReaderSession to NfcConnection
     16        and the ability to restart the whole session to NfcService. The former is used to recover from errors
     17        in the discovery stages, and the latter is used to recover from errors returned from authenticators
     18        in the request stages. For the latter, given NfcConnection is not awared of the syntax of FIDO2/U2F
     19        protocol, and CtapAuthenticator/U2fAuthenticator are not awared the transport of the underneath driver.
     20        A generic restartDiscovery process is added to each service and it is up to the actual service to
     21        implement the actual process such that AuthenticatorManager can arbitrarily call it after exceptions
     22        are returned to restart the whole NFC session. To achieve restartDiscovery, NfcConnection is made
     23        RefCounted as well such that both the NfcService and the CtapNfcDriver could hold it at the same time.
     24        CtapNfcDriver uses the connection to complete requests as before while NfcService has the new capability
     25        to use it to stop the current session when restartDiscovery kicks off.
     26
     27        * Platform/spi/Cocoa/NearFieldSPI.h:
     28        * UIProcess/WebAuthentication/AuthenticatorManager.cpp:
     29        (WebKit::AuthenticatorManager::serviceStatusUpdated):
     30        (WebKit::AuthenticatorManager::respondReceived):
     31        (WebKit::AuthenticatorManager::restartDiscovery):
     32        * UIProcess/WebAuthentication/AuthenticatorManager.h:
     33        * UIProcess/WebAuthentication/AuthenticatorTransportService.cpp:
     34        (WebKit::AuthenticatorTransportService::startDiscovery):
     35        (WebKit::AuthenticatorTransportService::restartDiscovery):
     36        * UIProcess/WebAuthentication/AuthenticatorTransportService.h:
     37        (WebKit::AuthenticatorTransportService::restartDiscoveryInternal):
     38        * UIProcess/WebAuthentication/Cocoa/NfcConnection.h:
     39        * UIProcess/WebAuthentication/Cocoa/NfcConnection.mm:
     40        (WebKit::NfcConnection::create):
     41        (WebKit::NfcConnection::NfcConnection):
     42        (WebKit::NfcConnection::~NfcConnection):
     43        (WebKit::NfcConnection::stop const):
     44        (WebKit::NfcConnection::didDetectTags):
     45        (WebKit::NfcConnection::restartPolling):
     46        (WebKit::NfcConnection::startPolling):
     47        (WebKit::NfcConnection::didDetectTags const): Deleted.
     48        * UIProcess/WebAuthentication/Cocoa/NfcService.h:
     49        * UIProcess/WebAuthentication/Cocoa/NfcService.mm:
     50        (WebKit::NfcService::NfcService):
     51        (WebKit::NfcService::didConnectTag):
     52        (WebKit::NfcService::didDetectMultipleTags const):
     53        (WebKit::NfcService::setConnection):
     54        (WebKit::NfcService::restartDiscoveryInternal):
     55        (WebKit::NfcService::platformStartDiscovery):
     56        (WebKit::NfcService::setDriver): Deleted.
     57        * UIProcess/WebAuthentication/Mock/MockNfcService.h:
     58        * UIProcess/WebAuthentication/Mock/MockNfcService.mm:
     59        (-[WKMockNFTag tagID]):
     60        (-[WKMockNFTag initWithNFTag:]):
     61        (-[WKMockNFTag dealloc]):
     62        (-[WKMockNFTag initWithType:]):
     63        (-[WKMockNFTag initWithType:tagID:]):
     64        (WebKit::MockNfcService::receiveStopPolling):
     65        (WebKit::MockNfcService::receiveStartPolling):
     66        (WebKit::MockNfcService::platformStartDiscovery):
     67        (WebKit::MockNfcService::detectTags):
     68        (WebKit::MockNfcService::detectTags const): Deleted.
     69        * UIProcess/WebAuthentication/fido/CtapNfcDriver.cpp:
     70        (WebKit::CtapNfcDriver::CtapNfcDriver):
     71        * UIProcess/WebAuthentication/fido/CtapNfcDriver.h:
     72        * UIProcess/WebAuthentication/fido/U2fAuthenticator.cpp:
     73
    1742019-10-26  youenn fablet  <youenn@apple.com>
    275
  • trunk/Source/WebKit/Platform/spi/Cocoa/NearFieldSPI.h

    r251602 r251645  
    9494@property (assign) id<NFReaderSessionDelegate> delegate;
    9595
    96 - (BOOL)startPolling;
     96- (BOOL)startPollingWithError:(NSError **)outError;
    9797- (BOOL)stopPolling;
    9898- (BOOL)connectTag:(NFTag*)tag;
    9999- (BOOL)disconnectTag;
    100100- (NSData*)transceive:(NSData*)capdu;
    101 - (NSError *)updateUIAlertMessage:(NSString *)message;
    102101@end
    103102
  • trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp

    r251602 r251645  
    214214}
    215215
     216void AuthenticatorManager::serviceStatusUpdated(WebAuthenticationStatus status)
     217{
     218    if (auto *panel = m_pendingRequestData.panel.get())
     219        panel->client().updatePanel(status);
     220}
     221
    216222void AuthenticatorManager::respondReceived(Respond&& respond)
    217223{
     
    231237    }
    232238    respondReceivedInternal(WTFMove(respond));
     239    restartDiscovery();
    233240}
    234241
     
    346353}
    347354
     355void AuthenticatorManager::restartDiscovery()
     356{
     357    for (auto& service : m_services)
     358        service->restartDiscovery();
     359}
     360
    348361} // namespace WebKit
    349362
  • trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h

    r251602 r251645  
    7474    // AuthenticatorTransportService::Observer
    7575    void authenticatorAdded(Ref<Authenticator>&&) final;
     76    void serviceStatusUpdated(WebAuthenticationStatus) final;
    7677
    7778    // Authenticator::Observer
     
    9192    void startRequest();
    9293    void resetState();
     94    void restartDiscovery();
    9395
    9496    // Request: We only allow one request per time. A new request will cancel any pending ones.
  • trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorTransportService.cpp

    r251602 r251645  
    7676void AuthenticatorTransportService::startDiscovery()
    7777{
    78     // Enforce asynchronous execution of makeCredential.
    7978    RunLoop::main().dispatch([weakThis = makeWeakPtr(*this)] {
    8079        if (!weakThis)
     
    8483}
    8584
     85void AuthenticatorTransportService::restartDiscovery()
     86{
     87    RunLoop::main().dispatch([weakThis = makeWeakPtr(*this)] {
     88        if (!weakThis)
     89            return;
     90        weakThis->restartDiscoveryInternal();
     91    });
     92}
     93
    8694} // namespace WebKit
    8795
  • trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorTransportService.h

    r251602 r251645  
    2828#if ENABLE(WEB_AUTHN)
    2929
     30#include "WebAuthenticationFlags.h"
    3031#include <WebCore/AuthenticatorTransport.h>
    3132#include <wtf/UniqueRef.h>
     
    4950
    5051        virtual void authenticatorAdded(Ref<Authenticator>&&) = 0;
     52        virtual void serviceStatusUpdated(WebAuthenticationStatus) = 0;
    5153    };
    5254
     
    5658    virtual ~AuthenticatorTransportService() = default;
    5759
    58     // This operation is guaranteed to execute asynchronously.
     60    // These operations are guaranteed to execute asynchronously.
    5961    void startDiscovery();
     62    void restartDiscovery();
    6063
    6164protected:
     
    6669private:
    6770    virtual void startDiscoveryInternal() = 0;
     71    // NFC service's polling is one shot. It halts after the first tags are detected.
     72    // Therefore, a restart process is needed to resume polling after exceptions.
     73    virtual void restartDiscoveryInternal() { };
    6874
    6975    WeakPtr<Observer> m_observer;
  • trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/NfcConnection.h

    r251602 r251645  
    2828#if ENABLE(WEB_AUTHN) && HAVE(NEAR_FIELD)
    2929
    30 #include <wtf/FastMalloc.h>
    31 #include <wtf/Noncopyable.h>
     30#include <wtf/RefCounted.h>
    3231#include <wtf/RetainPtr.h>
     32#include <wtf/RunLoop.h>
    3333#include <wtf/WeakPtr.h>
    3434
     
    4141class NfcService;
    4242
    43 class NfcConnection : public CanMakeWeakPtr<NfcConnection> {
    44     WTF_MAKE_FAST_ALLOCATED;
    45     WTF_MAKE_NONCOPYABLE(NfcConnection);
     43class NfcConnection : public RefCounted<NfcConnection>, public CanMakeWeakPtr<NfcConnection> {
    4644public:
    47     NfcConnection(RetainPtr<NFReaderSession>&&, NfcService&);
     45    static Ref<NfcConnection> create(RetainPtr<NFReaderSession>&&, NfcService&);
    4846    ~NfcConnection();
    4947
    5048    Vector<uint8_t> transact(Vector<uint8_t>&& data) const;
     49    void stop() const;
    5150
    5251    // For WKNFReaderSessionDelegate
    53     void didDetectTags(NSArray *) const;
     52    void didDetectTags(NSArray *);
    5453
    5554private:
     55    NfcConnection(RetainPtr<NFReaderSession>&&, NfcService&);
     56
     57    void restartPolling();
     58    void startPolling();
     59
    5660    RetainPtr<NFReaderSession> m_session;
    5761    RetainPtr<WKNFReaderSessionDelegate> m_delegate;
    5862    WeakPtr<NfcService> m_service;
     63    RunLoop::Timer<NfcConnection> m_retryTimer;
    5964};
    6065
  • trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/NfcConnection.mm

    r251602 r251645  
    4646} // namespace
    4747
     48Ref<NfcConnection> NfcConnection::create(RetainPtr<NFReaderSession>&& session, NfcService& service)
     49{
     50    return adoptRef(*new NfcConnection(WTFMove(session), service));
     51}
     52
    4853NfcConnection::NfcConnection(RetainPtr<NFReaderSession>&& session, NfcService& service)
    4954    : m_session(WTFMove(session))
    5055    , m_delegate(adoptNS([[WKNFReaderSessionDelegate alloc] initWithConnection:*this]))
    5156    , m_service(makeWeakPtr(service))
     57    , m_retryTimer(RunLoop::main(), this, &NfcConnection::startPolling)
    5258{
    5359    [m_session setDelegate:m_delegate.get()];
    54     [m_session startPolling];
     60    startPolling();
    5561}
    5662
    5763NfcConnection::~NfcConnection()
    5864{
    59     [m_session disconnectTag];
    60     [m_session stopPolling];
    61     [m_session endSession];
     65    stop();
    6266}
    6367
     
    7276}
    7377
    74 void NfcConnection::didDetectTags(NSArray *tags) const
     78void NfcConnection::stop() const
    7579{
    76     if (!m_service)
     80    [m_session disconnectTag];
     81    [m_session stopPolling];
     82    [m_session endSession];
     83}
     84
     85void NfcConnection::didDetectTags(NSArray *tags)
     86{
     87    if (!m_service || !tags.count)
    7788        return;
    7889
    79     // FIXME(200932): Warn users when multiple NFC tags present
    80     for (NFTag *tag in tags) {
    81         if (tag.type != NFTagTypeGeneric4A)
     90    // A physical NFC tag could have multiple interfaces.
     91    // Therefore, we use tagID to detect if there are multiple physical tags.
     92    NSData *tagID = ((NFTag *)tags[0]).tagID;
     93    for (NFTag *tag : tags) {
     94        if ([tagID isEqualToData:tag.tagID])
    8295            continue;
    83         if (![m_session connectTag:tag])
     96        m_service->didDetectMultipleTags();
     97        restartPolling();
     98        return;
     99    }
     100
     101    // FIXME(203234): Tell users to switch to a different tag if the tag is not of type NFTagTypeGeneric4A
     102    // or can't speak U2F/FIDO2.
     103    for (NFTag *tag : tags) {
     104        if (tag.type != NFTagTypeGeneric4A || ![m_session connectTag:tag])
    84105            continue;
    85106
     
    95116
    96117        m_service->didConnectTag();
    97         break;
     118        return;
    98119    }
     120    restartPolling();
     121}
     122
     123// NearField polling is a one shot polling. It halts after tags are detected.
     124// Therefore, a restart process is needed to resume polling after error.
     125void NfcConnection::restartPolling()
     126{
     127    [m_session stopPolling];
     128    m_retryTimer.startOneShot(1_s); // Magic number to give users enough time for reactions.
     129}
     130
     131void NfcConnection::startPolling()
     132{
     133    NSError *error = nil;
     134    [m_session startPollingWithError:&error];
     135    if (error)
     136        LOG_ERROR("Couldn't start NFC reader polling: %@", error);
    99137}
    100138
  • trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/NfcService.h

    r251602 r251645  
    2929
    3030#include "FidoService.h"
     31#include <wtf/RunLoop.h>
    3132
    3233OBJC_CLASS NFReaderSession;
     
    3435namespace WebKit {
    3536
    36 class CtapNfcDriver;
     37class NfcConnection;
    3738
    3839class NfcService : public FidoService {
     
    4344    // For NfcConnection.
    4445    void didConnectTag();
     46    void didDetectMultipleTags() const;
    4547
    4648#if HAVE(NEAR_FIELD)
    4749protected:
    48     void setDriver(std::unique_ptr<CtapNfcDriver>&&);
     50    void setConnection(Ref<NfcConnection>&&); // For MockNfcConnection
    4951#endif
    5052
    5153private:
    5254    void startDiscoveryInternal() final;
    53     void continueAddDeviceAfterGetInfo(Vector<uint8_t>&& response);
     55    void restartDiscoveryInternal() final;
    5456
    5557    // Overrided by MockNfcService.
     
    5961    // Only one reader session is allowed per time.
    6062    // Keep the reader session alive here when it tries to connect to a tag.
    61     std::unique_ptr<CtapNfcDriver> m_driver;
     63    RefPtr<NfcConnection> m_connection;
    6264#endif
     65    RunLoop::Timer<NfcService> m_restartTimer;
    6366};
    6467
  • trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/NfcService.mm

    r251602 r251645  
    4242NfcService::NfcService(Observer& observer)
    4343    : FidoService(observer)
     44    , m_restartTimer(RunLoop::main(), this, &NfcService::platformStartDiscovery)
    4445{
    4546}
     
    5253{
    5354#if HAVE(NEAR_FIELD)
    54     getInfo(WTFMove(m_driver));
     55    auto connection = m_connection;
     56    ASSERT(connection);
     57    getInfo(WTF::makeUnique<CtapNfcDriver>(connection.releaseNonNull()));
    5558#endif
    5659}
    5760
     61void NfcService::didDetectMultipleTags() const
     62{
     63    if (auto* observer = this->observer())
     64        observer->serviceStatusUpdated(WebAuthenticationStatus::MultipleNFCTagsPresent);
     65}
     66
    5867#if HAVE(NEAR_FIELD)
    59 void NfcService::setDriver(std::unique_ptr<CtapNfcDriver>&& driver)
     68void NfcService::setConnection(Ref<NfcConnection>&& connection)
    6069{
    61     m_driver = WTFMove(driver);
     70    m_connection = WTFMove(connection);
    6271}
    6372#endif
     
    6675{
    6776    platformStartDiscovery();
     77}
     78
     79void NfcService::restartDiscoveryInternal()
     80{
     81#if HAVE(NEAR_FIELD)
     82    if (m_connection)
     83        m_connection->stop();
     84#endif
     85    m_restartTimer.startOneShot(1_s); // Magic number to give users enough time for reactions.
    6886}
    6987
     
    88106            }
    89107
    90             // CtapNfcDriver and NfcConnection will take care of polling tags and connecting to them.
    91             m_driver = WTF::makeUnique<CtapNfcDriver>(makeUniqueRef<NfcConnection>(WTFMove(session), *this));
     108            // NfcConnection will take care of polling tags and connecting to them.
     109            m_connection = NfcConnection::create(WTFMove(session), *this);
    92110        });
    93111    });
  • trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockNfcService.h

    r251602 r251645  
    4040
    4141    NSData* transceive();
     42    void receiveStopPolling();
     43    void receiveStartPolling();
    4244
    4345private:
  • trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockNfcService.mm

    r251602 r251645  
    3939#import "NearFieldSoftLink.h"
    4040
     41namespace {
     42uint8_t tagID1[] = { 0x01 };
     43uint8_t tagID2[] = { 0x02 };
     44}
     45
    4146#if HAVE(NEAR_FIELD)
    4247
     
    4449
    4550- (instancetype)initWithType:(NFTagType)type;
     51- (instancetype)initWithType:(NFTagType)type tagID:(NSData *)tagID;
    4652
    4753@end
     
    4955@implementation WKMockNFTag {
    5056    NFTagType _type;
     57    RetainPtr<NSData> _tagID;
    5158}
    5259
    5360@synthesize technology=_technology;
    54 @synthesize tagID=_tagID;
    5561@synthesize AppData=_AppData;
    5662@synthesize UID=_UID;
     
    6773}
    6874
     75- (NSData *)tagID
     76{
     77    return _tagID.get();
     78}
     79
    6980- (instancetype)initWithNFTag:(id<NFTag>)tag
    7081{
    71     if ((self = [super init]))
     82    if ((self = [super init])) {
    7283        _type = tag.type;
     84        _tagID = tag.tagID;
     85    }
    7386    return self;
    7487}
     
    7689- (void)dealloc
    7790{
    78     [_tagID release];
    79     _tagID = nil;
    8091    [_AppData release];
    8192    _AppData = nil;
     
    98109- (instancetype)initWithType:(NFTagType)type
    99110{
    100     if ((self = [super init]))
     111    return [self initWithType:type tagID:adoptNS([[NSData alloc] initWithBytes:tagID1 length:sizeof(tagID1)]).get()];
     112}
     113
     114- (instancetype)initWithType:(NFTagType)type tagID:(NSData *)tagID
     115{
     116    if ((self = [super init])) {
    101117        _type = type;
     118        _tagID = tagID;
     119    }
    102120    return self;
    103121}
     
    133151}
    134152
     153static BOOL NFReaderSessionStopPolling(id, SEL)
     154{
     155    if (!globalNfcService)
     156        return NO;
     157    globalNfcService->receiveStopPolling();
     158    return YES;
     159}
     160
     161static BOOL NFReaderSessionStartPollingWithError(id, SEL, NSError **)
     162{
     163    if (!globalNfcService)
     164        return NO;
     165    globalNfcService->receiveStartPolling();
     166    return YES;
     167}
     168
    135169static NSData* NFReaderSessionTransceive(id, SEL, NSData *)
    136170{
     
    158192    m_configuration.nfc->payloadBase64.remove(0);
    159193    return [result autorelease];
     194}
     195
     196void MockNfcService::receiveStopPolling()
     197{
     198    // For purpose of restart polling.
     199    m_configuration.nfc->multiplePhysicalTags = false;
     200}
     201
     202void MockNfcService::receiveStartPolling()
     203{
     204    RunLoop::main().dispatch([weakThis = makeWeakPtr(*this)] {
     205        if (!weakThis)
     206            return;
     207        weakThis->detectTags();
     208    });
    160209}
    161210
     
    178227        method_setImplementation(methodToSwizzle3, (IMP)NFReaderSessionTransceive);
    179228
     229        Method methodToSwizzle4 = class_getInstanceMethod(getNFReaderSessionClass(), @selector(stopPolling));
     230        method_setImplementation(methodToSwizzle4, (IMP)NFReaderSessionStopPolling);
     231
     232        Method methodToSwizzle5 = class_getInstanceMethod(getNFReaderSessionClass(), @selector(startPollingWithError:));
     233        method_setImplementation(methodToSwizzle5, (IMP)NFReaderSessionStartPollingWithError);
     234
    180235        auto readerSession = adoptNS([allocNFReaderSessionInstance() init]);
    181         setDriver(WTF::makeUnique<CtapNfcDriver>(makeUniqueRef<NfcConnection>(readerSession.get(), *this)));
    182 
    183         RunLoop::main().dispatch([weakThis = makeWeakPtr(*this)] {
    184             if (!weakThis)
    185                 return;
    186             weakThis->detectTags();
    187         });
    188         return;
     236        setConnection(NfcConnection::create(readerSession.get(), *this));
    189237    }
    190238    LOG_ERROR("No nfc authenticators is available.");
     
    208256            [tags addObject:adoptNS([[WKMockNFTag alloc] initWithType:NFTagTypeGeneric4A]).get()];
    209257
     258        if (configuration.nfc->multiplePhysicalTags)
     259            [tags addObject:adoptNS([[WKMockNFTag alloc] initWithType:NFTagTypeGeneric4A tagID:adoptNS([[NSData alloc] initWithBytes:tagID2 length:sizeof(tagID2)]).get()]).get()];
     260
    210261        [globalNFReaderSessionDelegate readerSession:nil didDetectTags:tags.get()];
    211262    });
  • trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapNfcDriver.cpp

    r251602 r251645  
    3737using namespace fido;
    3838
    39 CtapNfcDriver::CtapNfcDriver(UniqueRef<NfcConnection>&& connection)
     39CtapNfcDriver::CtapNfcDriver(Ref<NfcConnection>&& connection)
    4040    : m_connection(WTFMove(connection))
    4141{
  • trunk/Source/WebKit/UIProcess/WebAuthentication/fido/CtapNfcDriver.h

    r251602 r251645  
    3838class CtapNfcDriver : public CtapDriver {
    3939public:
    40     explicit CtapNfcDriver(UniqueRef<NfcConnection>&&);
     40    explicit CtapNfcDriver(Ref<NfcConnection>&&);
    4141
    4242    void transact(Vector<uint8_t>&& data, ResponseCallback&&) final;
     
    4545    void respondAsync(ResponseCallback&&, Vector<uint8_t>&& response) const;
    4646
    47     UniqueRef<NfcConnection> m_connection;
     47    Ref<NfcConnection> m_connection;
    4848};
    4949
  • trunk/Source/WebKit/UIProcess/WebAuthentication/fido/U2fAuthenticator.cpp

    r251602 r251645  
    3434#include <WebCore/U2fCommandConstructor.h>
    3535#include <WebCore/U2fResponseConverter.h>
    36 #include <wtf/RunLoop.h>
    3736#include <wtf/text/StringConcatenateNumbers.h>
    3837
  • trunk/Tools/ChangeLog

    r251643 r251645  
     12019-10-21  Jiewen Tan  <jiewen_tan@apple.com>
     2
     3        [WebAuthn] Warn users when multiple NFC tags present
     4        https://bugs.webkit.org/show_bug.cgi?id=200932
     5        <rdar://problem/54890736>
     6
     7        Reviewed by Brent Fulgham.
     8
     9        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
     10        * TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm:
     11        (-[TestWebAuthenticationPanelDelegate panel:updateWebAuthenticationPanel:]):
     12        (TestWebKitAPI::TEST):
     13        Adds a new test for -[_WKWebAuthenticationPanelDelegate panel:updateWebAuthenticationPanel:].
     14        * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-nfc-multiple-tags.html: Added.
     15
    1162019-10-27  Claudio Saavedra  <csaavedra@igalia.com>
    217
  • trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj

    r251602 r251645  
    351351                5797FE311EB15A6800B2F4A0 /* NavigationClientDefaultCrypto.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5797FE2F1EB15A5F00B2F4A0 /* NavigationClientDefaultCrypto.cpp */; };
    352352                5797FE331EB15AB100B2F4A0 /* navigation-client-default-crypto.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5797FE321EB15A8900B2F4A0 /* navigation-client-default-crypto.html */; };
     353                5798337C235EB689008E5547 /* web-authentication-get-assertion-nfc-multiple-tags.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5798337B235EB65C008E5547 /* web-authentication-get-assertion-nfc-multiple-tags.html */; };
    353354                57A79857224AB34E00A7F6F1 /* WebCryptoMasterKey.mm in Sources */ = {isa = PBXBuildFile; fileRef = 57A79856224AB34E00A7F6F1 /* WebCryptoMasterKey.mm */; };
    354355                57C3FA661F7C248F009D4B80 /* WeakPtr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB9BC371A67482300FE5678 /* WeakPtr.cpp */; };
     
    14541455                                577454D02359B378008E1ED7 /* web-authentication-get-assertion-hid-no-credentials.html in Copy Resources */,
    14551456                                57663DEC234F1F9300E85E09 /* web-authentication-get-assertion-hid.html in Copy Resources */,
     1457                                5798337C235EB689008E5547 /* web-authentication-get-assertion-nfc-multiple-tags.html in Copy Resources */,
    14561458                                57663DEA234EA66D00E85E09 /* web-authentication-get-assertion-nfc.html in Copy Resources */,
    14571459                                577454D22359BB01008E1ED7 /* web-authentication-get-assertion-u2f-no-credentials.html in Copy Resources */,
     
    18901892                5797FE2F1EB15A5F00B2F4A0 /* NavigationClientDefaultCrypto.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NavigationClientDefaultCrypto.cpp; sourceTree = "<group>"; };
    18911893                5797FE321EB15A8900B2F4A0 /* navigation-client-default-crypto.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "navigation-client-default-crypto.html"; sourceTree = "<group>"; };
     1894                5798337B235EB65C008E5547 /* web-authentication-get-assertion-nfc-multiple-tags.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "web-authentication-get-assertion-nfc-multiple-tags.html"; sourceTree = "<group>"; };
    18921895                5798E2AF1CAF5C2800C5CBA0 /* ProvisionalURLNotChange.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ProvisionalURLNotChange.mm; sourceTree = "<group>"; };
    18931896                57A79856224AB34E00A7F6F1 /* WebCryptoMasterKey.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = WebCryptoMasterKey.mm; sourceTree = "<group>"; };
     
    34133416                                577454CF2359B338008E1ED7 /* web-authentication-get-assertion-hid-no-credentials.html */,
    34143417                                57663DEB234F1F8000E85E09 /* web-authentication-get-assertion-hid.html */,
     3418                                5798337B235EB65C008E5547 /* web-authentication-get-assertion-nfc-multiple-tags.html */,
    34153419                                57663DE9234EA60B00E85E09 /* web-authentication-get-assertion-nfc.html */,
    34163420                                577454D12359BAD5008E1ED7 /* web-authentication-get-assertion-u2f-no-credentials.html */,
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm

    r251602 r251645  
    4343static bool webAuthenticationPanelFailed = false;
    4444static bool webAuthenticationPanelSucceded = false;
     45static bool webAuthenticationPanelUpdateMultipleNFCTagsPresent = false;
    4546static bool webAuthenticationPanelUpdateNoCredentialsFound = false;
    4647static RetainPtr<_WKWebAuthenticationPanel> gPanel;
     
    5455{
    5556    ASSERT_NE(panel, nil);
    56     if (update == _WKWebAuthenticationPanelUpdateNoCredentialsFound)
     57    if (update == _WKWebAuthenticationPanelUpdateMultipleNFCTagsPresent) {
     58        webAuthenticationPanelUpdateMultipleNFCTagsPresent = true;
     59        return;
     60    }
     61    if (update == _WKWebAuthenticationPanelUpdateNoCredentialsFound) {
    5762        webAuthenticationPanelUpdateNoCredentialsFound = true;
     63        return;
     64    }
    5865}
    5966
     
    607614}
    608615
     616#if HAVE(NEAR_FIELD)
     617TEST(WebAuthenticationPanel, PanelMultipleNFCTagsPresent)
     618{
     619    reset();
     620    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-get-assertion-nfc-multiple-tags" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
     621
     622    auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
     623    [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
     624
     625    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSZeroRect configuration:configuration]);
     626    auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
     627    [webView setUIDelegate:delegate.get()];
     628
     629    [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
     630    Util::run(&webAuthenticationPanelRan);
     631    Util::run(&webAuthenticationPanelUpdateMultipleNFCTagsPresent);
     632}
     633#endif
     634
    609635} // namespace TestWebKitAPI
    610636
Note: See TracChangeset for help on using the changeset viewer.