Repository navigation
Expand file tree
/
Copy pathopenapi.yml
More file actions
321 lines (302 loc) · 14.7 KB
/
Copy pathopenapi.yml
File metadata and controls
321 lines (302 loc) · 14.7 KB
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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
openapi: "3.1.1"
info:
title: Monitor Breach Alerts API
description: |
Monitor's external API for Firefox clients.
Remote Settings owns breach facts, this API owns per-user resolution state. The client joins them on the HIBP breach name, returned here as `id`.
"Monitored" means the account's verified primary and secondary email addresses.
version: "1.0.0"
license:
name: MPL-2.0
identifier: MPL-2.0
# The web app already owns /api/v1.
servers:
- url: https://monitor.mozilla.org/api/firefox/v1
description: Production endpoint
- url: https://monitor-stage.allizom.org/api/firefox/v1
description: Stage endpoint
- url: https://dev.monitor.nonprod.webservices.mozgcp.net/api/firefox/v1
description: Dev endpoint
- url: http://localhost:6060/api/firefox/v1
description: Local development server
security:
- bearerAuth: []
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
# We introspect the token with FxA.
description: An OAuth access token granted by Firefox Accounts (FxA), carrying the `https://identity.mozilla.com/apps/monitor` scope. Treat it as opaque. We identify the user by the token's FxA uid only, never by cookies or a Monitor website session.
schemas:
Error:
type: object
description: Body of every non-2xx response we generate. Our WAF can answer first, and Next.js answers an unsupported method with a bare `405`. Neither carries this body nor a `code`. Branch on `code`. `message` is for logs and bug reports, it is not stable, so do not match on it.
required:
- code
- message
properties:
code:
description: Stable, machine-readable cause, kebab-case. Today `token-invalid`, `missing-scope`, `no-monitor-account`, `invalid-request`, `rate-limited`, `internal-error`, `unavailable`. We may add values in the future, so fall back to the status code for one you do not recognise.
type: string
message:
type: string
ResolvedDataClass:
description: A data class we report as resolved. `passwords` and `email-addresses` today, but this is an open set, so accept a value you do not recognise instead of rejecting the response.
type: string
BreachResolution:
description: One entry of a `POST /user/breaches/resolutions` body. Names a breach to mark resolved. We ignore unknown properties, so older servers accept newer clients.
type: object
required:
- breachId
properties:
breachId:
description: The HIBP breach name, as returned by `GET /user/breaches` in `id`.
type: string
UserBreachState:
type: object
description: One entry of `breachedAccounts`. What a single address has resolved for the breach holding it. Each address carries its own list, so two addresses in the same breach can differ.
required:
- email
- resolvedDataClasses
properties:
email:
description: A monitored address the breach affected.
type: string
format: email
resolvedDataClasses:
# No timestamp, we store a checked list.
description: Data classes this user has marked resolved for this breach, in the privacy panel or on the Monitor website. A breach is resolved once this array holds every `Passwords` or `Email addresses` the Remote Settings record lists. That record can carry other data classes, and only the ones this API serves appear here.
type: array
items:
$ref: "#/components/schemas/ResolvedDataClass"
UserBreach:
type: object
description: One item of the `GET /user/breaches` response. A breach, plus one `UserBreachState` for each monitored address it affected.
required:
- id
- breachedAccounts
properties:
id:
# Not the internal numeric breach id.
description: The HIBP breach name, for example `Adobe`. It is the `Name` of the matching `fxmonitor-breaches` record, matched exactly and case sensitively.
type: string
breachedAccounts:
type: array
minItems: 1
items:
$ref: "#/components/schemas/UserBreachState"
responses:
Unauthorized:
description: No usable token. The `Authorization` header is missing or malformed, the token is not an access token, or FxA reports it as inactive, for example expired or revoked. Get a new token from FxA and retry once.
headers:
WWW-Authenticate:
required: true
description: Always `Bearer`, per RFC 6750.
schema:
type: string
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
examples:
missingToken:
summary: No Bearer token in the `Authorization` header
value:
code: token-invalid
message: Missing or malformed token.
inactiveToken:
summary: FxA reports the token as inactive
value:
code: token-invalid
message: FxA reports this token as inactive.
Forbidden:
description: |
The token is valid but does not grant access. A new token will not help.
- `missing-scope`: the token lacks the Monitor scope. FxA should never issue such a token for this API, so this points to a client bug. Do not retry.
- `no-monitor-account`: this FxA account has no Monitor account, and this API never creates one. The user may sign up on the Monitor website later, but Monitor has no way to tell Firefox when they do, so ask again on your next scheduled poll.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
examples:
missingScope:
summary: Token does not carry the Monitor scope
value:
code: missing-scope
message: This token lacks the https://identity.mozilla.com/apps/monitor scope.
noMonitorAccount:
summary: No Monitor subscriber for this FxA account
value:
code: no-monitor-account
message: This Firefox account has no Monitor subscriber.
TooManyRequests:
description: Rate limited. Each account gets an hourly request budget, shared by both endpoints. Wait the interval in `Retry-After` if it is present, otherwise back off and retry.
headers:
Retry-After:
required: false
description: Seconds to wait before retrying.
schema:
type: integer
minimum: 1
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
code: rate-limited
message: Too many requests. Retry after 60 seconds.
InternalError:
description: Something failed on our side. Retry, and keep any state you already hold.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
code: internal-error
message: Monitor could not complete this request.
ServiceUnavailable:
description: Monitor is unavailable, or FxA could not verify the token. Safe to retry, after the interval in `Retry-After` when it is present.
headers:
Retry-After:
required: false
description: Seconds to wait before retrying.
schema:
type: integer
minimum: 1
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
code: unavailable
message: Monitor is temporarily unavailable.
paths:
/user/breaches:
get:
operationId: listUserBreaches
summary: List breaches affecting this user
# Passwords and emails, what the panel acts on.
description: |
Breaches that leaked passwords or email addresses, with resolution state for each monitored email the breach affected.
Sensitive breaches, as HIBP marks them, are never returned. The privacy panel must not surface one, so there is no parameter to ask for them. Remote Settings does carry them, flagged, because the credential manager is allowed to.
**Freshness.** Breach metadata comes from a catalog we refresh every 10 minutes, so a new breach can take that long to appear. The per user match is live on every request and is not cached, so there is no last known result to serve when it fails.
**Joining.** Almost every `id` resolves in `fxmonitor-breaches`, but the collection is slower than this API. It publishes daily, and each batch is signed only after a person reviews it, deliberately, because it ships to every Firefox. So a breach can be live here for days before Firefox sees it, and a HIBP rename retires an id. Skip an id you cannot resolve rather than erroring, and pick it up on a later poll.
**Cadence.** Poll no more than hourly, and once a day is enough. Breaches reach HIBP weeks or months after the incident, so polling harder gains nothing. Each account gets an hourly request budget, shared by both endpoints, and we answer the rest with a `429`.
**Unknown parameters.** We ignore query parameters we do not recognise, so adding one never breaks a request.
parameters:
- name: domain
in: query
required: false
description: Filter by the breached site's primary domain, matched exactly. A breach filed under `twitter.com` does not match `x.com`.
schema:
type: string
minLength: 1
examples:
example:
summary: Example domain
value: example.com
responses:
"200":
description: |
Breaches affecting emails this user is monitoring.
A breach missing from this list means we checked and this user is not affected by it. An empty array means that is true for every breach. A `200` is never a partial answer, so when the lookup fails we send a `503` instead.
headers:
Cache-Control:
description: Always `private, no-store`. This response is scoped to one user and must never be cached by a shared intermediary.
schema:
type: string
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/UserBreach"
examples:
hasBreaches:
summary: Breaches found
value:
- id: "Adobe"
breachedAccounts:
- email: "alice@example.com"
resolvedDataClasses: ["passwords"]
- id: "Facebook"
breachedAccounts:
- email: "alice@example.com"
resolvedDataClasses: []
noResults:
summary: No breaches found
value: []
"400":
description: A query parameter failed validation, for example an empty or repeated `domain`.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
code: invalid-request
message: "domain must not be empty."
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"429":
$ref: "#/components/responses/TooManyRequests"
"500":
$ref: "#/components/responses/InternalError"
"503":
$ref: "#/components/responses/ServiceUnavailable"
description: FxA could not verify the token, or we could not check this user against HIBP, so we do not know what they are affected by. Keep the breaches you already hold, do not clear them, and retry, after the interval in `Retry-After` when it is present.
/user/breaches/resolutions:
post:
operationId: createBreachResolutions
summary: Mark breaches resolved
description: |
Mark one or more breaches resolved, like "Mark as done" in the privacy panel. Resolutions only move one way, this API cannot unresolve a breach.
Each entry resolves every data class this API serves that the breach leaked, `passwords` and `email-addresses`, for every monitored address the breach affected. There is no way to target one address or data class.
We skip a `breachId` that matches no breach affecting this user, for example one HIBP renamed or retired, and still apply the rest. There is nothing to resolve for it, so there is nothing to retry.
Idempotent per `breachId`. Repeated entries merge, and order does not matter.
Atomic: one write covers every matched breach, so a request never applies halfway.
requestBody:
required: true
content:
application/json:
schema:
type: array
minItems: 1
items:
$ref: "#/components/schemas/BreachResolution"
examples:
resolveOneBreach:
summary: Resolve one breach
value:
- breachId: "Adobe"
resolveMultipleBreaches:
summary: Resolve multiple breaches
value:
- breachId: "Adobe"
- breachId: "LinkedIn"
responses:
"204":
description: Every matched breach is resolved. Unmatched ones were skipped.
"400":
description: The body is not a non-empty JSON array of objects, each with a non-empty string `breachId`. Unknown properties and query parameters are ignored, not rejected.
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
example:
code: invalid-request
message: "[0].breachId must be a non-empty string."
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"429":
$ref: "#/components/responses/TooManyRequests"
"500":
$ref: "#/components/responses/InternalError"
description: Something failed on our side. The batch may or may not be applied, so retry it, repeats are safe.
"503":
$ref: "#/components/responses/ServiceUnavailable"
description: FxA could not verify the token, we could not check this user against HIBP, or Monitor is unavailable. Nothing was written. Retry after the interval in `Retry-After` when it is present, otherwise later. Repeats are safe.