diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 939e534..9e37753 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,28 +1,29 @@
-# How to Contribute
-
-We'd love to accept your patches and contributions to this project. There are
-just a few small guidelines you need to follow.
-
-## Contributor License Agreement
-
-Contributions to this project must be accompanied by a Contributor License
-Agreement. You (or your employer) retain the copyright to your contribution;
-this simply gives us permission to use and redistribute your contributions as
-part of the project. Head over to to see
-your current agreements on file or to sign a new one.
-
-You generally only need to submit a CLA once, so if you've already submitted one
-(even if it was for a different project), you probably don't need to do it
-again.
-
-## Code reviews
-
-All submissions, including submissions by project members, require review. We
-use GitHub pull requests for this purpose. Consult
-[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
-information on using pull requests.
-
-## Community Guidelines
-
-This project follows [Google's Open Source Community
-Guidelines](https://opensource.google.com/conduct/).
+# Contributing
+
+We welcome contributions from the community. Please read the following guidelines carefully to
+maximize the chances of your PR being merged.
+
+## Coding Style
+
+* To ensure your change passes format checks, run `make check`. To format your files, you can run `make format`.
+* We follow standard Go table-driven tests and use the `testify` library to assert correctness.
+ To verify all tests pass, you can run `make test`.
+
+## Code Reviews
+
+* The pull request title should describe what the change does and not embed issue numbers.
+ The pull request should only be blank when the change is minor. Any feature should include
+ a description of the change and what motivated it. If the change or design changes through
+ review, please keep the title and description updated accordingly.
+* A single approval is sufficient to merge. If a reviewer asks for
+ changes in a PR they should be addressed before the PR is merged,
+ even if another reviewer has already approved the PR.
+* During the review, address the comments and commit the changes
+ _without_ squashing the commits. This facilitates incremental reviews
+ since the reviewer does not go through all the code again to find out
+ what has changed since the last review. When a change goes out of sync with main,
+ please rebase and force push, keeping the original commits where practical.
+* Commits are squashed prior to merging a pull request, using the title
+ as commit message by default. Maintainers may request contributors to
+ edit the pull request tite to ensure that it remains descriptive as a
+ commit message. Alternatively, maintainers may change the commit message directly.
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
new file mode 100644
index 0000000..14a3a8e
--- /dev/null
+++ b/DEVELOPMENT.md
@@ -0,0 +1,92 @@
+# Developer guide
+
+All the build targets are self-explanatory and can be listed with:
+
+```bash
+$ make help
+```
+
+The following software and tools are needed to build the project and run the tests:
+
+* [Go](https://golang.org/dl/)
+* [GNU make](https://www.gnu.org/software/make/)
+* [Docker](https://docs.docker.com/get-docker/)
+
+
+## Generating the API code
+
+The configuration options are defined in the [config](config/) directory using [Protocol Buffers](https://protobuf.dev/).
+To generate the configuration API code after doing changes to the `.proto` files, run:
+
+```bash
+$ make generate
+```
+
+There is no need to run `generate` after checking out the code; it's only needed when changes are made to
+the `.proto` files.
+
+
+## Building the binary
+
+To build the binary simply run:
+
+```bash
+$ make build # Builds a dynamically linked binary
+$ make static # Builds a statically linked binary
+```
+
+The resulting binaries will be in the `bin/` directory. You can play with the
+`TARGETS` environment variable to control the operating systems and architectures you want
+to build for.
+
+
+## Docker image
+
+To build the Docker image, run:
+
+```bash
+$ make docker # Build a single-arch Docker image tagged with "-latest-$arch"
+$ make docker-push # Build and push the multi-arch Docker images to the registry
+```
+
+This will automatically build the required binaries and create a Docker image with them.
+
+The `make docker` target will produce images that are suitable to be used in the `e2e` tests.
+The `make docker-push` target will produce multi-arch images and push them to the registry.
+You can use the `DOCKER_TARGETS` environment variable to control the operating systems and architectures
+you want to build the Docker images for.
+
+
+## Testing
+
+The main testing targets are:
+
+```bash
+$ make test # Run the unit tests
+$ make lint # Run the linters
+$ make e2e # Run the end-to-end tests
+```
+
+### e2e tests
+
+The end-to-end tests are found in the [e2e](e2e/) directory. Each subdirectory contains a test suite
+that can be run independently. The `make e2e` target will run all the test suites by default. To run
+individual suites, simply run `make e2e/`. For example:
+
+```bash
+$ make e2e # Run all the e2e suites
+$ make e2e/keycloak # Run the 'keycloak' e2e suite
+
+# Examples with custom test options
+$ E2E_TEST_OPTS="-v -count=1" make e2e # Run all the e2e suites with verbose output and no caching
+$ E2E_PRESERVE_LOGS=true make e2e # Preserve the container logs even if tests succeed
+```
+
+> [!Note]
+> The end-to-end tests use the `authservice` Docker image, and it **must be up-to-date**.
+> Make sure you run `make clean docker` before running the tests
+
+The end-to-end tests use Docker Compose to set up the required infrastructure before running the tests.
+Once the tests are done, the infrastructure is automatically torn down if tests pass, or left running
+if tests fail, to facilitate troubleshooting. Container logs are also captured upon test failure, to
+aid in debugging.
diff --git a/README.md b/README.md
index dab55fb..023a9ac 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,9 @@
An implementation of [Envoy](https://envoyproxy.io) [External Authorization](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/ext_authz_filter),
focused on delivering authN/Z solutions for [Istio](https://istio.io) and [Kubernetes](https://kubernetes.io).
+This project is a port of the [istio-ecosystem/authservice](https://github.com/istio-ecosystem/authservice)
+project from C++ to Go.
+
## Introduction
`authservice` helps delegate the [OIDC Authorization Code Grant Flow](https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth)
@@ -14,40 +17,26 @@ including [Authentication Policy](https://istio.io/docs/tasks/security/authn-pol
Together, they allow developers to protect their APIs and web apps without any application code required.
Some of the features it provides:
-- Transparent login and logout
- - Retrieves OAuth2 Access tokens, ID tokens, and refresh tokens
-- Fine-grained control over which url paths are protected
-- Session management
- - Configuration of session lifetime and idle timeouts
- - Refreshes expired tokens automatically
-- Compatible with any standard OIDC Provider
-- Supports multiple OIDC Providers for same application
-- Trusts custom CA certs when talking to OIDC Providers
-- Works either at the sidecar or gateway level
-
-## Using the `authservice` docker image
+* Transparent login and logout
+ * Retrieves OAuth2 Access tokens, ID tokens, and refresh tokens
+* Fine-grained control over which url paths are protected
+* Session management
+ * Configuration of session lifetime and idle timeouts
+ * Refreshes expired tokens automatically
+* Compatible with any standard OIDC Provider
+* Supports multiple OIDC Providers for same application
+* Trusts custom CA certs when talking to OIDC Providers
+* Works either at the sidecar or gateway level
-The `authservice` images are hosted on [authservice's GitHub Package Registry](https://github.com/istio-ecosystem/authservice/packages).
## How does authservice work?
-We have created a [flowchart](https://miro.com/app/board/o9J_kvus6b4=/) to explain how authservice makes decisions at different points in the login lifecycle.
+[This flowchart](https://miro.com/app/board/o9J_kvus6b4=/) explains how `authservice`
+makes decisions at different points in the login lifecycle.
## Contributing
-To get started:
-
-- [Contributing guide](./CONTRIBUTING.md)
-
-## Roadmap
-See the [authservice github Project](https://github.com/istio-ecosystem/authservice/projects/1)
-
-Additional features being considered:
-- A more Istio-integrated experience of deploying/configuring/enabling `authservice`
- (e.g.: extending Istio Authentication Policy to include `authservice` configs).
-
-## Contributing & Contact
+Contributions are very welcome! Please read the [Contributing guidelines](CONTRIBUTING.md)
+to get started.
-We welcome feedback and contributions. Aside from submitting Github issues/PRs, you can reach out at `#oidc-proposal`
-or `#security` channel on [Istio’s Slack](https://istio.slack.com/) workspace
-([here's how to join](https://istio.io/about/community/join/)).
+Detailed development instructions can be found in the [Development guide](DEVELOPMENT.md).
diff --git a/e2e/keycloak/README.md b/e2e/keycloak/README.md
new file mode 100644
index 0000000..308c5d0
--- /dev/null
+++ b/e2e/keycloak/README.md
@@ -0,0 +1,22 @@
+# Keycloak e2e tests
+
+The Keycloak e2e test suite contains tests that use the Keycloak OIDC provider. A
+Keycloak instance is deployed and configured in the Docker environment as the backend
+OIDC provider.
+
+The setup is performed in the [setup-keycloak.sh](setup-keycloak.sh) script, which
+configures the default `master` realm with:
+
+* A user named `authservice` with a predefined password.
+* A client named `authservice` with a predefined secret.
+
+The user and client will be used in the e2e tests to verify the entire Authorization Code flow.
+
+## Docker host name resolution
+
+The Keycloak end-to-end tests rely on the host `host.docker.internal` to resolve to the host machine,
+so you may need to add an entry to your `/etc/hosts` file to make it work. For example:
+
+```bash
+$ echo "127.0.0.1 host.docker.internal" >> /etc/hosts
+```
diff --git a/e2e/mock/README.md b/e2e/mock/README.md
new file mode 100644
index 0000000..b6e323d
--- /dev/null
+++ b/e2e/mock/README.md
@@ -0,0 +1,7 @@
+# Mock e2e tests
+
+The `mock` e2e test suite contains tests that use the `mock` OIDC provider.
+The suite is mostly used to verify the correct behavior of the different configuration
+options without making real requests to an OIDC provider.
+
+It can be used for rapid prorotyping and development of new features.
diff --git a/e2e/redis/README.md b/e2e/redis/README.md
new file mode 100644
index 0000000..0036c56
--- /dev/null
+++ b/e2e/redis/README.md
@@ -0,0 +1,5 @@
+# Redis e2e tests
+
+The Redis e2e test suite contains tests that verify the correct behavior of the Redis
+session store for the OIDC providers. It targets the `SessionStore` interface directly
+and verifies the contents of the Redis database on each operation.
diff --git a/e2e/suite.mk b/e2e/suite.mk
index 8ad2f4b..a9bfe8e 100644
--- a/e2e/suite.mk
+++ b/e2e/suite.mk
@@ -12,6 +12,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+# THis file contains the common e2e targets and variables for all e2e suites.
+# When adding a suite, create a new directory under e2e/ and add a Makefile that
+# includes this file.
+
# Force run of the e2e tests
E2E_TEST_OPTS ?= -count=1
@@ -26,7 +30,7 @@ e2e: e2e-pre
.PHONY: e2e-test
e2e-test:
- go test $(E2E_TEST_OPTS) ./... || ( $(MAKE) e2e-post-error; exit 1 )
+ @go test $(E2E_TEST_OPTS) ./... || ( $(MAKE) e2e-post-error; exit 1 )
.PHONY: e2e-pre
e2e-pre::