feat: Full rework of account management

Complete rewrite of the account management and related systems.
Makes the architecture more modular,
allowing for easier extensions and configurations.
This commit is contained in:
Joachim Van Herwegen
2022-03-16 10:12:13 +01:00
parent ade977bb4f
commit a47f5236ef
366 changed files with 12345 additions and 5111 deletions

View File

@@ -32,7 +32,7 @@ the [changelog](https://github.com/CommunitySolidServer/CommunitySolidServer/blo
* [Quickly starting the server](usage/starting-server.md)
* [Basic example HTTP requests](usage/example-requests.md)
* [Editing the metadata of a resource](usage/metadata.md)
* [How to use the Identity Provider](usage/identity-provider.md)
* [How to use the Identity Provider and accounts](usage/identity-provider.md)
* [How to automate authentication](usage/client-credentials.md)
* [How to automatically seed pods on startup](usage/seeding-pods.md)
* [Receiving notifications when resources change](usage/notifications.md)

View File

@@ -0,0 +1,27 @@
# JSON API controls
A large part of every response of the JSON API is the `controls` block.
These are generated by using nested `ControlHandler` objects.
These take as input a key/value with the values being either routes or other interaction handlers.
These will then be executed to determine the values of the output JSON object, with the same keys.
By using other `ControlHandler`s in the input map, we can create nested objects.
The default structure of these handlers is as follows:
```mermaid
flowchart LR
RootControlHandler("<strong>RootControlHandler</strong><br>ControlHandler")
RootControlHandler --controls--> ControlHandler("<strong>ControlHandler</strong><br>ControlHandler")
ControlHandler --main--> MainControlHandler("<strong>MainControlHandler</strong><br>ControlHandler")
ControlHandler --account--> AccountControlHandler("<strong>AccountControlHandler</strong><br>ControlHandler")
ControlHandler --password--> PasswordControlHandler("<strong>PasswordControlHandler</strong><br>ControlHandler")
ControlHandler --"oidc"--> OidcControlHandler("<strong>OidcControlHandler</strong><br>OidcControlHandler")
ControlHandler --html--> HtmlControlHandler("<strong>HtmlControlHandler</strong><br>ControlHandler")
HtmlControlHandler --main--> MainHtmlControlHandler("<strong>MainHtmlControlHandler</strong><br>ControlHandler")
HtmlControlHandler --account--> AccountHtmlControlHandler("<strong>AccountHtmlControlHandler</strong><br>ControlHandler")
HtmlControlHandler --password--> PasswordHtmlControlHandler("<strong>PasswordHtmlControlHandler</strong><br>ControlHandler")
```
Each of these control handlers then has a map of routes which link to the actual API endpoints.
How to add these can be seen [here](routes.md#adding-the-necessary-controls).

View File

@@ -0,0 +1,58 @@
# Account management
The main entry point is the `IdentityProviderHandler`,
which routes all requests targeting a resource starting with `/.account/` into this handler,
after which it goes through similar parsing handlers as described [here](../protocol/overview.md),
the flow of which is shown below:
```mermaid
flowchart LR
Handler("<strong>IdentityProviderHandler</strong><br>RouterHandler")
ParsingHandler("<strong>IdentityProviderParsingHandler</strong><br>AuthorizingHttpHandler")
AuthorizingHandler("<strong>IdentityProviderAuthorizingHandler</strong><br>AuthorizingHttpHandler")
Handler --> ParsingHandler
ParsingHandler --> AuthorizingHandler
AuthorizingHandler --> HttpHandler("<strong>IdentityProviderHttpHandler</strong><br>IdentityProviderHttpHandler")
```
The `IdentityProviderHttpHandler` is where the actual differentiation of this component starts.
It handles identifying the account based on the supplied cookie and determining the active OIDC interaction,
after which it calls an `InteractionHandler` with this additional input.
The `InteractionHandler` is many handlers chained together as follows:
```mermaid
flowchart TD
HttpHandler("<strong>IdentityProviderHttpHandler</strong><br>IdentityProviderHttpHandler")
HttpHandler --> InteractionHandler("<strong>InteractionHandler</strong><br>WaterfallHandler")
InteractionHandler --> InteractionHandlerArgs
subgraph InteractionHandlerArgs[" "]
HtmlViewHandler("<strong>HtmlViewHandler</strong><br>HtmlViewHandler")
LockingInteractionHandler("<strong>LockingInteractionHandler</strong><br>LockingInteractionHandler")
end
LockingInteractionHandler --> JsonConversionHandler("<strong>JsonConversionHandler</strong><br>JsonConversionHandler")
JsonConversionHandler --> VersionHandler("<strong>VersionHandler</strong><br>VersionHandler")
VersionHandler --> CookieInteractionHandler("<strong>CookieInteractionHandler</strong><br>CookieInteractionHandler")
CookieInteractionHandler --> RootControlHandler("<strong>RootControlHandler</strong><br>ControlHandler")
RootControlHandler --> LocationInteractionHandler("<strong>LocationInteractionHandler</strong><br>LocationInteractionHandler")
LocationInteractionHandler --> InteractionRouteHandler("<strong>InteractionRouteHandler</strong><br>WaterfallHandler")
```
The `HtmlViewHandler` catches all request that request an HTML output.
This class keeps a list of HTML pages and their corresponding URL and returns them when needed.
If the request is for the JSON API,
the request goes through a chain of handlers, each responsible for a specific step in the API process.
We'll list and summarize these here:
* `LockingInteractionHandler`: In case the request is authenticated,
this requests a lock on that account to prevent simultaneous operations on the same account.
* `JsonConversionHandler`: Converts the streaming input into a JSON object.
* `VersionHandler`: Adds a version number to all output.
* `CookieInteractionHandler`: Refreshes the cookie if necessary and adds relevant cookie metadata to the output.
* `RootControlHandler`: Responsible for adding all the [controls](controls.md) to the output.
Will take as input multiple other control handlers which create the nested values in the `controls` field.
* `LocationInteractionHandler`: Catches redirect errors and converts them to JSON objects with a `location` field.
* `InteractionRouteHandler`: A `WaterfallHandler` containing an entry for every supported API [route](routes.md).

View File

@@ -0,0 +1,126 @@
# Account API routes
All entries contained in the `urn:solid-server:default:InteractionRouteHandler` have a similar structure:
an `InteractionRouteHandler`, or `AuthorizedRouteHandler` for authenticated requests,
which checks if the request targets a specific URL
and redirects the request to its source if there is a match.
Its source is quite often a `ViewInteractionHandler`,
which returns a specific view on GET requests and performs an operation on POST requests,
but other handlers can also occur.
Below we will give an example of one API route and all the components that are necessary to add it to the server.
## Route handler
```json
{
"@id": "urn:solid-server:default:AccountWebIdRouter",
"@type": "AuthorizedRouteHandler",
"route": {
"@id": "urn:solid-server:default:AccountWebIdRoute",
"@type": "RelativePathInteractionRoute",
"base": { "@id": "urn:solid-server:default:AccountIdRoute" },
"relativePath": "webid/"
},
"source": { "@id": "urn:solid-server:default:WebIdHandler" }
}
```
The main entry point is the route handler,
which determines the URL necessary to reach this API.
In this case we create a new route, relative to the `urn:solid-server:default:AccountIdRoute`.
That route specifically matches URLs of the format `http://localhost:3000/.account/account/<accountId>/`.
Here we create a route relative to that one by appending `webid`,
so the resulting route would match `http://localhost:3000/.account/account/<accountId>/webid/`.
Since an `AuthorizedRouteHandler` is used here,
the request also needs to be authenticated using an account cookie.
If there is match, the request will be sent to the `urn:solid-server:default:WebIdHandler`.
## Interaction handler
```json
{
"@id": "urn:solid-server:default:WebIdHandler",
"@type": "ViewInteractionHandler",
"source": {
"@id": "urn:solid-server:default:LinkWebIdHandler",
"@type": "LinkWebIdHandler",
"baseUrl": { "@id": "urn:solid-server:default:variable:baseUrl" },
"ownershipValidator": { "@id": "urn:solid-server:default:OwnershipValidator" },
"accountStore": { "@id": "urn:solid-server:default:AccountStore" },
"webIdStore": { "@id": "urn:solid-server:default:WebIdStore" },
"identifierStrategy": { "@id": "urn:solid-server:default:IdentifierStrategy" }
}
}
```
The interaction handler is the class that performs the necessary operation based on the request.
Often these are wrapped in a `ViewInteractionHandler`,
which allows classes to have different support for GET and POST requests.
## Exposing the API
```json
{
"@id": "urn:solid-server:default:InteractionRouteHandler",
"@type": "WaterfallHandler",
"handlers": [
{ "@id": "urn:solid-server:default:AccountWebIdRouter" }
]
}
```
To make sure the API can be accessed,
it needs to be added to the list of `urn:solid-server:default:InteractionRouteHandler`.
This is the main handler that contains entries for all the APIs.
This block of Components.js adds the route handler defined above to that list.
## Adding the necessary controls
```json
{
"@id": "urn:solid-server:default:AccountControlHandler",
"@type": "ControlHandler",
"controls": [{
"ControlHandler:_controls_key": "webId",
"ControlHandler:_controls_value": { "@id": "urn:solid-server:default:AccountWebIdRoute" }
}]
}
```
To make sure people can find the API,
it is necessary to link it through the associated `controls` object.
This API is related to account management,
so we add its route in the account controls with the key `webId`.
More information about controls can be found [here](controls.md).
## Adding HTML
```json
{
"@id": "urn:solid-server:default:HtmlViewHandler",
"@type": "HtmlViewHandler",
"templates": [{
"@id": "urn:solid-server:default:LinkWebIdHtml",
"@type": "HtmlViewEntry",
"filePath": "@css:templates/identity/account/link-webid.html.ejs",
"route": { "@id": "urn:solid-server:default:AccountWebIdRoute" }
}]
}
```
Some API routes also have an associated HTML page,
in which case the page needs to be added to the `urn:solid-server:default:HtmlViewHandler`,
which is what we do here.
Usually you will also want to add HTML controls so the page can be found.
```json
{
"@id": "urn:solid-server:default:AccountHtmlControlHandler",
"@type": "ControlHandler",
"controls": [{
"ControlHandler:_controls_key": "linkWebId",
"ControlHandler:_controls_value": { "@id": "urn:solid-server:default:AccountWebIdRoute" }
}]
}
```

View File

@@ -88,8 +88,9 @@ More on this can be found in the [identity provider](../../../usage/identity-pro
The `urn:solid-server:default:IdentityProviderHttpHandler` handles everything
related to our custom identity provider API, such as registering, logging in, returning the relevant HTML pages, etc.
All these requests are identified by being on the `/idp/` subpath.
All these requests are identified by being on the `/.account/` subpath.
More information on the API can be found in the [identity provider](../../../usage/identity-provider) documentation
The architectural overview can be found [here](accounts/overview.md).
## LdpHandler

View File

@@ -10,7 +10,7 @@ Below is a simplified view of how these handlers are linked.
```mermaid
flowchart LR
LdpHandler("<strong>LdpHandler</strong><br>ParsingHttphandler")
LdpHandler("<strong>LdpHandler</strong><br>ParsingHttpHandler")
LdpHandler --> AuthorizingHttpHandler("<br>AuthorizingHttpHandler")
AuthorizingHttpHandler --> OperationHandler("<strong>OperationHandler</strong><br><i>OperationHandler</i>")
OperationHandler --> ResourceStore("<strong>ResourceStore</strong><br><i>ResourceStore</i>")

View File

@@ -0,0 +1,281 @@
# Account management JSON API
Everything related to account management is done through a JSON API,
of which we will describe all paths below.
There are also HTML pages available to handle account management
that use these APIs internally.
Links to these can be found in the HTML controls
All APIs expect JSON as input, and will return JSON objects as output.
## Finding API URLs
All URLs below are relative to the index account API URL, which by default is `http://localhost:3000/.account/`.
Every response of an API request will contain a `controls` object,
containing all the URLs of the other API endpoints.
It is generally advised to make use of these controls instead of hardcoding the URLs.
Only the initial index URL needs to be known then to find the controls.
Certain controls will be missing if those features are disabled in the configuration.
## API requests
Many APIs require a POST request to perform an action.
When doing a GET request on these APIs they will return an object describing what input is expected for the POST.
## Authorization
After logging in, the API will return a `set-cookie` header.
This cookie is necessary to have access to many of the APIs.
When including this cookie, the controls object will also be extended with new URLs that are now accessible.
When logging in, the response body JSON body will also contain a `cookie` field containing the cookie value.
Instead of using cookies,
this value can also be used in an `Authorization` header with auth scheme `CSS-Account-Cookie`
to achieve the same result.
The expiration time of this cookie will be refreshed
every time there is a successful request to the server with that cookie.
## Redirecting
As redirects through status codes 3xx can make working with JSON APIs more difficult,
the API will never make use of this.
Instead, if a redirect is required after an action,
the response JSON object will return a `location` field.
This is the next URL that should be fetched.
This is mostly relevant in OIDC interactions as these cause the interaction to progress.
## Controls
Below is an overview of all the keys in a controls object returned by the server,
with all features enabled.
An example of what such an object looks like can be found at the [bottom](#example) of the page.
### controls.main
General controls that require no authentication.
#### controls.main.index
General entrypoint to the API.
Returns an empty object, including the controls, on all GET requests.
#### controls.main.logins
Returns an overview of all login systems available on the server in `logins` object.
Keys are a string description of the login system and values are links to their login pages.
This can be used to let users choose how they want to log in.
By default, the object only contains the email/password login system.
### controls.account
All controls related to account management.
All of these require authorization, except for the create action.
#### controls.account.create
Creates a new account on empty POST requests.
The response contains the necessary cookie values to log and a `resource` field containing the URL of the account.
This account can not be used until a login method has been added to it.
All other interactions will fail until this is the case.
See the [controls.password.create](#controlspasswordcreate) section below for more information on how to do this.
This account will expire after some time if no login method is added.
#### controls.account.logout
Logs the account out on an empty POST request.
Invalidates the cookie that was used.
#### controls.account.webId
POST requests link a WebID to the account,
allowing the account to identify as that WebID during an OIDC authentication interaction.
Expected input is an object containing a `webId` field.
If the chosen WebID is contained within a Solid pod associated with this account,
the request will succeed immediately.
If not, an error will be thrown,
asking the user to add a specific triple to the WebID to confirm that they are the owner.
After this triple is added, a second request will be successful.
#### controls.account.pod
Creates a Solid pod for the account on POST requests.
The only required field is `name`, which will determine the name of the pod.
Additionally, a `settings` object can be sent along,
the values of which will be sent to the templates used when generating the pod.
If this `settings` object contains a `webId` field,
that WebID will be the WebID that has initial access to the pod.
If no WebID value is provided,
a WebID will be generated in the pod and immediately linked to the account
as described in [controls.account.webID](#controlsaccountwebid).
This WebID will then be the WebID that has initial access.
#### controls.account.clientCredentials
Creates a client credentials token on POST requests.
More information on these tokens can be found [here](../client-credentials.md).
Expected input is an object containing a `name` and `webId` field.
The name is optional and will be used to name the token,
the WebID determines which WebID you will identify as when using that token.
It needs to be a WebID linked to the account as described in [controls.account.webID](#controlsaccountwebid).
#### controls.account.account
This value corresponds to the resource URL of the account you received when creating it.
This returns all resources linked to this account, such as login methods, WebIDs, pods, and client credentials tokens.
Below is an example response object:
```json
{
"logins": {
"password": {
"test@example.com": "http://localhost:3000/.account/account/c63c9e6f-48f8-40d0-8fec-238da893a7f2/login/password/test%40example.com/"
}
},
"pods": {
"http://localhost:3000/test/": "http://localhost:3000/.account/account/c63c9e6f-48f8-40d0-8fec-238da893a7f2/pod/7def7830df1161e422537db594ad2b7412ffb735e0e2320cf3e90db19cd969f9/"
},
"webIds": {
"http://localhost:3000/test/profile/card#me": "http://localhost:3000/.account/account/c63c9e6f-48f8-40d0-8fec-238da893a7f2/webid/5c1b70d3ffaa840394dda86889ed1569cf897ef3d6041fb4c9513f82144cbb7f/"
},
"clientCredentials": {
"token_562cdeb5-d4b2-4905-9e62-8969ac10daaa": "http://localhost:3000/.account/account/c63c9e6f-48f8-40d0-8fec-238da893a7f2/client-credentials/token_562cdeb5-d4b2-4905-9e62-8969ac10daaa/"
},
"settings": {}
}
```
In each of the sub-objects, the key is always the unique identifier of whatever is being described,
while the value is the resource URL that can potentially be used to modify the resource.
Removing an entry can be done by sending a DELETE request to the resource URL,
except for pods, which cannot be deleted.
Login methods can only be deleted if the account has at least 1 login method remaining afterwards.
The password login resource URL can also be used to modify the password,
which can be done by sending a POST request to it with the body containing an `oldPassword` and a `newPassword` field.
### controls.password
Controls related to managing the email/password login method.
#### controls.password.create
POST requests create an email/password login and adds it to the account you are logged in as.
Expects `email` and `password` fields.
#### controls.password.login
POST requests log a user in and return the relevant cookie values.
Expected fields are `email`, `password`, and optionally a `remember` boolean.
The `remember` value determines if the returned cookie is only valid for the session,
or for a longer time.
#### controls.password.forgot
Can be used when a user forgets their password.
POST requests with an `email` field will send an email with a link to reset the password.
#### controls.password.reset
Used to handle reset password URLs generated when a user forgets their password.
Expected input values for the POST request are `recordId`,
which was generated when sending the reset mail,
and `password` with the new password value.
### controls.oidc
These controls are related to completing OIDC interactions.
#### controls.oidc.cancel
Sending a POST request to this API will cancel the OIDC interaction
and return the user to the client that started the interaction.
#### controls.oidc.prompt
This API is used to determine what the next necessary step is in the OIDC interaction.
The response will contain a `location` field,
containing the URL to the next page the user should go to,
and a `prompt` field,
indicating the next step that is necessary to progress the OIDC interaction.
The three possible prompts are the following:
* **account**: The user needs to log in, so they have an account cookie.
* **login**: The user needs to pick the WebID they want to use in the resulting OIDC token.
* **consent**: The user needs to consent to the interaction.
#### controls.oidc.webId
Relevant for solving the **login** prompt.
GET request will return a list of WebIDs the user can choose from.
This is the same result as requesting the account information and looking at the linked WebIDs.
The POST requests expects a `webId` value and optionally a `remember` boolean.
The latter determines if the server should remember the picked WebID for later interactions.
#### controls.oidc.forgetWebId
POST requests to this API will cause the OIDC interaction to forget the picked WebID
so a new one can be picked by the user.
#### controls.oidc.consent
A GET request to this API will return all the relevant information about the client doing the request.
A POST requests causes the OIDC interaction to finish.
It can have an optional `remember` value, which allows for refresh tokens if it is set to true.
#### controls.html
All these controls link to HTML pages and are thus mostly relevant to provide links to let the user navigate around.
## Example
Below is an example of a controls object in a response.
```json
{
"main": {
"index": "http://localhost:3000/.account/",
"logins": "http://localhost:3000/.account/login/"
},
"account": {
"create": "http://localhost:3000/.account/account/",
"logout": "http://localhost:3000/.account/account/ade5c046-e882-4b56-80f4-18cb16433360/logout/",
"webId": "http://localhost:3000/.account/account/ade5c046-e882-4b56-80f4-18cb16433360/webid/",
"pod": "http://localhost:3000/.account/account/ade5c046-e882-4b56-80f4-18cb16433360/pod/",
"clientCredentials": "http://localhost:3000/.account/account/ade5c046-e882-4b56-80f4-18cb16433360/client-credentials/",
"account": "http://localhost:3000/.account/account/ade5c046-e882-4b56-80f4-18cb16433360/"
},
"password": {
"create": "http://localhost:3000/.account/account/ade5c046-e882-4b56-80f4-18cb16433360/login/password/",
"login": "http://localhost:3000/.account/login/password/",
"forgot": "http://localhost:3000/.account/login/password/forgot/",
"reset": "http://localhost:3000/.account/login/password/reset/"
},
"oidc": {
"cancel": "http://localhost:3000/.account/oidc/cancel/",
"prompt": "http://localhost:3000/.account/oidc/prompt/",
"webId": "http://localhost:3000/.account/oidc/pick-webid/",
"forgetWebId": "http://localhost:3000/.account/oidc/forget-webid/",
"consent": "http://localhost:3000/.account/oidc/consent/"
},
"html": {
"main": {
"login": "http://localhost:3000/.account/login/"
},
"account": {
"createClientCredentials": "http://localhost:3000/.account/account/ade5c046-e882-4b56-80f4-18cb16433360/client-credentials/",
"createPod": "http://localhost:3000/.account/account/ade5c046-e882-4b56-80f4-18cb16433360/pod/",
"linkWebId": "http://localhost:3000/.account/account/ade5c046-e882-4b56-80f4-18cb16433360/webid/",
"account": "http://localhost:3000/.account/account/ade5c046-e882-4b56-80f4-18cb16433360/"
},
"password": {
"register": "http://localhost:3000/.account/login/password/register/",
"login": "http://localhost:3000/.account/login/password/",
"create": "http://localhost:3000/.account/account/ade5c046-e882-4b56-80f4-18cb16433360/login/password/",
"forgot": "http://localhost:3000/.account/login/password/forgot/"
}
}
}
```

View File

@@ -0,0 +1,118 @@
# Adding a new login method
By default, the server allows users to use email/password combinations to identify as the owner of their account.
But, just like with many other parts of the server,
this can be extended so other login methods can be used.
Here we'll cover everything that is necessary.
## Components
These are the components that are needed for adding a new login method.
Not all of these are mandatory,
but they can make the life of the user easier when trying to find and use the new method.
Also have a look at the general [structure](../../architecture/features/accounts/routes.md)
of new API components to see what is expected of such a component.
### Create component
There needs to be one or more components that allow a user
to create an instance of the new login method and assign it to their account.
The `CreatePasswordHandler` can be used as an example.
This does not necessarily have to happen in a single request,
potentially multiple requests can be used if the user has to perform actions on an external site for example.
The only thing that matters is that at the end there is a new entry in the account's `logins` object.
When adding logins of your method a new key will need to be chosen to group these logins together.
The email/password method uses `password` for example.
A new storage will probably need to be created to storage relevant metadata about this login method entry.
Below is an example of how the `PasswordStore` is created:
```json
{
"@id": "urn:solid-server:default:PasswordStore",
"@type": "BasePasswordStore",
"storage": {
"@id": "urn:solid-server:default:PasswordStorage",
"@type": "EncodingPathStorage",
"relativePath": "/accounts/logins/password/",
"source": {
"@id": "urn:solid-server:default:KeyValueStorage"
}
}
}
```
### Login component
After creating a login instance, a user needs to be able to log in using the new method.
This can again be done with multiple API calls if necessary,
but the final one needs to be one that handles the necessary actions
such as creating a cookie and finishing the OIDC interaction if necessary.
The `ResolveLoginHandler` can be extended to take care of most of this,
the `PasswordLoginHandler` provides an example of this.
### Additional components
Besides creating a login instance and logging in,
it is always possible to offer additional functionality specific to this login method.
The email/password method, for example, also has components for password recovery and updating a password.
### HTML pages
To make the life easier for users,
at the very least you probably want to make an HTML page which people can use
to create an instance of your login method.
Besides that you could also make a page where people can combine creating an account with creating a login instance.
The `templates/identity` folder contains all the pages the server has by default,
which can be used as inspiration.
These pages need to be linked to the `urn:solid-server:default:HtmlViewHandler`.
Below is an example of this:
```json
{
"@id": "urn:solid-server:default:HtmlViewHandler",
"@type": "HtmlViewHandler",
"templates": [{
"@id": "urn:solid-server:default:CreatePasswordHtml",
"@type": "HtmlViewEntry",
"filePath": "@css:templates/identity/password/create.html.ejs",
"route": {
"@id": "urn:solid-server:default:AccountPasswordRoute"
}
}]
}
```
### Updating the login handler
The `urn:solid-server:default:LoginHandler` returns a list of available login methods,
which are used to offer users a choice of which login method they want to use on the default login page.
If you want the new method to also be offered you will have to add similar Components.js configuration:
```json
{
"@id": "urn:solid-server:default:LoginHandler",
"@type": "ControlHandler",
"controls": [
{
"ControlHandler:_controls_key": "Email/password combination",
"ControlHandler:_controls_value": {
"@id": "urn:solid-server:default:LoginPasswordRoute"
}
}
]
}
```
### Controls
All new relevant API endpoints should be added to the controls object,
otherwise there is no way for users to find out where to send their requests.
Similarly, links to the HTML pages should also be in the controls, so they can be navigated to.
Examples of how to do this can be found [here](../../architecture/features/accounts/routes.md).
The default account overview page makes some assumptions about the controls when building the page.
Specifically, it checks if `controls.html.<LOGIN_METHOD>.create` exists,
if yes, it automatically creates a link on the page so users can create new login instances for their account.

View File

@@ -0,0 +1,60 @@
# Migrating account data from v6 to v7
Below is a description of the changes that are necessary to migration account data from v6 to v7 of the server.
Note that the resource identifier values are bas64 encoded before being appended to the storage location.
* "Forgot password" records
* **Storage location**
* Old: `.internal/forgot-password/`
* New: `.internal/accounts/login/password/forgot/`
* **Resource identifiers**
* Old: `"forgot-password-resource-identifier/" + recordId`
* New: `recordId`
* **Data format**
* Old: `{ recordId, email }`
* New: `email`
* **Notes**
* Just deleting all existing records is an acceptable solution as these do not contain important information.
* Client credentials tokens
* **Storage location**
* Old: `.internal/accounts/credentials/`
* New: `.internal/accounts/client-credentials/`
* **Resource identifiers**
* No change
* **Data format**
* Old: `{ webId, secret }`
* New: `{ accountId, webId, secret }`
* **Notes**
* Account IDs will need to be generated first before these can be transferred.
* Account and password data
* **Storage location**
* Old: `.internal/accounts/`
* New: Split up over the following:
* `.internal/accounts/data/`
* `.internal/accounts/webIds/`
* `.internal/accounts/logins/password/`
* **Resource identifiers**
* Old: `"account/" + encodeURIComponent(email)` or `webId`
* New:
* `.internal/accounts/data/`: Newly generated account ID.
* `.internal/accounts/webIds/`: `webID`
* `.internal/accounts/logins/password/`: `encodeURIComponent(email.toLowerCase())`
* **Data format**
* Old: `{ webId, email, password, verified }` or `{ useIdp, podBaseUrl?, clientCredentials? }`
* New:
* `.internal/accounts/data/`: `{ id, logins: { password }, pods, webIds, clientCredentials }`
* `.internal/accounts/webIds/`: `accountId[]`
* `.internal/accounts/logins/password/`: `{ accountId, password, verified }`
* **Notes**
* First account IDs need to be generated,
then login/pod/webId/clientCredentials resources need to be generated,
and then the account needs to be updated with those resources.
* Resource URLs are generated as follows:
* Passwords: `<baseUrl>/.account/account/<accountID>/login/password/<encodeURIComponent(email.toLowerCase())>`
* Pods: `<baseUrl>/.account/account/<accountID>/pod/<sha256Hash(podBaseUrl)>`
* WebIds: `<baseUrl>/.account/account/<accountID>/webid/<sha256Hash(webId)>`
* Client Credentials: `<baseUrl>/.account/account/<accountID>/client-credentials/<token>`
* The above URLs are the values in all the account objects,
the keys are the corresponding (lowercase) email, pod base URL, webID, and token name.
* Only WebIDs where `useIdp` is `true` need to be linked to the account.
* In the previous version, a WebID will be linked to exactly 1 account.

View File

@@ -8,48 +8,18 @@ It is recommended to use the latest version
of the [Solid authentication client](https://github.com/inrupt/solid-client-authn-js)
to interact with the server.
The links here assume the server is hosted at `http://localhost:3000/`.
It also provides account management options for creating pods and WebIDs to be used during authentication,
which are discussed more in-depth below.
The links on this page assume the server is hosted at `http://localhost:3000/`.
## Registering an account
To register an account, you can go to `http://localhost:3000/idp/register/` if this feature is enabled,
which it is on most configurations we provide.
Currently our registration page ties 3 features together on the same page:
* Creating an account on the server.
* Creating or linking a WebID to your account.
* Creating a pod on the server.
### Account
To create an account you need to provide an email address and password.
To register an account, you can go to `http://localhost:3000/.account/password/register/`, if this feature is enabled.
There you can create an account with the email/password login method.
The password will be salted and hashed before being stored.
As of now, the account is only used to log in and identify yourself to the IDP
when you want to do an authenticated request,
but in future the plan is to also use this for account/pod management.
Afterwards you will be redirected to the account page where you can create pods and link WebIDs to your account.
### WebID
We require each account to have a corresponding WebID.
You can either let the server create a WebID for you in a pod,
which will also need to be created then,
or you can link an already existing WebID you have on an external server.
In case you try to link your own WebID, you can choose if you want to be able
to use this server as your IDP for this WebID.
If not, you can still create a pod,
but you will not be able to direct the authentication client to this server to identify yourself.
Additionally, if you try to register with an external WebID,
the first attempt will return an error indicating you need to add an identification triple to your WebID.
After doing that you can try to register again.
This is how we verify you are the owner of that WebID.
After registration the next page will inform you
that you have to add an additional triple to your WebID if you want to use the server as your IDP.
All of the above is automated if you create the WebID on the server itself.
### Pod
### Creating a pod
To create a pod you simply have to fill in the name you want your pod to have.
This will then be used to generate the full URL of your pod.
@@ -57,23 +27,42 @@ For example, if you choose the name `test`,
your pod would be located at `http://localhost:3000/test/`
and your generated WebID would be `http://localhost:3000/test/profile/card#me`.
If you fill in a WebID when creating the pod,
that WebID will be the one that has access to all data in the pod.
If you don't, a WebID will be created in the pod and immediately linked to your account,
allowing you to use it for authentication and accessing the data in that pod
The generated name also depends on the configuration you chose for your server.
If you are using the subdomain feature,
such as being done in the `config/memory-subdomains.json` configuration,
the generated pod URL would be `http://test.localhost:3000/`.
### WebIDs
To use Solid authentication,
you need to link at least one WebID to your account.
This can happen automatically when creating a pod as mentioned above,
or can be done manually with external WebIDs.
If you try to link an external WebID,
the first attempt will return an error indicating you need to add an identification triple to your WebID.
After doing that you can try to register again.
This is how we verify you are the owner of that WebID.
Afterwards the page will inform you
that you have to add a triple to your WebID if you want to use the server as your IDP.
## Logging in
When using an authenticating client,
you will be redirected to a login screen asking for your email and password.
After that you will be redirected to a page showing some basic information about the client.
There you need to consent that this client is allowed to identify using your WebID.
After that you will be redirected to a page showing some basic information about the client
where you can pick the WebID you want to use.
There you need to consent that this client is allowed to identify using that WebID.
As a result the server will send a token back to the client
that contains all the information needed to use your WebID.
## Forgot password
If you forgot your password, you can recover it by going to `http://localhost:3000/idp/forgotpassword/`.
If you forgot your password, you can recover it by going to `http://localhost:3000/.account/login/password/forgot/`.
There you can enter your email address to get a recovery mail to reset your password.
This feature only works if a mail server was configured,
which by default is not the case.
@@ -81,63 +70,11 @@ which by default is not the case.
## JSON API
All of the above happens through HTML pages provided by the server.
By default, the server uses the templates found in `/templates/identity/email-password/`
By default, the server uses the templates found in `/templates/identity/`
but different templates can be used through configuration.
These templates all make use of a JSON API exposed by the server.
For example, when doing a GET request to `http://localhost:3000/idp/register/`
with a JSON accept header, the following JSON is returned:
```json
{
"required": {
"email": "string",
"password": "string",
"confirmPassword": "string",
"createWebId": "boolean",
"register": "boolean",
"createPod": "boolean",
"rootPod": "boolean"
},
"optional": {
"webId": "string",
"podName": "string",
"template": "string"
},
"controls": {
"register": "http://localhost:3000/idp/register/",
"index": "http://localhost:3000/idp/",
"prompt": "http://localhost:3000/idp/prompt/",
"login": "http://localhost:3000/idp/login/",
"forgotPassword": "http://localhost:3000/idp/forgotpassword/"
},
"apiVersion": "0.3"
}
```
The `required` and `optional` fields indicate which input fields are expected by the API.
These correspond to the fields of the HTML registration page.
To register a user, you can do a POST request with a JSON body containing the correct fields:
```json
{
"email": "test@example.com",
"password": "secret",
"confirmPassword": "secret",
"createWebId": true,
"register": true,
"createPod": true,
"rootPod": false,
"podName": "test"
}
```
Two fields here that are not covered on the HTML page above are `rootPod` and `template`.
`rootPod` tells the server to put the pod in the root of the server instead of a location based on the `podName`.
By default the server will reject requests where this is `true`.
`template` is only used by servers running the `config/dynamic.json` configuration,
which is a very custom setup where every pod can have a different Components.js configuration,
so this value can usually be ignored.
A full description of this API can be found [here](account/json-api.md).
## IDP configuration
@@ -175,14 +112,31 @@ which you will need to copy over to your base configuration and then remove the
There is only one option here. This import contains all the core components necessary to make the IDP work.
In case you need to make some changes to core IDP settings, this is where you would have to look.
### interaction
Here you determine which features of account management are available.
`default.json` allows everything, while `no-accounts.json` and `no-pods.json`
disable account and pod creation respectively.
Taking one of those latter options will disable the relevant JSON APIs and HTML pages.
### pod
The `pod` options determines how pods are created. `static.json` is the expected pod behaviour as described above.
`dynamic.json` is an experimental feature that allows users
to have a custom Components.js configuration for their own pod.
When using such a setup, a JSON file will be written containing all the information of the user pods
When using such a configuration, a JSON file will be written containing all the information of the user pods,
so they can be recreated when the server restarts.
### registration
## Adding a new login method to the server
This setting allows you to enable/disable registration on the server.
Due to its modular nature,
it is possible to add new login methods to the server,
allowing users to log in different ways than just the standard email/password combination.
More information on what is required can be found [here](account/login-method.md).
## Data migration
Going from v6 to v7 of the server, the account management is completely rewritten,
including how account data is stored on the server.
More information about how account data of an existing server can be migrated to the newer version
can be found [here](account/migration.md).

View File

@@ -1,39 +1,36 @@
# How to seed Accounts and Pods
If you need to seed accounts and pods,
the `--seededPodConfigJson` command line option can be used
the `--seedConfig` command line option can be used
with as value the path to a JSON file containing configurations for every required pod.
The file needs to contain an array of JSON objects,
with each object containing at least a `podName`, `email`, and `password` field.
with each object containing at least an `email`, and `password` field.
Multiple pod objects can also be assigned to such an object in the `pods` array to create pods for the account,
with contents being the same as its corresponding JSON [API](account/json-api.md#controlsaccountpod).
For example:
```json
[
{
"podName": "example",
"email": "hello@example.com",
"password": "abc123"
}
]
```
You may optionally specify other parameters
as described in the [Identity Provider documentation](identity-provider.md#json-api).
For example, to set up a pod without registering the generated WebID with the Identity Provider:
```json
[
},
{
"podName": "example",
"email": "hello@example.com",
"password": "abc123",
"webId": "https://id.inrupt.com/example",
"register": false
"email": "hello2@example.com",
"password": "123abc",
"pods": [
{ "name": "pod1" },
{ "name": "pod2" }
]
}
]
```
This feature cannot be used to register pods with pre-existing WebIDs,
which requires an interactive validation step.
which requires an interactive validation step,
unless you disable the WebID ownership check in your server configuration.
Note that pod seeding is made for a default server setup with standard email/password login.
If you [add a new login method](account/login-method.md)
you will need to create a new implementation of pod seeding if you want to use it.

View File

@@ -61,7 +61,7 @@ to some commonly used settings:
| `--sparqlEndpoint, -s` | | URL of the SPARQL endpoint, when using a quadstore-based configuration. |
| `--showStackTrace, -t` | false | Enables detailed logging on error output. |
| `--podConfigJson` | `./pod-config.json` | Path to the file that keeps track of dynamic Pod configurations. Only relevant when using `@css:config/dynamic.json`. |
| `--seededPodConfigJson` | | Path to the file that keeps track of seeded Pod configurations. |
| `--seedConfig` | | Path to the file that keeps track of seeded account configurations. |
| `--mainModulePath, -m` | | Path from where Components.js will start its lookup when initializing configurations. |
| `--workers, -w` | `1` | Run in multithreaded mode using workers. Special values are `-1` (scale to `num_cores-1`), `0` (scale to `num_cores`) and 1 (singlethreaded). |

View File

@@ -80,10 +80,15 @@ nav:
- Usage:
- Example request: usage/example-requests.md
- Metadata: usage/metadata.md
- Identity provider: usage/identity-provider.md
- Identity provider:
- Overview: usage/identity-provider.md
- JSON API: usage/account/json-api.md
- New login method: usage/account/login-method.md
- Data migration: usage/account/migration.md
- Client credentials: usage/client-credentials.md
- Seeding pods: usage/seeding-pods.md
- Notifications: usage/notifications.md
- Development server: usage/dev-configuration.md
- Architecture:
- Overview: architecture/overview.md
- Dependency injection: architecture/dependency-injection.md
@@ -97,6 +102,10 @@ nav:
- Parsing: architecture/features/protocol/parsing.md
- Authorization: architecture/features/protocol/authorization.md
- Resource Store: architecture/features/protocol/resource-store.md
- Account management:
- Overview: architecture/features/accounts/overview.md
- Controls: architecture/features/accounts/controls.md
- Routes: architecture/features/accounts/routes.md
- Notifications: architecture/features/notifications.md
- Contributing:
- Pull requests: contributing/making-changes.md