diff --git a/book.toml b/book.toml new file mode 100644 index 000000000..680cc40cb --- /dev/null +++ b/book.toml @@ -0,0 +1,22 @@ +# Documentation for possible options in this file is at +# https://rust-lang.github.io/mdBook/format/config.html +[book] +title = "Matrix Authentication Service" +authors = ["The Matrix.org Foundation C.I.C."] +language = "en" +multilingual = false + +src = "docs" + +[build] +build-dir = "target/book" + +[output.html] +# The URL visitors will be directed to when they try to edit a page +edit-url-template = "https://github.com/matrix-org/synapse/edit/main/{path}" + +# The source code URL of the repository +git-repository-url = "https://github.com/matrix-org/matrix-authentication-service" + +# The path that the docs are hosted on +site-url = "/matrix-authentication-service/" diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..8055157d0 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,3 @@ +# About this documentation + +This documentation is intended to give an overview of how the `matrix-authentication-service` works, both from a admin perspective as well as from a developper perspective. diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md new file mode 100644 index 000000000..38941fa95 --- /dev/null +++ b/docs/SUMMARY.md @@ -0,0 +1,24 @@ +# Summary + +# Introduction + +- [About this documentation](./README.md) + +# Usage + +- [Installation](./usage/installation.md) +- [Configuration](./usage/configuration.md) +- [Using the service](./usage/usage.md) +- [Command line tool](./usage/cli/README.md) + - [`config`](./usage/cli/config.md) + - [`database`](./usage/cli/database.md) + - [`manage`](./usage/cli/manage.md) + - [`server`](./usage/cli/server.md) + - [`templates`](./usage/cli/templates.md) + +# Development + +- [Architecture](./development/architecture.md) +- [Database](./development/database.md) +- [Routing with `warp`](./development/warp.md) +- [Templates]() diff --git a/docs/development/architecture.md b/docs/development/architecture.md new file mode 100644 index 000000000..4943570fe --- /dev/null +++ b/docs/development/architecture.md @@ -0,0 +1,84 @@ +# Architecture + +The service is meant to be easily embeddable, with only a dependency to a database. +It is also meant to stay lightweight in terms of resource usage and easily scalable horizontally. + +## Workspace and crate split + +The whole repository is a [Cargo Workspace](https://doc.rust-lang.org/book/ch14-03-cargo-workspaces.html) that includes multiple crates under the `/crates` directory. + +This includes: + + - `oauth2-types`: Useful structures and types to deal with OAuth 2.0/OpenID Connect endpoints. This might end up published as a standalone library as it can be useful in other contexts. + - `mas-config`: Configuration parsing and loading + - `mas-core`: Main logic, includes templates, database interactions and HTTP routes + - `mas-cli`: Command line utility, main entry point + +## Important crates + +The project makes use of a few important crates. + +### Async runtime: `tokio` + +[Tokio](https://tokio.rs/) is the async runtime used by the project. +The choice of runtime does not have much impact on most of the code. + +It has an impact when: + + - spawning asynchronous work (as in "not awaiting on it immediately") + - running CPU-intensive tasks. They should be ran in a blocking context using `tokio::task::spawn_blocking`. This includes password hashing and other crypto operations. + - when dealing with shared memory, e.g. mutexes, rwlocks, etc. + +### Logging: `tracing` + +Logging is handled through the [`tracing`](https://docs.rs/tracing/*/tracing/) crate. +It provides a way to emit structured log messages at various levels. + +```rust +use tracing::{info, debug}; + +info!("Logging some things"); +debug!(user = "john", "Structured stuff"); +``` + +`tracing` also provides ways to create spans to better understand where a logging message comes from. +In the future, it will help building OpenTelemetry-compatible distributed traces to help with debugging. + +`tracing` is becoming the standard to log things in Rust. +By itself it will do nothing unless a subscriber is installed to -for example- log the events to the console. + +The CLI installs [`tracing-subcriber`](https://docs.rs/tracing-subscriber/*/tracing_subscriber/) on startup to log in the console. +It looks for a `RUST_LOG` environment variable to determine what event should be logged. + +### Error management: `thiserror` / `anyhow` + +[`thiserror`](https://docs.rs/thiserror/*/thiserror/) helps defining custom error types. +This is especially useful for errors that should be handled in a specific way, while being able to augment underlying errors with additional context. + +[`anyhow`](https://docs.rs/anyhow/*/anyhow/) helps dealing with chains of errors. +It allows for quickly adding additional context around an error while it is being propagated. + +Both crates work well together and complement each other. + +### Database interactions: `sqlx` + +Interactions with the database are done through [`sqlx`](https://github.com/launchbadge/sqlx), an async, pure-Rust SQL library with compile-time check of queries. +It also handles schema migrations. + +### Web framework: `warp` + +[`warp`](https://docs.rs/warp/*/warp/) is an easy, macro-free web framework. +Its composability makes a lot of sense when implementing OAuth 2.0 endpoints, because of the need to deal with a lot of different scenarios. + +### Templates: `tera` + +[Tera](https://tera.netlify.app/) was chosen as template engine for its simplicity as well as its ability to load templates at runtime. +The builtin templates are embedded in the final binary through some macro magic. + +The downside of Tera compared to compile-time template engines is the possibility of runtime crashes. +This can however be somewhat mitigated with unit tests. + +### Crates from *RustCrypto* + +The [RustCrypto team](https://github.com/RustCrypto) offer high quality, independent crates for dealing with cryptography. +The whole project is highly modular and APIs are coherent between crates. diff --git a/docs/development/database.md b/docs/development/database.md new file mode 100644 index 000000000..bc6644752 --- /dev/null +++ b/docs/development/database.md @@ -0,0 +1,96 @@ +# Database + +Interactions with the database goes through `sqlx`. +It provides async database operations with connection pooling, migrations support and compile-time check of queries through macros. + +## Compile-time check of queries + +To be able to check queries, `sqlx` has to introspect the live database. +Usually it does so by having the database available at compile time, but to avoid that we're using the `offline` feature of `sqlx`, which saves the introspection informatons as a flat file in the repository. + +Preparing this flat file is done through `sqlx-cli`, and should be done everytime the database schema or the queries changed. + +```sh +# Install the CLI +cargo install sqlx-cli --no-default-features --features postgres + +cd crates/core/ # Must be in the mas-core crate folder +export DATABASE_URL=postgresql:///matrix_auth +cargo sqlx prepare +``` + +## Migrations + +Migration files live in the `migrations` folder in the `mas-core` crate. + +```sh +cd crates/core/ # Again, in the mas-core crate folder +export DATABASE_URL=postgresql:///matrix_auth +cargo sqlx migrate run # Run pending migrations +cargo sqlx migrate revert # Revert the last migration +cargo sqlx migrate add -r [description] # Add new migration files +``` + +Note that migrations are embedded in the final binary and can be run from the service CLI tool. + +## Writing database interactions + +A typical interaction with the database look like this: + +```rust +pub async fn lookup_session( + executor: impl Executor<'_, Database = Postgres>, + id: i64, +) -> anyhow::Result { + sqlx::query_as!( + SessionInfo, // Struct that will be filled with the result + r#" + SELECT + s.id, + u.id as user_id, + u.username, + s.active, + s.created_at, + a.created_at as "last_authd_at?" + FROM user_sessions s + INNER JOIN users u + ON s.user_id = u.id + LEFT JOIN user_session_authentications a + ON a.session_id = s.id + WHERE s.id = $1 + ORDER BY a.created_at DESC + LIMIT 1 + "#, + id, // Query parameter + ) + .fetch_one(executor) + .await + // Providing some context when there is an error + .context("could not fetch session") +} +``` + +Note that we pass an `impl Executor` as parameter here. +This allows us to use this function from either a simple connection or from an active transaction. + +The caveat here is that the `executor` can be used only once, so if an interaction needs to do multiple queries, it should probably take an `impl Acquire` to then acquire a transaction and do multiple interactions. + +```rust +pub async fn login( + conn: impl Acquire<'_, Database = Postgres>, + username: &str, + password: String, +) -> Result { + let mut txn = conn.begin().await.context("could not start transaction")?; + // First interaction + let user = lookup_user_by_username(&mut txn, username)?; + // Second interaction + let mut session = start_session(&mut txn, user).await?; + // Third interaction + session.last_authd_at = + Some(authenticate_session(&mut txn, session.id, password).await?); + // Commit the transaction once everything went fine + txn.commit().await.context("could not commit transaction")?; + Ok(session) +} +``` diff --git a/docs/development/warp.md b/docs/development/warp.md new file mode 100644 index 000000000..082625d01 --- /dev/null +++ b/docs/development/warp.md @@ -0,0 +1,91 @@ +# `warp` + +Warp has a pretty unique approach in terms of routing. +It does not have a central router, rather a chain of filters composed together. + +It encourages writing reusable filters to handle stuff like authentication, extracting user sessions, starting database transactions, etc. + +Everything related to `warp` currently lives in the `mas-core` crate: + +- `crates/core/src/` + - `handlers/`: The actual handlers for each route + - `oauth2/`: Everything related to OAuth 2.0/OIDC endpoints + - `views/`: HTML views (login, registration, account management, etc.) + - `filters/`: Reusable, composable filters + - `reply/`: Composable replies + +## Defining a new endpoint + +We usually keep one endpoint per file and use module roots to combine the filters of endpoints. + +This is how it looks like in the current hierarchy at time of writing: + - `mod.rs`: combines the filters from `oauth2`, `views` and `health` + - `oauth2/` + - `mod.rs`: combines filters from `authorization`, `discovery`, etc. + - `authorization.rs`: handles `GET /oauth2/authorize` and `GET /oauth2/authorize/step` + - `discovery.rs`: handles `GET /.well-known/openid-configuration` + - ... + - `views/` + - `mod.rs`: combines the filters from `index`, `login`, `logout`, etc. + - `index.rs`: handles `GET /` + - `login.rs`: handles `GET /login` and `POST /login` + - `logout.rs`: handles `POST /logout` + - ... + - `health.rs`: handles `GET /health` + +All filters are functions that take their dependencies (the database connection pool, the template engine, etc.) as parameters and return an `impl warp::Filter`. + +```rust +// crates/core/src/handlers/hello.rs + +// Don't be scared by the type at the end, just copy-paste it +pub(super) fn filter( + pool: &PgPool, + templates: &Templates, + cookies_config: &CookiesConfig, +) -> impl Filter + Clone + Send + Sync + 'static { + // Handles `GET /hello/:param` + warp::path!("hello" / String) + .and(warp::get()) + // Pass the template engine + .and(with_templates(templates)) + // Extract the current user session + .and(optional_session(pool, cookies_config)) + .and_then(get) +} + +async fn get( + // Parameter from the route + parameter: String, + // Template engine + templates: Templates, + // The current user session + session: Option, +) -> Result { + let ctx = SomeTemplateContext::new(parameter) + .maybe_with_session(session); + + let content = templates.render_something(&ctx)?; + let reply = html(content); + Ok(reply) +} +``` + +And then, it can be attached to the root handler: + +```rust +// crates/core/src/handlers/mod.rs + +use self::{health::filter as health, oauth2::filter as oauth2, hello::filter as hello}; + +pub fn root( + pool: &PgPool, + templates: &Templates, + config: &RootConfig, +) -> impl Filter + Clone + Send + Sync + 'static { + health(pool) + .or(oauth2(pool, templates, &config.oauth2, &config.cookies)) + // Attach it here, passing the right dependencies + .or(hello(pool, templates, &config.cookies)) +} +``` diff --git a/docs/usage/cli/README.md b/docs/usage/cli/README.md new file mode 100644 index 000000000..778296bc1 --- /dev/null +++ b/docs/usage/cli/README.md @@ -0,0 +1,40 @@ +# Command line tool + +The command line interface provides subcommands that helps running the service. + +## Logging + +The overall log level of the CLI can be changed via the `RUST_LOG` environment variable. +Default log level is `info`. +Valid levels from least to most verbose are `error`, `warn`, `info`, `debug` and `trace`. + +## Global flags + +### `--config` + +Sets the configuration file to load. +It can be repeated multiple times to merge multiple files together. + +--- + +``` +mas-cli + +USAGE: + mas-cli [OPTIONS] [SUBCOMMAND] + +FLAGS: + -h, --help Print help information + -V, --version Print version information + +OPTIONS: + -c, --config ... Path to the configuration file [default: config.yaml] + +SUBCOMMANDS: + config Configuration-related commands + database Manage the database + help Print this message or the help of the given subcommand(s) + manage Manage the instance + server Runs the web server + templates Templates-related commands +``` diff --git a/docs/usage/cli/config.md b/docs/usage/cli/config.md new file mode 100644 index 000000000..1e6d3fa08 --- /dev/null +++ b/docs/usage/cli/config.md @@ -0,0 +1,35 @@ +# `config` + +Helps dealing with the configuration + +## `config check` + +Check the validity of configuration files. + +```console +$ mas-cli config check --config=config.yaml +INFO mas_cli::config: Configuration file looks good path=["config.yaml"] +``` + +## `config dump` + +Dump the merged configuration tree. + +```console +$ mas-cli config dump --config=first.yaml --config=second.yaml +--- +oauth2: + # ... +``` + +## `config generate` + +Generate a sample configuration file. +It generates random signing keys (`.oauth2.keys`) and the cookie encryption secret (`.cookies.secret`). + +```console +$ mas-cli config generate > config.yaml +INFO generate: mas_config::oauth2: Generating keys... +INFO generate:rsa: mas_config::oauth2: Done generating RSA key +INFO generate:ecdsa: mas_config::oauth2: Done generating ECDSA key +``` diff --git a/docs/usage/cli/database.md b/docs/usage/cli/database.md new file mode 100644 index 000000000..f2c9b32b0 --- /dev/null +++ b/docs/usage/cli/database.md @@ -0,0 +1,11 @@ +# `database` + +Run database-related operations + +## `database migrate` + +Run the pending database migrations + +``` +$ mas-cli database migrate +``` diff --git a/docs/usage/cli/manage.md b/docs/usage/cli/manage.md new file mode 100644 index 000000000..10d92ad86 --- /dev/null +++ b/docs/usage/cli/manage.md @@ -0,0 +1,12 @@ +# `manage` + +Includes admin-related subcommands. + +## `manage register ` + +Register a new user + +```console +$ mas-cli manage register johndoe hunter2 +INFO mas_cli::manage: User registered user=User { id: 2, username: "johndoe" } +``` diff --git a/docs/usage/cli/server.md b/docs/usage/cli/server.md new file mode 100644 index 000000000..bff9a7e54 --- /dev/null +++ b/docs/usage/cli/server.md @@ -0,0 +1,12 @@ +# `server` + +Runs the authentication service. + +``` +$ mas-cli server +INFO mas_cli::server: Starting task scheduler +INFO mas_core::templates: Loading builtin templates +INFO mas_cli::server: Listening on http://0.0.0.0:8080 +``` + +A `--migrate` flag can be set to automatically run pending database migrations on startup. diff --git a/docs/usage/cli/templates.md b/docs/usage/cli/templates.md new file mode 100644 index 000000000..380970a55 --- /dev/null +++ b/docs/usage/cli/templates.md @@ -0,0 +1,37 @@ +# `templates` + +Helps customizing templates. + +## `templates save ` + +Save the builtin template in the specified folder. + +```console +$ mas-cli templates save ./templates +INFO mas_core::templates: Wrote template path="./templates/login.html" +INFO mas_core::templates: Wrote template path="./templates/register.html" +INFO mas_core::templates: Wrote template path="./templates/index.html" +INFO mas_core::templates: Wrote template path="./templates/reauth.html" +INFO mas_core::templates: Wrote template path="./templates/form_post.html" +INFO mas_core::templates: Wrote template path="./templates/error.html" +INFO mas_core::templates: Wrote template path="./templates/base.html" +``` + +By default this command won't overwrite existing files, but this behavior can be changed by adding the `--overwrite` flag. + +## `templates check ` + +Check the validity of the templates in the specified folder. +It compiles the templates and then renders them with different contexts. + +```console +$ mas-cli templates check ./templates +INFO mas_core::templates: Loading builtin templates +INFO mas_core::templates: Loading templates from filesystem path=./templates/**/*.{html,txt} +INFO mas_core::templates::check: Rendering template name="login.html" context={"csrf_token":"fake_csrf_token","form":{"fields_errors":{},"form_errors":[],"has_errors":false}} +INFO mas_core::templates::check: Rendering template name="register.html" context={"__UNUSED":null,"csrf_token":"fake_csrf_token"} +INFO mas_core::templates::check: Rendering template name="index.html" context={"csrf_token":"fake_csrf_token","current_session":{"active":true,"created_at":"2021-09-24T13:26:52.962135085Z","id":1,"last_authd_at":"2021-09-24T13:26:52.962135316Z","user_id":2,"username":"john"},"discovery_url":"https://example.com/.well-known/openid-configuration"} +... +``` + +Builtin templates are still loaded by default when running this command, but this can be skipped by adding the `--skip-builtin` flag. diff --git a/docs/usage/configuration.md b/docs/usage/configuration.md new file mode 100644 index 000000000..a29311cda --- /dev/null +++ b/docs/usage/configuration.md @@ -0,0 +1,212 @@ +# Configuration + +The service needs some configuration to work. +This includes random, private keys and secrets. + +A sample configuration file can be generated using `mas-cli config generate`. + +```sh +mas-cli config generate > config.yaml +mas-cli config check --config=config.yaml +# if --config is omitted, it will try to load `config.yaml` +# in the current working directory +mas-cli config check +``` + +Or, with Docker: + +```sh +docker run --rm \ + ghcr.io/matrix-org/matrix-authentication-service:main \ + config generate > config.yaml + +docker run --rm -v `pwd`:/wd \ + ghcr.io/matrix-org/matrix-authentication-service:main \ + --config=/wd/config.yaml config dump + +# or, mounting only the config file and omitting `--config` +docker run --rm -v `pwd`/config.yaml:/config.yaml \ + ghcr.io/matrix-org/matrix-authentication-service:main \ + config dump +``` + +Note that with Docker, the config file must be mounted inside the container with `-v/--volume`. + +## Minimal configuration + +Here is a minimal configuration needed to have the server running. +It is strongly recommended to generate the initial configuration using the `config generate` to have unique secrets and signing keys. +Check the next section to know about each section. + +```yaml +# config.yaml +database: + uri: postgresql:///matrix_auth + +cookies: + secret: c7e42fb8baba8f228b2e169fdf4c8216dffd5d33ad18bafd8b928c09ca46c718 + +oauth2: + keys: + - type: rsa + key: | + -----BEGIN PRIVATE KEY----- + MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7iinu0NXjWP5/ + /4OqyqOMI5uLJIHSrYIZLUlWMldtXmNy0c/pan+gxvZogiYx0cNydO/FogNbC4oD + yj7RIF+QcWJ8wcdG94/P+Xs3HFQzIZfwF+78RWQQJ7nQFekXJ1wQSXV4giw9b4XR + YkoVhHlyxyYGBFffO//DtYVto4uHvXVL0M27bV6l1K8VKspF72gb8Vt44V8OX5hT + sEsYW8SjOD1neEoVKiY6XP63cAG9FTB4a4sKkcUqwjrKEYKio/JLujmCl96eLN18 + cuqr6XuSDKvuVJtb+ZNLJi61vIOlD8cz3wu37hr3PCUZ+Ko9Ley+QfopJ3WYFxrI + IjQKb0W5AgMBAAECggEBAK87ZfsTfwczPHn1Ed4gAbkL/GaC8hscrJdBzWiRGUfE + DkBW82IydJaR0ePM2EtsqKblxLRxsZj8qzTnYNKe4SxiBZh0p/MTlnjJr+vKuJIe + LY3VjySA4gKGXASmtGlCCa/eM7kqSJQPBIakxHxej+xDULAGluSrd0wy7D2JtvJY + 5By+2apILUujBZZU/iUyB2AqK6IrzALo2gTV9Jhun9Wz0k3DXZBGd41v42BhZ+Rx + bHHgpuUTyDQOpKqJ5g1kN4qGlN/CeoontxcE5NOSgtipWeQEuelT8t4eZKHTXBS+ + Byd+uFe8oobWRY2crLptX8TZEENH7wX7y2YgNYUbeZECgYEA95YRhDuukcrDiNuF + fOXs+99XFORsKTbtZYwrouc7PI2CYb0I9ezoQMu3dzWIwOTUQHea5qCo/dYzbeED + fNzwPb2zaWaWFbkEWVTOwRewL+NfP+Ek2Nml+dVJm3d35qdIHYV+9gAcI87iCHxA + gqc13ZS4ba+5/vV6OYwNSAeW0TcCgYEAwem1F9zhVVOIReh3JaJLpZn0xuvZs5kN + TzvFXar33LKdulk5liC3L7ZrqspGESU0JC3pANR8PwuNteEEdHnkC5UTEqMf/fxG + j5CObJl+e2CiV2CNbe+3IQ1PKSxopD+Sq65ze7aP2moVZg94mbw4dsN+uY5QEql1 + Bmq0b7Wm2I8CgYBOqlDgefIKgqlEF7O/LnLwyFKr4bP4GGqvZC0NMnkg0TmHAoAR + W3ej9tZROyI7X7mMzjPaaVuoY2Gt3Nu11aFDjL2vlJfFSSb3lzmmInepj43ZBxkl + CWpyCfG8QuZG1AnWz266jOhj/DzXQ1tf5+72e2Vp/HaVaruuAzDJHRgvWwKBgAHy + aMEOlKyYpBufk+Kq2HuXKh/9KjhlZv7OqNKh7s8mc/L1BmD9fxlZiYczdLSjXPyo + AVjiyUSQxyF2WucYejOrkX90Z9PS/ppeZy+r8tsmQzsBWyopZ/tK+Op+6aYMhVp3 + 6+zoDlWxDvnxWdKhUyfOGq2eQiuNzAD+fUVJ25z9AoGARUJ4C87X7vH4QnsIE0g+ + ni0xSs8DgSq0v8+cJ7xwyN+NnC6eeU9N2U/m/5anLEM2GFjo4kghLDkC/2urc8dD + UtiisQjWz3O88QHqOvclEAmveuxfCYr/A/CWGdkH3YoK+AXIj3fkwb2Qd//rJ9zL + dT3XPCRatoVKLNKzUGNVcFM= + -----END PRIVATE KEY----- + - type: ecdsa + key: | + -----BEGIN PRIVATE KEY----- + MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgaa7KvLdq72Nb7i7pGm/6 + SCW0RAKFcVwz7P9/8Wo2TTShRANCAATlTf0uyezm7riXjZdn1XND00uf4d1tc1jc + V4CiFiDQsDX+3znAGxqhTuoOkVn/G5lwgE1cgTX57r9cyYkso9UY + -----END PRIVATE KEY----- +``` + +## Configuration sections + +### `http` + +Controls on what address the server should be listening on + +```yaml +http: + address: 0.0.0.0:8080 +``` + +### `cookies` + +Configuration related to encrypted cookies + +```yaml +cookies: + # Secret key used to encrypt cookies + secret: c7e42fb8baba8f228b2e169fdf4c8216dffd5d33ad18bafd8b928c09ca46c718 +``` + +### `database` + +Configure how to connect to the PostgreSQL database. + +```yaml +database: + # Full connection string as per + # https://www.postgresql.org/docs/13/libpq-connect.html#id-1.7.3.8.3.6 + uri: postgresql://user:password@hostname:5432/database?sslmode=require + + # -- OR -- + # Separate parameters + host: hostname + port: 5432 + #socket: + username: user + password: password + database: database + + # Additional parameters for the connection pool + min_connections: 0 + max_connections: 10 + connect_timeout: 30 + idle_timeout: 600 + max_lifetime: 1800 +``` + +### `templates` + +Allows loading custom templates + +```yaml +templates: + # From where to load the templates + # This is relative to the current working directory, *not* the config file + path: /to/templates + + # Whether builtin should be loaded or not + builtin: true +``` + +### `oauth2` + +Configuration related to the OAuth 2.0 endpoints + +```yaml +oauth2: + # URL where the server should be accessed + issuer: http://localhost:8080/ + + # List of OAuth 2.0 clients + clients: + # Confidential client + - client_id: first + client_secret: secret + # List of authorized redirect URIs + redirect_uris: + - http://localhost:1234/callback + # Public client, without client_secret + - client_id: second + + # Signing keys + keys: + # It needs at least an RSA key to work properly + - type: "rsa" + key: | + -----BEGIN PRIVATE KEY----- + MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC7iinu0NXjWP5/ + /4OqyqOMI5uLJIHSrYIZLUlWMldtXmNy0c/pan+gxvZogiYx0cNydO/FogNbC4oD + yj7RIF+QcWJ8wcdG94/P+Xs3HFQzIZfwF+78RWQQJ7nQFekXJ1wQSXV4giw9b4XR + YkoVhHlyxyYGBFffO//DtYVto4uHvXVL0M27bV6l1K8VKspF72gb8Vt44V8OX5hT + sEsYW8SjOD1neEoVKiY6XP63cAG9FTB4a4sKkcUqwjrKEYKio/JLujmCl96eLN18 + cuqr6XuSDKvuVJtb+ZNLJi61vIOlD8cz3wu37hr3PCUZ+Ko9Ley+QfopJ3WYFxrI + IjQKb0W5AgMBAAECggEBAK87ZfsTfwczPHn1Ed4gAbkL/GaC8hscrJdBzWiRGUfE + DkBW82IydJaR0ePM2EtsqKblxLRxsZj8qzTnYNKe4SxiBZh0p/MTlnjJr+vKuJIe + LY3VjySA4gKGXASmtGlCCa/eM7kqSJQPBIakxHxej+xDULAGluSrd0wy7D2JtvJY + 5By+2apILUujBZZU/iUyB2AqK6IrzALo2gTV9Jhun9Wz0k3DXZBGd41v42BhZ+Rx + bHHgpuUTyDQOpKqJ5g1kN4qGlN/CeoontxcE5NOSgtipWeQEuelT8t4eZKHTXBS+ + Byd+uFe8oobWRY2crLptX8TZEENH7wX7y2YgNYUbeZECgYEA95YRhDuukcrDiNuF + fOXs+99XFORsKTbtZYwrouc7PI2CYb0I9ezoQMu3dzWIwOTUQHea5qCo/dYzbeED + fNzwPb2zaWaWFbkEWVTOwRewL+NfP+Ek2Nml+dVJm3d35qdIHYV+9gAcI87iCHxA + gqc13ZS4ba+5/vV6OYwNSAeW0TcCgYEAwem1F9zhVVOIReh3JaJLpZn0xuvZs5kN + TzvFXar33LKdulk5liC3L7ZrqspGESU0JC3pANR8PwuNteEEdHnkC5UTEqMf/fxG + j5CObJl+e2CiV2CNbe+3IQ1PKSxopD+Sq65ze7aP2moVZg94mbw4dsN+uY5QEql1 + Bmq0b7Wm2I8CgYBOqlDgefIKgqlEF7O/LnLwyFKr4bP4GGqvZC0NMnkg0TmHAoAR + W3ej9tZROyI7X7mMzjPaaVuoY2Gt3Nu11aFDjL2vlJfFSSb3lzmmInepj43ZBxkl + CWpyCfG8QuZG1AnWz266jOhj/DzXQ1tf5+72e2Vp/HaVaruuAzDJHRgvWwKBgAHy + aMEOlKyYpBufk+Kq2HuXKh/9KjhlZv7OqNKh7s8mc/L1BmD9fxlZiYczdLSjXPyo + AVjiyUSQxyF2WucYejOrkX90Z9PS/ppeZy+r8tsmQzsBWyopZ/tK+Op+6aYMhVp3 + 6+zoDlWxDvnxWdKhUyfOGq2eQiuNzAD+fUVJ25z9AoGARUJ4C87X7vH4QnsIE0g+ + ni0xSs8DgSq0v8+cJ7xwyN+NnC6eeU9N2U/m/5anLEM2GFjo4kghLDkC/2urc8dD + UtiisQjWz3O88QHqOvclEAmveuxfCYr/A/CWGdkH3YoK+AXIj3fkwb2Qd//rJ9zL + dT3XPCRatoVKLNKzUGNVcFM= + -----END PRIVATE KEY----- + - type: "ecdsa" + key: | + -----BEGIN PRIVATE KEY----- + MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgaa7KvLdq72Nb7i7pGm/6 + SCW0RAKFcVwz7P9/8Wo2TTShRANCAATlTf0uyezm7riXjZdn1XND00uf4d1tc1jc + V4CiFiDQsDX+3znAGxqhTuoOkVn/G5lwgE1cgTX57r9cyYkso9UY + -----END PRIVATE KEY----- +``` diff --git a/docs/usage/installation.md b/docs/usage/installation.md new file mode 100644 index 000000000..792fe72d1 --- /dev/null +++ b/docs/usage/installation.md @@ -0,0 +1,62 @@ +# Installation + +## Requirements + + - A PostgreSQL database + - Either: + - A [Rust toolchain](https://www.rust-lang.org/learn/get-started) (recommended for development) + - or [Docker](https://www.docker.com/get-started) (or a compatible container runtime) + +## Installing from the source + +1. Get the source + ```sh + git clone https://github.com/matrix-org/matrix-authentication-service.git + cd matrix-authentication-service + ``` +2. Compile the CLI + ``` + cargo build --release + ``` +3. Grab the built binary + ``` + cp ./target/release/mas-cli ~/.local/bin # Copy the binary somewhere in $PATH + mas-cli --help # Should display the help message + ``` + +## Running from the Docker image + +A Docker image is available here: [`ghcr.io/matrix-org/matrix-authentication-service:main`](https://ghcr.io/matrix-org/matrix-authentication-service:main) + +--- + +```sh +docker run --rm ghcr.io/matrix-org/matrix-authentication-service:main --help +``` +``` +mas-cli + +USAGE: + mas-cli [OPTIONS] [SUBCOMMAND] + +FLAGS: + -h, --help Print help information + -V, --version Print version information + +OPTIONS: + -c, --config ... Path to the configuration file [default: config.yaml] + +SUBCOMMANDS: + config Configuration-related commands + database Manage the database + help Print this message or the help of the given subcommand(s) + manage Manage the instance + server Runs the web server + templates Templates-related commands +``` + +Note that when running in a Docker environment + +--- + +The next step is to generate the configuration file and tweak it to reach the PostgreSQL database. diff --git a/docs/usage/usage.md b/docs/usage/usage.md new file mode 100644 index 000000000..b13e02b7f --- /dev/null +++ b/docs/usage/usage.md @@ -0,0 +1,75 @@ +# Using the service + +## Running + +Once the configuration is done, one should run the database migrations by running + +```sh +mas-cli database migrate +``` + +The server can then be started by running + +```sh +mas-cli server +``` + +``` +Sep 24 14:42:42.743 INFO mas_cli::server: Starting task scheduler +Sep 24 14:42:42.743 INFO mas_core::templates: Loading builtin templates +Sep 24 14:42:42.752 INFO mas_cli::server: Listening on http://0.0.0.0:8080 +``` + +The server should now be accessible through . + +**Note**: when running with Docker, the port used by the server should be exposed with the `-p` flag: + +```sh +docker run --rm \ + -v `pwd`/config.yaml:/config.yaml \ + -p 8080:8080 \ + ghcr.io/matrix-org/matrix-authentication-service:main \ + server +``` + +## Registering, logging in and out + +Through the interface, users are able to create an account by clicking the `Register` button on the top right (or going to [`/register`](http://localhost:8080/register). +They can then end their session by clicking the `Log out` button and log back in. + +## Playing around with the playground + +The OpenID Foundation hosts a OpenID Connect Playground where one can test logging in through an OIDC provider: https://openidconnect.net/ + +### Step 1: Add the client to the server config + +Add the following section to the server configuration file `config.yaml`: + +```yaml +oauth2: + # ... + clients: + - client_id: oidc-playground + client_secret: verysecret + redirect_uris: + - "https://openidconnect.net/callback" +``` + +### Step 2: Change the playground configuration + + - Navigate to [the playground](https://openidconnect.net/) + - Click on "Configuration" + - Server template: *Custom* + - Paste the discovery document URL found on the service homepage (e.g. `http://localhost:8080/.well-known/openid-configuration`) + - Click "Use discovery document" ; it should fill out the authorization, token and token keys endpoints + - Set the OIDC Client ID to `oidc-playground` and the Client Secret to `verysecret` (must match the ones in the configuration) + - Click "Save" + +### Step 3: Run the OpenID Connect flow + +Start the flow by clicking the "Start" button. +It should redirect the browser to the authentication service. +If a user is already logged in, it should redirect back to the playground immediately. + +Follow the flow to see the code exchange happen. +Note that the last step only works if the service is accessible through the internet.