Skip to content

Commit 77a453b

Browse files
committed
docs: Write a getting started guide
1 parent de2fab0 commit 77a453b

2 files changed

Lines changed: 291 additions & 0 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ You can then execute the following flows:
3434
As we are still in the progress of documenting everything,
3535
the above scripts are the best way to learn about how everything works.
3636

37+
A more extensive getting started guide can be found
38+
in [documentation/getting-started.md](documentation/getting-started.md).
39+
3740
## Demonstration
3841

3942
Instead of running `yarn start`, you can run `yarn start:demo` to start the server with an alternative configuration.

documentation/getting-started.md

Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
# Getting started
2+
3+
This tutorial uses step-by-step examples to explain
4+
how the different pieces in this repository work together
5+
to combine a User Managed Acces (UMA) authorization server with a Solid resource server,
6+
and which additions we have made to make this work.
7+
The main goal is to have a grasp on how to use this project,
8+
which means some concepts of the several protocols involved here
9+
are simplified or omitted.
10+
11+
For the full details, we refer to the official documentation:
12+
* UMA: https://docs.kantarainitiative.org/uma/wg/rec-oauth-uma-grant-2.0.html
13+
* Federated UMA: https://docs.kantarainitiative.org/uma/wg/rec-oauth-uma-federated-authz-2.0.html
14+
* Solid: https://solidproject.org/TR/
15+
* A4DS: https://spec.knows.idlab.ugent.be/A4DS/L1/latest/
16+
* This covers changes and choices made in this repository
17+
18+
It is recommended to go through the
19+
[Community Solid Server (CSS) tutorial](https://github.com/CommunitySolidServer/tutorials/blob/main/getting-started.md),
20+
as that covers the basics of Solid and the CSS,
21+
which is used as a basis for this repository and this guide.
22+
23+
Note that this repository, and how the protocols are implemented, is still changing,
24+
so some information might change depending on which version and branch you're using.
25+
26+
## Index
27+
28+
- [Getting started](#getting-started)
29+
* [Starting the server](#starting-the-server)
30+
* [Authenticating the Resource Server](#authenticating-the-resource-server)
31+
* [Resource registration](#resource-registration)
32+
+ [About identifiers](#about-identifiers)
33+
* [Resource access](#resource-access)
34+
+ [Informing the UMA Authorization Server](#informing-the-uma-authorization-server)
35+
+ [Generating a ticket](#generating-a-ticket)
36+
- [Publicly accessible resources](#publicly-accessible-resources)
37+
+ [Exchange ticket](#exchange-ticket)
38+
- [Claim security](#claim-security)
39+
+ [Generate token](#generate-token)
40+
+ [Use token](#use-token)
41+
* [Policies](#policies)
42+
* [Adding or changing policies](#adding-or-changing-policies)
43+
44+
<small><i><a href='http://ecotrust-canada.github.io/markdown-toc/'>Table of contents generated with markdown-toc</a></i></small>
45+
46+
## Starting the server
47+
48+
This repository contains several startup and test scripts,
49+
as described in the [README](../README.md).
50+
In this guide, we describe more in-depth what happens in some of these scripts.
51+
52+
To begin, run the `yarn start` script,
53+
which sets up the necessary servers.
54+
Specifically, this starts a UMA Authorization Server (AS) at `http://localhost:4000`,
55+
and a Solid Resource Server (RS) at `http://localhost:3000`.
56+
It also initializes several resources in-memory,
57+
using [pod seeding](https://communitysolidserver.github.io/CommunitySolidServer/latest/usage/seeding-pods/),
58+
to make it easier to get started.
59+
60+
You can see the AS working by going to <http://localhost:4000/uma/.well-known/uma2-configuration>.
61+
This page contains all the relevant APIs of the UMA server,
62+
which are used in the next steps.
63+
64+
## Authenticating the Resource Server
65+
66+
Throughout this guide, there are several instances where the RS has to send an HTTP request to the AS.
67+
The AS needs some way to verify if the request comes from the RS.
68+
The current implementation makes use of [HTTP signatures](https://datatracker.ietf.org/doc/html/rfc9421).
69+
To enable this, the RS needs to expose a [JSON Web Key](https://datatracker.ietf.org/doc/html/rfc7517).
70+
This key can be found at the `jwks_uri` API of OpenID configuration of the RS,
71+
seen at <http://localhost:3000/.well-known/openid-configuration>.
72+
The RS uses that same key to sign its messages as described in the RFC,
73+
using the [http-message-signatures](https://www.npmjs.com/package/http-message-signatures) library.
74+
This is done for every HTTP request the RS sends to the AS in the following sections.
75+
76+
## Resource registration
77+
78+
The Federated UMA specification requires that the RS registers every resource at the AS.
79+
This way the AS knows for which resources it needs to manage the access.
80+
As several resources are created immediately due to the pod seeding,
81+
these all need to be registered at the AS.
82+
The RS does this by POSTing a request to the `resource_registration_endpoint` API with the following body:
83+
```json
84+
{
85+
"resource_scopes": [
86+
"urn:example:css:modes:read",
87+
"urn:example:css:modes:append",
88+
"urn:example:css:modes:create",
89+
"urn:example:css:modes:delete",
90+
"urn:example:css:modes:write"
91+
]
92+
}
93+
```
94+
95+
This tells the AS that it should register a new resource,
96+
and what its available scopes are.
97+
The above scopes are those currently supported by the server setup,
98+
and are mostly based on the similar scopes defined by
99+
the [WAC specification](https://solid.github.io/web-access-control-spec/).
100+
The `create` scope is different and indicates the client wants to create a new resource in the given container.
101+
102+
When the AS receives this request, it mints a new identifier.
103+
This identifier is used to represent the resource on the AS side,
104+
in the relevant policies that determine access.
105+
The Solid identifier of the resource is irrelevant,
106+
and not even known by the AS.
107+
If the request is successful,
108+
the AS responds with a 201 status code.
109+
The location header contains the new identifier.
110+
The RS stores this identifier, linked to the Solid identifier, for future use.
111+
112+
### About identifiers
113+
114+
As mentioned above, the UMA identifier and Solid identifier are independent identifiers,
115+
with the UMA AS only knowing the former.
116+
This means that whoever writes the policies that determine access,
117+
need to be aware of the UMA identifiers of resources.
118+
Work is currently being done on having an API that provides all the necessary information,
119+
so users are informed of which resources correspond to which identifiers.
120+
As this is currently not clear to users yet,
121+
the "minted" identifier on the UMA server is the same as the Solid identifier.
122+
To inform the UMA server of what the Solid identifier is,
123+
the Solid RS needs to add a `name` field to the registration body described above,
124+
with the value being the Solid identifier. E.g.:
125+
```json
126+
{
127+
"name": "http://localhost:3000/my/resource",
128+
"resource_scopes": [
129+
"urn:example:css:modes:read",
130+
"urn:example:css:modes:append",
131+
"urn:example:css:modes:create",
132+
"urn:example:css:modes:delete",
133+
"urn:example:css:modes:write"
134+
]
135+
}
136+
```
137+
In the future, this field will be used to describe the resource instead of using it as the actual identifier.
138+
139+
## Resource access
140+
141+
When trying to access a resource,
142+
several steps have to be taken by both the client and both servers.
143+
These are described in-depth in the relevant specifications.
144+
In this section, we go through all the steps of a single PUT request,
145+
targeting `http://localhost:3000/alice/private/resource.txt`.
146+
This same example can be seen in [scripts/test-private.ts](../scripts/test-private.ts).
147+
148+
### Informing the UMA Authorization Server
149+
150+
The first step of a request is the same as it is for a Solid server with a standard authorization server:
151+
send the PUT request to the Solid RS.
152+
As usual, the RS first determines which scopes are necessary.
153+
As `http://localhost:3000/alice/private/resource.txt` does not exist yet,
154+
these scopes need to indicate that the client wants to create a new resource.
155+
When using UMA, scopes need to be determined on registered resources.
156+
This means that it is not possible to require scopes on resources that do not exist yet,
157+
as is possible with WAC.
158+
To support this requirement, when a new resource needs to be created,
159+
`create` permissions will be required on the first existing parent container.
160+
Since `http://localhost:3000/alice/private/` also does not exist,
161+
this request requires `create` permissions on `http://localhost:3000/alice/`.
162+
163+
Once the RS determines the scopes, it contacts the AS through the `permission_endpoint` API.
164+
It performs a `POST` request with a JSON body containing the UMA identifier of the resource
165+
and the requested scopes, which looks as follows:
166+
```json
167+
{
168+
"resource_id": "12345", // Assume this is the UMA ID of http://localhost:3000/alice/
169+
"resource_scopes": [
170+
"urn:example:css:modes:create"
171+
]
172+
}
173+
```
174+
175+
### Generating a ticket
176+
177+
The first thing the AS has to do when receiving any HTTP request is to validate the signature, as discussed above.
178+
Afterward, it creates a ticket identifier, links it with the request body,
179+
and responds to the RS request with a 201 status code.
180+
The location header of the response contains the ticket identifier.
181+
The RS then responds to the client, which is still waiting for a response,
182+
with a 401 status code.
183+
To inform the client on how to acquire access,
184+
the 401 response has a `WWW-Authenticate` header
185+
with value `UMA realm="solid", as_uri="http://localhost:4000/uma/", ticket="TICKET_ID"`.
186+
The client then parses this header to know where the AS is,
187+
and what the ticket identifier is that it needs to present there.
188+
189+
#### Publicly accessible resources
190+
191+
UMA requires the above process for every resource access.
192+
This makes it impossible to have public resources that can be accessed with, for example, a simple GET request.
193+
To still allow for such situations,
194+
the AS will return a 200 response, instead of a 201,
195+
if it determines no claims are required to perform the request.
196+
In that case, the RS will execute the client's request immediately, instead of returning a 401 with a ticket.
197+
198+
### Exchange ticket
199+
200+
To receive access, the client has to exchange the ticket for a token at the AS.
201+
This is done through the `token_endpoint` API.
202+
Besides the ticket, the client has to include the necessary claims to identify itself.
203+
The only claim currently supported by the AS, is the WebID,
204+
which for this example is `https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me`.
205+
To make the request, the client performs a POST with the following JSON body:
206+
```json
207+
{
208+
"grant_type": "urn:ietf:params:oauth:grant-type:uma-ticket",
209+
"ticket": "TICKET_ID",
210+
"claim_token": "https%3A%2F%2Fwoslabbi.pod.knows.idlab.ugent.be%2Fprofile%2Fcard%23me",
211+
"claim_token_format": "urn:solidlab:uma:claims:formats:webid"
212+
}
213+
```
214+
The `claim_token_format` explains to the AS how the `claim_token` should be interpreted.
215+
In this case, this is a custom format designed for this server.
216+
217+
#### Claim security
218+
219+
In the above body, the claim token format is a string representing a WebID.
220+
No actual authentication or verification takes place here,
221+
meaning anyone can insert any WebID they want.
222+
This allows for easy testing and examples,
223+
but will be changed to an actual safe method, such as an OIDC ID token, in the future.
224+
225+
### Generate token
226+
227+
Once the AS receives a token request, it has to match the ticket ID
228+
with the scopes it stored internally in a previous step.
229+
Based on the stored policies, it then determines if the provided claims are sufficient to allow the request.
230+
How these policies work will be covered later on.
231+
If successful, the server will return a 200 response with a JSON body containing, among others,
232+
an `access_token` field containing the access token, and a `token_type` field describing the token type.
233+
If the claims are insufficient, a 403 response will be given instead.
234+
235+
### Use token
236+
237+
When receiving the access token, the client can perform the same request as it did in the first step,
238+
but now include an `Authorization` header with value `TOKEN_TYPE ACCESS_TOKEN`,
239+
based on the response values in the previous step.
240+
When receiving this, the RS validates the token with the AS,
241+
similarly how this is done with a standard Solid server with OIDC.
242+
If the token is valid,
243+
it then performs the request the client wanted.
244+
245+
## Policies
246+
247+
To determine the allowed scopes on a resource,
248+
the AS makes use of ODRL policies,
249+
for which we refer to the [specification](https://www.w3.org/TR/odrl-model/) for the full details.
250+
For our purposes, the AS does not use everything from the ODRL specification yet, such as refinements and duties,
251+
but only the core building blocks.
252+
Below is an example of the policy that allowed the example above to succeed:
253+
```ttl
254+
@prefix ex: <http://example.org/1707120963224#> .
255+
@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
256+
257+
ex:usagePolicy a odrl:Agreement ;
258+
odrl:permission ex:permission .
259+
ex:permission a odrl:Permission ;
260+
odrl:action odrl:create ;
261+
odrl:target <alice/private/> , <alice/private/resource.txt> ;
262+
odrl:assignee <https://woslabbi.pod.knows.idlab.ugent.be/profile/card#me> .
263+
```
264+
This policy says that the above WebID has access to the `create` scope
265+
on `<alice/private/>` and `<alice/private/resource.txt>`.
266+
The server is configured so the base URL of all policy documents is the URL of the RS, `http://localhost:3000/`,
267+
to make sure the identifiers match the Solid resource identifiers as discussed [above](#about-identifiers).
268+
269+
## Adding or changing policies
270+
271+
When starting the server with `yarn start`,
272+
the server is configured to load all policies from a specified folder.
273+
In this case, this is [packages/uma/config/rules/policy](../packages/uma/config/rules/policy).
274+
This is done by setting the Components.js variable `urn:uma:variables:policyBaseIRI` to the necessary folder,
275+
as can be seen in the start script at [packages/uma/bin/main.js](../packages/uma/bin/main.js).
276+
With this setup,
277+
there is no way to change the policies while the server is running.
278+
The only way is to change the folder and restart the server.
279+
280+
An alternative setup is used with the `yarn start:demo` script.
281+
There the server is configured to read all policies from a specific Solid container,
282+
set by the `urn:uma:variables:policyContainer` variable.
283+
This way, it is possible to modify the policies at run-time by changing the contents of that container.
284+
In this case, it is important to make sure UMA is not used to handle the access of that container,
285+
as that would prevent the UMA server from readings its contents to determine the policies.
286+
287+
Both of these options have their issues,
288+
so work is being done on having a more secure and usable solution.

0 commit comments

Comments
 (0)