fxa-auth-server/docs/api.md

3159 строки
92 KiB
Markdown

# Firefox Accounts authentication server API
This document is automatically generated.
If you are editing it,
read [this section](#this-document) first.
<!--begin-abstract-->
This document provides protocol-level details
of the Firefox Accounts auth server API.
For a prose description of the client/server protocol
and details on how each parameter is derived,
see the [API design document](https://wiki.mozilla.org/Identity/AttachedServices/KeyServerProtocol).
For a reference client implementation,
see [`mozilla/fxa-js-client`](https://github.com/mozilla/fxa-js-client).
<!--end-abstract-->
* [Overview](#overview)
* [URL structure](#url-structure)
* [Request format](#request-format)
* [Response format](#response-format)
* [Defined errors](#defined-errors)
* [Responses from intermediary servers](#responses-from-intermediary-servers)
* [Validation](#validation)
* [API endpoints](#api-endpoints)
* [Account](#account)
* [POST /account/create](#post-accountcreate)
* [POST /account/login](#post-accountlogin)
* [GET /account/status (:lock::unlock: sessionToken)](#get-accountstatus)
* [POST /account/status](#post-accountstatus)
* [GET /account/profile (:lock: sessionToken, oauthToken)](#get-accountprofile)
* [GET /account/keys (:lock: keyFetchToken)](#get-accountkeys)
* [POST /account/unlock/resend_code](#post-accountunlockresend_code)
* [POST /account/unlock/verify_code](#post-accountunlockverify_code)
* [POST /account/reset (:lock: accountResetToken)](#post-accountreset)
* [POST /account/destroy (:lock::unlock: sessionToken)](#post-accountdestroy)
* [Devices and sessions](#devices-and-sessions)
* [POST /account/device (:lock: sessionToken)](#post-accountdevice)
* [GET /account/device/commands (:lock: sessionToken)](#get-accountdevicecommands)
* [POST /account/devices/invoke_command (:lock: sessionToken)](#post-accountdevicesinvoke_command)
* [POST /account/devices/notify (:lock: sessionToken)](#post-accountdevicesnotify)
* [GET /account/devices (:lock: sessionToken)](#get-accountdevices)
* [GET /account/sessions (:lock: sessionToken)](#get-accountsessions)
* [POST /account/device/destroy (:lock: sessionToken)](#post-accountdevicedestroy)
* [Emails](#emails)
* [GET /recovery_email/status (:lock: sessionToken)](#get-recovery_emailstatus)
* [POST /recovery_email/resend_code (:lock: sessionToken)](#post-recovery_emailresend_code)
* [POST /recovery_email/verify_code](#post-recovery_emailverify_code)
* [GET /recovery_emails (:lock: sessionToken)](#get-recovery_emails)
* [POST /recovery_email (:lock: sessionToken)](#post-recovery_email)
* [POST /recovery_email/destroy (:lock: sessionToken)](#post-recovery_emaildestroy)
* [POST /recovery_email/set_primary (:lock: sessionToken)](#post-recovery_emailset_primary)
* [Password](#password)
* [POST /password/change/start](#post-passwordchangestart)
* [POST /password/change/finish (:lock: passwordChangeToken)](#post-passwordchangefinish)
* [POST /password/forgot/send_code](#post-passwordforgotsend_code)
* [POST /password/forgot/resend_code (:lock: passwordForgotToken)](#post-passwordforgotresend_code)
* [POST /password/forgot/verify_code (:lock: passwordForgotToken)](#post-passwordforgotverify_code)
* [GET /password/forgot/status (:lock: passwordForgotToken)](#get-passwordforgotstatus)
* [Recovery codes](#recovery-codes)
* [GET /recoveryCodes (:lock: sessionToken)](#get-recoverycodes)
* [POST /session/verify/recoveryCode (:lock: sessionToken)](#post-sessionverifyrecoverycode)
* [Recovery key](#recovery-key)
* [POST /recoveryKey (:lock: sessionToken)](#post-recoverykey)
* [GET /recoveryKey/{recoveryKeyId} (:lock: accountResetToken)](#get-recoverykeyrecoverykeyid)
* [POST /recoveryKey/exists (:lock::unlock: sessionToken)](#post-recoverykeyexists)
* [DELETE /recoveryKey (:lock: sessionToken)](#delete-recoverykey)
* [Session](#session)
* [POST /session/destroy (:lock: sessionToken)](#post-sessiondestroy)
* [POST /session/reauth (:lock: sessionToken)](#post-sessionreauth)
* [GET /session/status (:lock: sessionToken)](#get-sessionstatus)
* [POST /session/duplicate (:lock: sessionToken)](#post-sessionduplicate)
* [Sign](#sign)
* [POST /certificate/sign (:lock: sessionToken)](#post-certificatesign)
* [Signin codes](#signin-codes)
* [POST /signinCodes/consume](#post-signincodesconsume)
* [Sms](#sms)
* [POST /sms (:lock: sessionToken)](#post-sms)
* [GET /sms/status (:lock: sessionToken)](#get-smsstatus)
* [Token codes](#token-codes)
* [POST /session/verify/token (:lock: sessionToken)](#post-sessionverifytoken)
* [Totp](#totp)
* [POST /totp/create (:lock: sessionToken)](#post-totpcreate)
* [POST /totp/destroy (:lock: sessionToken)](#post-totpdestroy)
* [GET /totp/exists (:lock: sessionToken)](#get-totpexists)
* [POST /session/verify/totp (:lock: sessionToken)](#post-sessionverifytotp)
* [Unblock codes](#unblock-codes)
* [POST /account/login/send_unblock_code](#post-accountloginsend_unblock_code)
* [POST /account/login/reject_unblock_code](#post-accountloginreject_unblock_code)
* [Util](#util)
* [POST /get_random_bytes](#post-get_random_bytes)
* [GET /verify_email](#get-verify_email)
* [GET /complete_reset_password](#get-complete_reset_password)
* [Example flows](#example-flows)
* [Back-off protocol](#back-off-protocol)
* [This document](#this-document)
## Overview
### URL structure
<!--begin-url-structure-->
All requests use URLs of the form:
```
https://<base-URI>/v1/<endpoint-path>
```
Note that:
* All API access must be over a properly-validated HTTPS connection.
* The URL embeds a version identifier `v1`.
Future revisions of this API may introduce new version numbers.
* The base URI of the server may be configured on a per-client basis:
* For a list of development servers
see [Firefox Accounts deployments on MDN](https://developer.mozilla.org/en-US/Firefox_Accounts#Firefox_Accounts_deployments).
* The canonical URL for Mozilla's hosted Firefox Accounts server
is `https://api.accounts.firefox.com/v1`.
<!--end-url-structure-->
### Request format
<!--begin-request-format-->
Requests that require authentication
use [Hawk](https://github.com/hueniverse/hawk) request signatures.
These endpoints are marked
with a :lock: icon.
Where the authentication is optional,
there will also be a :question: icon.
All POST requests must have a content-type of `application/json`
with a UTF8-encoded JSON body
and must specify the content-length header.
Keys and other binary data are included in the JSON
as hexadecimal strings.
The following request headers may be specified
to influence the behaviour of the server:
* `Accept-Language`
may be used to localize
emails and SMS messages.
<!--end-request-format-->
### Response format
<!--begin-response-format-->
All requests receive
a JSON response body
with a `Content-Type: application/json` header
and appropriate `Content-Length` set.
The body structure
depends on the endpoint returning it.
Successful responses will have
an HTTP status code of 200
and a `Timestamp` header
that contains the current server time
in seconds since the epoch.
Error responses caused by invalid client behaviour
will have an HTTP status code in the 4xx range.
Error responses caused by server-side problems
will have an HTTP status code in the 5xx range.
Failures due to invalid behavior from the client
To simplify error handling for the client,
the type of error is indicated by both
a defined HTTP status code
and an application-specific `errno` in the body.
For example:
```js
{
"code": 400, // Matches the HTTP status code
"errno": 107, // Stable application-level error number
"error": "Bad Request", // String description of the error type
"message": "Invalid parameter in request body", // Specific error message
"info": "https://docs.dev.lcip.og/errors/1234" // Link to more information
}
```
Responses for some errors may include additional parameters.
<!--end-response-format-->
#### Defined errors
The currently-defined values
for `code` and `errno` are:
* `code: 400, errno: 100`:
Incorrect Database Patch Level
* `code: 400, errno: 101`:
Account already exists
* `code: 400, errno: 102`:
Unknown account
* `code: 400, errno: 103`:
Incorrect password
* `code: 400, errno: 104`:
Unverified account
* `code: 400, errno: 105`:
Invalid verification code
* `code: 400, errno: 106`:
Invalid JSON in request body
* `code: 400, errno: 107`:
Invalid parameter in request body
* `code: 400, errno: 108`:
Missing parameter in request body
* `code: 401, errno: 109`:
Invalid request signature
* `code: 401, errno: 110`:
Invalid authentication token in request signature
* `code: 401, errno: 111`:
Invalid timestamp in request signature
* `code: 411, errno: 112`:
Missing content-length header
* `code: 413, errno: 113`:
Request body too large
* `code: 429, errno: 114`:
Client has sent too many requests
* `code: 401, errno: 115`:
Invalid nonce in request signature
* `code: 410, errno: 116`:
This endpoint is no longer supported
* `code: 400, errno: 120`:
Incorrect email case
* `code: 400, errno: 123`:
Unknown device
* `code: 400, errno: 124`:
Session already registered by another device
* `code: 400, errno: 125`:
The request was blocked for security reasons
* `code: 400, errno: 126`:
Account must be reset
* `code: 400, errno: 127`:
Invalid unblock code
* `code: 400, errno: 129`:
Invalid phone number
* `code: 400, errno: 130`:
Invalid region
* `code: 400, errno: 131`:
Invalid message id
* `code: 500, errno: 132`:
Message rejected
* `code: 400, errno: 133`:
Email account sent complaint
* `code: 400, errno: 134`:
Email account hard bounced
* `code: 400, errno: 135`:
Email account soft bounced
* `code: 400, errno: 136`:
Email already exists
* `code: 400, errno: 137`:
Can not delete primary email
* `code: 400, errno: 138`:
Unverified session
* `code: 400, errno: 139`:
Can not add secondary email that is same as your primary
* `code: 400, errno: 140`:
Email already exists
* `code: 400, errno: 141`:
Email already exists
* `code: 400, errno: 142`:
Sign in with this email type is not currently supported
* `code: 400, errno: 143`:
Unknown email
* `code: 400, errno: 144`:
Email already exists
* `code: 400, errno: 145`:
Reset password with this email type is not currently supported
* `code: 400, errno: 146`:
Invalid signin code
* `code: 400, errno: 147`:
Can not change primary email to an unverified email
* `code: 400, errno: 148`:
Can not change primary email to an email that does not belong to this account
* `code: 400, errno: 149`:
This email can not currently be used to login
* `code: 400, errno: 150`:
Can not resend email code to an email that does not belong to this account
* `code: 400, errno: 151`:
Failed to send email
* `code: 400, errno: 152`:
Invalid token verification code
* `code: 400, errno: 153`:
Expired token verification code
* `code: 400, errno: 154`:
A TOTP token already exists for this account.
* `code: 400, errno: 155`:
A TOTP token not found.
* `code: 400, errno: 156`:
Recovery code not found.
* `code: 400, errno: 157`:
Unavailable device command.
* `code: 400, errno: 158`:
Recovery key not found.
* `code: 400, errno: 159`:
Recovery key is not valid.
* `code: 400, errno: 160`:
This request requires two step authentication enabled on your account.
* `code: 400, errno: 161`:
Recovery key already exists.
* `code: 503, errno: 201`:
Service unavailable
* `code: 503, errno: 202`:
Feature not enabled
* `code: 500, errno: 203`:
A backend service request failed.
* `code: 500, errno: 999`:
Unspecified error
The following errors
include additional response properties:
* `errno: 100`: level, levelRequired
* `errno: 101`: email
* `errno: 102`: email
* `errno: 103`: email
* `errno: 105`
* `errno: 107`: validation
* `errno: 108`: param
* `errno: 111`: serverTime
* `errno: 114`: retryAfter, retryAfterLocalized, verificationMethod, verificationReason
* `errno: 120`: email
* `errno: 124`: deviceId
* `errno: 125`: verificationMethod, verificationReason
* `errno: 126`: email
* `errno: 130`: region
* `errno: 132`: reason, reasonCode
* `errno: 133`: bouncedAt
* `errno: 134`: bouncedAt
* `errno: 135`: bouncedAt
* `errno: 152`
* `errno: 153`
* `errno: 201`: retryAfter
* `errno: 202`: retryAfter
* `errno: 203`: service, operation
#### Responses from intermediary servers
<!--begin-responses-from-intermediary-servers-->
As with any HTTP-based API,
clients must handle standard errors that may be returned
by proxies, load-balancers or other intermediary servers.
These non-application responses can be identified
by the absence of a correctly-formatted JSON response body.
Common examples include:
* `413 Request Entity Too Large`:
may be returned by an upstream proxy server.
* `502 Gateway Timeout`:
may be returned if a load-balancer can't connect to application servers.
<!--end-responses-from-intermediary-servers-->
### Validation
In the documentation that follows,
some properties of requests and responses
are validated by common code
that has been refactored and extracted.
For reference,
those common validations are defined here.
#### lib/routes/validators
* `HEX_STRING`: `/^(?:[a-fA-F0-9]{2})+$/`
* `BASE_36`: `/^[a-zA-Z0-9]*$/`
* `URL_SAFE_BASE_64`: `/^[A-Za-z0-9_-]+$/`
* `DISPLAY_SAFE_UNICODE`: `/^(?:[^\u0000-\u001F\u007F\u0080-\u009F\u2028-\u2029\uD800-\uDFFF\uE000-\uF8FF\uFFF9-\uFFFF])*$/`
* `DISPLAY_SAFE_UNICODE_WITH_NON_BMP`: `/^(?:[^\u0000-\u001F\u007F\u0080-\u009F\u2028-\u2029\uE000-\uF8FF\uFFF9-\uFFFF])*$/`
* `service`: `string, max(16), regex(/^[a-zA-Z0-9\-]*$/g)`
* `verificationMethod`: `string, valid()`
* `authPW`: `string, length(64), regex(HEX_STRING), required`
* `wrapKb`: `string, length(64), regex(/^(?:[a-fA-F0-9]{2})+$/)`
* `recoveryKeyId`: `string, regex(HEX_STRING), max(32)`
* `recoveryData`: `string, regex(/[a-zA-Z0-9.]/), max(1024), required`
* `E164_NUMBER`: `/^\+[1-9]\d{1,14}$/`
* `DIGITS`: `/^[0-9]+$/`
* `DEVICE_COMMAND_NAME`: `/^[a-zA-Z0-9._\/\-:]{1,100}$/`
* `IP_ADDRESS`: `string, ip`
#### lib/metrics/context
* `SCHEMA`: object({
* `deviceId`: string, length(32), regex(HEX_STRING), optional
* `flowId`: string, length(64), regex(HEX_STRING), optional
* `flowBeginTime`: number, integer, positive, optional
* `utmCampaign`: string, optional
* `utmContent`: string, optional
* `utmMedium`: string, optional
* `utmSource`: string, optional
* `utmTerm`: string, optional
}), unknown(false), and('flowId', 'flowBeginTime')
* `schema`: SCHEMA.optional
* `requiredSchema`: SCHEMA.required
#### lib/features
* `schema`: array, items(string), optional
#### lib/devices
* `schema`: {
* `id`: isA.string.length(32).regex(HEX_STRING)
* `location`: isA.object({
* `city`: isA.string.optional.allow(null)
* `country`: isA.string.optional.allow(null)
* `state`: isA.string.optional.allow(null)
* `stateCode`: isA.string.optional.allow(null)
* })
* `name`: isA.string.max(255).regex(DISPLAY_SAFE_UNICODE_WITH_NON_BMP)
* `nameResponse`: isA.string.max(255)
* `type`: isA.string.max(16)
* `pushCallback`: validators.pushCallbackUrl({ scheme: 'https' }).regex(PUSH_SERVER_REGEX).max(255).allow('')
* `pushPublicKey`: isA.string.max(88).regex(URL_SAFE_BASE_64).allow('')
* `pushAuthKey`: isA.string.max(24).regex(URL_SAFE_BASE_64).allow('')
* `pushEndpointExpired`: isA.boolean.strict
* `availableCommands`: isA.object.pattern(validators.DEVICE_COMMAND_NAME
* `isA.string.max(2048))
}
## API endpoints
### Account
#### POST /account/create
<!--begin-route-post-accountcreate-->
Creates a user account.
The client provides the email address
with which this account will be associated
and a stretched password.
Stretching is detailed
on the [onepw](https://github.com/mozilla/fxa-auth-server/wiki/onepw-protocol#creating-the-account) wiki page.
This endpoint may send a verification email to the user.
Callers may optionally provide the `service` parameter
to indicate which service they are acting on behalf of.
This is an opaque alphanumeric token
that will be embedded in the verification link
as a query parameter.
Creating an account also logs in.
The response contains a `sessionToken`
and, optionally, a `keyFetchToken`
if the url has a query parameter of `keys=true`.
<!--end-route-post-accountcreate-->
##### Query parameters
* `keys`: *boolean, optional*
<!--begin-query-param-post-accountcreate-keys-->
Indicates whether a key-fetch token should be returned in the success response.
<!--end-query-param-post-accountcreate-keys-->
* `service`: *validators.service*
<!--begin-query-param-post-accountcreate-service-->
Opaque alphanumeric token to be included in verification links.
<!--end-query-param-post-accountcreate-service-->
##### Request body
* `email`: *validators.email.required*
<!--begin-request-body-post-accountcreate-email-->
The primary email for this account.
<!--end-request-body-post-accountcreate-email-->
* `authPW`: *validators.authPW*
<!--begin-request-body-post-accountcreate-authPW-->
The PBKDF2/HKDF-stretched password as a hex string.
<!--end-request-body-post-accountcreate-authPW-->
* `preVerified`: *boolean*
<!--begin-request-body-post-accountcreate-preVerified-->
<!--end-request-body-post-accountcreate-preVerified-->
* `service`: *validators.service*
<!--begin-request-body-post-accountcreate-service-->
Opaque alphanumeric token to be included in verification links.
<!--end-request-body-post-accountcreate-service-->
* `redirectTo`: *validators.redirectTo(config.smtp.redirectDomain).optional*
<!--begin-request-body-post-accountcreate-redirectTo-->
URL that the client should be redirected to after handling the request.
<!--end-request-body-post-accountcreate-redirectTo-->
* `resume`: *string, max(2048), optional*
<!--begin-request-body-post-accountcreate-resume-->
Opaque URL-encoded string to be included in the verification link as a query parameter.
<!--end-request-body-post-accountcreate-resume-->
* `metricsContext`: *metricsContext.schema*
<!--begin-request-body-post-accountcreate-metricsContext-->
<!--end-request-body-post-accountcreate-metricsContext-->
##### Response body
* `uid`: *string, regex(HEX_STRING), required*
<!--begin-response-body-post-accountcreate-uid-->
<!--end-response-body-post-accountcreate-uid-->
* `sessionToken`: *string, regex(HEX_STRING), required*
<!--begin-response-body-post-accountcreate-sessionToken-->
<!--end-response-body-post-accountcreate-sessionToken-->
* `keyFetchToken`: *string, regex(HEX_STRING), optional*
<!--begin-response-body-post-accountcreate-keyFetchToken-->
<!--end-response-body-post-accountcreate-keyFetchToken-->
* `authAt`: *number, integer*
<!--begin-response-body-post-accountcreate-authAt-->
Authentication time for the session (seconds since epoch).
<!--end-response-body-post-accountcreate-authAt-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 101`:
Account already exists
* `code: 400, errno: 144`:
Email already exists
#### POST /account/login
<!--begin-route-post-accountlogin-->
Obtain a `sessionToken` and, optionally, a `keyFetchToken` if `keys=true`.
<!--end-route-post-accountlogin-->
##### Query parameters
* `keys`: *boolean, optional*
<!--begin-query-param-post-accountlogin-keys-->
Indicates whether a key-fetch token should be returned in the success response.
<!--end-query-param-post-accountlogin-keys-->
* `service`: *validators.service*
<!--begin-query-param-post-accountlogin-service-->
Opaque alphanumeric token to be included in verification links.
<!--end-query-param-post-accountlogin-service-->
* `verificationMethod`: *validators.verificationMethod.optional*
<!--begin-query-param-post-accountlogin-verificationMethod-->
If this param is specified, it forces the login to be verified using the
specified method.
Currently supported methods:
* `email`
* Sends an email with a confirmation link.
* `email-2fa`
* Sends an email with a confirmation code.
* `email-captcha`
* Sends an email with an unblock code.
<!--end-query-param-post-accountlogin-verificationMethod-->
##### Request body
* `email`: *validators.email.required*
<!--begin-request-body-post-accountlogin-email-->
The primary email for this account.
<!--end-request-body-post-accountlogin-email-->
* `authPW`: *validators.authPW*
<!--begin-request-body-post-accountlogin-authPW-->
The PBKDF2/HKDF stretched password as a hex string.
<!--end-request-body-post-accountlogin-authPW-->
* `service`: *validators.service*
<!--begin-request-body-post-accountlogin-service-->
Opaque alphanumeric token to be included in verification links.
<!--end-request-body-post-accountlogin-service-->
* `redirectTo`: *validators.redirectTo(config.smtp.redirectDomain).optional*
<!--begin-request-body-post-accountlogin-redirectTo-->
<!--end-request-body-post-accountlogin-redirectTo-->
* `resume`: *string, optional*
<!--begin-request-body-post-accountlogin-resume-->
Opaque URL-encoded string to be included in the verification link as a query parameter.
<!--end-request-body-post-accountlogin-resume-->
* `reason`: *string, max(16), optional*
<!--begin-request-body-post-accountlogin-reason-->
Alphanumeric string indicating the reason for establishing a new session; may be "login" (the default) or "reconnect".
<!--end-request-body-post-accountlogin-reason-->
* `unblockCode`: *signinUtils.validators.UNBLOCK_CODE*
<!--begin-request-body-post-accountlogin-unblockCode-->
Alphanumeric code used to unblock certain rate-limitings.
<!--end-request-body-post-accountlogin-unblockCode-->
* `metricsContext`: *metricsContext.schema*
<!--begin-request-body-post-accountlogin-metricsContext-->
<!--end-request-body-post-accountlogin-metricsContext-->
* `originalLoginEmail`: *validators.email.optional*
<!--begin-request-body-post-accountlogin-originalLoginEmail-->
This parameter is the original email used to login with. Typically, it is specified after a user logins with a different email case, or changed their primary email address.
<!--end-request-body-post-accountlogin-originalLoginEmail-->
* `verificationMethod`: *validators.verificationMethod.optional*
<!--begin-request-body-post-accountlogin-verificationMethod-->
If this param is specified, it forces the login to be verified using the
specified method.
Currently supported methods:
* `email`
* Sends an email with a confirmation link.
* `email-2fa`
* Sends an email with a confirmation code.
* `email-captcha`
* Sends an email with an unblock code.
<!--end-request-body-post-accountlogin-verificationMethod-->
##### Response body
* `uid`: *string, regex(HEX_STRING), required*
<!--begin-response-body-post-accountlogin-uid-->
<!--end-response-body-post-accountlogin-uid-->
* `sessionToken`: *string, regex(HEX_STRING), required*
<!--begin-response-body-post-accountlogin-sessionToken-->
<!--end-response-body-post-accountlogin-sessionToken-->
* `keyFetchToken`: *string, regex(HEX_STRING), optional*
<!--begin-response-body-post-accountlogin-keyFetchToken-->
<!--end-response-body-post-accountlogin-keyFetchToken-->
* `verificationMethod`: *string, optional*
<!--begin-response-body-post-accountlogin-verificationMethod-->
The medium for how the user can verify.
<!--end-response-body-post-accountlogin-verificationMethod-->
* `verificationReason`: *string, optional*
<!--begin-response-body-post-accountlogin-verificationReason-->
The authentication method that required additional verification.
<!--end-response-body-post-accountlogin-verificationReason-->
* `verified`: *boolean, required*
<!--begin-response-body-post-accountlogin-verified-->
<!--end-response-body-post-accountlogin-verified-->
* `authAt`: *number, integer*
<!--begin-response-body-post-accountlogin-authAt-->
Authentication time for the session (seconds since epoch).
<!--end-response-body-post-accountlogin-authAt-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 102`:
Unknown account
* `code: 400, errno: 103`:
Incorrect password
* `code: 400, errno: 125`:
The request was blocked for security reasons
* `code: 400, errno: 127`:
Invalid unblock code
* `code: 400, errno: 142`:
Sign in with this email type is not currently supported
* `code: 400, errno: 149`:
This email can not currently be used to login
* `code: 400, errno: 160`:
This request requires two step authentication enabled on your account.
#### GET /account/status
:lock::unlock: Optionally HAWK-authenticated with session token
<!--begin-route-get-accountstatus-->
Gets the status of an account.
<!--end-route-get-accountstatus-->
##### Query parameters
* `uid`: *string, min(32), max(32), regex(validators.HEX_STRING)*
<!--begin-query-param-get-accountstatus-uid-->
<!--end-query-param-get-accountstatus-uid-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 108`:
Missing parameter in request body
#### POST /account/status
<!--begin-route-post-accountstatus-->
Gets the status of an account
without exposing user data
through query params.
This endpoint is rate limited
by [fxa-customs-server](https://github.com/mozilla/fxa-customs-server).
<!--end-route-post-accountstatus-->
##### Request body
* `email`: *validators.email.required*
<!--begin-request-body-post-accountstatus-email-->
<!--end-request-body-post-accountstatus-email-->
##### Response body
* `exists`: *boolean, required*
<!--begin-response-body-post-accountstatus-exists-->
<!--end-response-body-post-accountstatus-exists-->
#### GET /account/profile
:lock: authenticated with OAuth bearer token, or HAWK-authenticated with session token
<!--begin-route-get-accountprofile-->
Get the email and locale of a user.
If an OAuth bearer token is used,
the values returned depend on
the scopes that the token is authorized for:
* `email` requires `profile:email` scope.
* `locale` requires `profile:locale` scope.
* `authenticationMethods` and `authenticatorAssuranceLevel` require `profile:amr` scope.
The `profile` scope includes all the above sub-scopes.
<!--end-route-get-accountprofile-->
##### Response body
* `email`: *string, optional*
<!--begin-response-body-get-accountprofile-email-->
<!--end-response-body-get-accountprofile-email-->
* `locale`: *string, optional, allow(null)*
<!--begin-response-body-get-accountprofile-locale-->
<!--end-response-body-get-accountprofile-locale-->
* `authenticationMethods`: *array, items(string, required), optional*
<!--begin-response-body-get-accountprofile-authenticationMethods-->
<!--end-response-body-get-accountprofile-authenticationMethods-->
* `authenticatorAssuranceLevel`: *number, min(0)*
<!--begin-response-body-get-accountprofile-authenticatorAssuranceLevel-->
<!--end-response-body-get-accountprofile-authenticatorAssuranceLevel-->
#### GET /account/keys
:lock: HAWK-authenticated with key fetch token
<!--begin-route-get-accountkeys-->
Get the base-16 bundle of encrypted `kA|wrapKb`.
The return value must be decrypted
with a key derived from `keyFetchToken`,
then `wrapKb` must be further decrypted
with a key derived from the user's password.
Since `keyFetchToken` is single-use,
this can only be done once per session.
Note that `keyFetchToken` is consumed
regardless of whether the request succeeds or fails.
This request will fail
unless the account's email address and current session
has been verified.
<!--end-route-get-accountkeys-->
##### Response body
* `bundle`: *string, regex(validators.HEX_STRING)*
<!--begin-response-body-get-accountkeys-bundle-->
See [decrypting the bundle](https://wiki.mozilla.org/Identity/AttachedServices/KeyServerProtocol#Decrypting_the_getToken2_Response)
for information on how to extract `kA|wrapKb`
from the bundle.
<!--end-response-body-get-accountkeys-bundle-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 104`:
Unverified account
#### POST /account/unlock/resend_code
<!--begin-route-post-accountunlockresend_code-->
This endpoint is deprecated.
<!--end-route-post-accountunlockresend_code-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 410, errno: 116`:
This endpoint is no longer supported
#### POST /account/unlock/verify_code
<!--begin-route-post-accountunlockverify_code-->
This endpoint is deprecated.
<!--end-route-post-accountunlockverify_code-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 410, errno: 116`:
This endpoint is no longer supported
#### POST /account/reset
:lock: HAWK-authenticated with account reset token
<!--begin-route-post-accountreset-->
This sets the account password
and resets `wrapKb` to a new random value.
Account reset tokens are single-use
and consumed regardless of
whether the request succeeds or fails.
They are returned by
the `POST /password/forgot/verify_code` endpoint.
The caller can optionally request
a new `sessionToken` and `keyFetchToken`.
<!--end-route-post-accountreset-->
##### Query parameters
* `keys`: *boolean, optional*
<!--begin-query-param-post-accountreset-keys-->
Indicates whether a new `keyFetchToken` is required, default to `false`.
<!--end-query-param-post-accountreset-keys-->
##### Request body
* `authPW`: *validators.authPW*
<!--begin-request-body-post-accountreset-authPW-->
The PBKDF2/HKDF-stretched password as a hex string.
<!--end-request-body-post-accountreset-authPW-->
* `wrapKb`: *validators.wrapKb.optional*
<!--begin-request-body-post-accountreset-wrapKb-->
<!--end-request-body-post-accountreset-wrapKb-->
* `recoveryKeyId`: *validators.recoveryKeyId.optional*
<!--begin-request-body-post-accountreset-recoveryKeyId-->
<!--end-request-body-post-accountreset-recoveryKeyId-->
* `sessionToken`: *boolean, optional*
<!--begin-request-body-post-accountreset-sessionToken-->
Indicates whether a new `sessionToken` is required, default to `false`.
<!--end-request-body-post-accountreset-sessionToken-->
* `metricsContext`: *metricsContext.schema*
<!--begin-request-body-post-accountreset-metricsContext-->
<!--end-request-body-post-accountreset-metricsContext-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 108`:
Missing parameter in request body
#### POST /account/destroy
:lock::unlock: Optionally HAWK-authenticated with session token
<!--begin-route-post-accountdestroy-->
Deletes an account.
All stored data is erased.
The client should seek user confirmation first.
The client should erase data
stored on any attached services
before deleting the user's account data.
<!--end-route-post-accountdestroy-->
##### Request body
* `email`: *validators.email.required*
<!--begin-request-body-post-accountdestroy-email-->
Primary email address of the account.
<!--end-request-body-post-accountdestroy-email-->
* `authPW`: *validators.authPW*
<!--begin-request-body-post-accountdestroy-authPW-->
The PBKDF2/HKDF-stretched password as a hex string.
<!--end-request-body-post-accountdestroy-authPW-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 103`:
Incorrect password
* `code: 400, errno: 138`:
Unverified session
### Devices and sessions
#### POST /account/device
:lock: HAWK-authenticated with session token
<!--begin-route-post-accountdevice-->
Either:
* Registers a new device for this session
if no device id is specified, or;
* Updates existing device details for this session
if a device id is specified.
If no device id is specified,
both `name` and `type` must be provided.
If a device id is specified,
at least one of `name`, `type`, `pushCallback`
or the tuple `{ pushCallback, pushPublicKey, pushAuthKey }`
must be present.
Beware that if you provide `pushCallback`
without the pair `{ pushPublicKey, pushAuthKey }`,
both of those keys will be reset
to the empty string.
`pushEndpointExpired` will be reset to false on update if the tuple
`{ pushCallback, pushPublicKey, pushAuthKey }` is specified.
Devices should register with this endpoint
before attempting to obtain a signed certificate
and perform their first sync,
so that an appropriate device name
can be made available to other connected devices.
<!--end-route-post-accountdevice-->
##### Request body
* `id`: *DEVICES_SCHEMA.id.required*
<!--begin-request-body-post-accountdevice-id-->
<!--end-request-body-post-accountdevice-id-->
* `name`: *DEVICES_SCHEMA.name.optional*;<br />or *DEVICES_SCHEMA.name.required*
<!--begin-request-body-post-accountdevice-name-->
<!--end-request-body-post-accountdevice-name-->
* `type`: *DEVICES_SCHEMA.type.optional*;<br />or *DEVICES_SCHEMA.type.required*
<!--begin-request-body-post-accountdevice-type-->
<!--end-request-body-post-accountdevice-type-->
* `pushCallback`: *DEVICES_SCHEMA.pushCallback.optional*
<!--begin-request-body-post-accountdevice-pushCallback-->
<!--end-request-body-post-accountdevice-pushCallback-->
* `pushPublicKey`: *DEVICES_SCHEMA.pushPublicKey.optional*
<!--begin-request-body-post-accountdevice-pushPublicKey-->
<!--end-request-body-post-accountdevice-pushPublicKey-->
* `pushAuthKey`: *DEVICES_SCHEMA.pushAuthKey.optional*
<!--begin-request-body-post-accountdevice-pushAuthKey-->
<!--end-request-body-post-accountdevice-pushAuthKey-->
* `availableCommands`: *DEVICES_SCHEMA.availableCommands.optional*
<!--begin-request-body-post-accountdevice-availableCommands-->
<!--end-request-body-post-accountdevice-availableCommands-->
* `capabilities`: *array, length(0), optional*
<!--begin-request-body-post-accountdevice-capabilities-->
<!--end-request-body-post-accountdevice-capabilities-->
##### Response body
* `id`: *DEVICES_SCHEMA.id.required*
<!--begin-response-body-post-accountdevice-id-->
<!--end-response-body-post-accountdevice-id-->
* `createdAt`: *number, positive, optional*
<!--begin-response-body-post-accountdevice-createdAt-->
<!--end-response-body-post-accountdevice-createdAt-->
* `name`: *DEVICES_SCHEMA.nameResponse.optional*
<!--begin-response-body-post-accountdevice-name-->
<!--end-response-body-post-accountdevice-name-->
* `type`: *DEVICES_SCHEMA.type.optional*
<!--begin-response-body-post-accountdevice-type-->
<!--end-response-body-post-accountdevice-type-->
* `pushCallback`: *DEVICES_SCHEMA.pushCallback.optional*
<!--begin-response-body-post-accountdevice-pushCallback-->
<!--end-response-body-post-accountdevice-pushCallback-->
* `pushPublicKey`: *DEVICES_SCHEMA.pushPublicKey.optional*
<!--begin-response-body-post-accountdevice-pushPublicKey-->
<!--end-response-body-post-accountdevice-pushPublicKey-->
* `pushAuthKey`: *DEVICES_SCHEMA.pushAuthKey.optional*
<!--begin-response-body-post-accountdevice-pushAuthKey-->
<!--end-response-body-post-accountdevice-pushAuthKey-->
* `pushEndpointExpired`: *DEVICES_SCHEMA.pushEndpointExpired.optional*
<!--begin-response-body-post-accountdevice-pushEndpointExpired-->
<!--end-response-body-post-accountdevice-pushEndpointExpired-->
* `availableCommands`: *DEVICES_SCHEMA.availableCommands.optional*
<!--begin-response-body-post-accountdevice-availableCommands-->
<!--end-response-body-post-accountdevice-availableCommands-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 107`:
Invalid parameter in request body
* `code: 503, errno: 202`:
Feature not enabled
#### GET /account/device/commands
:lock: HAWK-authenticated with session token
<!--begin-route-get-accountdevicecommands-->
Fetches commands enqueued for the current device
by prior calls to `/account/devices/invoke_command`.
The device can page through the enqueued commands
by using the `index` and `limit` parameters.
For more details,
see the [device registration](device_registration.md) docs.
<!--end-route-get-accountdevicecommands-->
##### Query parameters
* `index`: *number, optional*
<!--begin-query-param-get-accountdevicecommands-index-->
The index of the most recently seen command item.
Only commands enqueued after the given index will be returned.
<!--end-query-param-get-accountdevicecommands-index-->
* `limit`: *number, optional, min(0), max(100), default(100)*
<!--begin-query-param-get-accountdevicecommands-limit-->
The maximum number of commands to return.
The default and maximum value for `limit` is 100.
<!--end-query-param-get-accountdevicecommands-limit-->
##### Response body
* `index`: *number, required*
<!--begin-response-body-get-accountdevicecommands-index-->
The largest index of the commands returned in this response.
This value can be passed as the `index` parameter
in subsequent calls in order to page through all the items.
<!--end-response-body-get-accountdevicecommands-index-->
* `last`: *boolean, optional*
<!--begin-response-body-get-accountdevicecommands-last-->
Indicates whether more commands and enqueued than could
be returned within the specific limit.
<!--end-response-body-get-accountdevicecommands-last-->
* `messages`: *array, items(object({ index: number, required, data: object({ command: string, max(255), required, payload: object, required, sender: DEVICES_SCHEMA.id, optional }), required })), optional*
<!--begin-response-body-get-accountdevicecommands-messages-->
An array of individual commands for the device to process.
<!--end-response-body-get-accountdevicecommands-messages-->
#### POST /account/devices/invoke_command
:lock: HAWK-authenticated with session token
<!--begin-route-post-accountdevicesinvoke_command-->
Enqueues a command to be invoked on a target device.
For more details,
see the [device registration](device_registration.md) docs.
<!--end-route-post-accountdevicesinvoke_command-->
##### Request body
* `target`: *DEVICES_SCHEMA.id.required*
<!--begin-request-body-post-accountdevicesinvoke_command-target-->
The id of the device on which to invoke the command.
<!--end-request-body-post-accountdevicesinvoke_command-target-->
* `command`: *string, required*
<!--begin-request-body-post-accountdevicesinvoke_command-command-->
The id of the command to be invoked,
as found in the device's `availableCommands` set.
<!--end-request-body-post-accountdevicesinvoke_command-command-->
* `payload`: *object, required*
<!--begin-request-body-post-accountdevicesinvoke_command-payload-->
Opaque payload to be forwarded to the device.
<!--end-request-body-post-accountdevicesinvoke_command-payload-->
* `ttl`: *number, integer, min(0), max(10000000), optional*
<!--begin-request-body-post-accountdevicesinvoke_command-ttl-->
The time in milliseconds after which the command should expire,
if not processed by the device.
<!--end-request-body-post-accountdevicesinvoke_command-ttl-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 157`:
Unavailable device command.
#### POST /account/devices/notify
:lock: HAWK-authenticated with session token
<!--begin-route-post-accountdevicesnotify-->
Notifies a set of devices associated with the user's account
of an event by sending a browser push notification.
A typical use case would be
to send a notification to another device
after sending a tab with Sync,
so it can sync too
and display the tab in a timely manner.
<!--end-route-post-accountdevicesnotify-->
##### Request body
* `to`: *string, valid('all'), required*;<br />or *array, items(string, length(32), regex(HEX_STRING)), required*
<!--begin-request-body-post-accountdevicesnotify-to-->
Devices to notify.
May be the string `'all'`
or an array
containing the relevant device ids.
<!--end-request-body-post-accountdevicesnotify-to-->
* `_endpointAction`: *string, valid('accountVerify'), optional*
<!--begin-request-body-post-accountdevicesnotify-_endpointAction-->
<!--end-request-body-post-accountdevicesnotify-_endpointAction-->
* `excluded`: *array, items(string, length(32), regex(HEX_STRING)), optional*
<!--begin-request-body-post-accountdevicesnotify-excluded-->
Array of device ids
to exclude from the notification.
Ignored unless `to:"all"` is specified.
<!--end-request-body-post-accountdevicesnotify-excluded-->
* `payload`: *object, when('_endpointAction', { is: 'accountVerify', then: required, otherwise: required })*
<!--begin-request-body-post-accountdevicesnotify-payload-->
Push payload,
validated against [`pushpayloads.schema.json`](pushpayloads.schema.json).
<!--end-request-body-post-accountdevicesnotify-payload-->
* `TTL`: *number, integer, min(0), optional*
<!--begin-request-body-post-accountdevicesnotify-TTL-->
Push notification TTL,
defaults to `0`.
<!--end-request-body-post-accountdevicesnotify-TTL-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 107`:
Invalid parameter in request body
* `code: 503, errno: 202`:
Feature not enabled
#### GET /account/devices
:lock: HAWK-authenticated with session token
<!--begin-route-get-accountdevices-->
Returns an array
of registered device objects
for the authenticated user.
<!--end-route-get-accountdevices-->
##### Response body
* `id`: *DEVICES_SCHEMA.id.required*
<!--begin-response-body-get-accountdevices-id-->
<!--end-response-body-get-accountdevices-id-->
* `isCurrentDevice`: *boolean, required*
<!--begin-response-body-get-accountdevices-isCurrentDevice-->
<!--end-response-body-get-accountdevices-isCurrentDevice-->
* `lastAccessTime`: *number, min(0), required, allow(null)*
<!--begin-response-body-get-accountdevices-lastAccessTime-->
<!--end-response-body-get-accountdevices-lastAccessTime-->
* `lastAccessTimeFormatted`: *string, optional, allow('')*
<!--begin-response-body-get-accountdevices-lastAccessTimeFormatted-->
<!--end-response-body-get-accountdevices-lastAccessTimeFormatted-->
* `approximateLastAccessTime`: *number, min(earliestSaneTimestamp), optional*
<!--begin-response-body-get-accountdevices-approximateLastAccessTime-->
<!--end-response-body-get-accountdevices-approximateLastAccessTime-->
* `approximateLastAccessTimeFormatted`: *string, optional, allow('')*
<!--begin-response-body-get-accountdevices-approximateLastAccessTimeFormatted-->
<!--end-response-body-get-accountdevices-approximateLastAccessTimeFormatted-->
* `location`: *DEVICES_SCHEMA.location*
<!--begin-response-body-get-accountdevices-location-->
<!--end-response-body-get-accountdevices-location-->
* `name`: *DEVICES_SCHEMA.nameResponse.allow('').required*
<!--begin-response-body-get-accountdevices-name-->
<!--end-response-body-get-accountdevices-name-->
* `type`: *DEVICES_SCHEMA.type.required*
<!--begin-response-body-get-accountdevices-type-->
<!--end-response-body-get-accountdevices-type-->
* `pushCallback`: *DEVICES_SCHEMA.pushCallback.allow(null).optional*
<!--begin-response-body-get-accountdevices-pushCallback-->
<!--end-response-body-get-accountdevices-pushCallback-->
* `pushPublicKey`: *DEVICES_SCHEMA.pushPublicKey.allow(null).optional*
<!--begin-response-body-get-accountdevices-pushPublicKey-->
<!--end-response-body-get-accountdevices-pushPublicKey-->
* `pushAuthKey`: *DEVICES_SCHEMA.pushAuthKey.allow(null).optional*
<!--begin-response-body-get-accountdevices-pushAuthKey-->
<!--end-response-body-get-accountdevices-pushAuthKey-->
* `pushEndpointExpired`: *DEVICES_SCHEMA.pushEndpointExpired.optional*
<!--begin-response-body-get-accountdevices-pushEndpointExpired-->
<!--end-response-body-get-accountdevices-pushEndpointExpired-->
* `availableCommands`: *DEVICES_SCHEMA.availableCommands.optional*
<!--begin-response-body-get-accountdevices-availableCommands-->
<!--end-response-body-get-accountdevices-availableCommands-->
#### GET /account/sessions
:lock: HAWK-authenticated with session token
<!--begin-route-get-accountsessions-->
Returns an array
of session objects
for the authenticated user.
<!--end-route-get-accountsessions-->
##### Response body
* `id`: *string, regex(HEX_STRING), required*
<!--begin-response-body-get-accountsessions-id-->
<!--end-response-body-get-accountsessions-id-->
* `lastAccessTime`: *number, min(0), required, allow(null)*
<!--begin-response-body-get-accountsessions-lastAccessTime-->
<!--end-response-body-get-accountsessions-lastAccessTime-->
* `lastAccessTimeFormatted`: *string, optional, allow('')*
<!--begin-response-body-get-accountsessions-lastAccessTimeFormatted-->
<!--end-response-body-get-accountsessions-lastAccessTimeFormatted-->
* `approximateLastAccessTime`: *number, min(earliestSaneTimestamp), optional*
<!--begin-response-body-get-accountsessions-approximateLastAccessTime-->
<!--end-response-body-get-accountsessions-approximateLastAccessTime-->
* `approximateLastAccessTimeFormatted`: *string, optional, allow('')*
<!--begin-response-body-get-accountsessions-approximateLastAccessTimeFormatted-->
<!--end-response-body-get-accountsessions-approximateLastAccessTimeFormatted-->
* `createdTime`: *number, min(0), required, allow(null)*
<!--begin-response-body-get-accountsessions-createdTime-->
<!--end-response-body-get-accountsessions-createdTime-->
* `createdTimeFormatted`: *string, optional, allow('')*
<!--begin-response-body-get-accountsessions-createdTimeFormatted-->
<!--end-response-body-get-accountsessions-createdTimeFormatted-->
* `location`: *DEVICES_SCHEMA.location*
<!--begin-response-body-get-accountsessions-location-->
Object containing the client's state and country
<!--end-response-body-get-accountsessions-location-->
* `userAgent`: *string, max(255), required, allow('')*
<!--begin-response-body-get-accountsessions-userAgent-->
<!--end-response-body-get-accountsessions-userAgent-->
* `os`: *string, max(255), allow(''), allow(null)*
<!--begin-response-body-get-accountsessions-os-->
<!--end-response-body-get-accountsessions-os-->
* `deviceId`: *DEVICES_SCHEMA.id.allow(null).required*
<!--begin-response-body-get-accountsessions-deviceId-->
<!--end-response-body-get-accountsessions-deviceId-->
* `deviceName`: *DEVICES_SCHEMA.nameResponse.allow('').allow(null).required*
<!--begin-response-body-get-accountsessions-deviceName-->
<!--end-response-body-get-accountsessions-deviceName-->
* `deviceAvailableCommands`: *DEVICES_SCHEMA.availableCommands.allow(null).required*
<!--begin-response-body-get-accountsessions-deviceAvailableCommands-->
<!--end-response-body-get-accountsessions-deviceAvailableCommands-->
* `deviceType`: *DEVICES_SCHEMA.type.allow(null).required*
<!--begin-response-body-get-accountsessions-deviceType-->
<!--end-response-body-get-accountsessions-deviceType-->
* `deviceCallbackURL`: *DEVICES_SCHEMA.pushCallback.allow(null).required*
<!--begin-response-body-get-accountsessions-deviceCallbackURL-->
<!--end-response-body-get-accountsessions-deviceCallbackURL-->
* `deviceCallbackPublicKey`: *DEVICES_SCHEMA.pushPublicKey.allow(null).required*
<!--begin-response-body-get-accountsessions-deviceCallbackPublicKey-->
<!--end-response-body-get-accountsessions-deviceCallbackPublicKey-->
* `deviceCallbackAuthKey`: *DEVICES_SCHEMA.pushAuthKey.allow(null).required*
<!--begin-response-body-get-accountsessions-deviceCallbackAuthKey-->
<!--end-response-body-get-accountsessions-deviceCallbackAuthKey-->
* `deviceCallbackIsExpired`: *DEVICES_SCHEMA.pushEndpointExpired.allow(null).required*
<!--begin-response-body-get-accountsessions-deviceCallbackIsExpired-->
<!--end-response-body-get-accountsessions-deviceCallbackIsExpired-->
* `isDevice`: *boolean, required*
<!--begin-response-body-get-accountsessions-isDevice-->
<!--end-response-body-get-accountsessions-isDevice-->
* `isCurrentDevice`: *boolean, required*
<!--begin-response-body-get-accountsessions-isCurrentDevice-->
<!--end-response-body-get-accountsessions-isCurrentDevice-->
#### POST /account/device/destroy
:lock: HAWK-authenticated with session token
<!--begin-route-post-accountdevicedestroy-->
Destroys a device record
and the associated `sessionToken`
for the authenticated user.
The identified device must sign in again
to use the API after this request has succeeded.
<!--end-route-post-accountdevicedestroy-->
##### Request body
* `id`: *DEVICES_SCHEMA.id.required*
<!--begin-request-body-post-accountdevicedestroy-id-->
<!--end-request-body-post-accountdevicedestroy-id-->
### Emails
#### GET /recovery_email/status
:lock: HAWK-authenticated with session token
<!--begin-route-get-recovery_emailstatus-->
Returns the "verified" status
for the account's recovery email address.
Currently, each account is associated
with exactly one email address.
This address must be verified
before the account can be used
(specifically, `POST /certificate/sign` and `GET /account/keys`
will return errors until the address is verified).
In the future, this may be expanded to include multiple addresses,
and/or alternate types of recovery methods (e.g. SMS).
A new API will be provided for this extra functionality.
This call is used to determine the current state
(verified or unverified)
of the account.
During account creation,
until the address is verified,
the agent can poll this method
to discover when it should proceed
with `POST /certificate/sign` and `GET /account/keys`.
<!--end-route-get-recovery_emailstatus-->
##### Query parameters
* `reason`: *string, max(16), optional*
<!--begin-query-param-get-recovery_emailstatus-reason-->
<!--end-query-param-get-recovery_emailstatus-reason-->
##### Response body
* `email`: *string, required*
<!--begin-response-body-get-recovery_emailstatus-email-->
<!--end-response-body-get-recovery_emailstatus-email-->
* `verified`: *boolean, required*
<!--begin-response-body-get-recovery_emailstatus-verified-->
<!--end-response-body-get-recovery_emailstatus-verified-->
* `sessionVerified`: *boolean, optional*
<!--begin-response-body-get-recovery_emailstatus-sessionVerified-->
<!--end-response-body-get-recovery_emailstatus-sessionVerified-->
* `emailVerified`: *boolean, optional*
<!--begin-response-body-get-recovery_emailstatus-emailVerified-->
<!--end-response-body-get-recovery_emailstatus-emailVerified-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 401, errno: 110`:
Invalid authentication token in request signature
#### POST /recovery_email/resend_code
:lock: HAWK-authenticated with session token
<!--begin-route-post-recovery_emailresend_code-->
Re-sends a verification code
to the account's recovery email address.
The code is first sent when the account is created,
but if the user thinks the message was lost
or accidentally deleted,
they can request a new message to be sent
via this endpoint.
The new message will contain the same code
as the original message.
When this code is provided to `/v1/recovery_email/verify_code`,
the email will be marked as "verified".
This endpoint may send a verification email to the user.
Callers may optionally provide
the `service` parameter to indicate
what identity-attached service
they're acting on behalf of.
This is an opaque alphanumeric token
that will be embedded
in the verification link
as a query parameter.
<!--end-route-post-recovery_emailresend_code-->
##### Query parameters
* `service`: *validators.service*
<!--begin-query-param-post-recovery_emailresend_code-service-->
Opaque alphanumeric token to be included in verification links.
<!--end-query-param-post-recovery_emailresend_code-service-->
* `type`: *string, max(32), alphanum, allow(), optional*
<!--begin-query-param-post-recovery_emailresend_code-type-->
<!--end-query-param-post-recovery_emailresend_code-type-->
##### Request body
* `email`: *validators.email.optional*
<!--begin-request-body-post-recovery_emailresend_code-email-->
<!--end-request-body-post-recovery_emailresend_code-email-->
* `service`: *validators.service*
<!--begin-request-body-post-recovery_emailresend_code-service-->
Opaque alphanumeric token to be included in verification links.
<!--end-request-body-post-recovery_emailresend_code-service-->
* `redirectTo`: *validators.redirectTo(config.smtp.redirectDomain).optional*
<!--begin-request-body-post-recovery_emailresend_code-redirectTo-->
<!--end-request-body-post-recovery_emailresend_code-redirectTo-->
* `resume`: *string, max(2048), optional*
<!--begin-request-body-post-recovery_emailresend_code-resume-->
Opaque URL-encoded string to be included in the verification link as a query parameter.
<!--end-request-body-post-recovery_emailresend_code-resume-->
* `metricsContext`: *metricsContext.schema*
<!--begin-request-body-post-recovery_emailresend_code-metricsContext-->
<!--end-request-body-post-recovery_emailresend_code-metricsContext-->
* `type`: *string, max(32), alphanum, allow(), optional*
<!--begin-request-body-post-recovery_emailresend_code-type-->
<!--end-request-body-post-recovery_emailresend_code-type-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 150`:
Can not resend email code to an email that does not belong to this account
#### POST /recovery_email/verify_code
<!--begin-route-post-recovery_emailverify_code-->
Verify tokens and/or recovery emails for an account.
If a valid token code is detected,
the account email and tokens will be set to verified.
If a valid email code is detected,
the email will be marked as verified.
The verification code will be a random token,
delivered in the fragment identifier of a URL
sent to the user's email address.
Navigating to the URL opens a page
that extracts the code from the fragment identifier
and performs a POST to `/recovery_email/verify_code`.
The link can be clicked from any browser,
not just the one being attached to the Firefox account.
<!--end-route-post-recovery_emailverify_code-->
##### Query parameters
* `service`: *validators.service*
<!--begin-query-param-post-recovery_emailverify_code-service-->
Opaque alphanumeric token to be included in verification links.
<!--end-query-param-post-recovery_emailverify_code-service-->
* `reminder`: *string, max(32), alphanum, optional*
<!--begin-query-param-post-recovery_emailverify_code-reminder-->
Deprecated.
<!--end-query-param-post-recovery_emailverify_code-reminder-->
* `type`: *string, max(32), alphanum, optional*
<!--begin-query-param-post-recovery_emailverify_code-type-->
The type of code being verified.
<!--end-query-param-post-recovery_emailverify_code-type-->
##### Request body
* `uid`: *string, max(32), regex(HEX_STRING), required*
<!--begin-request-body-post-recovery_emailverify_code-uid-->
<!--end-request-body-post-recovery_emailverify_code-uid-->
* `code`: *string, min(32), max(32), regex(HEX_STRING), required*
<!--begin-request-body-post-recovery_emailverify_code-code-->
<!--end-request-body-post-recovery_emailverify_code-code-->
* `service`: *validators.service*
<!--begin-request-body-post-recovery_emailverify_code-service-->
Opaque alphanumeric token to be included in verification links.
<!--end-request-body-post-recovery_emailverify_code-service-->
* `reminder`: *string, max(32), alphanum, optional*
<!--begin-request-body-post-recovery_emailverify_code-reminder-->
Deprecated.
<!--end-request-body-post-recovery_emailverify_code-reminder-->
* `type`: *string, max(32), alphanum, optional*
<!--begin-request-body-post-recovery_emailverify_code-type-->
The type of code being verified.
<!--end-request-body-post-recovery_emailverify_code-type-->
* `marketingOptIn`: *boolean*
<!--begin-request-body-post-recovery_emailverify_code-marketingOptIn-->
Set to true if the user has opted-in to our marketing. When verified,
the auth-server will notify Basket.
<!--end-request-body-post-recovery_emailverify_code-marketingOptIn-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 105`:
Invalid verification code
#### GET /recovery_emails
:lock: HAWK-authenticated with session token
<!--begin-route-get-recovery_emails-->
Returns an array of objects
containing details of the email addresses
associated with the logged-in user.
Currently,
the primary email address
is always the one
from the `accounts` table.
<!--end-route-get-recovery_emails-->
##### Response body
* `verified`: *boolean, required*
<!--begin-response-body-get-recovery_emails-verified-->
<!--end-response-body-get-recovery_emails-verified-->
* `isPrimary`: *boolean, required*
<!--begin-response-body-get-recovery_emails-isPrimary-->
<!--end-response-body-get-recovery_emails-isPrimary-->
* `email`: *validators.email.required*
<!--begin-response-body-get-recovery_emails-email-->
<!--end-response-body-get-recovery_emails-email-->
#### POST /recovery_email
:lock: HAWK-authenticated with session token
<!--begin-route-post-recovery_email-->
Add a secondary email address
to the logged-in account.
The created address will be unverified
and will not replace the primary email address.
<!--end-route-post-recovery_email-->
##### Request body
* `email`: *validators.email.required*
<!--begin-request-body-post-recovery_email-email-->
The email address to add to the account.
<!--end-request-body-post-recovery_email-email-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 104`:
Unverified account
* `code: 400, errno: 138`:
Unverified session
* `code: 400, errno: 139`:
Can not add secondary email that is same as your primary
* `code: 400, errno: 140`:
Email already exists
* `code: 400, errno: 141`:
Email already exists
* `code: 400, errno: 151`:
Failed to send email
#### POST /recovery_email/destroy
:lock: HAWK-authenticated with session token
<!--begin-route-post-recovery_emaildestroy-->
Delete an email address
associated with the logged-in user.
<!--end-route-post-recovery_emaildestroy-->
##### Request body
* `email`: *validators.email.required*
<!--begin-request-body-post-recovery_emaildestroy-email-->
The email address to delete.
<!--end-request-body-post-recovery_emaildestroy-email-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 138`:
Unverified session
#### POST /recovery_email/set_primary
:lock: HAWK-authenticated with session token
<!--begin-route-post-recovery_emailset_primary-->
This endpoint changes a user's primary email address. This email address must
belong to the user and be verified.
<!--end-route-post-recovery_emailset_primary-->
##### Request body
* `email`: *validators.email.required*
<!--begin-request-body-post-recovery_emailset_primary-email-->
The new primary email address of the user.
<!--end-request-body-post-recovery_emailset_primary-email-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 138`:
Unverified session
* `code: 400, errno: 147`:
Can not change primary email to an unverified email
* `code: 400, errno: 148`:
Can not change primary email to an email that does not belong to this account
### Password
#### POST /password/change/start
<!--begin-route-post-passwordchangestart-->
Begin the "change password" process.
Returns a single-use `passwordChangeToken`,
to be sent to `POST /password/change/finish`.
Also returns a single-use `keyFetchToken`.
<!--end-route-post-passwordchangestart-->
##### Request body
* `email`: *validators.email.required*
<!--begin-request-body-post-passwordchangestart-email-->
Primary email address of the account.
<!--end-request-body-post-passwordchangestart-email-->
* `oldAuthPW`: *validators.authPW*
<!--begin-request-body-post-passwordchangestart-oldAuthPW-->
The PBKDF2/HKDF-stretched password as a hex string.
<!--end-request-body-post-passwordchangestart-oldAuthPW-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 103`:
Incorrect password
#### POST /password/change/finish
:lock: HAWK-authenticated with password change token
<!--begin-route-post-passwordchangefinish-->
Change the password and update `wrapKb`.
Optionally returns `sessionToken` and `keyFetchToken`.
<!--end-route-post-passwordchangefinish-->
##### Query parameters
* `keys`: *boolean, optional*
<!--begin-query-param-post-passwordchangefinish-keys-->
Indicates whether a new `keyFetchToken` is required, default to `false`.
<!--end-query-param-post-passwordchangefinish-keys-->
##### Request body
* `authPW`: *validators.authPW*
<!--begin-request-body-post-passwordchangefinish-authPW-->
The PBKDF2/HKDF-stretched password as a hex string.
<!--end-request-body-post-passwordchangefinish-authPW-->
* `wrapKb`: *validators.wrapKb*
<!--begin-request-body-post-passwordchangefinish-wrapKb-->
The new `wrapKb` value as a hex string.
<!--end-request-body-post-passwordchangefinish-wrapKb-->
* `sessionToken`: *string, min(64), max(64), regex(HEX_STRING), optional*
<!--begin-request-body-post-passwordchangefinish-sessionToken-->
Indicates whether a new `sessionToken` is required, default to `false`.
<!--end-request-body-post-passwordchangefinish-sessionToken-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 138`:
Unverified session
#### POST /password/forgot/send_code
<!--begin-route-post-passwordforgotsend_code-->
Requests a "reset password" code
to be sent to the user's recovery email.
The user should type this code into the agent,
which will then submit it
to `POST /password/forgot/verify_code`.
The code will be either 8 or 16 digits long,
with the length indicated in the response.
The email will either contain the code itself
or the URL for a web page that displays the code.
The response includes `passwordForgotToken`,
which must be submitted with the code
to `POST /password/forgot/verify_code`.
The response also specifies the TTL of `passwordForgotToken`
and an upper limit on the number of times
the token may be submitted.
By limiting the number of submission attempts,
we also limit an attacker's ability to guess the code.
After the token expires,
or the maximum number of submissions has been made,
the agent must call this endpoint again
to generate a new code and token pair.
Each account can have at most
one `passwordForgotToken` valid at a time.
Calling this endpoint causes existing tokens
to be invalidated and a new one created.
Each token is associated with a specific code,
so by extension the codes are invalidated
with their tokens.
<!--end-route-post-passwordforgotsend_code-->
##### Query parameters
* `service`: *validators.service*
<!--begin-query-param-post-passwordforgotsend_code-service-->
Identifies the relying service
the user was interacting with
that triggered the password reset.
<!--end-query-param-post-passwordforgotsend_code-service-->
* `keys`: *boolean, optional*
<!--begin-query-param-post-passwordforgotsend_code-keys-->
<!--end-query-param-post-passwordforgotsend_code-keys-->
##### Request body
* `email`: *validators.email.required*
<!--begin-request-body-post-passwordforgotsend_code-email-->
Recovery email for the account.
<!--end-request-body-post-passwordforgotsend_code-email-->
* `service`: *validators.service*
<!--begin-request-body-post-passwordforgotsend_code-service-->
Identifies the relying service
the user was interacting with
that triggered the password reset.
<!--end-request-body-post-passwordforgotsend_code-service-->
* `redirectTo`: *validators.redirectTo(redirectDomain).optional*
<!--begin-request-body-post-passwordforgotsend_code-redirectTo-->
URL that the client should be redirected to
after handling the request.
<!--end-request-body-post-passwordforgotsend_code-redirectTo-->
* `resume`: *string, max(2048), optional*
<!--begin-request-body-post-passwordforgotsend_code-resume-->
Opaque URL-encoded string to be included in the verification link as a query parameter.
<!--end-request-body-post-passwordforgotsend_code-resume-->
* `metricsContext`: *metricsContext.schema*
<!--begin-request-body-post-passwordforgotsend_code-metricsContext-->
<!--end-request-body-post-passwordforgotsend_code-metricsContext-->
##### Response body
* `passwordForgotToken`: *string*
<!--begin-response-body-post-passwordforgotsend_code-passwordForgotToken-->
<!--end-response-body-post-passwordforgotsend_code-passwordForgotToken-->
* `ttl`: *number*
<!--begin-response-body-post-passwordforgotsend_code-ttl-->
<!--end-response-body-post-passwordforgotsend_code-ttl-->
* `codeLength`: *number*
<!--begin-response-body-post-passwordforgotsend_code-codeLength-->
<!--end-response-body-post-passwordforgotsend_code-codeLength-->
* `tries`: *number*
<!--begin-response-body-post-passwordforgotsend_code-tries-->
<!--end-response-body-post-passwordforgotsend_code-tries-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 145`:
Reset password with this email type is not currently supported
#### POST /password/forgot/resend_code
:lock: HAWK-authenticated with password forgot token
<!--begin-route-post-passwordforgotresend_code-->
Resends the email
from `POST /password/forgot/send_code`,
for use when the original email
has been lost or accidentally deleted.
This endpoint requires the `passwordForgotToken`
returned in the original response,
so only the original client which started the process
may request a resent message.
The response will match that from
`POST /password/forgot/send_code`,
except `ttl` will be lower
to indicate the shorter validity period.
`tries` will also be lower
if `POST /password/forgot/verify_code`
has been called.
<!--end-route-post-passwordforgotresend_code-->
##### Query parameters
* `service`: *validators.service*
<!--begin-query-param-post-passwordforgotresend_code-service-->
Identifies the relying service
the user was interacting with
that triggered the password reset.
<!--end-query-param-post-passwordforgotresend_code-service-->
##### Request body
* `email`: *validators.email.required*
<!--begin-request-body-post-passwordforgotresend_code-email-->
Recovery email for the account.
<!--end-request-body-post-passwordforgotresend_code-email-->
* `service`: *validators.service*
<!--begin-request-body-post-passwordforgotresend_code-service-->
Identifies the relying service
the user was interacting with
that triggered the password reset.
<!--end-request-body-post-passwordforgotresend_code-service-->
* `redirectTo`: *validators.redirectTo(redirectDomain).optional*
<!--begin-request-body-post-passwordforgotresend_code-redirectTo-->
URL that the client should be redirected to
after handling the request.
<!--end-request-body-post-passwordforgotresend_code-redirectTo-->
* `resume`: *string, max(2048), optional*
<!--begin-request-body-post-passwordforgotresend_code-resume-->
Opaque URL-encoded string to be included in the verification link as a query parameter.
<!--end-request-body-post-passwordforgotresend_code-resume-->
* `metricsContext`: *metricsContext.schema*
<!--begin-request-body-post-passwordforgotresend_code-metricsContext-->
<!--end-request-body-post-passwordforgotresend_code-metricsContext-->
##### Response body
* `passwordForgotToken`: *string*
<!--begin-response-body-post-passwordforgotresend_code-passwordForgotToken-->
<!--end-response-body-post-passwordforgotresend_code-passwordForgotToken-->
* `ttl`: *number*
<!--begin-response-body-post-passwordforgotresend_code-ttl-->
<!--end-response-body-post-passwordforgotresend_code-ttl-->
* `codeLength`: *number*
<!--begin-response-body-post-passwordforgotresend_code-codeLength-->
<!--end-response-body-post-passwordforgotresend_code-codeLength-->
* `tries`: *number*
<!--begin-response-body-post-passwordforgotresend_code-tries-->
<!--end-response-body-post-passwordforgotresend_code-tries-->
#### POST /password/forgot/verify_code
:lock: HAWK-authenticated with password forgot token
<!--begin-route-post-passwordforgotverify_code-->
The code returned by `POST /v1/password/forgot/send_code`
should be submitted to this endpoint
with the `passwordForgotToken`.
For successful requests,
the server will return `accountResetToken`,
to be submitted in requests to `POST /account/reset`
to reset the account password and `wrapKb`.
<!--end-route-post-passwordforgotverify_code-->
##### Request body
* `code`: *string, min(32), max(32), regex(HEX_STRING), required*
<!--begin-request-body-post-passwordforgotverify_code-code-->
The code sent to the user's recovery email.
<!--end-request-body-post-passwordforgotverify_code-code-->
* `metricsContext`: *metricsContext.schema*
<!--begin-request-body-post-passwordforgotverify_code-metricsContext-->
<!--end-request-body-post-passwordforgotverify_code-metricsContext-->
* `accountResetWithRecoveryKey`: *boolean, optional*
<!--begin-request-body-post-passwordforgotverify_code-accountResetWithRecoveryKey-->
<!--end-request-body-post-passwordforgotverify_code-accountResetWithRecoveryKey-->
##### Response body
* `accountResetToken`: *string*
<!--begin-response-body-post-passwordforgotverify_code-accountResetToken-->
<!--end-response-body-post-passwordforgotverify_code-accountResetToken-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 105`:
Invalid verification code
#### GET /password/forgot/status
:lock: HAWK-authenticated with password forgot token
<!--begin-route-get-passwordforgotstatus-->
Returns the status of a `passwordForgotToken`.
Success responses indicate
the token has not yet been consumed.
For consumed or expired tokens,
an HTTP `401` response
with `errno: 110`
will be returned.
<!--end-route-get-passwordforgotstatus-->
##### Response body
* `tries`: *number*
<!--begin-response-body-get-passwordforgotstatus-tries-->
<!--end-response-body-get-passwordforgotstatus-tries-->
* `ttl`: *number*
<!--begin-response-body-get-passwordforgotstatus-ttl-->
<!--end-response-body-get-passwordforgotstatus-ttl-->
### Recovery codes
#### GET /recoveryCodes
:lock: HAWK-authenticated with session token
<!--begin-route-get-recoverycodes-->
Return new recovery codes while removing old ones.
<!--end-route-get-recoverycodes-->
##### Response body
* `recoveryCodes`: *array, items(string)*
<!--begin-response-body-get-recoverycodes-recoveryCodes-->
<!--end-response-body-get-recoverycodes-recoveryCodes-->
#### POST /session/verify/recoveryCode
:lock: HAWK-authenticated with session token
<!--begin-route-post-sessionverifyrecoverycode-->
Verify a session using a recovery code.
<!--end-route-post-sessionverifyrecoverycode-->
##### Request body
* `code`: *string, max(RECOVERY_CODE_SANE_MAX_LENGTH), regex(BASE_36), required*
<!--begin-request-body-post-sessionverifyrecoverycode-code-->
<!--end-request-body-post-sessionverifyrecoverycode-code-->
* `metricsContext`: *metricsContext.schema*
<!--begin-request-body-post-sessionverifyrecoverycode-metricsContext-->
<!--end-request-body-post-sessionverifyrecoverycode-metricsContext-->
##### Response body
* `remaining`: *number*
<!--begin-response-body-post-sessionverifyrecoverycode-remaining-->
<!--end-response-body-post-sessionverifyrecoverycode-remaining-->
### Recovery key
#### POST /recoveryKey
:lock: HAWK-authenticated with session token
<!--begin-route-post-recoverykey-->
Creates a new recovery key for a user.
Recovery keys are one-time-use tokens
that can be used to recover the user's kB
if they forget their password.
For more details, see the
[recovery keys](recovery_keys.md) docs.
<!--end-route-post-recoverykey-->
##### Request body
* `recoveryKeyId`: *validators.recoveryKeyId*
<!--begin-request-body-post-recoverykey-recoveryKeyId-->
A unique identifier for this recovery key, derived from the key via HKDF.
<!--end-request-body-post-recoverykey-recoveryKeyId-->
* `recoveryData`: *validators.recoveryData*
<!--begin-request-body-post-recoverykey-recoveryData-->
An encrypted bundle containing the user's kB.
<!--end-request-body-post-recoverykey-recoveryData-->
#### GET /recoveryKey/{recoveryKeyId}
:lock: HAWK-authenticated with account reset token
<!--begin-route-get-recoverykeyrecoverykeyid-->
Retrieve the account recovery data associated with the given recovery key.
<!--end-route-get-recoverykeyrecoverykeyid-->
#### POST /recoveryKey/exists
:lock::unlock: Optionally HAWK-authenticated with session token
<!--begin-route-post-recoverykeyexists-->
This route checks to see if given user has setup an account recovery key.
When used during the password reset flow, an email can be provided (instead
of a sessionToken) to check for the status. However, when
using an email, the request is rate limited.
<!--end-route-post-recoverykeyexists-->
##### Request body
* `email`: *validators.email.optional*
<!--begin-request-body-post-recoverykeyexists-email-->
<!--end-request-body-post-recoverykeyexists-email-->
##### Response body
* `exists`: *boolean, required*
<!--begin-response-body-post-recoverykeyexists-exists-->
<!--end-response-body-post-recoverykeyexists-exists-->
#### DELETE /recoveryKey
:lock: HAWK-authenticated with session token
<!--begin-route-delete-recoverykey-->
This route remove an account's recovery key. When the key is
removed, it can no longer be used to restore an account's kB.
<!--end-route-delete-recoverykey-->
### Session
#### POST /session/destroy
:lock: HAWK-authenticated with session token
<!--begin-route-post-sessiondestroy-->
Destroys the current session
and invalidates `sessionToken`,
to be called when a user signs out.
To sign back in,
a call must be made to
`POST /account/login`
to obtain a new `sessionToken`.
<!--end-route-post-sessiondestroy-->
##### Request body
* `customSessionToken`: *string, min(64), max(64), regex(HEX_STRING), optional*
<!--begin-request-body-post-sessiondestroy-customSessionToken-->
Custom session token id to destroy.
<!--end-request-body-post-sessiondestroy-customSessionToken-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 401, errno: 110`:
Invalid authentication token in request signature
#### POST /session/reauth
:lock: HAWK-authenticated with session token
<!--begin-route-post-sessionreauth-->
Re-authenticate an existing session token.
This is equivalent to calling `/account/login`,
but it re-uses an existing session token
rather than generating a new one,
allowing the caller to maintain session state
such as verification and device registration.
<!--end-route-post-sessionreauth-->
##### Query parameters
* `keys`: *boolean, optional*
<!--begin-query-param-post-sessionreauth-keys-->
<!--end-query-param-post-sessionreauth-keys-->
* `service`: *validators.service*
<!--begin-query-param-post-sessionreauth-service-->
<!--end-query-param-post-sessionreauth-service-->
* `verificationMethod`: *validators.verificationMethod.optional*
<!--begin-query-param-post-sessionreauth-verificationMethod-->
<!--end-query-param-post-sessionreauth-verificationMethod-->
##### Request body
* `email`: *validators.email.required*
<!--begin-request-body-post-sessionreauth-email-->
<!--end-request-body-post-sessionreauth-email-->
* `authPW`: *validators.authPW*
<!--begin-request-body-post-sessionreauth-authPW-->
<!--end-request-body-post-sessionreauth-authPW-->
* `service`: *validators.service*
<!--begin-request-body-post-sessionreauth-service-->
<!--end-request-body-post-sessionreauth-service-->
* `redirectTo`: *validators.redirectTo(config.smtp.redirectDomain).optional*
<!--begin-request-body-post-sessionreauth-redirectTo-->
<!--end-request-body-post-sessionreauth-redirectTo-->
* `resume`: *string, optional*
<!--begin-request-body-post-sessionreauth-resume-->
<!--end-request-body-post-sessionreauth-resume-->
* `reason`: *string, max(16), optional*
<!--begin-request-body-post-sessionreauth-reason-->
<!--end-request-body-post-sessionreauth-reason-->
* `unblockCode`: *signinUtils.validators.UNBLOCK_CODE*
<!--begin-request-body-post-sessionreauth-unblockCode-->
<!--end-request-body-post-sessionreauth-unblockCode-->
* `metricsContext`: *metricsContext.schema*
<!--begin-request-body-post-sessionreauth-metricsContext-->
<!--end-request-body-post-sessionreauth-metricsContext-->
* `originalLoginEmail`: *validators.email.optional*
<!--begin-request-body-post-sessionreauth-originalLoginEmail-->
<!--end-request-body-post-sessionreauth-originalLoginEmail-->
* `verificationMethod`: *validators.verificationMethod.optional*
<!--begin-request-body-post-sessionreauth-verificationMethod-->
<!--end-request-body-post-sessionreauth-verificationMethod-->
##### Response body
* `uid`: *string, regex(HEX_STRING), required*
<!--begin-response-body-post-sessionreauth-uid-->
<!--end-response-body-post-sessionreauth-uid-->
* `keyFetchToken`: *string, regex(HEX_STRING), optional*
<!--begin-response-body-post-sessionreauth-keyFetchToken-->
<!--end-response-body-post-sessionreauth-keyFetchToken-->
* `verificationMethod`: *string, optional*
<!--begin-response-body-post-sessionreauth-verificationMethod-->
<!--end-response-body-post-sessionreauth-verificationMethod-->
* `verificationReason`: *string, optional*
<!--begin-response-body-post-sessionreauth-verificationReason-->
<!--end-response-body-post-sessionreauth-verificationReason-->
* `verified`: *boolean, required*
<!--begin-response-body-post-sessionreauth-verified-->
<!--end-response-body-post-sessionreauth-verified-->
* `authAt`: *number, integer*
<!--begin-response-body-post-sessionreauth-authAt-->
<!--end-response-body-post-sessionreauth-authAt-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 102`:
Unknown account
* `code: 400, errno: 103`:
Incorrect password
* `code: 400, errno: 125`:
The request was blocked for security reasons
* `code: 400, errno: 127`:
Invalid unblock code
* `code: 400, errno: 142`:
Sign in with this email type is not currently supported
* `code: 400, errno: 149`:
This email can not currently be used to login
* `code: 400, errno: 160`:
This request requires two step authentication enabled on your account.
#### GET /session/status
:lock: HAWK-authenticated with session token
<!--begin-route-get-sessionstatus-->
Returns a success response
if the session token is valid.
<!--end-route-get-sessionstatus-->
##### Response body
* `state`: *string, required*
<!--begin-response-body-get-sessionstatus-state-->
<!--end-response-body-get-sessionstatus-state-->
* `uid`: *string, regex(HEX_STRING), required*
<!--begin-response-body-get-sessionstatus-uid-->
<!--end-response-body-get-sessionstatus-uid-->
#### POST /session/duplicate
:lock: HAWK-authenticated with session token
<!--begin-route-post-sessionduplicate-->
Create a new sessionToken
that duplicates the current session.
It will have the same verification status
as the current session,
but will have a distinct verification code.
<!--end-route-post-sessionduplicate-->
##### Request body
* `reason`: *string, max(16), optional*
<!--begin-request-body-post-sessionduplicate-reason-->
<!--end-request-body-post-sessionduplicate-reason-->
### Sign
#### POST /certificate/sign
:lock: HAWK-authenticated with session token
<!--begin-route-post-certificatesign-->
Sign a BrowserID public key.
The server is given a public key
and returns a signed certificate
using the same JWT-like mechanism
as a BrowserID primary IdP would
(see [browserid-certifier](https://github.com/mozilla/browserid-certifier) for details).
The signed certificate includes
a `principal.email` property
to indicate the Firefox Account identifier
(a UUID at the account server's primary domain)
and is stamped with an expiry time
based on the `duration` parameter.
This request will fail unless the
primary email address for the account
has been verified.
Clients should include a query parameter, `service`,
for metrics and validation purposes.
The value of `service` should be
`sync` when connecting to Firefox Sync
or the OAuth `client_id`
when connecting to an OAuth relier.
If you do not specify a `service` parameter,
or if you specify `service=sync`,
this endpoint assumes the request is from
a legacy Sync client.
If the session token
doesn't have a corresponding device record,
one will be created automatically by the server.
The signed certificate includes these additional claims:
* `fxa-generation`:
A number that increases
each time the user's password is changed.
* `fxa-lastAuthAt`:
Authentication time for this session,
in seconds since epoch.
* `fxa-verifiedEmail`:
The user's verified recovery email address.
<!--end-route-post-certificatesign-->
##### Query parameters
* `service`: *validators.service*
<!--begin-query-param-post-certificatesign-service-->
<!--end-query-param-post-certificatesign-service-->
##### Request body
* `publicKey`: *object({ algorithm: string, valid('RS', 'DS'), required, n: string, e: string, y: string, p: string, q: string, g: string, version: string }), required*
<!--begin-request-body-post-certificatesign-publicKey-->
The key to sign
(run `bin/generate-keypair`
from [browserid-crypto](https://github.com/mozilla/browserid-crypto)).
<!--end-request-body-post-certificatesign-publicKey-->
* `duration`: *number, integer, min(0), max(), required*
<!--begin-request-body-post-certificatesign-duration-->
Time interval in milliseconds
until the certificate will expire,
up to a maximum of 24 hours.
<!--end-request-body-post-certificatesign-duration-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 104`:
Unverified account
* `code: 400, errno: 108`:
Missing parameter in request body
* `code: 400, errno: 138`:
Unverified session
### Signin codes
#### POST /signinCodes/consume
<!--begin-route-post-signincodesconsume-->
Exchange a single-use signin code
for an email address.
<!--end-route-post-signincodesconsume-->
##### Request body
* `code`: *string, regex(validators.URL_SAFE_BASE_64), required*
<!--begin-request-body-post-signincodesconsume-code-->
The signin code.
<!--end-request-body-post-signincodesconsume-code-->
* `metricsContext`: *metricsContext.requiredSchema*
<!--begin-request-body-post-signincodesconsume-metricsContext-->
Metrics context data for the new flow.
<!--end-request-body-post-signincodesconsume-metricsContext-->
##### Response body
* `email`: *validators.email.required*
<!--begin-response-body-post-signincodesconsume-email-->
The email address associated with the signin code.
<!--end-response-body-post-signincodesconsume-email-->
### Sms
#### POST /sms
:lock: HAWK-authenticated with session token
<!--begin-route-post-sms-->
Sends an SMS message.
<!--end-route-post-sms-->
##### Request body
* `phoneNumber`: *string, regex(validators.E164_NUMBER), required*
<!--begin-request-body-post-sms-phoneNumber-->
The phone number to send the message to, in E.164 format.
<!--end-request-body-post-sms-phoneNumber-->
* `messageId`: *number, positive, required*
<!--begin-request-body-post-sms-messageId-->
The id of the message to send.
<!--end-request-body-post-sms-messageId-->
* `metricsContext`: *metricsContext.schema*
<!--begin-request-body-post-sms-metricsContext-->
Metrics context data for the request.
<!--end-request-body-post-sms-metricsContext-->
* `features`: *features.schema*
<!--begin-request-body-post-sms-features-->
Enabled features for the request.
<!--end-request-body-post-sms-features-->
##### Error responses
Failing requests may be caused
by the following errors
(this is not an exhaustive list):
* `code: 400, errno: 129`:
Invalid phone number
* `code: 400, errno: 130`:
Invalid region
#### GET /sms/status
:lock: HAWK-authenticated with session token
<!--begin-route-get-smsstatus-->
Returns SMS status for the current user.
<!--end-route-get-smsstatus-->
##### Query parameters
* `country`: *string, regex(/^[A-Z][A-Z]$/), optional*
<!--begin-query-param-get-smsstatus-country-->
Skip geo-lookup
and act as if the user
is in the specified country.
<!--end-query-param-get-smsstatus-country-->
### Token codes
#### POST /session/verify/token
:lock: HAWK-authenticated with session token
<!--begin-route-post-sessionverifytoken-->
Verify a session using a token code.
<!--end-route-post-sessionverifytoken-->
##### Request body
* `code`: *string, min(TOKEN_CODE_LENGTH), max(TOKEN_CODE_LENGTH), regex(DIGITS), required*
<!--begin-request-body-post-sessionverifytoken-code-->
The code
<!--end-request-body-post-sessionverifytoken-code-->
* `uid`: *string, max(32), regex(HEX_STRING), optional*
<!--begin-request-body-post-sessionverifytoken-uid-->
The uid associated with the token code
<!--end-request-body-post-sessionverifytoken-uid-->
### Totp
#### POST /totp/create
:lock: HAWK-authenticated with session token
<!--begin-route-post-totpcreate-->
Create a new randomly generated TOTP token for a user if they do
not currently have one.
<!--end-route-post-totpcreate-->
##### Request body
* `metricsContext`: *metricsContext.schema*
<!--begin-request-body-post-totpcreate-metricsContext-->
<!--end-request-body-post-totpcreate-metricsContext-->
#### POST /totp/destroy
:lock: HAWK-authenticated with session token
<!--begin-route-post-totpdestroy-->
Deletes the current TOTP token for the user.
<!--end-route-post-totpdestroy-->
#### GET /totp/exists
:lock: HAWK-authenticated with session token
<!--begin-route-get-totpexists-->
Checks to see if the user has a TOTP token.
<!--end-route-get-totpexists-->
#### POST /session/verify/totp
:lock: HAWK-authenticated with session token
<!--begin-route-post-sessionverifytotp-->
Verifies the current session if the passed TOTP code is valid.
<!--end-route-post-sessionverifytotp-->
##### Request body
* `code`: *string, max(32), regex(validators.DIGITS), required*
<!--begin-request-body-post-sessionverifytotp-code-->
The TOTP code to check
<!--end-request-body-post-sessionverifytotp-code-->
* `metricsContext`: *metricsContext.schema*
<!--begin-request-body-post-sessionverifytotp-metricsContext-->
<!--end-request-body-post-sessionverifytotp-metricsContext-->
* `service`: *validators.service*
<!--begin-request-body-post-sessionverifytotp-service-->
<!--end-request-body-post-sessionverifytotp-service-->
##### Response body
* `success`: *boolean, required*
<!--begin-response-body-post-sessionverifytotp-success-->
<!--end-response-body-post-sessionverifytotp-success-->
* `recoveryCodes`: *array, items(string), optional*
<!--begin-response-body-post-sessionverifytotp-recoveryCodes-->
<!--end-response-body-post-sessionverifytotp-recoveryCodes-->
### Unblock codes
#### POST /account/login/send_unblock_code
<!--begin-route-post-accountloginsend_unblock_code-->
Send an unblock code via email
to reset rate-limiting for an account.
<!--end-route-post-accountloginsend_unblock_code-->
##### Request body
* `email`: *validators.email.required*
<!--begin-request-body-post-accountloginsend_unblock_code-email-->
Primary email for the account.
<!--end-request-body-post-accountloginsend_unblock_code-email-->
* `metricsContext`: *metricsContext.schema*
<!--begin-request-body-post-accountloginsend_unblock_code-metricsContext-->
<!--end-request-body-post-accountloginsend_unblock_code-metricsContext-->
#### POST /account/login/reject_unblock_code
<!--begin-route-post-accountloginreject_unblock_code-->
Used to reject and report
unblock codes that were not requested by the user.
<!--end-route-post-accountloginreject_unblock_code-->
##### Request body
* `uid`: *string, max(32), regex(HEX_STRING), required*
<!--begin-request-body-post-accountloginreject_unblock_code-uid-->
The user id.
<!--end-request-body-post-accountloginreject_unblock_code-uid-->
* `unblockCode`: *string, regex(BASE_36), length(unblockCodeLen), required*
<!--begin-request-body-post-accountloginreject_unblock_code-unblockCode-->
The unblock code.
<!--end-request-body-post-accountloginreject_unblock_code-unblockCode-->
### Util
#### POST /get_random_bytes
<!--begin-route-post-get_random_bytes-->
Get 32 bytes of random data.
This should be combined with locally-sourced entropy
when creating salts, etc.
<!--end-route-post-get_random_bytes-->
#### GET /verify_email
<!--begin-route-get-verify_email-->
<!--end-route-get-verify_email-->
##### Query parameters
* `code`: *string, max(32), regex(HEX_STRING), required*
<!--begin-query-param-get-verify_email-code-->
<!--end-query-param-get-verify_email-code-->
* `uid`: *string, max(32), regex(HEX_STRING), required*
<!--begin-query-param-get-verify_email-uid-->
<!--end-query-param-get-verify_email-uid-->
* `service`: *string, max(16), alphanum, optional*
<!--begin-query-param-get-verify_email-service-->
<!--end-query-param-get-verify_email-service-->
* `redirectTo`: *validators.redirectTo(redirectDomain).optional*
<!--begin-query-param-get-verify_email-redirectTo-->
<!--end-query-param-get-verify_email-redirectTo-->
#### GET /complete_reset_password
<!--begin-route-get-complete_reset_password-->
<!--end-route-get-complete_reset_password-->
##### Query parameters
* `email`: *validators.email.required*
<!--begin-query-param-get-complete_reset_password-email-->
<!--end-query-param-get-complete_reset_password-email-->
* `code`: *string, max(32), regex(HEX_STRING), required*
<!--begin-query-param-get-complete_reset_password-code-->
<!--end-query-param-get-complete_reset_password-code-->
* `token`: *string, max(64), regex(HEX_STRING), required*
<!--begin-query-param-get-complete_reset_password-token-->
<!--end-query-param-get-complete_reset_password-token-->
* `service`: *string, max(16), alphanum, optional*
<!--begin-query-param-get-complete_reset_password-service-->
<!--end-query-param-get-complete_reset_password-service-->
* `redirectTo`: *validators.redirectTo(redirectDomain).optional*
<!--begin-query-param-get-complete_reset_password-redirectTo-->
<!--end-query-param-get-complete_reset_password-redirectTo-->
## Back-off protocol
<!--begin-back-off-protocol-->
During periods of heavy load,
the server may request that clients enter a "back-off" state,
in which they avoid making further requests.
At such times,
it will return a `503 Service Unavailable` response
with a `Retry-After` header denoting the number of seconds to wait
before issuing any further requests.
It will also include `errno: 201`
and a `retryAfter` field
matching the value of the `Retry-After` header
in the body.
For example,
the following response indicates that the client
should suspend making further requests
for 30 seconds:
```
HTTP/1.1 503 Service Unavailable
Retry-After: 30
Content-Type: application/json
{
"code": 503,
"errno": 201,
"error": "Service Unavailable",
"message": "Service unavailable",
"info": "https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#response-format",
"retryAfter": 30,
"retryAfterLocalized": "in a few seconds"
}
```
<!--end-back-off-protocol-->
## This document
<!--begin-this-document-->
This document is automatically generated
by [a script](../scripts/write-api-docs.js)
that parses the source code
and the document itself.
All changes to this document will be lost
unless they are made inside
delimiting HTML comments of the form:
```html
<!--begin-foo-bar-->
YOUR CHANGE GOES HERE
<!--end-foo-bar->
```
`foo-bar` must be a tag
that, when camel-cased,
matches a property name in the data
for the [mustache template](../scripts/api-docs.mustache)
this document is generated from.
If you want to change
the structure of the document,
you must make your changes
to `scripts/api-docs.mustache`
rather than in this document directly.
<!--end-this-document-->