Near Field Communication (NFC) enables wireless communication between two devices at close proximity, usually less than a few centimeters. NFC is an international standard (ISO/IEC 18092) defining an interface and protocol for simple wireless interconnection of closely coupled devices operating at 13.56 MHz. The hardware standard is deined in [[[NFC-STANDARDS]]].

This document defines an API to enable selected use-cases based on NFC technology.

Implementers need to be aware that this specification is considered unstable. Implementers who are not taking part in the discussions will find the specification changing out from under them in incompatible ways. Vendors interested in implementing this specification before it eventually reaches the Candidate Recommendation phase should subscribe to the repository on GitHub and take part in the discussions.

This document defines conformance criteria that apply to a single product: the UA (user agent) that implements the interfaces it contains.

Terminology and conventions

The Augmented Backus-Naur Form (ABNF) notation used is specified in [[RFC5234]].

Security related terms

The term expressed permission refers to an act by the user, e.g. via user interface or setting or host device platform features, using which the user approves the permission of a browsing context to access the given functionality.

The term ask for forgiveness refers to some form of unobtrusive notification that informs the user of an operation while it is running. UAs SHOULD provide the user with means to ignore similar future operations from the same origin and advertise this to the user.

The term prearranged trust relationship means that the UA has already established a trust relationship for a certain operation using a platform specific mechanism, so that an expressed permission from the user is not any more needed. See also this section in the Security and Privacy document.

The term obtain permission for a certain operation indicates that the UA has either obtained expressed permission, or asked for forgiveness, or ensured a prearranged trust relationship exists.

The Web NFC permission name is defined as "`nfc`".

NFC specific terms

NFC stands for Near Field Communications, short-range wireless technology operating at 13.56 MHz which enables communication between devices at a distance less than 10 cm. The NFC communications protocols and data exchange formats, and are based on existing radio-frequency identification (RFID) standards, including ISO/IEC 14443 and FeliCa. The NFC standards include ISO/IEC 18092[5] and those defined by the NFC Forum. See NFC Forum Technical Specifications for a complete listing.

An NFC adapter is the software entity in the underlying platform which provides access to NFC functionality implemented in a given hardware element (NFC chip). A device may have multiple NFC adapters, for instance a built-in one, and one or more attached via USB.

An NFC tag is a passive NFC device. The NFC tag is powered by magnetic induction when an active NFC device is in proximity range. An NFC tag contains a single NDEF message.

The way of reading the message may happen through proprietary technologies, which require the reader and the tag to be of the same manufacturer. Implementations are expected to encapsulate this.

An NFC peer is an active, powered device, which can interact with other devices in order to exchange data using NFC.

An NFC device is either an NFC peer, or an NFC tag.

An NDEF message encapsulates one or more application-defined NDEF records. NDEF is an abbreviation for NFC Forum Data Exchange Format, a lightweight binary message format. NDEF messages can be stored on an NFC tag or exchanged between NFC-enabled devices.

The term NFC content is a synonym for NDEF message, which can originate either from an NFC tag or an NFC peer.

The NDEF record and fields

An NDEF record is a part of an NDEF message. Each record is a binary structure that contains a data payload, as well as associated type information. In addition to this, it includes information about how the data is structured, like payload size, whether the data is chunked over multiple records etc.

A Web NFC message consists of a sequence of NDEF records.

A generic record looks like the following:

Only the first three bytes (lines in figure) are mandatory. First the header byte, followed by the TYPE LENGTH field and PAYLOAD LENGTH field, which may both be zero.

The MB field (bit `7`, message begin) indicates whether this is the beginning of a message, the ME field (bit `6`, message end) indicates whether it is the end. The CF field (bit `5`, chunk flag) indicates whether the payload is chunked across multiple records.

Web NFC turns all chunked records into logical records.

The SR field (bit `4`, short record) indicates whether the record is a short record. A short record is one with a payload length <= `255` bytes. Normal records can have payload lengths exceeding `255` bytes up to a maximum of `4` GB. Short records only use one byte to indicate length, whether as normal records use `4` bytes (`2``32``-1` bytes).

The IL field (bit `3`, id length) indicates whether an ID LENGTH field is available. This optional identifier is a URL.

The TNF field (bit `0-2`, type name format) indicates the format of the type name and is often exposed by native NFC software stacks. The field can take binary values denoting the following NDEF record payload types:

TNF value Description
0 Empty record
1 NFC Forum well-known type record
2 MIME type record
3 Absolute-URL record
4 NFC Forum external type record
5 Unknown record
6 Unchanged record
7 Reserved for future use

Other fields include TYPE LENGTH field, TYPE field, ID LENGTH field, ID field, PAYLOAD LENGTH field and the PAYLOAD field.

Record types

Empty NDEF records (TNF 0)

The empty records, which have no payload or type and are used to indicate empty tags.

Well-known records (TNF 1)

The NFC Forum has standardized a small set of useful RTD (Record Type Definition) types for use in the NFC Forum well-known type records, for instance text, URL, and binary data such as media. In addition, there are record types designed for more complex interactions, such as smart poster (containing optional embedded records for url, text, signature and actions), and handover records. Most of these are defined in [[[NFC-RTD]]].

For well-known records, the RTD type is stored in the TYPE field and is "`T`" (`0x54`) for text, "`U`" (`0x55`) for url and "`Sp`" (`0x53`, `0x70`) for smart poster.

Other known RTD types supported by the platform and not Web NFC are "`ac`" (`0x61`, `0x63`) for alternative carrier, "`Hc`" (`0x48`, `0x63`) for handover carrier, "`Hr`" (`0x48`, `0x72`) for handover request, and "`Hs`" (`0x48`, `0x73`) for handover select.

An NFC handover defines RTD and the corresponding message structure that allows negotiation and activation of an alternative communication carrier, such as Bluetooth or WiFi. The negotiated communication carrier would then be used (separately) to perform certain activities between the two devices, such as sending photos to the other device, printing to a Bluetooth printer or streaming video to a television set.

Smart poster embeds additional records inside its payload. Below you see an example embedding a text and a url record.

MIME type records (TNF 2)

The MIME type records are records that store `opaque` data with associated MIME type.

Web NFC has special handling for working with JSON MIME type data.

Absolute-URL records (TNF 3)

In absolute-URL records the TYPE field contains the absolute-URL string, and not the payload.

NOTE: Some platforms, like Windows Phone have stored additional data in the payload, but any payload data in these records are ignored by other platforms such as Android. On Android, reading such a record, will attempt to load the URL in Chrome and it is as such not intended for client applications.

External type records (TNF 4)

The NFC Forum external type records are for client specified data and must have a type name following the [[[NFC-RTD]]] standard.

Web NFC defines a special external type record that is not accessible to client applications.

Web NFC author records (TNF 4)

When writing data with Web NFC, a special author type record is written in addition to the other records. This record is specificed by this specification and is specific to Web NFC. The record indicates that the containing NDEF message is targeted for browsing contexts using this API and contains information useful for handling the NDEF message with the algorithms defined in this specification. The format is as follows:

The message author host is a serialized host.

The message author is a message author host, optionally followed by a path-absolute-URL string. This enables matching Web NFC content with URL patterns specified by {{NFCReader}}s. For NDEF messages that are not Web NFC messages, the message author is `null`.

The term Web NFC content denotes all Web NFC messages contained within an NDEF message, identified by the message author. This version of the specification supports one author type record per NDEF message.

As part of the NDEF record, an ID field may be present in each record for application specific usages. According to the [[[NFC-STANDARDS]]] it contains a URL with the maximum length of 256 bytes. This URL is used for identifying the NDEF record payload in an application specific way.

Unknown type records (TNF 5)

The unknown records are records that store `opaque` data without associated MIME type.

Unchanged type records (TNF 6)

The unchanged records are record chunks of a chunked data set, and is used for any, but the first record.

First record:
Intermediate record:
Last record:

Any implementation of Web NFC MUST transparently expose chunked records as single logical records.

Introduction

In general, there are following groups of user scenarios for NFC:

NFC works using magnetic induction, meaning that the reader will emit a small electric charge which then creates a magnetic field. This field powers the passive device which turns it into electrical impulses to communicate data. Thus, when the devices are within range, a read is always performed (see NFC Analog Specification and NFC Digital Protocol, NFC Forum, 2006). The peer-to-peer connection works in a similar way, as the device periodically switches into a so-called initiator mode in order to scan for targets, then later to fall back into target mode. If a target is found, the data is read the same way as for tags.

As NFC is based on existing RFID standards, many NFC chipsets support reading RFID tags, but many of these are only supported by single vendors and not part of the NFC standards. Though certain devices support reading and writing to these, it is not a goal of this document to support proprietary tags or support interoperability with legacy systems.

The NFC Forum has mandated the support of five different tag types to be operable with NFC devices. The same is required on operating systems such as Android.

  1. NFC Forum Type 1: This tag is based on the ISO/IEC 14443-3A (also known as NFC-A, as defined in ISO/IEC 14443-3:2011, Part 3: Initialization and anticollision). The tags are rewritable and can be configured to become read-only. Memory size can be between `96` bytes and `2` Kbytes. Communication speed is `106` kbit/sec.
  2. NFC Forum Type 2: This tag is also based on the ISO/IEC 14443-3A (NFC-A). The tags are rewritable and can be configured to become read-only. Memory size can be between `48` bytes and `2` Kbytes. Communication speed is `106` kbit/sec. In contrast to Type 1, Type 2 has anti-collision protection for dealing with multiple tags within the NFC field.
  3. NFC Forum Type 3: This tag is based on the Japanese Industrial Standard (JIS) X 6319-4, commonly known as FeliCa. The tags are preconfigured to be either rewritable or read-only. Memory availability is variable, theoretical memory limit is `1` MByte per service. Communication speed is `106` kbit/sec. Like Type 2, it supports anti-collision protection.
  4. NFC Forum Type 4 (November 2010): This tag is based on the ISO/IEC 14443 like Type 1 and 2, but it supports either NFC-A or NFC-B for communication. On top of that the tag may support the Data Exchange Protocol (aka ISO-DEP) defined in ISO/IEC 14443 (ISO/IEC 14443-4:2008 Part 4: Transmission protocol). Like Type 3, the tags are preconfigured to be either rewritable or read-only. Variable memory, up to `32` KB per service. Supports three different communication speeds `106` or `212` or `424` Kbits/s.
  5. NFC Forum Type 5: This tag is based on the NFC-V RF technology which is based on ISO/IEC 15693 and allows reading and writing an NDEF message on a ISO/IEC 15693 RF tag that is accessible by long range RFID readers as well. The NFC communication is limited to short distance and may use the Active Communication Mode of ISO/IEC 18092 where the sending peer generates the field which balances power consumption and improves link stability.

In addition to data types standardized for NDEF records by the NFC Forum, many commercial products such as bus cards, door openers etc use different card specific data and protocol extensions which require specific NFC chips (same vendor of card and reader) in order to function.

Card emulation mode capabilities also depend on the NFC chip in the device. For payments, a Secure Element is often needed.

This document does not aim supporting all possible use cases of NFC technology, but only a few use cases which are considered relevant to be used by web pages in browsers, using the browser security model.

Examples

This section shows how developers can make use of the various features of this specification.

Use Cases

A few Web NFC user scenarios are described in the Use Cases document. These user scenarios can be grouped by criteria based on security, privacy and feature categories, resulting in generic flows as follows.

Reading an NFC tag

  1. Reading an NFC tag containing a Web NFC message, when the {{Document}} of the top-level browsing context using the Web NFC API is visible and focused. For instance, a web page instructs the user to tap an NFC tag, and then receives information from the tag.
  2. Reading an NFC tag containing other than Web NFC message, when the {{Document}} of the top-level browsing context using the Web NFC API is visible and focused.
  3. Reading an NFC tag when no {{Document}} using the Web NFC API is visible or focused.

    This use case is not supported in this version of the specification, and it has low priority for future versions as well.

Writing to an NFC tag

The user opens a web page which can write an NFC tag. The write operations may be one of the following:

  1. Writing to an empty NFC tag.
  2. Writing to an NFC tag which already contains a Web NFC message with a different message author (i.e. overwriting a web-specific tag).
  3. Writing to an NFC tag which already contains a Web NFC message with the same message author (i.e. updating own tag).
  4. Writing to other, writable NFC tags (i.e. overwriting a generic tag).

Note that an NFC write operation to an NFC tag always involves also a read operation.

Pushing data to an NFC peer device

In general, pushing data to another Web NFC capable device requires that on the initiating device the user would first have to navigate to a web site. The user would then touch the device against another Web NFC equipped device, and data transfer would occur.

On the receiving device the UA will dispatch the content to an application registered and eligible to handle the content, and if that application is a browser which has a {{Document}} of the top-level browsing context visible and focused with active {{NFCReader}}, then the content is delivered to the page through the NFCReadingEvent.

Handover to another wireless connection type

NFC supports handover protocols to Bluetooth or WiFi connectivity for the purpose of larger volume data transfer. The user touches another NFC capable device, and as a result configuration data is sent for a new Bluetooth or WiFi connection, which is then established between the devices.

This use case is not supported in this version of the specification.

Payment scenarios

Payment scenarios with Web NFC generally do not refer to supporting the payment process itself, but associating the payment status with a web page in order to have secondary actions. For instance, the user buys goods in a store, and payments options include contactless payment using NFC technology. In general, touching the device to the point of sales terminal receiver area will result in a transaction between the secure element from the device and the point of sales terminal. With the Web NFC API, if the user navigates to a web site before paying, there may be interaction with that site regarding the payment, e.g. the user could get points and discounts, or get delivered application or service specific data (e.g. tickets, keys, etc) to the device.

This use case is not supported in this version of the specification.

Support for multiple NFC adapters

Users may attach one or more external NFC adapters to their devices, in addition to a built-in adapter. Users may use either NFC adapter.

Features

High level features for the Web NFC specification include the following:

  1. Support devices with single or multiple NFC adapters. If there are multiple adapters present when invoking an NFC function then the UA operates all NFC adapters in parallel.
  2. Support communication with active (powered devices such as readers, phones) and passive (smart cards, tags, etc) devices.
  3. Allow users to act on (e.g. read, write or transceive) discovered NFC devices (passive and active), as well as access the payload which were read in the process as Web NFC messages.
  4. Allow users to write a payload via NDEF records to compatible devices, such as writeable tags, when they come in range, as Web NFC messages.
  5. [future] Allow manual connection for various technologies such as NFC-A and NFC-F depending on the secondary device.
  6. [future] Allow NFC handover to Bluetooth or WiFi.
  7. [future] Allow card emulation with secure element or host card emulation.

This specification makes a few simplifications in what use cases and data types the Web NFC API can handle:

Security and Privacy

The trust model, attacker model, threat model and possible mitigation proposals for the Web NFC API are presented in the Security and Privacy document. This section presents the chosen security and privacy model through normative requirements to implementations.

Chain of trust

Web pages using the Web NFC API are not trusted. This means that the user needs to be aware of exactly what a web page is intending to do with NFC at any given moment. Implementations need to make sure that when the user authorizes a method of this API, then only that action is run, without side effects, and exactly in the context and the number of times the user allows the execution of NFC related operations, according to the algorithmic steps detailed in this specification.

The integrity of NFC content SHOULD NOT be trusted when used for implementing security policies, for instance the authenticity of message author, unless a prearranged trust relationship exists.

Threats

The main threats are summarized in the Security and Privacy document.

In this specification the following threats are handled with the highest priority:

Permissions and user prompts

This specification attempts to minimize user prompting and uses implicit security policies to address the threats. However, this specification does not describe, nor does it mandate specific user prompting policies. The term obtain permission is used for acquiring trust for a given operation.

The [[[PERMISSIONS]]] API is suggested to be used by UAs for implementing NFC related [[[PERMISSIONS]]] in order to minimize the need for user prompting.

All expressed permissions that are preserved beyond the current browsing session MUST be revocable.

Security policies

This section summarizes the security policies which are specified as normative requirements in the respective algorithms of this specification.

Secure Context

Only secure contexts are allowed to access NFC content. Browsers may ignore this rule for development purposes only.

Visible and focused document

Web NFC functionality is allowed only for the {{Document}} of the top-level browsing context, which must be visible and focused.

To determine if a given |document:Document| is visible and focused the user agent MUST run the following steps:

  1. If the currently focused area does not belong to |document|, return false.
  2. If |document|'s visibilityState is "hidden", return false.
  3. Return true.

This also means that UAs should block access to the NFC radio if the display is off or the device is locked. For backgrounded web pages, receiving and pushing NFC content must be suspended.

Permissions controls

Making an NFC tag read-only must obtain permission, or otherwise fail.

Setting up listeners for reading NFC content should obtain permission.

The process of reading an NDEF message does not need to obtain permission.

Pushing NFC content to an NFC peer does not need to obtain permission, but the other rules in this section apply. See the [[[#writing-or-pushing-content]]] section.

Pushing Web NFC content to an NFC tag does not need to obtain permission, if the message author host of the author type record on that NFC tag is equal to the serialized host of the current settings object's origin. Otherwise the UA must obtain permission for pushing NFC content which overwrites existing information. See also the [[[#writing-or-pushing-content]]] section.

Since all local content that a web page has access to can be shared with NFC, the user needs to be clearly aware about the permissions granted to the web page using the Web NFC API.

Record URL host and path when pushing

When pushing Web NFC content, the serialized host and the URL [= url/path =] of the current settings object when requesting the operation must be recorded in each sent NDEF message's author type record. For details see the [[[#writing-or-pushing-content]]] section.

Warn risk of physical location leak

When listening for and pushing NFC content, the UA may warn the user that the given origin may be able to infer physical location.

Restrict automatic handling

The payload data on NFC content is untrusted, and must not be used by the UA to do automatic handling such as opening a web page with a URL found in an NFC tag, unless the user approves that.

Data Representation

The NDEFMessage interface

The content of any Web NFC message is exposed by the NDEFMessage interface:

      [Constructor(NDEFMessageInit messageInit), Exposed=Window]
      interface NDEFMessage {
        readonly attribute USVString url;
        readonly attribute FrozenArray<NDEFRecord> records;
      };

      dictionary NDEFMessageInit {
        USVString url;
        sequence<NDEFRecordInit> records;
      };
    

The url property represents the message author of a received Web NFC message.

The records property represents a list of NDEF records defining the Web NFC message.

The NDEFMessageInit dictionary is used to initialize a Web NFC message. When used in the NFCWriter.push() method, its url member represents a URL [= url/path =] used for constructing the message author of the pushed Web NFC content.

The NDEFRecord interface

The content of any NDEF record is exposed by the NDEFRecord interface:

      typedef any NDEFRecordData;

      [Constructor(NDEFRecordInit recordInit), Exposed=Window]
      interface NDEFRecord {
        readonly attribute NDEFRecordType recordType;
        readonly attribute USVString mediaType;

        USVString? toText();
        [NewObject] ArrayBuffer? toArrayBuffer();
        [NewObject] object? toJSON();
      };

      dictionary NDEFRecordInit {
        NDEFRecordType recordType;
        USVString mediaType;

        NDEFRecordData data;
      };
    

A NDEFRecord object has the following internal slots:

Internal slot Initial value Description (non-normative)
[[\PayloadData]] Empty byte sequence. A byte sequence representing the whole or a subset of the PAYLOAD field data.

The NDEFRecordData is a union type representing data types allowed for NDEFRecordInit.data property.

The mediaType property represents the MIME type of the NDEF record payload.

The recordType property represents the NDEF record types.

The toText() method, when invoked, MUST return the result of running convert NDEFRecord.[[\PayloadData]] bytes with an NDEFRecord object and a `text` type.

The toArrayBuffer() method, when invoked, MUST return the result of running convert NDEFRecord.[[\PayloadData]] bytes with an NDEFRecord object and an `arrayBuffer` type.

The toJSON() method, when invoked, MUST return the result of running convert NDEFRecord.[[\PayloadData]] bytes with an NDEFRecord object and a `JSON` type.

The NDEFRecordInit dictionary is used to initialize an NDEF record with its type, MIME type and payload data via the members of recordType, mediaType, and data. The mapping from data types of an NDEFRecordInit to NDEF record types is presented in the algorithmic steps which handle the data and described in the [[[#steps-receiving]]] and [[[#writing-or-pushing-content]]] sections.

To convert NDEFRecord.[[\PayloadData]] bytes, pass a |record:NDEFRecord| and a |type|, run these steps:

  1. Let |bytes:byte sequence| be |record|.[[\PayloadData]].
  2. Let |recordType:NDEFRecordType| be the value of |record|'s recordType attribute.
  3. Switch on |type|:
    text
    1. If the |recordType| value is equal to "`empty`", return `null`.
    2. If the |recordType| value is equal to "`text`", then run the following sub-steps:
      1. Let |header| be the first byte of |bytes|.
      2. Let |charset| be "`utf-8`" if bit `7` (MB field) of |header| is equal to the value 0, or else "`utf-16be`".
      3. Let |offset| be the value given by bit `5` to bit `0` of the |header|.
      4. Let |buffer| be the |bytes|, from position |offset| + `1` to the end.
      5. If |charset| is equal to "`utf-8`", return the result of running UTF-8 decode on |buffer|.
      6. Otherwise, return the result of running decode on |buffer| with `encoding` set to "`utf-16be`".
    3. Otherwise, return the result of running UTF-8 decode on |bytes|.
    arrayBuffer
    1. If the |recordType| value is equal to "`json`" or "`opaque`", then return an {{ArrayBuffer}} whose contents are the |bytes|. Re-[= exception/throw =] any exceptions.
    2. Otherwise, return `null`.
    JSON
    1. If the |recordType| value is equal to "`json`" or "`opaque`", then return the result of running parse JSON from bytes on |bytes|. Re-[= exception/throw =] any exceptions.
    2. Otherwise, return `null`.

The NDEFRecordType string

This string defines the allowed types for a NDEFRecord. The [[[#data-mapping]]] section describes how NDEFRecordType is mapped to NDEF record types.

        typedef DOMString NDEFRecordType;
      

A set of known standardized values exists, but it is also possible for organizations to create their own custom external types, following the ABNF for external type record.

          ext-type             = reg-name ":" custom-type
          custom-type          = 1*(ALPHA / DIGIT / other)

          DIGIT                = %x30-39
          ALPHA                = %x41-5A / %x61-7A   ; A-Z / a-z
          other                = "(" / ")" / "+" / "," / "-" / ":" / "=" /
                                 "@" / ";" / "$" / "_" / "!" / "*" / "'" / "."
        
The `reg-name` value is a [=host/registrable domain=].

And additional ABNF exists for well-known type records:

          wkt-type             = (ALPHA / DIGIT) *(ALPHA / DIGIT / other)
        

The [[[NFC-RTD]]] defines every type in the well-known type records and external type records in terms of URNs, but only a subset of the URN is actually stored in the NDEF record's TYPE field, which corresponds to the above two ABNFs.

The "empty" string
The value representing empty NDEFRecord.
The "text" string
The value representing NDEFRecord of a text type.
The "url" string
The value representing NDEFRecord of a url type.
The "smart-poster" string
The value representing NDEFRecord of a smart-poster type.
The "json" string
The value representing NDEFRecord of a json type.
The "opaque" string
The value representing NDEFRecord of an opaque type.
An external type
A {{DOMString}} representing a custom type for the external type record. The type must follow the external type ABNF and thus be a registrable domain name owned by the issuing organization, a "`:`" and a type, e.g. "`w3.org:A`".

Data mapping

The mapping from data types of an NDEFRecordInit to NDEF record types, as used in the [[[#writing-or-pushing-content]]] section is as follows:

NDEFRecordInit recordType NDEFRecordInit mediaType NDEFRecordInit data NDEF record type
"`empty`" unused unused Empty record
"`text`" unused {{DOMString}} Well-known type record with type "`T`"
"`url`" unused {{DOMString}} Well-known type record with type "`U`"
"`json`" JSON MIME type [= JSON type =] MIME type record with type equal to MIME type.
"`opaque`" MIME type {{ArrayBuffer}} MIME type record
external type unused {{ArrayBuffer}} or
any typed array type
External type record

The mapping from NDEF record types to NDEFRecord, as used for incoming NDEF messages described in the [[[#steps-receiving]]] section, is as follows:

NDEF record type NDEFRecord recordType NDEFRecord mediaType NDEFRecord data getters
Empty record "`empty`" empty toText() or
toJSON() or
toArrayBuffer()
Well-known type record with type "`T`" "`text`" "`text/plain`" toText()
Well-known type record with type "`U`" "`url`" "`text/plain`" toText()
Well-known type record with type "`Sp`" "`smart-poster`" "" toRecord() or
toArrayBuffer()
Absolute-URL record "`url`" "`text/plain`" toText()
MIME type record with JSON MIME type "`json`" The MIME type used in the NDEF record toText() or
toJSON() or
toArrayBuffer()
MIME type record "`opaque`" The MIME type used in the NDEF record toText() or
toJSON() or
toArrayBuffer()
External type record with type other than `w3.org:A` external type "`application/octet-stream`" toText() or
toJSON() or
toArrayBuffer()
Any other NDEF record type "`opaque`" "`application/octet-stream`" toText() or
toJSON() or
toArrayBuffer()

The author type records MUST NOT be exposed to client browsing contexts.

The NFCReader and NFCWriter objects

The objects provide a way for the browsing context to use NFC functionality. They allow for pushing Web NFC messages to NFC tags or NFC peers within range, and to act on incoming Web NFC messages either from an NFC tag or an NFC peer.
    typedef (DOMString or ArrayBuffer or NDEFMessageInit) NDEFMessageSource;

    [Constructor, SecureContext, Exposed=Window]
    interface NFCWriter {
      Promise<void> push(NDEFMessageSource message, optional NFCPushOptions options={});
    };

    [Constructor, SecureContext, Exposed=Window]
    interface NFCReader : EventTarget {
      attribute EventHandler onreading;
      attribute EventHandler onerror;

      void scan(optional NFCScanOptions options={});
    };

    [Constructor(DOMString type, NFCReadingEventInit readingEventInitDict), SecureContext, Exposed=Window]
    interface NFCReadingEvent : Event {
      readonly attribute DOMString serialNumber;
      [SameObject] readonly attribute NDEFMessage message;
    };

    dictionary NFCReadingEventInit : EventInit {
      DOMString? serialNumber = "";
      required NDEFMessageInit message;
    };

    [Constructor(DOMString type, NFCErrorEventInit errorEventInitDict), SecureContext, Exposed=Window]
    interface NFCErrorEvent : Event {
      readonly attribute DOMException error;
    };

    dictionary NFCErrorEventInit : EventInit {
      required DOMException error;
    };

  

The NDEFMessageSource is a union type representing argument types accepted by the push() method.

The NFCReadingEvent is the event being dispatched on new NFC readings. The serialNumber property represents the serial number of the device used for anti-collision and identification, or empty string in case none is available. The message is an NDEFMessage object.

NFCReadingEventInit is used to initialize a new event with a serial number and the NDEFMessageInit data via the message member. If serialNumber is [= dictionary member/not present =] or is `null`, empty string will be used to init the event.

Though most tags will have a stable unique identifier (UID), not all have one and some tags even create a random number on each read. The serial number usually consists of 4 or 7 numbers, separated by `:`.

The NFCErrorEvent is the event being dispatched on errors, with the {{DOMException}} object as the error attribute.

NFCErrorEventInit is used in order to initialize a new event with a {{DOMException}} data via the error member.

The NFCWriter is an object used for writing data to NFC devices such as tags.

The NFCReader is an object used for reading data when a device, such as a tag, is within the magnetic induction field.

The onreading is an {{EventHandler}} which is called to notify that new reading is available.

The onerror is an {{EventHandler}} which is called to notify that an error happened.

NFC state associated with the settings object

The relevant settings object of the active document of a browsing context which supports NFC has an associated NFC state record with the following internal slots:

Internal Slot Description (non-normative)
[[\Suspended]] A boolean flag indicating whether NFC functionality is suspended or not, initially `false`.
[[\ActivatedReaderList]] A set of {{NFCReader}} instances initially set to the empty set.
[[\PendingPush]] A promise-writer tuple where the promise holds a pending {{Promise}} and writer holds a {{NFCWriter}}. Initially the slot is empty.

The activated reader objects is the value of the [[\ActivatedReaderList]] internal slot.

The pending push tuple is the value of the [[\PendingPush]] internal slot.

NFC is suspended if the [[\Suspended]] internal slot is `true`.

To suspend NFC, set the [[\Suspended]] internal slot to `true`.

To resume NFC, set the [[\Suspended]] internal slot to `false`.

Internal slots are used only as a notation in this specification, and implementations do not necessarily have to map them to explicit internal properties.

Handling NFC adapters

Implementations MAY use multiple NFC adapters according to the algorithmic steps described in this specification.

Handling Window visibility and focus

Each {{Window}} object where the Web NFC API is exposed has separate NFCWriter and NFCReader instances. The visible and focused state of the the top-level browsing context's {{Document}} determines the suspended state of the associated NFCWriter and NFCReader instances.

The term suspended in this specification refers to NFC operations being suspended, i.e. no NFC content is pushed by NFCWriters, and no received NFC content is presented to any {{NFCReader}} while suspended. However, platform level timers for the NFCWriter.push() method continue running, and if they expire, the event should be recorded and handled when execution next resumes, i.e. when the [= Window/focus =] event is fired on the {{Window}} object.

When the {{Document}} of the top-level browsing context using the Web NFC API is visible and focused, resume NFC. Otherwise, suspend NFC.

Aborting pending push operation

To attempt to abort a pending push operation on an environment settings object, perform the following steps:

  1. If there is no pending push tuple |tuple|, abort these steps.
  2. Let |writer:NFCWriter| be |tuple|'s writer.
  3. Let |p:Promise| be |tuple|'s promise.
  4. Stop the |writer|'s |timer| if it is active.
  5. If the |writer| has already initiated NFC data transfer, abort these steps.
  6. Clear the pending push tuple.
  7. Reject |p| with an {{"AbortError"}} {{DOMException}} and abort these steps.

Rejecting |p| will clear the pending push tuple and remove the async write handlers as it is a transformed promise with fulfillment and rejection handlers.

Releasing NFC

To release NFC on an environment settings object, perform the following steps:

  1. Suspend NFC.
  2. Attempt to abort a pending push operation.
  3. Stop the dispatch NFC content steps.
  4. Clear the activated reader objects.
  5. Release the NFC resources associated with |nfc| on the underlying platform.

The UA must release NFC given the document's relevant settings object as additional unloading document cleanup steps.

The NDEFCompatibility enum

To describe what NDEF compatible devices are accepted as vendor specific tags exist that support NDEF but which are not universally supported by all NFC readers, or by the NFC standard.

          enum NDEFCompatibility {
            "nfc-forum",
            "vendor",
            "any"
          };
        

nfc-forum
The enum value representing all active and passive NFC devices, supported by the NFC standard.
vendor
The enum value representing vendor specific NFC tags (passive device) that require specific reader chips.
any
The enum value representing all NDEF compatible devices that the reader chip can read.

The NFCPushOptions dictionary

      dictionary NFCPushOptions {
        NFCPushTarget target = "any";
        unrestricted double timeout = Infinity;
        boolean ignoreRead = true;
        AbortSignal? signal;
        NDEFCompatibility compatibility = "nfc-forum";
      };
    

The target property denotes the intended target for the pending push() operation.

The timeout property denotes the timeout for the pending push() operation expressed in milliseconds. The default value is implementation-dependent. The value `Infinity` means there is no timeout, i.e. no timer is started. After the |timeout| expires, the message set for pushing is cleared, an error is returned, and a new Web NFC message can be set for pushing.

When the value of the ignoreRead property is `true`, the push algorithm will skip invoking the receiving and parsing steps for an NFC tag.

The signal property allows to abort the push() operation.

The compatibility property denotes the accepted kind of NFC devices.

The NFCPushTarget enum

This enum defines the set of intended target values for the push() operation.

      enum NFCPushTarget {
        "tag",
        "peer",
        "any"
      };
    
tag
The enum value representing the intended target for the push() operation to be a NFC tag.
peer
The enum value representing the intended target for the push() operation to be a NFC peer.
any
The enum value representing the intended target for the push() operation to be a NFC tag or a NFC peer.

The NFCScanOptions dictionary

To describe which messages an application is interested in, the NFCScanOptions dictionary is used:

        dictionary NFCScanOptions {
          AbortSignal? signal;
          USVString url = "";
          NDEFRecordType recordType;
          USVString mediaType = "";
          NDEFCompatibility compatibility = "nfc-forum";
        };
      

The signal property allows to abort the scan() operation.

The url property denotes the URL pattern which is used for matching the message author of Web NFC messages which are being read. The default value `""` means that no matching is performed.

The recordType property denotes the enum value which is used for matching the recordType property of each NDEFRecord object in a Web NFC message. If the dictionary member is [= dictionary member/not present =], then it will be ignored by the NFC listen algorithm.

The mediaType property denotes the match pattern which is used for matching the mediaType property of each NDEFRecord object in a Web NFC message. The default value `""` means that no matching is performed.

The compatibility property denotes the accepted kind of NFC devices.

        const options = {
          url: "https://www.w3.org/*",  // any path from the domain is accepted
          recordType: "json",
          mediaType: "application/*+json"  // any JSON-based MIME type
        }
      
        const options = {
          url: "https://w3.org/info/restaurant/daily-menu/",
          recordType: "opaque",
          mediaType: "application/octet-stream"
        }
      

Writing or pushing content

This section describes how to write an NDEF message to an NFC tag or how to push it to an NFC peer device when it is next time in proximity range before a timer expires. At any time there is at maximum of two Web NFC messages that can be set for pushing for an origin: one targeted to NFC tags and one to NFC peers, until the current message is sent, a timeout happens, or the push is aborted.

The push() method

The NFCWriter.push method, when invoked, MUST run the push a message algorithm:

  1. Let |p:Promise| be a new {{Promise}} object.
  2. Let |message:NDEFMessageSource| be the first argument.
  3. Let |options:NFCPushOptions| be the second argument.
  4. Let |signal:AbortSignal| be the |options|’ dictionary member of the same name if present, or `null` otherwise.
  5. If there is no underlying NFC Adapter, or if a connection cannot be established, then reject |p| with a {{"NotSupportedError"}} {{DOMException}} and return |p|.
  6. If the UA is not allowed to access the underlying NFC Adapter (e.g. a user preference), then reject |p| with a {{"NotReadableError"}} {{DOMException}} and return |p|.
  7. If pushing data is not supported by the underlying NFC Adapter, then reject |p| with a {{"NotSupportedError"}} {{DOMException}} and return |p|.
  8. If |signal|’s [= AbortSignal/aborted flag =] is set, then reject |p| with an {{"AbortError"}} {{DOMException}} and return |p|.
  9. If |signal| is not `null`, then add the following abort steps to |signal|:
    1. If the instance has already initiated NFC data transfer, and it can't be aborted, ignore the abort signal and return.
    2. Stop the instance's |timer| if it is active.
    3. Reject |p| with an {{"AbortError"}} {{DOMException}} and abort these steps.
  10. Run the following steps in parallel:
    1. An implementation MAY reject |p| with a {{"NotSupportedError"}} {{DOMException}} and abort these steps.
      The UA might terminate message push at this point. The reasons for terminations are implementation details. For example, the user could have has set a preference to allow a given origin only to read, write, or push data to peers. Also, the implementation might be unable to support the operation requested.
    2. Let |target:NFCPushTarget| be |options|'s target.
    3. Let |timeout:number| be |options|'s timeout.
    4. Let |compatibility:NDEFCompatibility| be |options|'s compatibility.
    5. If the |message:NDEFMessageSource| parameter is not of type defined by the NDEFMessageSource union, reject |p| with a {{TypeError}}, and abort these steps.
    6. If the |message| parameter is of NDEFMessageInit type, and |message|'s records [= list/is empty =], reject |p| with a {{TypeError}} and abort these steps.
    7. If |timeout| value is `NaN` or negative, reject |p| with a {{TypeError}} and abort these steps.
    8. If |timeout| value is not supported by the UA, reject |p| with a {{"NotSupportedError"}} {{DOMException}} and abort these steps.
    9. Let |output| be the notation for the NDEF message to be created by UA, as the result of passing |message| to create Web NFC message. If this throws an exception, reject |p| with that exception and abort these steps.
    10. If |target| is "`any`", run the following steps twice, once with |slot| set to the value "`tag`", and once set to the value "`peer`"; otherwise run the following step once, with |slot| set to the value of |target|.
    11. Attempt to abort a pending push operation.
    12. Associate |output| with |slot|.
    13. If |timeout| value is not equal to `Infinity`, start a timer |timer| with the timeout value set to |timeout|.
    14. Add the following async write handlers:
      The |timer| expires
      1. Reject |p| with {{"TimeoutError"}} {{DOMException}}.
      An NFC device |device| comes within communication range
      1. Verify the following conditions:
        • if |device| is not officially supported by the NFC Forum, |compatibility| is "`vendor`" or "`any`".
        • if |device| is an NFC tag, |target| is "`tag`" or "`any`".
        • if |device| is an NFC peer, |target| is "`peer`" or "`any`".
        • NFC is not suspended.
        In case of success, run the following sub-steps:
        1. Stop |timer| if active.
        2. If |device| is an NFC tag,
        3. Initiate data transfer to |device| using |output| as buffer, using the NFC adapter in communication range with (connected to) |device|.
        4. If the transfer fails, reject |p| with {{"NetworkError"}} {{DOMException}} and abort these steps.

          Multiple adapters should be used sequentially by users. There is very small likelihood that a simultaneous tap will happen on two or multiple different and connected NFC adapters. If it happens, the user will likely need to repeat the taps until success, preferably one device at a time. The error here gives an indication that the operation needs to be repeated. Otherwise the user may think the operation succeeded on all connected NFC adapters.

        5. When the transfer has completed, clear |output| associated with |target|, resolve |p|.
    15. Let |transformedPromise| be the result of transforming |p| with a fulfillment and rejection handler that:
      1. Clears the pending push tuple.
      2. Removes async write handlers.
    16. Set pending push tuple to (`this`, |transformedPromise|).
    17. Return |transformedPromise|.

    If NFC is suspended, continue waiting until |timer| expires (if set), or promise is aborted by the user, or until an NFC device comes within communication range.

Obtaining push permission

To obtain push permission, run these steps:

  1. If there is a prearranged trust relationship, return `true`.
  2. Run the query a permission steps for the Web NFC permission name until completion.
    1. If it resolved with {{PermissionState["granted"]}} (i.e. an expressed permission has been granted to the origin and global object using the [[[PERMISSIONS]]] API), return `true`.
    2. Otherwise, if it resolved with {{PermissionState["prompt"]}}, then optionally request permission from the user for the Web NFC permission name. If that is granted, return `true`.

      The request permission steps are not yet clearly defined. At this point the UA asks the user about the policy to be used with the Web NFC permission name for the given origin and global object, if the user grants permission, return `true`.

  3. Return `false`.

Creating Web NFC message

To create Web NFC message given a |message:NDEFMessageSource| run these steps:

  1. Let |output| be the notation for the NDEF message to be created by the UA as a result of these steps.
  2. [= list/For each =] |record:NDEFRecordInit| in the list |message|'s records, run the following steps, or make sure that the underlying platform provides equivalent values to |ndef|:
    1. If |record|'s recordType is `undefined`:
      1. If |record|'s data is `undefined`, reject |promise| a {{TypeError}} and abort these steps.
      2. Otherwise, if the type of |record|'s data is {{DOMString}}, then set |record|'s recordType to "`text`".
      3. Otherwise, if the type of |record|'s data is an {{ArrayBuffer}}, then set |record|'s recordType to "`opaque`".
      4. Otherwise, set |record|'s recordType to "`json`".
    2. Let |ndef| be the result of passing |record| to the algorithm below switching on |record|'s recordType. If the algorithm throws an exception |e|, reject |promise| with |e| and abort these steps.
      "`empty`"
      "`text`"
      "`url`"
      "`json`"
      "`opaque`"
    3. Add |ndef| to |output|.
  3. Let |authorRecord| be the result of invoking create an author type record given |message|'s url. If this throws exception |e|, reject |promise| with |e| and abort these steps.
  4. Add |authorRecord| to |output|.

    Implementations may choose the location of the author type record within the NDEF message.

Mapping empty record to NDEF

To map empty record to NDEF given a |record:NDEFRecordInit|, run these steps:

  1. Let |ndef| be the notation for the NDEF record to be created by the UA.
  2. Set the |ndef|'s TNF field to `0` (empty record).
  3. Set the |ndef|'s IL field to `0`.
  4. Set |ndef|'s TYPE LENGTH field, and PAYLOAD LENGTH field to `0`, and omit TYPE field and PAYLOAD field.
  5. Return |ndef|.

Mapping string to NDEF

To map text to NDEF given a |record:NDEFRecordInit|, run these steps:

This is useful when clients specifically want to write text in a well-known type record. Other options would be to use the value "`opaque`" with an explicit MIME type text type, which allows for better differentiation, e.g. when using "`text/xml`", or "`text/vcard`".

  1. If the type of a |record|'s data is not a {{DOMString}}, [= exception/throw =] a {{TypeError}} and abort these steps.
  2. Let |mimeTypeRecord| be the MIME type returned by running parse a MIME type on |record|'s mediaType.
    1. If |mimeTypeRecord| is failure, let |mimeTypeRecord| be a new MIME type record whose type is "`text`", and subtype is "`plain`".
    2. If |mimeTypeRecord|'s type is not "`text`", then [= exception/throw =] a {{"SyntaxError"}} {{DOMException}} and abort these steps.
  3. Let |language:string| be |mimeTypeRecord|'s parameters["`lang`"] if it [= map/exists =], or "`en`".
  4. Let |charset:string| be |mimeTypeRecord|'s parameters["`charset`"] if it [= map/exists =], or "`utf-8`".
  5. If |charset| is not equal to "`utf-8`", [= exception/throw =] a {{TypeError}}.
  6. Let |header:byte| be a byte constructed the following way:
    1. Set bit `7` to the value `0` (meaning UTF-8 encoding).
    2. Set bit `6` to the value `0` (reserved).
    3. Let |languageLength:octed| be the length of the |language| string.
    4. If |languageLength| cannot be stored in 6 bit (|languageLength| > 63), [= exception/throw =] a {{SyntaxError}}.
    5. Set bit `5` to bit `0` to |languageLength|.
  7. Let |data:byte sequence| be an empty [= byte sequence =].
    1. Set the first byte (position 0) of |data| to |header|.
    2. Set position 1 (second byte) to position |languageLength| of |data| to |language|.
    3. Let |stream:byte stream| be the resulting byte stream of running UTF-8 encode on |record|'s data.
    4. Read bytes from |stream| into |data| (from position |languageLength| + 1) until read returns end-of-stream.
  8. Set |length:unsigned long| to the [=byte sequence/length=] of |data|.
  9. Let |ndefRecord| be the notation for the NDEF record to be created by the UA.
    1. Set the |ndefRecord|'s TNF field to `1` (well-known type record).
    2. Set the |ndefRecord|'s TYPE field to "`T`" (`0x54`).
    3. Set the |ndefRecord|'s PAYLOAD LENGTH field to |length|.
    4. If |length| > `0`, set the |ndefRecord|'s PAYLOAD field to |data|.
  10. Return |ndefRecord|.

The `lang=` parameter is a non-standard parameter to MIME types, but it is used in this specification in order to maintain compatibility with [[[NFC-STANDARDS]]].

Mapping URL to NDEF

To map a URL to NDEF given a |record:NDEFRecordInit|, run these steps:

  1. If |record|'s data is not a {{DOMString}}, [= exception/throw =] a {{TypeError}} and abort these steps.
  2. Let |url:URL| be the result of parsing |record|'s data.
  3. If |url| is failure, [= exception/throw =] a {{TypeError}} and abort these steps.
  4. Let |serializedURL:string| be serialization of |url|.
  5. Match the URI prefixes as defined in [[[NFC-STANDARDS]]], URI Record Type Definition specification, Section 3.2.2, against the |serializedURL|.
  6. Let |prefixString:string| be the matched prefix or else the empty string.
  7. Let |prefixByte:byte| be the corresponding prefix number, or else `0`.
  8. Let |shortenedURL:string| be |serializedURL| with |prefixString| removed from the start of the string.
  9. Let |data:byte sequence| be an empty [= byte sequence =].
    1. Set the first byte of |data| to |prefixByte|.
    2. Let |stream:byte stream| be the resulting byte stream of running UTF-8 encode on |shortenedURL|.
    3. Read bytes from |stream| into |data| (from position 1) until read returns end-of-stream.
  10. Set |length:unsigned long| to the [=byte sequence/length=] of |data|.
  11. Let |ndefRecord| be the notation for the NDEF record to be created by the UA.
    1. Set the |ndefRecord|'s TNF field to `1` (well-known type record).
    2. Set the |ndefRecord|'s TYPE field to "`U`" (`0x55`).
    3. Set the |ndefRecord|'s PAYLOAD LENGTH field to |length|.
    4. If |length| > `0`, set the |ndefRecord|'s PAYLOAD field to |data|.
  12. Return |ndefRecord|.

Mapping JSON to NDEF

To map JSON to NDEF given a |record:NDEFRecordInit|, run these steps:

  1. If the type of a |record|'s data is not a [= JSON type =], [= exception/throw =] a {{TypeError}} and abort these steps.
  2. Let |mimeTypeRecord| be the MIME type returned by running parse a MIME type on |record|'s mediaType.
    1. If |mimeTypeRecord| is failure, let |mimeTypeRecord| be a new MIME type record whose type is "`application`", and subtype is "`json`".
    2. If |mimeTypeRecord| is not a JSON MIME type, then [= exception/throw =] a {{"SyntaxError"}} {{DOMException}} and abort these steps.
  3. Let |data:byte sequence| be an empty [= byte sequence =].
    1. Let |stream:byte stream| be the resulting byte stream of executing serialize JSON to bytes on |record|'s data.
    2. Read bytes from |stream| into |data| until read returns end-of-stream.
  4. Set |length:unsigned long| to the [=byte sequence/length=] of |data|.
  5. Let |ndefRecord| be the notation for the NDEF record to be created by the UA.
    1. Set the |ndefRecord|'s TNF field to `2` (MIME type).
    2. Set the |ndefRecord|'s TYPE field to the result of serialize a MIME type with |mimeTypeRecord| as the input.
    3. Set the |ndefRecord|'s PAYLOAD LENGTH field to |length|.
    4. If |length| > `0`, set the |ndefRecord|'s PAYLOAD field to |data|.
  6. Return |ndefRecord|.

Mapping binary data to NDEF

To map binary data to NDEF given a |record:NDEFRecordInit|, run these steps:

  1. If the type of a |record|'s data is not an {{ArrayBuffer}}, [= exception/throw =] a {{TypeError}} and abort these steps.
  2. Let |mimeTypeRecord| be the MIME type returned by running parse a MIME type on |record|'s mediaType.
    1. If |mimeTypeRecord| is failure, let |mimeTypeRecord| be a new MIME type record whose type is "`application`", and subtype is "`octet-stream`".
  3. Set |arrayBuffer| to |record|'s data.
  4. Set |length:unsigned long| to |arrayBuffer|.[[\ArrayBufferByteLength]].
  5. Set |data:byte sequence| to |arrayBuffer|.[[\ArrayBufferData]].
  6. Let |ndefRecord| be the notation for the NDEF record to be created by the UA.
    1. Set the |ndefRecord|'s TNF field to `2` (MIME type).
    2. Set the |ndefRecord|'s TYPE field to the result of serialize a MIME type with |mimeTypeRecord| as the input.
    3. Set the |ndefRecord|'s PAYLOAD LENGTH field to |length|.
    4. If |length| > `0`, set the |ndefRecord|'s PAYLOAD field to |data|.
  7. Return |ndefRecord|.

Creating an author type record

To create an author type record given URL [= url/path =] |urlPath:string|, run these steps:

  1. Let |ndef| be the notation for the NDEF record to be created by the UA.
  2. Set |ndef|'s SR field to `1` (short record).
  3. Set |ndef|'s IL field to `0` (no ID LENGTH field and ID field).
  4. Set |ndef|'s CF field to `0` (no chunked record).
  5. Set |ndef|'s TNF field to `4` (external type record).
  6. Set |ndef|'s TYPE field to "`w3.org:A`".
  7. Set |ndef|'s PAYLOAD to the result of invoking create a message author given |urlPath|. If this throws an exception, re-[= exception/throw =] it.
  8. Return |ndef|.

Creating a message author

To create a message author given URL [= url/path =] |urlPath:string|, run these steps:

  1. Let |origin| be the current settings object's origin.
  2. Let |author| be |origin|'s host, serialized.
  3. Append |urlPath| to |author|.
  4. Let |urlRecord| be the result of parsing |author| with "`https://`" prepended.
  5. If |urlRecord| is failure, [= exception/throw =] a {{TypeError}} and abort these steps.
  6. Return |author|.

Listening for content

To listen for NFC content, the client MUST activate an {{NFCReader}} instance by calling NFCReader.scan(). When attaching an event listener for the "`reading`" event on it, NFC content is accessible to the client.

Each {{NFCReader}} can filter the NFC content based on data type, and the URL [= url/path =] of the browsing context which has been saved to the author type record of the NFC content.

If you filter by URL [= url/path =], that means it will be matched against the author type record, thus the presence of such is required. If you don't filter by URL [= url/path =], then all NFC devices are accepted.

The latter is matched against the URL patterns associated with the activated reader objects.

Match patterns

A match pattern is defined by the following ABNF:

          match-pattern  = top-level-type "/" [ tree "." ] subtype [ "+" suffix ] [ ";" parameters ]
          top-level-type = "*" / < VCHAR except "/" and "*" >
          subtype        = "*" / < VCHAR except "+" >
        
A match pattern is a glob used for matching MIME types, for instance the pattern "`application/*+json`" matches "`application/calendar+json`", but does not match "`application/json`". The pattern "`*/*json`", on the other hand, matches both.

URL patterns

A URL pattern is a URL record that can be used to match the message author of a Web NFC message. A valid URL pattern is a valid URL record whose [= url/scheme =] component is equal to "`https`".

A URL pattern's [= url/scheme =], [= url/host =] and [= url/path =] components that are used by the URL pattern match algorithm have the following matching rules:

URL pattern component Matching rule for message author
[= url/host =] exact match or ends with (URL pattern's [= url/host =] prepended with "`.`").
[= url/path =] If URL pattern's path is "`/*`", match any message author path. Otherwise, begins with URL pattern's [= url/path =].

For example, '`https://mydomain.com/*`' will match '`https://service.mydomain.com/myapp/`' and '`https://info.mydomain.com/general/`', while '`https://app.mydomain.com/contacts`' will match '`https://app.mydomain.com/contacts`' and '`https://app.mydomain.com/contacts/all`' The '`*`' is a valid character for the URL path component, therefore, '`https://www.mydomain.com/*`' pattern will match both '`https://www.mydomain.com/*`' and '`https://www.mydomain.com/service`' URLs.

URL pattern match algorithm

To match message author with URL pattern for a given message author and URL pattern, run these steps:
  1. Let |raw author| be a message author passed to this algorithm.
  2. Let |raw pattern| be a URL pattern passed to this algorithm.
  3. If |raw author| and |raw pattern| are empty strings, return `true`.
  4. Let |author| be the result of running the basic URL parser on |raw author|.
  5. If |author| is failure, return `false`.
  6. Let |pattern| be the result of running the basic URL parser on |raw pattern|.
  7. If |pattern| is failure, return `false`.
  8. Let |subdomain pattern| be the result of prepending "`.`" to |pattern|'s [= url/host =].
  9. If |author|'s [= url/host =] does not end with |subdomain pattern| and |author|'s [= url/host =] is not equal to |pattern|'s [= url/host =], return `false`.
  10. If |pattern|'s [= url/path =] is equal to "`/*`", return `true`.
  11. If |author|'s [= url/path =] begins with |pattern|'s [= url/path =], return `true`.
  12. Otherwise, return `false`.

The scan() method

The section [[[#steps-receiving]]] uses {{NFCReader}} instances to match incoming NFC content.

Multiple consecutive calls to the scan() method from the same origin create filters which are in OR relationship.

When the NFCReader.scan method is invoked, the UA MUST run the following NFC listen algorithm:

  1. Let |reader:NFCReader| be the {{NFCReader}} instance.
  2. Let |options| be first argument.
  3. [= list/For each =] |key| → |value| of |options|:
    1. If |key| equals "`signal`", set |reader|.[[\Signal]] to |value|.
    2. Otherwise, if |key| equals "`url`", set |reader|.[[\Url]] to |value|, prepended with "`https://`".
    3. Otherwise, if |key| equals "`recordType`", set |reader|.[[\RecordType]] to |value|.
    4. Otherwise, if |key| equals "`mediaType`", set |reader|.[[\MediaType]] to |value|.
    5. Otherwise, if |key| equals "`compatibility`", set |reader|.[[\Compatibility]] to |value|.
  4. If there is no underlying NFC Adapter, or if a connection cannot be established, then
    1. Let |e| be the result of [= exception/create =] a {{"NotSupportedError"}} {{DOMException}}.
    2. Fire an event named "`error`" at |reader| using NFCErrorEvent with its error attribute initialized to |e|.
    3. Return.
  5. If the UA is not allowed to access the underlying NFC Adapter (e.g. a user preference), then
    1. Let |e| be the result of [= exception/create =] a {{"NotReadableError"}} {{DOMException}}.
    2. Fire an event named "`error`" at |reader| using NFCErrorEvent with its error attribute initialized to |e|.
    3. Return.
  6. If |reader|.[[\Signal]]’s [= AbortSignal/aborted flag =] is set, then return.
  7. If |reader|.[[\Signal]] is not `null`, then add the following abort steps to |reader|.[[\Signal]]:
    1. Remove the {{NFCReader}} instance from the activated reader objects.
    2. If the activated reader objects [= list/is empty =], then make a request to stop listening to NDEF messages on all NFC adapters.
  8. Run the following steps in parallel:
    1. If the obtain reading permission steps return `false`, then
      1. Let |e:DOMException| be the result of [= exception/create =] a {{"NotAllowedError"}} {{DOMException}}.
      2. Fire an event named "`error`" at |reader| using NFCErrorEvent with its error attribute initialized to |e|.
      3. Return.
    2. If this is the first listener being set up, then make a request to all NFC adapters to listen to NDEF messages.
    3. If the request fails, then the UA may run the following sub-steps:
      1. Let |e:DOMException| be the result of [= exception/create =] a {{"NotSupportedError"}} {{DOMException}}.
      2. Fire an event named "`error`" at |reader| using NFCErrorEvent with its error attribute initialized to |e|.
      3. Return.
    4. If the |reader|.[[\Url]] is not an empty string and it is not a valid URL pattern, then
      1. Let |e:DOMException| be the result of [= exception/create =] a {{"SyntaxError"}} {{DOMException}}.
      2. Fire an event named "`error`" at |reader| using NFCErrorEvent with its error attribute initialized to |e|.
      3. Return.
    5. Add |reader| to the activated reader objects.
    6. If the {{Document}} of the top-level browsing context is not visible and focused (e.g. the user navigated to another page), then the registered activated reader objects still SHOULD continue to exist, but SHOULD become paused, i.e. the UA SHOULD NOT check and use them until the {{Document}} is visible and focused again.

To obtain reading permission, run these steps:

  1. If there is a prearranged trust relationship, return `true`.
  2. Otherwise, if the user has earlier denied permission for the calling origin for all future calls of scan() as well, then return `false`.
  3. Otherwise, UAs SHOULD ask for forgiveness with relevant information displayed to the user.

    The ask for forgiveness interaction might show choices like "block now" or "block forever", etc. If the user has chosen to "block forever" the given origin, it is the responsibility of the UA to remember these user choices for each origin, regardless of which NFC adapter is used, and consult them on later invocations.

    In this step UAs are advised to notify users about that reading NFC content may indirectly reveal the physical location of the user.

  4. Return `true`.

Receiving and parsing content

If there are any {{NFCReader}} instances in activated reader objects then UAs MUST listen to NDEF messages, according to step 3 of the NFC listen algorithm.

Parsing NDEF well-known `T` records

To parse a NDEF text record given a |ndefRecord| into a |record:NDEFRecord|, run these steps:

  1. Let |header:byte| be the first byte of |ndefRecord|'s PAYLOAD field.
  2. Let |languageLength:octed| be the value given by bit `5` to bit `0` of the |header|.
  3. Let |language:string| be the result of running ASCII decode on second byte to the |languageLength| + `1` byte, inclusive.
  4. Let |mimeType| be a MIME type with type "`text`", subtype "`plain`" and parameters equal to an empty ordered map.
  5. Set |mimeType|'s parameters["`lang`"] to |language|.
  6. Set |record|'s recordType to "`text`".
  7. Set |record|'s mediaType to the result of serialize a MIME type with |mimeType| as the input.
  8. Set |record|.[[\PayloadData]] to the byte sequence of |ndefRecord|'s PAYLOAD field.
  9. return |record|.

Using the encoder, it is only possible to encode as UTF-8, unless you do the encoding manually and use the "{{opaque}}" {{recordType}}, which allows you to write any opaque data.

When you write a string to the "{{text}}" {{recordType}}, it will be written as UTF-8. External applications have the ability to additionally encode this field as UTF-16BE, but that is transparent from the use of the {{NFCReader}} which will always return the data as a string.

As external applications may have encoded the "{{opaque}}" {{recordType}} using a different encoding, which can be decoded using [[[ENCODING]]] if you know the encoding.

Parsing NDEF well-known `U` records

To parse a NDEF URL record given a |ndefRecord| into a |record:NDEFRecord|, run these steps:

  1. Set |record|'s recordType to "`url`".
  2. Set |record|'s mediaType to "`text/plain`".
  3. Let |prefixByte:byte| be the value of the first byte of |ndefRecord|'s PAYLOAD field.
  4. If |prefixByte| is not `0`, then match the |prefixByte| against the prefix codes in [[[NFC-STANDARDS]]], URI Record Type Definition specification, Section 3.2.2.
  5. Let |prefixString:string| be the matched prefix or else the empty string.
  6. Let |buffer:byte sequence| be the byte sequence of |ndefRecords|'s PAYLOAD field from the second byte to the end.
  7. Set |record|.[[\PayloadData]] to |buffer|.
  8. return |record|.

Parsing NDEF well-known `Sp` records

To parse a NDEF smart-poster record given a |ndefRecord| into a |record:NDEFRecord|, run these steps:

  1. Set |record|'s recordType to "`smart-poster`".
  2. Set |record|'s mediaType to "".
  3. Set |record|.[[\PayloadData]] to the byte sequence of |ndefRecord|'s PAYLOAD field.
  4. return |record|.

Parsing NDEF MIME type records

To parse a NDEF MIME type record given a |ndefRecord| into a |record:NDEFRecord|, run these steps:

  1. Let |mimeType| be the MIME type returned by running parse a MIME type on |ndefRecord|'s TYPE field.
  2. If |mimeType| is a JSON MIME type, then
    1. Set |record|'s recordType to "`json`".
    2. Set |record|'s mediaType to the result of serialize a MIME type with |mimeType| as the input.
    3. Set |record|.[[\PayloadData]] to the byte sequence of |ndefRecord|'s PAYLOAD field.
  3. Otherwise,
    1. Set |record|'s recordType to "`opaque`".
    2. Set |record|'s mediaType to the result of serialize a MIME type with |mimeType| as the input.
    3. Set |record|.[[\PayloadData]] to the byte sequence of |ndefRecord|'s PAYLOAD field.
  4. return |record|.

Parsing NDEF absolute-URL records

To parse a NDEF absolute-URL record given a |ndefRecord| into a |record:NDEFRecord|, run these steps:

  1. Set |record|'s recordType to "`url`".
  2. Set |record|'s mediaType to "`text/plain`".
  3. Set |record|.[[\PayloadData]] to the value of |ndefRecord|'s TYPE field.
  4. return |record|.

Parsing NDEF external type records

To parse a NDEF external type record given a |ndefRecord| into a |record:NDEFRecord|, run these steps:

  1. Set |record|'s recordType to the value of |ndefRecord|'s TYPE field.
  2. Set |record|'s mediaType to "`application/octet-stream`".
  3. Set |record|.[[\PayloadData]] to the byte sequence of |ndefRecord|'s PAYLOAD field.
  4. return |record|.

Parsing NDEF unknown type records

To parse a NDEF unknown record given a |ndefRecord| into a |record:NDEFRecord|, run these steps:

  1. Set |record|'s recordType to "`opaque`".
  2. Set |record|.[[\PayloadData]] to the byte sequence of |ndefRecord|'s PAYLOAD field.
  3. return |record|.

The NFC reading algorithm

When the UA is to receive NFC content it MUST run the following algorithm:

The UA SHOULD represent an unformatted NFC tag as an NDEF message containing a single empty NDEF record.

  1. If NFC is suspended, abort these steps.
  2. Let |compatibility:NDEFCompatibility| be "`vendor`" if the read NDEF compatible device is not officially supported by the NFC Forum, or else "`nfc-forum`".
  3. Let |message:NDEFMessage| be a new NDEFMessage object, with |message|'s url set to `null` and |message|'s records set to the empty list.
  4. Let |serialNumber:serialNumber| be the device identifier as a series of numbers, or `null` if unavailable.
  5. If |serialNumber| is not `null`, set it to the string of U+003A (`:`) concatenating each number represented as ASCII hex digit, in the same order.
  6. Let |input| be the notation for the NDEF message which has been received.
  7. [= list/For each =] NDEF record which is part of |input|, run the following sub-steps:
    1. Let |ndef| be the notation for the current NDEF record.
    2. Let |record:NDEFRecord| be a new NDEFRecord object.
    3. If |ndef|'s TNF field is `0` (empty record), then set |record|'s recordType to "`empty`" and set |record|'s mediaType to `""`.
    4. If |ndef|'s TNF field is `1` (well-known type record):
      1. Set |record| to the result of the algorithm below switching on |ndef|'s TYPE field:
        "`T`" (`0x54`)
        "`U`" (`0x55`)
        "`Sp`" (`0x53` `0x70`)
    5. If |ndef|'s TNF field is `2` (MIME type record), then set |record| to the result of running parse a NDEF MIME type record on |ndef|, or make sure that the underlying platform provides equivalent values to the |record| object's properties.
    6. If |ndef|'s TNF field is `3` (absolute-URL record), then set |record| to the result of running parse a NDEF absolute-URL record on |ndef|.
    7. If |ndef|'s TNF field is `4` and |ndef|'s TYPE field is "`w3.org:A`" (author type record), then set |message|'s url to the |ndef|'s PAYLOAD field.
    8. Otherwise, if |ndef|'s TNF field is `4` (external type record), then set |record| to the result of running parse a NDEF external type record on |ndef|, or make sure that the underlying platform provides equivalent values to the |record| object's properties.
    9. Otherwise, if |ndef|'s TNF field is `5` (unknown record) then set |record| to the result of running parse a NDEF unknown record on |ndef|, or make sure that the underlying platform provides equivalent values to the |record| object's properties.
    10. Otherwise, skip to the next NDEF record in |input|.
    11. Append |record| to |message|'s records.
  8. If NFC is not suspended and |message|'s records [= list/is not empty =], run the dispatch NFC content steps with given |serialNumber|, |message| and |compatibility|.

Dispatching NFC content

To dispatch NFC content given a |serialNumber:serialNumber| of type serialNumber, |message:NDEFMessage| of type NDEFMessage and |compatibility:NDEFCompatibility| of type NDEFCompatibility, run these steps:

  1. [= list/For each =] {{NFCReader}} instance |reader:NFCReader| in the activated reader objects, run the following sub-steps:
    1. Let |match:boolean| be the result of running URL pattern match, with |reader|.[[\Url]] as the URL pattern and |message|'s url as the message author.
    2. If |match| is `false`, [= iteration/continue =].
    3. If |reader|.[[\RecordType]] is [= dictionary member/present =] and it is not equal to any |record:NDEFRecord|.[[\RecordType]] where |record| is an element of |message|, [= iteration/continue =].
    4. If |reader|.[[\MediaType]] is not `""` and it is not equal to any |record|'s mediaType where |record| is an element of |message|, [= iteration/continue =].
    5. If |reader|.[[\Compatibility]] is not "`any`", and not equal to |compatibility|, [= iteration/continue =].
    6. Fire an event named "`reading`" at |reader| using NFCReadingEvent with its serialNumber attribute initialized to |serialNumber| and message attribute initialized to |message|.

Acknowledgments

The editors would like to thank Jeffrey Yasskin, Anne van Kesteren, Anssi Kostiainen, Domenic Denicola, Daniel Ehrenberg, Jonas Sicking, Don Coleman, Salvatore Iovene, Rijubrata Bhaumik, Wanming Lin, and François Beaufort for their contributions to this document.

Special thanks to Luc Yriarte and Samuel Ortiz for their initial work on exposing NFC to the web platform, and for their support for the current approach.