-
Notifications
You must be signed in to change notification settings - Fork 12
/
index.bs
227 lines (151 loc) · 13 KB
/
index.bs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
<pre class="metadata">
Title: Origin-bound one-time codes delivered via SMS
Shortname: sms-one-time-codes
Repository: wicg/sms-one-time-codes
URL: https://wicg.github.io/sms-one-time-codes/
Group: WICG
Status: CG-DRAFT
Level: None
Editor: Theresa O’Connor, w3cid 40614, Apple https://apple.com, [email protected]
Editor: Sam Goto, Google https://google.com, [email protected]
Abstract: This specification defines a way to format SMS messages for use with browser autofill features such as HTML's autocomplete=one-time-code.
Markup Shorthands: markdown yes, css no
Complain About: accidental-2119 true
</pre>
<pre class="link-defaults">
spec:infra; type:dfn; text:size; for:list
spec:infra; type:dfn; text:string
spec:url; type:dfn; text:scheme
</pre>
<div class="non-normative">
<h2 id="intro" class=no-num>Introduction</h2>
<em>This section is non-normative.</em>
Many websites deliver one-time codes over SMS. [[GSM-SMS]]
Without a standard format for such messages, programmatic extraction of codes from them has to rely on heuristics, which are often unreliable and error-prone. Additionally, without a mechanism for associating such codes with specific websites, users might be tricked into providing the code to malicious sites.
This specification defines a format for the delivery of one-time codes over SMS. This format associates the one-time code with a specific website.
</div>
<h2 id="infra">Infrastructure</h2>
This specification depends on the Infra Standard. [[!INFRA]]
<h2 id="origin-bound-one-time-codes">Origin-bound one-time codes</h2>
An <dfn export>origin-bound one-time code</dfn> is a [=tuple=] consisting of a top-level origin (an [=/origin=]), an embedded origin (an [=/origin=] or `null`), and a code (a [=string=]).
<div class=example>
((`"https"`, `"example.com"`, `null`, `null`), `null`, `"747723"`) is an [=origin-bound one-time code=] whose top-level origin is (`"https"`, `"example.com"`, `null`, `null`), whose embedded origin is `null`, and whose code is `"747723"`.
</div>
<div class=example>
((`"https"`, `"example.com"`, `null`, `null`), (`"https"`, `"ecommerce.example"`, `null`, `null`), `"747723"`) is an [=origin-bound one-time code=] whose origin is (`"https"`, `"example.com"`, `null`, `null`), whose embedded origin is (`"https"`, `"ecommerce.example"`, `null`, `null`), and whose code is `"747723"`.
</div>
<h3 id="usage">Usage</h3>
Many User Agents help users fill out forms on websites. Sites can use features like <a href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#attr-fe-autocomplete-one-time-code">`autocomplete=one-time-code`</a> to hint to User Agents that they could assist the user with providing a one-time code to the website. [[HTML]]
Note: This specification does not impose any requirements or restrictions on the use of one-time codes which are not [=origin-bound one-time codes=].
<!-- We should be able to reference autocomplete=one-time-code with Bikeshed syntax along the lines of <{html/autocomplete/one-time-code}>. See whatwg/html#5418. -->
User Agents determine whether or not to assist the user to provide an origin-bound one-time code to a website with [=origin-bound one-time code=] |otc| and {{Document}} |doc| by running these steps:
1. If |doc| is not the [=active document=] of a [=/browsing context=], return failure.
1. Let |context| be |doc|'s [=Document/browsing context=].
1. If |context| is a [=top-level browsing context=]. run these steps:
1. If |otc|'s embedded origin is not `null`, return failure.
1. If |otc|'s top-level origin is [=same origin=] with |doc|'s [=Document/origin=], return `"origin"`.
1. If |otc|'s top-level origin is [=same site=] with |doc|'s [=Document/origin=], return `"site"`.
1. Return failure.
1. If |otc|'s embedded origin is `null`, return failure.
1. Let |match type| be `"origin"`.
1. If |otc|'s embedded origin is not [=same origin=] with |doc|'s [=Document/origin=], set |match type| to `"site"`.
1. If |otc|'s embedded origin is not [=same site=] with |doc|'s [=Document/origin=], return failure.
1. Set |context| to its [=parent browsing context=].
1. While |context| is not a [=top-level browsing context=], run these steps:
1. If |context|'s [=active document=]'s [=Document/origin=] is [=same origin=] with neither |otc|'s embedded origin nor |otc|'s top-level origin, set |match type| to `"site"`.
1. If |context|'s [=active document=]'s [=Document/origin=] is [=same site=] with neither |otc|'s embedded origin nor |otc|'s top-level origin, return failure.
1. Set |context| to its [=parent browsing context=].
1. If |context| is not a [=top-level browsing context=], return failure.
1. If |context|'s [=active document=]'s [=Document/origin=] is [=same origin=] with |otc|'s top-level origin, return |match type|.
1. If |context|'s [=active document=]'s [=Document/origin=] is [=same site=] with |otc|'s top-level origin, return `"site"`.
1. Return failure.
If the above steps returned `"origin"` or `"site"`, the User Agent may assist the user with providing the [=origin-bound one-time code=]'s code to the website.
If the above steps returned `"site"`, the User Agent should indicate the [=origin-bound one-time code=]'s top-level and embedded origins to the user when assisting them.
If the above steps returned failure, the User Agent should not assist the user with providing the [=origin-bound one-time code=]'s code to the website.
Note: because the [=schemes=] of an [=origin-bound one-time code=]'s top-level and embedded origins are always `"https"`, assisting the user with providing [=origin-bound one-time codes=] is only available in [=secure contexts=].
<h2 id="format">Message format</h2>
An <dfn export>origin-bound one-time code message</dfn> is a [=string=] for which
<a lt="parse an origin-bound one-time code message">parsing an origin-bound one-time code message</a> successfully returns an [=origin-bound one-time code=].
<div class="non-normative">
<h3 id="authoring">Authoring</h3>
<em>This section is non-normative. [[#parsing]] is the normative text.</em>
[=Origin-bound one-time code messages=] can optionally begin with human-readable <dfn for="origin-bound one-time code message">explanatory text</dfn>. This consists of all but the last line of the message. The last line of the message contains both a <dfn for="origin-bound one-time code message">top-level host</dfn> and a <dfn for="origin-bound one-time code message">code</dfn>, each prefixed with a sigil: U+0040 (@) before the [=origin-bound one-time code message/top-level host=], and U+0023 (#) before the [=code=]. Following the [=code=], an <dfn for="origin-bound one-time code message">embedded host</dfn> can be specified. It is preceeded with a U+0040 (@) sigil.
<div class="example">
In the following [=origin-bound one-time code message=], the [=origin-bound one-time code message/top-level host=] is `"example.com"`, the [=code=] is `"747723"`, no [=origin-bound one-time code message/embedded host=] is specified, and the [=explanatory text=] is `"747723 is your ExampleCo authentication code.\n\n"`.
```
"747723 is your ExampleCo authentication code.
@example.com #747723"
```
</div>
<div class="example">
In the following [=origin-bound one-time code message=], the [=origin-bound one-time code message/top-level host=] is `"example.com"`, the [=code=] is `"747723"`, the [=origin-bound one-time code message/embedded host=] is `"ecommerce.example"`, and the [=explanatory text=] is `"747723 is your ExampleCo authentication code.\n\n"`.
```
"747723 is your ExampleCo authentication code.
@example.com #747723 @ecommerce.example"
```
</div>
The order of fields in the last line is always [=origin-bound one-time code message/top-level host=], [=code=], and [=origin-bound one-time code message/embedded host=] (if present). Nothing can come before the [=origin-bound one-time code message/top-level host=] in the last line.
<div class="example">
The message `"something @example.com #747723"` is not an [=origin-bound one-time code message=], because it doesn't start with the [=origin-bound one-time code message/top-level host=].
</div>
<div class="example">
The message `"#747723 @ecommerce.example @example.com"` is not an [=origin-bound one-time code message=], because the fields are in the wrong order.
</div>
Exactly one U+0020 (SPACE) separates the values in the last line of the message.
<div class="example">
The message `"@example.com code #747723"` is not an [=origin-bound one-time code message=], because several characters appear between the two values on the last line of the message.
</div>
Trailing text in the last line is ignored. This is because we might identify additional information to include in [=origin-bound one-time code messages=] in the future. If we do, new syntax could be introduced after the existing syntax in the last line.
<div class="example">
In the [=origin-bound one-time code message=] `"@example.com #747723 @ecommerce.example $future"`, the [=origin-bound one-time code message/top-level host=] is `"example.com"`, the [=code=] is `"747723"`, the [=origin-bound one-time code message/embedded host=] is `"ecommerce.example"`, and the [=explanatory text=] is `""`. The trailing text `" %future"` is ignored.
</div>
</div>
<h3 id="parsing">Parsing</h3>
To <dfn export type="abstract-op">parse an origin-bound one-time code message</dfn> from |message|, run these steps:
1. Let |line| be the [=last line=] of |message|, and |position| be 0.
1. If |position| points past the end of |line|, return failure.
1. Let |top-level host| be the result of [=extract a marked token|extracting a marked token=] from |line| at |position| with marker U+0040 (@).
1. If |top-level host| is failure, return failure.
1. Let |top-level origin| be the [=/origin=] (`"https"`, |top-level host|, `null`, `null`).
1. If |position| points past the end of |line|, return failure.
1. If the [=code point=] at |position| within |line| is not U+0020 (SPACE), return failure.
1. Advance |position| by 1.
1. If |position| points past the end of |line|, return failure.
1. Let |code| be the result of [=extract a marked token|extracting a marked token=] from |line| at |position| with marker U+0023 (#).
1. If |code| is failure, return failure.
1. Let |embedded origin| be null.
1. If |position| does not point past the end of |line|, and if the [=code point=] at |position| within |line| is U+0020 (SPACE), run the following steps:
1. Advance |position| by 1.
1. Let |embedded host| be the result of [=extract a marked token|extracting a marked token=] from |line| at |position| with marker U+0040 (@).
1. If |embedded host| is failure, set |embedded origin| to null. Otherwise, set |embedded origin| to the [=/origin=] (`"https"`, |embedded host|, `null`, `null`).
1. Return the [=origin-bound one-time code=] (|top-level origin|, |embedded origin|, |code|).
To <dfn type="abstract-op">extract a marked token</dfn> from |string| at |position| with [=code point=] |marker|, run the following steps:
1. If |position| points past the end of |string|, return failure.
1. If the [=code point=] at |position| within |string| is not |marker|, return failure.
1. Advance |position| by 1.
1. If |position| points past the end of |string|, return failure.
1. Let |token| be the result of [=collecting a sequence of code points=] which are not [=ASCII whitespace=] from |string| with |position|.
1. If |token| is the empty string, return failure.
1. Return |token|.
The <dfn type=abstract-op>last line</dfn> of |string| is the result of running these steps:
1. [=Normalize newlines=] in |string|.
1. Let |lines| be the result of <a lt="strictly split a string">strictly splitting</a> |string| on U+000A (LF).
1. Return the last item of |lines|.
<h2 id="security-considerations">Security considerations</h2>
This specification attempts to mitigate the phishing risk associated with the delivery of one-time codes over SMS by enabling User Agents to know what website the one-time code is intended for.
This specification does not attempt to mitigate other risks associated with the delivery of one-time codes over SMS, such as SMS spoofing, SIM swapping, SIM cloning, ISMI-catchers, or interception of the message by an untrusted party.
Sites would do well to consider using non-SMS technologies such as [[WEBAUTHN]] for authentication or verification.
<h2 id="privacy-considerations">Privacy considerations</h2>
Any party which has access to a user's SMS messages (such as the user's cellular carrier, mobile operating system, or anyone who intercepted the message) can learn that the user has an account on the service identified in an [=origin-bound one-time code message=] delivered over SMS.
On some platforms, User Agents might need access to all incoming SMS messages—even messages which are not [=origin-bound one-time code messages=]—in order to support the autofilling of [=origin-bound one-time codes=] delivered over SMS in [=origin-bound one-time code messages=].
<h2 id="acknowedgements" class="no-num">Acknowledgements</h2>
Many thanks to
Aaron Parecki,
Elaine Knight,
Eric Shepherd,
Eryn Wells,
Jay Mulani,
Ricky Mondello,
and
Steven Soneff
for their valuable feedback on this proposal.