Security

EventStoreDB supports basic authentication for HTTP API calls, and access control lists (ACL).

Authentication

Creating users

EventStoreDB supports basic HTTP authentication to internal users. You create these users with the HTTP API or the admin console. You need to use the credentials of the default user in the request, which has the username of admin, and the password of changeit.

When using the HTTP API, you can send the following JSON payload to the server:

{
    "LoginName": "adminuser",
    "FullName": "EventStore Admin",
    "Groups": [
        "$admins",
        "DataScience"
    ],
    "Password": "aVerySecurePassword"
}
1
2
3
4
5
6
7
8
9
curl -i -d "@new-user.json" "http://127.0.0.1:2113/users" \
    -H "Content-Type:application/json"
1
2
HTTP/1.1 201 Created
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-Forwarded-Host, X-Forwarded-Prefix, X-PINGOTHER, Authorization, ES-LongPoll, ES-ExpectedVersion, ES-EventId, ES-EventType, ES-RequiresMaster, ES-HardDelete, ES-ResolveLinkTos
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ES-Position, ES-CurrentVersion
Location: http://127.0.0.1:2113/users/adminuser
Content-Type: application/json; charset=utf-8
Server: Mono-HTTPAPI/1.0
Date: Thu, 23 Aug 2018 09:08:40 GMT
Content-Length: 90
Keep-Alive: timeout=15,max=100

{
  "loginName": "adminuser",
  "success": true,
  "error": "Success",
  "msgTypeId": 50
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

Once you have added users, you can use their details with requests.

If you were to use the wrong user or no user when a request requires one, you receive a 401 Unauthorized response.

curl -i 'http://127.0.0.1:2113/streams/$all' -u admin:password
1
HTTP/1.1 401 Unauthorized
Access-Control-Allow-Methods:
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-Forwarded-Host, X-Forwarded-Prefix, X-PINGOTHER, Authorization, ES-LongPoll, ES-ExpectedVersion, ES-EventId, ES-EventType, ES-RequiresMaster, ES-HardDelete, ES-ResolveLinkTos
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ES-Position, ES-CurrentVersion
WWW-Authenticate: Basic realm="ES"
Content-Type:
Server: Mono-HTTPAPI/1.0
Date: Thu, 23 Aug 2018 09:27:34 GMT
Content-Length: 0
Keep-Alive: timeout=15,max=100
1
2
3
4
5
6
7
8
9
10
11

As you pass the username and password in the request we recommend you to enable SSL to encrypt the user information. Read this guide for instructions.

Access control lists

Alongside authentication, EventStoreDB supports per stream configuration of Access Control Lists (ACL). To configure the ACL of a stream go to its head and look for the metadata relationship link to fetch the metadata for the stream.

To set access control lists over HTTP you can post to the metadata stream as with setting any other metadata. You can also set Access Control Lists for a stream in the admin UI.

ACL example

The ACL below gives writer read and write permission on the stream, while reader has read permission on the stream. Only users in the $admins group can delete the stream or read and write the metadata.

The request body placed in the file named metadata.json:

[
    {
        "eventId": "7c314750-05e1-439f-b2eb-f5b0e019be72",
        "eventType": "$user-updated",
        "data": {
            "readRole": "$all",
            "metaReadRole": "$all"
        }
    }
]
1
2
3
4
5
6
7
8
9
10

Then, when you execute HTTP request as follows:

curl -i -d @metadata.json http://127.0.0.1:2113/streams/newstream/metadata \
    --user admin:changeit \
    -H "Content-Type: application/vnd.eventstore.events+json"
1
2
3

You get a confirmation from the server:

HTTP/1.1 201 Created
Access-Control-Allow-Methods: GET, POST, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-Forwarded-Host, X-Forwarded-Prefix, X-PINGOTHER, Authorization, ES-LongPoll, ES-ExpectedVersion, ES-EventId, ES-EventType, ES-RequiresMaster, ES-HardDelete, ES-ResolveLinkTos
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ES-Position, ES-CurrentVersion
Location: http://127.0.0.1:2113/streams/%24%24newstream/0
Content-Type: text/plain; charset=utf-8
Server: Mono-HTTPAPI/1.0
Date: Tue, 18 Sep 2018 09:38:56 GMT
Content-Length: 0
Keep-Alive: timeout=15,max=100
1
2
3
4
5
6
7
8
9
10
11

Default ACL

TIP

All these examples assume you have created a user named ouro with password ouroboros.

If you try to access the $settings stream as an unauthorized user, the server returns a 401 response.

curl -i http://127.0.0.1:2113/streams/%24settings \
    -u ouro:ouroboros
1
2
HTTP/1.1 401 Unauthorized
Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER, Authorization, ES-LongPoll, ES-ExpectedVersion, ES-EventId, ES-EventType, ES-RequiresMaster, ES-HardDelete, ES-ResolveLinkTo, ES-ExpectedVersion
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ES-Position
WWW-Authenticate: Basic realm="ES"
Content-Type: text/plain; charset=utf-8
Server: Mono-HTTPAPI/1.0
Date: Mon, 02 Mar 2015 15:21:27 GMT
Content-Length: 0
Keep-Alive: timeout=15,max=100
1
2
3
4
5
6
7
8
9
10
11

If you wanted to give ouro access by default to system streams, POST the following JSON:

{
  "$userStreamAcl": {
    "$r": "$all",
    "$w": "ouro",
    "$d": "ouro",
    "$mr": "ouro",
    "$mw": "ouro"
  },
  "$systemStreamAcl": {
    "$r": ["$admins", "ouro"],
    "$w": "$admins",
    "$d": "$admins",
    "$mr": "$admins",
    "$mw": "$admins"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

At which point ouro can read system streams by default:

curl -i http://127.0.0.1:2113/streams/%24settings \
    -u ouro:ouroboros
1
2
HTTP/1.1 200 OK
Access-Control-Allow-Methods: POST, DELETE, GET, OPTIONS
Access-Control-Allow-Headers: Content-Type, X-Requested-With, X-PINGOTHER, Authorization, ES-LongPoll, ES-ExpectedVersion, ES-EventId, ES-EventType, ES-RequiresMaster, ES-HardDelete, ES-ResolveLinkTo, ES-ExpectedVersion
Access-Control-Allow-Origin: *
Access-Control-Expose-Headers: Location, ES-Position
Cache-Control: max-age=0, no-cache, must-revalidate
Vary: Accept
ETag: "1;-1296467268"
Content-Type: application/atom+xml; charset=utf-8
Server: Mono-HTTPAPI/1.0
Date: Mon, 02 Mar 2015 15:25:17 GMT
Content-Length: 1286
Keep-Alive: timeout=15,max=100
1
2
3
4
5
6
7
8
9
10
11
12
13

You can also limit ACLs on particular streams which are then merged with the default ACLs.

{
  "$acl": {
    "$r": ["reader", "also-reader"]
  }
}
1
2
3
4
5

If you add the above to a stream's ACL, then it overrides the read permission on that stream to allow reader and also-reader to read streams, but not ouro, resulting in the effective ACL below.

{
  "$acl": {
    "$r": ["reader", "also-reader"],
    "$w": "ouro",
    "$d": "ouro",
    "$mr": "ouro",
    "$mw": "ouro"
  }
}
1
2
3
4
5
6
7
8
9

WARNING

Caching is allowed on a stream if you have enabled it to be visible to $all. This is as a performance optimization to avoid having to set cache=private on all data. If people are bookmarking your URIs and they were cached by an intermediary then they may still be accessible after you change the permissions from $all. While clients should not be bookmarking URIs in this way, it's an important consideration.