Macaroons¶
Introduction¶
The Macaroon API provides means to request and verify macaroons that can be used to authenticate requests against the Store.
The Macaroon API is exposed at the following base URLs:
Production: https://dashboard.snapcraft.io/dev/api
Response Format¶
JSON will be returned in all responses from the API, including errors.
Errors¶
The Macaroon API uses conventional HTTP response codes to indicate success or failure of an API request.
In general, codes in the 2xx range indicate success, codes in the 4xx range indicate an error that resulted from the provided information (e.g. a required parameter was missing) and codes in the 5xx range indicate an error with our servers.
Here is detailed the format for API responses that end in error.
This applies to all the 4xx responses, but also to some 5xx ones (if possible, the client should be prepared to handle 5xx responses with no informational body). Note that this structure format does not apply to 2xx and 3xx responses, as those are not errors.
Format¶
An error response body will contain the following field:
error_list
: a list of one or several items (never empty), each item described by…message
: a text in English describing the error that happened, ready to show to the user.code
: a short (but representative) string indicating concisely the error; it’s aimed for clients to take specific actions and react to the problem. See below for the list of existing codes.
Additionally and for backwards compatibility reasons, some other fields may be present as well, but are considered deprecated and will be removed in the near future.
No status or success indication is returned inside the response body, the client should react properly to the received HTTP return code according to its well established semantics.
Codes¶
These are the codes used in the response and their meanings:
assertion-creation-failed
: the request asked for an assertion to be created, but this failed.bad-request
: there is a problem in the structure of the request.feature-disabled
: the request cannot be fulfilled because the feature is currently disabled on the server side.internal-server-error
: some unexpected problem server side.invalid-field
: the field received in the request has format problems (e.g.: must be a number and it’s not) or value problems (e.g.: it contains an account-key-request assertion which does not match the user’s account).macaroon-permission-required
: the macaroon authorization is missing in the received request or not enough for it to be fulfilled.missing-field
: the request received must include a field which was not present.user-not-ready
: the user is not ready to issue the received request; normally some actions would need to be done in the user account before repeating the request.
Examples¶
A simple error:
{
"error_list": [{
"message": "The 'foo' field must not be empty",
"code": "invalid-field"
}]
}
A multiple error:
{
"error_list": [{
"message": "The 'foo' field is required",
"code": "missing-field"
}, {
"message": "The 'bar' field is required",
"code": "missing-field"
}, {
"message": "The 'baz' field must not be empty",
"code": "invalid-field"
}]
}
Reference¶
Request a macaroon¶
This endpoint is normally used without authentication, and returns a macaroon with a third-party caveat requiring a discharge from https://login.ubuntu.com.
- POST /dev/api/acl/¶
- Request JSON Object
permissions – a list of permissions to include in the macaroon
channels – a list of channels to restrict the macaroon to, allows wildcards in the fnmatch format
packages – a list of packages to restrict the macaroon to
expires – a timestamp (string formatted per ISO8601) after which` the macaroon should not be valid (UTC only values are allowed)
- Response JSON Object
macaroon (string) – the serialized macaroon
- Status Codes
200 OK – success
400 Bad Request – error
The current values are valid permissions
edit_account
allows updating account informationmodify_account_key
allows modifying, registering new or revoking existing account keys.package_access
allows downloads (installs) of any snap revisions as well as reading current details accessible by the authenticated principal.package_register
allows registering new snap names.package_push
allows pushing new snap revisions, restricted to thepackages
values, if specified.package_release
allows releasing existing snap revisions, restricted to the`channels
andpackages
values, if specified.package_update
allows updating details on existing snaps, restricted to thepackages
values, if specified.package_metrics
allows access to snap metrics, restricted to thepackages
values, if specified.package_manage
allows managing snap developers (collaborators), restricted topackages
values, if specified.package_upload
is equivalent topackage_register
,package_push
,package_release
,package_update
andpackage_metrics
set of permissions altogether.package_upload_request
: allows new name registrations and requesting a dischargedpackage_upload
macaroon, see Note below.
When specifying a list of packages
to restrict the macaroon, those can be
indicated in two ways:
by name/series: each item in the list should be a json dict like
{"name": "the-name", "series": "16"}
by snap_id: each item in the list should be a json dict like
{"snap_id": "some-snap-id-1234"}
Note
When requesting a package_upload
macaroon, a discharged
package_upload_request
macaroon may be supplied as authorisation
for the request; in that case, that is taken as authorisation to issue
package_upload
macaroons with more specific constraints that do not
require a discharge from https://login.ubuntu.com.
Expiry¶
Using the following permissions will, by default, cause the macaroon to have an expiry date of one year from the date of the request. Including any of these in the request will create this expiry date, no matter which other permissions are also requested. Shorter durations can be created using the expires parameter.
edit_account
modify_account_key
package_access
store_admin
store_review
For any other mix of permissions, the expires timestamp can be omitted, creating a macaroon which will never expire. Passing a timestamp of more than one year is also valid in this situation.
Usage¶
Read-only access¶
Request a generic macaroon for retrieving details from all snaps accessible by the context principal.
Request:
POST /dev/api/acl/ HTTP/1.1
Host: dashboard.snapcraft.io
Content-Type: application/json
{
"permissions": ["package_access"],
}
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"macaroon": "the-serialized-macaroon-data"
}
Restricted upload rights¶
Request a macaroon for uploading new revisions of a specific snap and releasing only to the “edge” channel.
Request:
POST /dev/api/acl/ HTTP/1.1
Host: dashboard.snapcraft.io
Content-Type: application/json
{
"permissions": ["package_upload"],
"channels": ["edge"],
"packages": [{"name": "foo", "series": "16"}]
}
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"macaroon": "the-serialized-macaroon-data"
}
Managing snap collaborators¶
Request a macaroon for managing collaborators of a specific snap.
Request:
POST /dev/api/acl/ HTTP/1.1
Host: dashboard.snapcraft.io
Content-Type: application/json
{
"permissions": ["package_manage"],
"packages": [{"snap_id": "foo-id-1234"}]
}
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"macaroon": "the-serialized-macaroon-data"
}
Editing account information¶
Request a macaroon for editing an account information.
Request:
POST /dev/api/acl/ HTTP/1.1
Host: dashboard.snapcraft.io
Content-Type: application/json
{
"permissions": ["edit_account"],
}
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"macaroon": "the-serialized-macaroon-data"
}
Register/Revoke keys¶
Request a macaroon for modifying account keys.
Request:
POST /dev/api/acl/ HTTP/1.1
Host: dashboard.snapcraft.io
Content-Type: application/json
{
"permissions": ["modify_account_key"],
}
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"macaroon": "the-serialized-macaroon-data"
}
Error messages¶
If the request was made for an invalid permission to be included, the server will respond with something like:
Request:
POST /dev/api/acl/ HTTP/1.1
Host: dashboard.snapcraft.io
Content-Type: application/json
{
"permissions": ["package_delete"],
}
Response:
HTTP/1.1 400 BAD REQUEST
Content-Type: application/json; charset=utf-8
{
"error_list": [
{
"message": "Permission is not valid: package_delete",
"code": "invalid-request",
"extra": {"permission": "package_delete"}
}
]
}
If the request is invalid in some other way, the server will respond accordingly. For example:
Request:
POST /dev/api/acl/ HTTP/1.1
Host: dashboard.snapcraft.io
Content-Type: application/json
{
"permissions": "package_access",
}
Response:
HTTP/1.1 400 BAD REQUEST
Content-Type: application/json; charset=utf-8
{
"error_list": [
{
"message": "Expected permissions to be a list. Got: package_access",
"code": "invalid-request"
}
]
}
If a macaroon was requested for a particular package that doesn’t exist, the server will respond with a generic 404 response.
Response:
HTTP/1.1 404 NOT FOUND
Validate a discharged macaroon¶
- POST /dev/api/acl/verify/¶
- Request JSON Object
auth_data – a json object containing authorization
- Response JSON Object
allowed (boolean) – the macaroon is valid and the request should be allowed
device_refresh_required (boolean) – a device macaroon is expired and needs to be refreshed (obsolete: device macaroons are no longer issued by this service)
refresh_required (boolean) – the macaroon is expired and needs to be refreshed
account – a json object representing details about the account
device – a json object representing details about the device (obsolete: device macaroons are no longer issued by this service)
last_auth – a timestamp (string formatted per ISO8601) indicating when the user last authenticated
permissions – a list of permissions associated with the macaroon
snap_ids – a list of snap identifiers to which the macaroon is constrained
channels – a list of channel names to which the macaroon is constrained
- Status Codes
200 OK – success
400 Bad Request – error
404 Not Found – not found
The auth_data object should contain one of:
authorization
: the value of the Authorization header of the request being verified
The account
data, if not null
will be a json object containing the fields:
email
: the primary email of the accountdisplayname
: the name used to identify the accountopenid
: the openid identifier for the accountverified
: whether the account has been verified
The device
data, if not null
will be a json object containing the fields:
brand
: the brand of the devicemodel
: the model of the deviceserial
: the serial number of the device
Usage¶
Submit the request to verify a macaroon used for authorization.
Request
POST /dev/api/acl/verify/ HTTP/1.1
Host: dashboard.snapcraft.io
Content-Type: application/json
{
"auth_data": {
"authorization": "Macaroon root=root-macaroon-data, discharge=discharge-macaroon-data"
}
}
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"allowed": true,
"device_refresh_required": false,
"refresh_required": false,
"account": {
"email": "user@example.org",
"displayname": "The User",
"openid": "oid1234",
"verified": true
},
"device": null,
"last_auth": "2016-05-26T12:53:23Z",
"permissions": ["package_access"],
"snap_ids": null,
"channels": null
}
If the macaroon was expired the following response would be returned instead
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"allowed": false,
"device_refresh_required": false,
"refresh_required": true,
"account": null,
"device": null,
"last_auth": null,
"permissions": null,
"snap_ids": null,
"channels": null
}
Alternatively, if the macaroon was invalid in any way, the server would reply with
Response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
{
"allowed": false,
"device_refresh_required": false,
"refresh_required": false,
"account": null,
"device": null,
"last_auth": null,
"permissions": null,
"snap_ids": null,
"channels": null
}
In case the original request was invalid, for example if the auth_data parameter was missed, the server would return the following response
Response:
HTTP/1.1 400 BAD REQUEST
Content-Type: application/json; charset=utf-8
{
"error_list": [
{
"message": "Missing expected \"auth_data\" parameter.",
"code": "invalid-request",
}
]
}