RFC 8908

Captive Portal API

πŸ“… published September 2020, from ietf

RFC 8908: Captive Portal API 🧚✨

okay bestie, RFC 8908 is the Captive Portal API 🌸 It's a Standards Track doc from the IETF, dated September 2020, so it's a real, official spec, not a vibe πŸ’…

πŸ“‹ Title block tea: Written by T. Pauly, Ed. (Apple Inc.) and D. Thakore, Ed. (CableLabs). Category is Standards Track, ISSN 2070-1721. The header doesn't list anything it obsoletes or updates. This is part 1 of 3, so more is coming 🫢

Abstract

This doc describes an HTTP API that lets clients talk to a Captive Portal system πŸ’– With it, clients can figure out how to escape captivity and fetch the state of their Captive Portal sessions. Main character energy πŸ‘‘


Status of This Memo

okay so this one's the official paperwork πŸ“œ

This is an Internet Standards Track document.

This document is a product of the Internet Engineering Task Force (IETF). It represents the consensus of the IETF community. It has received public review and has been approved for publication by the Internet Engineering Steering Group (IESG). Further information on Internet Standards is available in Section 2 of RFC 7841.

Info about the current status of this doc, any errata, and how to send feedback can be found at https://www.rfc-editor.org/info/rfc8908 πŸ”—


Copyright (c) 2020 IETF Trust and the persons identified as the document authors. All rights reserved. πŸŽ€

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions and are provided without warranty as described in the Simplified BSD License.


Table of Contents πŸ—ΊοΈ

1.  Introduction
2.  Terminology
3.  Workflow
4.  API Connection Details
  4.1.  Server Authentication
5.  API State Structure
6.  Example Interaction
7.  Security Considerations
  7.1.  Privacy Considerations
8.  IANA Considerations
  8.1.  Captive Portal API JSON Media Type Registration
  8.2.  Captive Portal API Keys Registry
9.  References
  9.1.  Normative References
  9.2.  Informative References
Acknowledgments
Authors' Addresses

1. Introduction ✨

okay so this doc describes a HyperText Transfer Protocol (HTTP) Application Programming Interface (API) that lets clients interact with a Captive Portal system πŸ¦‹ The API was designed to meet the requirements in the Captive Portal Architecture [CAPPORT-ARCH]. Specifically, it gives you:

  • The state of captivity, which is whether or not the client has access to the Internet.
  • A URI for a user-facing web portal that you can use to get out of captivity.
  • Authenticated and encrypted connections, using TLS for both the API and the user-facing web portal πŸ”

2. Terminology πŸ“–

This doc uses the terms and components from [CAPPORT-ARCH] and also defines these:

Captive Portal Client The client that talks to the Captive Portal API is usually some app running on the user equipment that's connected to the captive network. In this doc it's also called the "client" πŸ’…

Captive Portal API Server The server that exposes the APIs defined here to the client. In this doc it's also called the "API server" πŸ–₯️

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. 🎯


3. Workflow πŸ”„

The Captive Portal Architecture defines several categories of interaction between clients and Captive Portal systems:

  1. Provisioning, where a client finds out that a network has a captive portal and learns the URI of the API server.
  2. API Server interaction, where a client queries the state of captivity and gets the info it needs to get out of captivity.
  3. Enforcement, where the enforcement device in the network blocks disallowed traffic. 🚫

This doc defines the mechanisms used in the second category. It's assumed that the client has already found the location of the Captive Portal API server as part of provisioning. A set of mechanisms for discovering the API server endpoint is defined in [RFC8910] 🧭


4. API Connection Details πŸ”

The API server endpoint MUST be accessed over HTTP using an https URI [RFC2818] and SHOULD use the default https port. For example, if the Captive Portal API server is hosted at "example.org", the URI of the API could be https://example.org/captive-portal/api. πŸ’–

The client SHOULD NOT assume that the URI of the API server for a given network will stay the same. It SHOULD rely on the discovery or provisioning process each time it joins the network. πŸ”

As described in Section 3 of [CAPPORT-ARCH], the identity of the client needs to be visible to the Captive Portal API server so the server can reply with the client's portal state correctly. If the identifier the Captive Portal system uses is the client's set of IP addresses, the system needs to make sure those same IP addresses are visible to both the API server and the enforcement device πŸ‘€

If the API server needs info about the client's identity that isn't otherwise visible to it, the URI given to the client during provisioning SHOULD be distinct per client. So depending on how the Captive Portal system is set up, the URI will be unique for each client host and between sessions for the same client host. ✨

For example, a Captive Portal system that uses per-client session URIs could use https://example.org/captive-portal/api/X54PD39JV as its API URI. πŸŽ€

4.1. Server Authentication πŸ›‘οΈ

Accessing the Captive Portal API over an HTTPS connection is a two-for-one deal 🫢 First, the encrypted connection protects the integrity and confidentiality of the API exchange from other parties on the local network. Second, it gives the client a chance to authenticate the server hosting the API. This authentication lets the client make sure the entity providing the Captive Portal API has a valid certificate for the hostname the network provisioned using the mechanisms in [RFC8910], by validating that a DNS-ID [RFC6125] on the certificate equals the provisioned hostname.

Clients doing revocation checking will need some way to access revocation info for the certificates the API server presents. Online Certificate Status Protocol [RFC6960] (OCSP) stapling, using the TLS Certificate Status Request extension [RFC6066], SHOULD be used. OCSP stapling lets a client do revocation checks without opening new connections πŸ™Œ For other forms of revocation checking, especially for clients that don't support OCSP stapling, a captive network SHOULD permit connections to OCSP responders or Certificate Revocation Lists (CRLs) referenced by the certificates the API server provides. For more on certificate revocation checks, see Section 6.5 of BCP 195 [RFC7525]. Besides OCSP responders and CRLs, a captive network SHOULD also permit connections to Network Time Protocol (NTP) [RFC5905] servers or other time-sync mechanisms, so clients can accurately validate certificates. ⏰

Certificates with missing intermediate certificates that rely on clients validating the certificate chain using the URI in the Authority Information Access (AIA) extension [RFC5280] SHOULD NOT be used by the Captive Portal API server. If the certificates do need AIA, the captive network MUST allow client access to the host specified in that URI. 🚧

🚨 Big deal alert: If the client can't validate the certificate the API server presents, it MUST NOT proceed with any of the API interaction behavior described in this document. The client will go on interacting with the captive network as if the API capabilities weren't there at all. It may still be possible for the user to get on the network if the network redirects a cleartext webpage to a web portal 🫠

5. API State Structure πŸ“¦βœ¨

Okay so the Captive Portal API data is all written in JavaScript Object Notation (JSON) [RFC8259] πŸ’… Requests and responses use the "application/captive+json" media type. Clients SHOULD put this media type in an Accept header on their GET requests πŸŽ€, and servers MUST mark it as their Content-Type header in responses 🫢

There's one key that absolutely MUST show up at the top level of the JSON the API server sends back 😭

+=========+=========+============================================+
| Key     | Type    | Description                                |
+=========+=========+============================================+
| captive | boolean | Indicates whether the client is in a state |
|         |         | of captivity, i.e, it has not satisfied    |
|         |         | the conditions to access the external      |
|         |         | network.  If the client is captive (i.e.,  |
|         |         | captive=true), it will still be allowed    |
|         |         | enough access for it to perform server     |
|         |         | authentication (Section 4.1).              |
+---------+---------+--------------------------------------------+

                             Table 1

Then there are some optional keys that can also show up at the top level 🌸

+====================+=========+==================================+
| Key                | Type    | Description                      |
+====================+=========+==================================+
| user-portal-url    | string  | Provides the URL of a web portal |
|                    |         | that MUST be accessed over TLS   |
|                    |         | with which a user can interact.  |
+--------------------+---------+----------------------------------+
| venue-info-url     | string  | Provides the URL of a webpage or |
|                    |         | site that SHOULD be accessed     |
|                    |         | over TLS on which the operator   |
|                    |         | of the network has information   |
|                    |         | that it wishes to share with the |
|                    |         | user (e.g., store info, maps,    |
|                    |         | flight status, or                |
|                    |         | entertainment).                  |
+--------------------+---------+----------------------------------+
| can-extend-session | boolean | Indicates that the URL specified |
|                    |         | as "user-portal-url" allows the  |
|                    |         | user to extend a session once    |
|                    |         | the client is no longer in a     |
|                    |         | state of captivity.  This        |
|                    |         | provides a hint that a client    |
|                    |         | system can suggest accessing the |
|                    |         | portal URL to the user when the  |
|                    |         | session is near its limit in     |
|                    |         | terms of time or bytes.          |
+--------------------+---------+----------------------------------+
| seconds-remaining  | number  | An integer that indicates the    |
|                    |         | number of seconds remaining,     |
|                    |         | after which the client will be   |
|                    |         | placed into a captive state.     |
|                    |         | The API server SHOULD include    |
|                    |         | this value if the client is not  |
|                    |         | captive (i.e., captive=false)    |
|                    |         | and the client session is time-  |
|                    |         | limited and SHOULD omit this     |
|                    |         | value for captive clients (i.e., |
|                    |         | captive=true) or when the        |
|                    |         | session is not time-limited.     |
+--------------------+---------+----------------------------------+
| bytes-remaining    | number  | An integer that indicates the    |
|                    |         | number of bytes remaining, after |
|                    |         | which the client will be placed  |
|                    |         | into a captive state.  The byte  |
|                    |         | count represents the sum of the  |
|                    |         | total number of IP packet (layer |
|                    |         | 3) bytes sent and received by    |
|                    |         | the client, including IP         |
|                    |         | headers.  Captive Portal systems |
|                    |         | might not count traffic to       |
|                    |         | whitelisted servers, such as the |
|                    |         | API server, but clients cannot   |
|                    |         | rely on such behavior.  The API  |
|                    |         | server SHOULD include this value |
|                    |         | if the client is not captive     |
|                    |         | (i.e., captive=false) and the    |
|                    |         | client session is byte-limited   |
|                    |         | and SHOULD omit this value for   |
|                    |         | captive clients (i.e.,           |
|                    |         | captive=true) or when the        |
|                    |         | session is not byte-limited.     |
+--------------------+---------+----------------------------------+

                              Table 2

You can extend the valid JSON keys by adding entries to the Captive Portal API Keys Registry (Section 8.2) 🧚 If a client gets a key it doesn't recognize, it MUST ignore that key and any values attached to it πŸ™ˆ Every key that isn't defined in this document as "required" counts as optional, so no stress πŸ’œ

Captive Portal JSON can carry per-client data that really shouldn't be stored in an intermediary cache πŸ™… So API servers SHOULD set the Cache-Control header field to "private" or something even stricter, like "no-store" [RFC7234] πŸ”’

How often the client asks for fresh JSON is up to the implementation πŸ€·β€β™€οΈ It can be based on user interaction or on those seconds and bytes remaining numbers ⏱️ If at any point the client doesn't get valid JSON from the API server, whether because of an error or because nothing came back at all, it SHOULD keep using the most recent valid content it received. If it never received any content before, it SHOULD proceed to interact with the captive network as if the API capabilities weren't present 🌈

6. Example Interaction πŸ¦‹

Once a client connected to a captive network finds the URI of the API server, it asks the API server what its captive state is and what it needs to escape captivity πŸ‘€ In this example, the client discovered https://example.org/captive-portal/api/X54PD39JV using one of the mechanisms defined in [RFC8910] πŸ”

To ask for the Captive Portal JSON content, the client sends an HTTP GET request πŸ“¨

GET /captive-portal/api/X54PD39JV HTTP/1.1
Host: example.org
Accept: application/captive+json

The server then answers with the JSON content for that client πŸ’Œ

HTTP/1.1 200 OK
Cache-Control: private
Date: Mon, 02 Mar 2020 05:07:35 GMT
Content-Type: application/captive+json

{
   "captive": true,
   "user-portal-url": "https://example.org/portal.html"
}

Once the client has that info, it uses it to send the user to the web portal (the one named in the user-portal-url value) so they can get access to the external network πŸšͺ After the user meets the requirements for external network access, the client SHOULD query the API server again to check that it's no longer captive βœ…

When the client asks for the Captive Portal JSON content after getting external network access, the server responds with updated JSON content πŸŽ‰

HTTP/1.1 200 OK
Cache-Control: private
Date: Mon, 02 Mar 2020 05:08:13 GMT
Content-Type: application/captive+json

{
   "captive": false,
   "user-portal-url": "https://example.org/portal.html",
   "venue-info-url": "https://flight.example.com/entertainment",
   "seconds-remaining": 326,
   "can-extend-session": true
}

7. Security Considerations πŸ”

Making communication between client hosts and Captive Portal systems safer is one of the main goals of this protocol, so it's giving security glow-up πŸ’– Client traffic is protected from passive listeners on the local network, because clients and the API server have to use TLS-encrypted connections, as Section 4 describes πŸ›‘οΈ All communication between the clients and the API server MUST be encrypted πŸ”’

On top of encryption, this protocol asks for a basic level of authentication from the API server, as Section 4.1 describes 🫑 The API server MUST present a valid certificate that the client can run revocation checks on πŸ“œ That lets the client confirm the API server actually has authority for the hostname the network gave it through [RFC8910] βœ… Just a heads up, though: this check only confirms the API server matches what the network's provisioning mechanism (like DHCP or IPv6 Router Advertisements) handed over. It doesn't validate the security of those provisioning mechanisms or the user's trust relationship with the network ⚠️

7.1. Privacy Considerations πŸ•΅οΈ

Info passed between a client and the user-facing web portal can include personal stuff like a full name and credit card details πŸ’³ So it's really important that both the user-facing web portal and the API server that points a client to the web portal are only accessed over encrypted connections πŸ”

Here's the tea, though 🍡 Even though the web portal uses TLS, the authentication only checks that the web portal server matches the name in the URI the API server gave. Since the user never typed that name in, the hostname shown to them might contain "confusable characters" that can mislead them 😬 See Section 12.5 of [RFC8264] for more on confusable characters πŸ“–

8. IANA Considerations πŸ“

IANA has registered the "application/captive+json" media type (Section 8.1) and created a registry for fields in that format (Section 8.2) πŸŽ€

8.1. Captive Portal API JSON Media Type Registration ✨

okay so this is the bit where we officially register the media type for Captive Portal API JSON text, which is application/captive+json πŸŽ€ it's giving official paperwork energy but it's SO important for the vibe πŸ’…

Type name:  application

Subtype name:  captive+json

Required parameters:  N/A

Optional parameters:  N/A

Encoding considerations:  Encoding considerations are identical to
   those specified for the "application/json" media type.

Security considerations:  See Section 7

Interoperability considerations:  This document specifies format of
   conforming messages and the interpretation thereof.

Published specification:  RFC 8908

Applications that use this media type:  This media type is intended
   to be used by servers presenting the Captive Portal API, and
   clients connecting to such captive networks.

Fragment identifier considerations:  N/A

Additional Information:  N/A

Person and email address to contact for further information:
   See Authors' Addresses section

Intended usage:  COMMON

Restrictions on usage:  N/A

Author:  CAPPORT IETF WG

Change controller:  IETF

8.2. Captive Portal API Keys Registry πŸ¦‹

so IANA is making a brand new registry called "Captive Portal API Keys" 🧚 its whole job is reserving JSON keys for the Captive Portal API data structures. the starter lineup lives in Section 5, so go check that out if you want the full guest list πŸ’œ

every entry in this registry has these fields:

  • Key: the JSON key being registered, written as a string πŸ“
  • Type: the type of the JSON value stored, picked from one of the value types defined in [RFC8259] 🌸
  • Description: a short explanation of what the value means, how it might get used, and/or how clients should read it 🫢
  • Reference: a pointer to a spec that defines the key and explains how to use it ✨

new entries get handed out by IANA under the Specification Required policy [RFC8126] ngl this is lowkey a pretty serious bouncer situation πŸ’€ the designated expert is expected to check that there's real documentation for the new key in a permanent, publicly available spec, like an Internet-Draft or an RFC πŸ“œ they're also expected to make sure the new key has a clear meaning and won't cause confusion or overlap with the keys that already exist πŸ”₯ and if a key is for a niche, non-generic use case, especially one that isn't part of an IETF document, it's encouraged to wear a domain-specific prefix 🌈 main character energy, but make it specific πŸ’…


9. References 🌈

9.1. Normative References

Okay bestie, these are the must-read-first references πŸ“š they're the ones this document literally leans on.

[RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
           Requirement Levels", BCP 14, RFC 2119,
           DOI 10.17487/RFC2119, March 1997,
           <https://www.rfc-editor.org/info/rfc2119>.

[RFC2818]  Rescorla, E., "HTTP Over TLS", RFC 2818,
           DOI 10.17487/RFC2818, May 2000,
           <https://www.rfc-editor.org/info/rfc2818>.

[RFC5280]  Cooper, D., Santesson, S., Farrell, S., Boeyen, S.,
           Housley, R., and W. Polk, "Internet X.509 Public Key
           Infrastructure Certificate and Certificate Revocation List
           (CRL) Profile", RFC 5280, DOI 10.17487/RFC5280, May 2008,
           <https://www.rfc-editor.org/info/rfc5280>.

[RFC5905]  Mills, D., Martin, J., Ed., Burbank, J., and W. Kasch,
           "Network Time Protocol Version 4: Protocol and Algorithms
           Specification", RFC 5905, DOI 10.17487/RFC5905, June 2010,
           <https://www.rfc-editor.org/info/rfc5905>.

[RFC6066]  Eastlake 3rd, D., "Transport Layer Security (TLS)
           Extensions: Extension Definitions", RFC 6066,
           DOI 10.17487/RFC6066, January 2011,
           <https://www.rfc-editor.org/info/rfc6066>.

[RFC6125]  Saint-Andre, P. and J. Hodges, "Representation and
           Verification of Domain-Based Application Service Identity
           within Internet Public Key Infrastructure Using X.509
           (PKIX) Certificates in the Context of Transport Layer
           Security (TLS)", RFC 6125, DOI 10.17487/RFC6125, March
           2011, <https://www.rfc-editor.org/info/rfc6125>.

[RFC6960]  Santesson, S., Myers, M., Ankney, R., Malpani, A.,
           Galperin, S., and C. Adams, "X.509 Internet Public Key
           Infrastructure Online Certificate Status Protocol - OCSP",
           RFC 6960, DOI 10.17487/RFC6960, June 2013,
           <https://www.rfc-editor.org/info/rfc6960>.

[RFC7234]  Fielding, R., Ed., Nottingham, M., Ed., and J. Reschke,
           Ed., "Hypertext Transfer Protocol (HTTP/1.1): Caching",
           RFC 7234, DOI 10.17487/RFC7234, June 2014,
           <https://www.rfc-editor.org/info/rfc7234>.

[RFC8126]  Cotton, M., Leiba, B., and T. Narten, "Guidelines for
           Writing an IANA Considerations Section in RFCs", BCP 26,
           RFC 8126, DOI 10.17487/RFC8126, June 2017,
           <https://www.rfc-editor.org/info/rfc8126>.

[RFC8174]  Leiba, B., "Ambiguity of Uppercase vs Lowercase in RFC
           2119 Key Words", BCP 14, RFC 8174, DOI 10.17487/RFC8174,
           May 2017, <https://www.rfc-editor.org/info/rfc8174>.

[RFC8259]  Bray, T., Ed., "The JavaScript Object Notation (JSON) Data
           Interchange Format", STD 90, RFC 8259,
           DOI 10.17487/RFC8259, December 2017,
           <https://www.rfc-editor.org/info/rfc8259>.

9.2. Informative References

These ones are extra reading for the curious πŸ€“ useful background, not required homework.

[CAPPORT-ARCH]
           Larose, K., Dolson, D., and H. Liu, "CAPPORT
           Architecture", Work in Progress, Internet-Draft, draft-
           ietf-capport-architecture-08, 11 May 2020,
           <https://tools.ietf.org/html/draft-ietf-capport-
           architecture-08>.

[RFC7525]  Sheffer, Y., Holz, R., and P. Saint-Andre,
           "Recommendations for Secure Use of Transport Layer
           Security (TLS) and Datagram Transport Layer Security
           (DTLS)", BCP 195, RFC 7525, DOI 10.17487/RFC7525, May
           2015, <https://www.rfc-editor.org/info/rfc7525>.

[RFC8264]  Saint-Andre, P. and M. Blanchet, "PRECIS Framework:
           Preparation, Enforcement, and Comparison of
           Internationalized Strings in Application Protocols",
           RFC 8264, DOI 10.17487/RFC8264, October 2017,
           <https://www.rfc-editor.org/info/rfc8264>.

[RFC8910]  Kumari, W. and E. Kline, "Captive-Portal Identification in
           DHCP and Router Advertisement (RA)", RFC 8910,
           DOI 10.17487/RFC8910, September 2020,
           <https://www.rfc-editor.org/info/rfc8910>.

Acknowledgments πŸ’–

this work was kicked off by Mark Donnelly and Margaret Cullen 🫢 huge thank-you to everyone in the CAPPORT Working Group who dropped their input. you all literally made this happen πŸŽ€

Authors' Addresses 🌸

here's where to find the editors if you have questions πŸ’Œ

Tommy Pauly (editor)
Apple Inc.
One Apple Park Way
Cupertino, CA 95014
United States of America

Email: tpauly@apple.com


Darshak Thakore (editor)
CableLabs
858 Coal Creek Circle
Louisville, CO 80027
United States of America

Email: d.thakore@cablelabs.com
πŸ“¦ where this page comes from