diff --git a/documentation/docs/examples/full_example/full_example.py b/documentation/docs/examples/full_example/full_example.py
new file mode 100644
index 0000000..644c276
--- /dev/null
+++ b/documentation/docs/examples/full_example/full_example.py
@@ -0,0 +1,242 @@
+from typing import List, Optional
+
+import uvicorn
+from fastapi import FastAPI, Depends, Query, Body, Request
+from fastapi.responses import JSONResponse
+from pydantic import SecretStr
+
+from fastapi_keycloak import (
+ FastAPIKeycloak,
+ OIDCUser,
+ UsernamePassword,
+ HTTPMethod,
+ KeycloakUser,
+ KeycloakGroup,
+ KeycloakError
+)
+
+app = FastAPI()
+idp = FastAPIKeycloak(
+ server_url="http://localhost:8085/auth",
+ client_id="test-client",
+ client_secret="GzgACcJzhzQ4j8kWhmhazt7WSdxDVUyE",
+ admin_client_secret="BIcczGsZ6I8W5zf0rZg5qSexlloQLPKB",
+ realm="Test",
+ callback_uri="http://localhost:8081/callback"
+)
+idp.add_swagger_config(app)
+
+
+# Custom error handler for showing Keycloak errors on FastAPI
+@app.exception_handler(KeycloakError)
+async def keycloak_exception_handler(request: Request, exc: KeycloakError):
+ return JSONResponse(
+ status_code=exc.status_code,
+ content={"message": exc.reason},
+ )
+
+
+# Admin
+
+@app.post("/proxy", tags=["admin-cli"])
+def proxy_admin_request(
+ relative_path: str,
+ method: HTTPMethod,
+ additional_headers: dict = Body(None),
+ payload: dict = Body(None),
+):
+ return idp.proxy(
+ additional_headers=additional_headers,
+ relative_path=relative_path,
+ method=method,
+ payload=payload
+ )
+
+
+@app.get("/identity-providers", tags=["admin-cli"])
+def get_identity_providers():
+ return idp.get_identity_providers()
+
+
+@app.get("/idp-configuration", tags=["admin-cli"])
+def get_idp_config():
+ return idp.open_id_configuration
+
+
+# User Management
+
+@app.get("/users", tags=["user-management"])
+def get_users():
+ return idp.get_all_users()
+
+
+@app.get("/user", tags=["user-management"])
+def get_user_by_query(query: str = None):
+ return idp.get_user(query=query)
+
+
+@app.post("/users", tags=["user-management"])
+def create_user(
+ first_name: str, last_name: str, email: str, password: SecretStr, id: str = None
+):
+ return idp.create_user(
+ first_name=first_name,
+ last_name=last_name,
+ username=email,
+ email=email,
+ password=password.get_secret_value(),
+ id=id
+ )
+
+
+@app.get("/user/{user_id}", tags=["user-management"])
+def get_user(user_id: str = None):
+ return idp.get_user(user_id=user_id)
+
+
+@app.put("/user", tags=["user-management"])
+def update_user(user: KeycloakUser):
+ return idp.update_user(user=user)
+
+
+@app.delete("/user/{user_id}", tags=["user-management"])
+def delete_user(user_id: str):
+ return idp.delete_user(user_id=user_id)
+
+
+@app.put("/user/{user_id}/change-password", tags=["user-management"])
+def change_password(user_id: str, new_password: SecretStr):
+ return idp.change_password(user_id=user_id, new_password=new_password)
+
+
+@app.put("/user/{user_id}/send-email-verification", tags=["user-management"])
+def send_email_verification(user_id: str):
+ return idp.send_email_verification(user_id=user_id)
+
+
+# Role Management
+
+@app.get("/roles", tags=["role-management"])
+def get_all_roles():
+ return idp.get_all_roles()
+
+
+@app.get("/role/{role_name}", tags=["role-management"])
+def get_role(role_name: str):
+ return idp.get_roles([role_name])
+
+
+@app.post("/roles", tags=["role-management"])
+def add_role(role_name: str):
+ return idp.create_role(role_name=role_name)
+
+
+@app.delete("/roles", tags=["role-management"])
+def delete_roles(role_name: str):
+ return idp.delete_role(role_name=role_name)
+
+
+# Group Management
+
+@app.get("/groups", tags=["group-management"])
+def get_all_groups():
+ return idp.get_all_groups()
+
+
+@app.get("/group/{group_name}", tags=["group-management"])
+def get_group(group_name: str):
+ return idp.get_groups([group_name])
+
+
+@app.get("/group-by-path/{path: path}", tags=["group-management"])
+def get_group_by_path(path: str):
+ return idp.get_group_by_path(path)
+
+
+@app.post("/groups", tags=["group-management"])
+def add_group(group_name: str, parent_id: Optional[str] = None):
+ return idp.create_group(group_name=group_name, parent=parent_id)
+
+
+@app.delete("/groups", tags=["group-management"])
+def delete_groups(group_id: str):
+ return idp.delete_group(group_id=group_id)
+
+
+# User Roles
+
+@app.post("/users/{user_id}/roles", tags=["user-roles"])
+def add_roles_to_user(user_id: str, roles: Optional[List[str]] = Query(None)):
+ return idp.add_user_roles(user_id=user_id, roles=roles)
+
+
+@app.get("/users/{user_id}/roles", tags=["user-roles"])
+def get_user_roles(user_id: str):
+ return idp.get_user_roles(user_id=user_id)
+
+
+@app.delete("/users/{user_id}/roles", tags=["user-roles"])
+def delete_roles_from_user(user_id: str, roles: Optional[List[str]] = Query(None)):
+ return idp.remove_user_roles(user_id=user_id, roles=roles)
+
+
+# User Groups
+
+@app.post("/users/{user_id}/groups", tags=["user-groups"])
+def add_group_to_user(user_id: str, group_id: str):
+ return idp.add_user_group(user_id=user_id, group_id=group_id)
+
+
+@app.get("/users/{user_id}/groups", tags=["user-groups"])
+def get_user_groups(user_id: str):
+ return idp.get_user_groups(user_id=user_id)
+
+
+@app.delete("/users/{user_id}/groups", tags=["user-groups"])
+def delete_groups_from_user(user_id: str, group_id: str):
+ return idp.remove_user_group(user_id=user_id, group_id=group_id)
+
+
+# Example User Requests
+
+@app.get("/protected", tags=["example-user-request"])
+def protected(user: OIDCUser = Depends(idp.get_current_user())):
+ return user
+
+
+@app.get("/current_user/roles", tags=["example-user-request"])
+def get_current_users_roles(user: OIDCUser = Depends(idp.get_current_user())):
+ return user.roles
+
+
+@app.get("/admin", tags=["example-user-request"])
+def company_admin(user: OIDCUser = Depends(idp.get_current_user(required_roles=["admin"]))):
+ return f"Hi admin {user}"
+
+
+@app.post("/login", tags=["example-user-request"])
+def login(user: UsernamePassword = Body(...)):
+ return idp.user_login(
+ username=user.username, password=user.password.get_secret_value()
+ )
+
+
+# Auth Flow
+
+@app.get("/login-link", tags=["auth-flow"])
+def login_redirect():
+ return idp.login_uri
+
+
+@app.get("/callback", tags=["auth-flow"])
+def callback(session_state: str, code: str):
+ return idp.exchange_authorization_code(session_state=session_state, code=code)
+
+
+@app.get("/logout", tags=["auth-flow"])
+def logout():
+ return idp.logout_uri
+
+
+if __name__ == "__main__":
+ uvicorn.run("app:app", host="127.0.0.1", port=8081)
diff --git a/documentation/docs/examples/introduction/app.py b/documentation/docs/examples/introduction/app.py
new file mode 100644
index 0000000..ed3bae0
--- /dev/null
+++ b/documentation/docs/examples/introduction/app.py
@@ -0,0 +1,28 @@
+import uvicorn
+from fastapi import FastAPI, Depends
+
+from fastapi_keycloak import FastAPIKeycloak, OIDCUser
+
+app = FastAPI()
+idp = FastAPIKeycloak(
+ server_url="https://auth.some-domain.com/auth",
+ client_id="some-client",
+ client_secret="some-client-secret",
+ admin_client_secret="admin-cli-secret",
+ realm="some-realm-name",
+ callback_uri="http://localhost:8081/callback"
+)
+idp.add_swagger_config(app)
+
+@app.get("/admin")
+def admin(user: OIDCUser = Depends(idp.get_current_user(required_roles=["admin"]))):
+ return f'Hi premium user {user}'
+
+
+@app.get("/user/roles")
+def user_roles(user: OIDCUser = Depends(idp.get_current_user)):
+ return f'{user.roles}'
+
+
+if __name__ == '__main__':
+ uvicorn.run('app:app', host="127.0.0.1", port=8081)
diff --git a/documentation/docs/examples/quickstart/app.py b/documentation/docs/examples/quickstart/app.py
new file mode 100644
index 0000000..b945f19
--- /dev/null
+++ b/documentation/docs/examples/quickstart/app.py
@@ -0,0 +1,44 @@
+import uvicorn
+from fastapi import FastAPI, Depends
+from fastapi.responses import RedirectResponse
+from fastapi_keycloak import FastAPIKeycloak, OIDCUser
+
+app = FastAPI()
+idp = FastAPIKeycloak(
+ server_url="http://localhost:8085/auth",
+ client_id="test-client",
+ client_secret="GzgACcJzhzQ4j8kWhmhazt7WSdxDVUyE",
+ admin_client_secret="BIcczGsZ6I8W5zf0rZg5qSexlloQLPKB",
+ realm="Test",
+ callback_uri="http://localhost:8081/callback"
+)
+idp.add_swagger_config(app)
+
+
+@app.get("/") # Unprotected
+def root():
+ return 'Hello World'
+
+
+@app.get("/user") # Requires logged in
+def current_users(user: OIDCUser = Depends(idp.get_current_user())):
+ return user
+
+
+@app.get("/admin") # Requires the admin role
+def company_admin(user: OIDCUser = Depends(idp.get_current_user(required_roles=["admin"]))):
+ return f'Hi admin {user}'
+
+
+@app.get("/login")
+def login_redirect():
+ return RedirectResponse(idp.login_uri)
+
+
+@app.get("/callback")
+def callback(session_state: str, code: str):
+ return idp.exchange_authorization_code(session_state=session_state, code=code) # This will return an access token
+
+
+if __name__ == '__main__':
+ uvicorn.run('app:app', host="127.0.0.1", port=8081)
diff --git a/documentation/docs/examples/quickstart/docker-compose.yaml b/documentation/docs/examples/quickstart/docker-compose.yaml
new file mode 100644
index 0000000..74106f6
--- /dev/null
+++ b/documentation/docs/examples/quickstart/docker-compose.yaml
@@ -0,0 +1,35 @@
+version: '3'
+
+services:
+ postgres:
+ image: postgres
+ environment:
+ POSTGRES_DB: testkeycloakdb
+ POSTGRES_USER: testkeycloakuser
+ POSTGRES_PASSWORD: testkeycloakpassword
+ restart:
+ always
+
+ keycloak:
+ image: jboss/keycloak:16.1.0
+ volumes:
+ - ./realm-export.json:/opt/jboss/keycloak/imports/realm-export.json
+ command:
+ - "-b 0.0.0.0 -Dkeycloak.profile.feature.upload_scripts=enabled -Dkeycloak.import=/opt/jboss/keycloak/imports/realm-export.json"
+ environment:
+ DB_VENDOR: POSTGRES
+ DB_ADDR: postgres
+ DB_DATABASE: testkeycloakdb
+ DB_USER: testkeycloakuser
+ DB_SCHEMA: public
+ DB_PASSWORD: testkeycloakpassword
+ KEYCLOAK_USER: keycloakuser
+ KEYCLOAK_PASSWORD: keycloakpassword
+ PROXY_ADDRESS_FORWARDING: "true"
+ KEYCLOAK_LOGLEVEL: DEBUG
+ ports:
+ - '8085:8080'
+ depends_on:
+ - postgres
+ restart:
+ always
diff --git a/documentation/docs/downloads/realm-export.json b/documentation/docs/examples/quickstart/realm-export.json
similarity index 100%
rename from documentation/docs/downloads/realm-export.json
rename to documentation/docs/examples/quickstart/realm-export.json
diff --git a/documentation/docs/full_example.md b/documentation/docs/full_example.md
index 2f6b0be..b7951fc 100644
--- a/documentation/docs/full_example.md
+++ b/documentation/docs/full_example.md
@@ -1,246 +1,5 @@
# Example Usage
```python
-from typing import List, Optional
-
-import uvicorn
-from fastapi import FastAPI, Depends, Query, Body, Request
-from fastapi.responses import JSONResponse
-from pydantic import SecretStr
-
-from fastapi_keycloak import (
- FastAPIKeycloak,
- OIDCUser,
- UsernamePassword,
- HTTPMethod,
- KeycloakUser,
- KeycloakGroup,
- KeycloakError
-)
-
-app = FastAPI()
-idp = FastAPIKeycloak(
- server_url="http://localhost:8085/auth",
- client_id="test-client",
- client_secret="GzgACcJzhzQ4j8kWhmhazt7WSdxDVUyE",
- admin_client_secret="BIcczGsZ6I8W5zf0rZg5qSexlloQLPKB",
- realm="Test",
- callback_uri="http://localhost:8081/callback"
-)
-idp.add_swagger_config(app)
-
-
-# Custom error handler for showing Keycloak errors on FastAPI
-@app.exception_handler(KeycloakError)
-async def keycloak_exception_handler(request: Request, exc: KeycloakError):
- return JSONResponse(
- status_code=exc.status_code,
- content={"message": exc.reason},
- )
-
-
-# Admin
-
-@app.post("/proxy", tags=["admin-cli"])
-def proxy_admin_request(
- relative_path: str,
- method: HTTPMethod,
- additional_headers: dict = Body(None),
- payload: dict = Body(None),
-):
- return idp.proxy(
- additional_headers=additional_headers,
- relative_path=relative_path,
- method=method,
- payload=payload
- )
-
-
-@app.get("/identity-providers", tags=["admin-cli"])
-def get_identity_providers():
- return idp.get_identity_providers()
-
-
-@app.get("/idp-configuration", tags=["admin-cli"])
-def get_idp_config():
- return idp.open_id_configuration
-
-
-# User Management
-
-@app.get("/users", tags=["user-management"])
-def get_users():
- return idp.get_all_users()
-
-
-@app.get("/user", tags=["user-management"])
-def get_user_by_query(query: str = None):
- return idp.get_user(query=query)
-
-
-@app.post("/users", tags=["user-management"])
-def create_user(
- first_name: str, last_name: str, email: str, password: SecretStr, id: str = None
-):
- return idp.create_user(
- first_name=first_name,
- last_name=last_name,
- username=email,
- email=email,
- password=password.get_secret_value(),
- id=id
- )
-
-
-@app.get("/user/{user_id}", tags=["user-management"])
-def get_user(user_id: str = None):
- return idp.get_user(user_id=user_id)
-
-
-@app.put("/user", tags=["user-management"])
-def update_user(user: KeycloakUser):
- return idp.update_user(user=user)
-
-
-@app.delete("/user/{user_id}", tags=["user-management"])
-def delete_user(user_id: str):
- return idp.delete_user(user_id=user_id)
-
-
-@app.put("/user/{user_id}/change-password", tags=["user-management"])
-def change_password(user_id: str, new_password: SecretStr):
- return idp.change_password(user_id=user_id, new_password=new_password)
-
-
-@app.put("/user/{user_id}/send-email-verification", tags=["user-management"])
-def send_email_verification(user_id: str):
- return idp.send_email_verification(user_id=user_id)
-
-
-# Role Management
-
-@app.get("/roles", tags=["role-management"])
-def get_all_roles():
- return idp.get_all_roles()
-
-
-@app.get("/role/{role_name}", tags=["role-management"])
-def get_role(role_name: str):
- return idp.get_roles([role_name])
-
-
-@app.post("/roles", tags=["role-management"])
-def add_role(role_name: str):
- return idp.create_role(role_name=role_name)
-
-
-@app.delete("/roles", tags=["role-management"])
-def delete_roles(role_name: str):
- return idp.delete_role(role_name=role_name)
-
-
-# Group Management
-
-@app.get("/groups", tags=["group-management"])
-def get_all_groups():
- return idp.get_all_groups()
-
-
-@app.get("/group/{group_name}", tags=["group-management"])
-def get_group(group_name: str):
- return idp.get_groups([group_name])
-
-
-@app.get("/group-by-path/{path: path}", tags=["group-management"])
-def get_group_by_path(path: str):
- return idp.get_group_by_path(path)
-
-
-@app.post("/groups", tags=["group-management"])
-def add_group(group_name: str, parent_id: Optional[str] = None):
- return idp.create_group(group_name=group_name, parent=parent_id)
-
-
-@app.delete("/groups", tags=["group-management"])
-def delete_groups(group_id: str):
- return idp.delete_group(group_id=group_id)
-
-
-# User Roles
-
-@app.post("/users/{user_id}/roles", tags=["user-roles"])
-def add_roles_to_user(user_id: str, roles: Optional[List[str]] = Query(None)):
- return idp.add_user_roles(user_id=user_id, roles=roles)
-
-
-@app.get("/users/{user_id}/roles", tags=["user-roles"])
-def get_user_roles(user_id: str):
- return idp.get_user_roles(user_id=user_id)
-
-
-@app.delete("/users/{user_id}/roles", tags=["user-roles"])
-def delete_roles_from_user(user_id: str, roles: Optional[List[str]] = Query(None)):
- return idp.remove_user_roles(user_id=user_id, roles=roles)
-
-
-# User Groups
-
-@app.post("/users/{user_id}/groups", tags=["user-groups"])
-def add_group_to_user(user_id: str, group_id: str):
- return idp.add_user_group(user_id=user_id, group_id=group_id)
-
-
-@app.get("/users/{user_id}/groups", tags=["user-groups"])
-def get_user_groups(user_id: str):
- return idp.get_user_groups(user_id=user_id)
-
-
-@app.delete("/users/{user_id}/groups", tags=["user-groups"])
-def delete_groups_from_user(user_id: str, group_id: str):
- return idp.remove_user_group(user_id=user_id, group_id=group_id)
-
-
-# Example User Requests
-
-@app.get("/protected", tags=["example-user-request"])
-def protected(user: OIDCUser = Depends(idp.get_current_user())):
- return user
-
-
-@app.get("/current_user/roles", tags=["example-user-request"])
-def get_current_users_roles(user: OIDCUser = Depends(idp.get_current_user())):
- return user.roles
-
-
-@app.get("/admin", tags=["example-user-request"])
-def company_admin(user: OIDCUser = Depends(idp.get_current_user(required_roles=["admin"]))):
- return f"Hi admin {user}"
-
-
-@app.post("/login", tags=["example-user-request"])
-def login(user: UsernamePassword = Body(...)):
- return idp.user_login(
- username=user.username, password=user.password.get_secret_value()
- )
-
-
-# Auth Flow
-
-@app.get("/login-link", tags=["auth-flow"])
-def login_redirect():
- return idp.login_uri
-
-
-@app.get("/callback", tags=["auth-flow"])
-def callback(session_state: str, code: str):
- return idp.exchange_authorization_code(session_state=session_state, code=code)
-
-
-@app.get("/logout", tags=["auth-flow"])
-def logout():
- return idp.logout_uri
-
-
-if __name__ == "__main__":
- uvicorn.run("app:app", host="127.0.0.1", port=8081)
-```
\ No newline at end of file
+{!examples/full_example/full_example.py!}
+```
diff --git a/documentation/docs/index.md b/documentation/docs/index.md
index 0b602b6..3a973e0 100644
--- a/documentation/docs/index.md
+++ b/documentation/docs/index.md
@@ -35,32 +35,5 @@ This example assumes you use a frontend technology (such as React, Vue, or whate
### app.py
```python
-import uvicorn
-from fastapi import FastAPI, Depends
-
-from fastapi_keycloak import FastAPIKeycloak, OIDCUser
-
-app = FastAPI()
-idp = FastAPIKeycloak(
- server_url="https://auth.some-domain.com/auth",
- client_id="some-client",
- client_secret="some-client-secret",
- admin_client_secret="admin-cli-secret",
- realm="some-realm-name",
- callback_uri="http://localhost:8081/callback"
-)
-idp.add_swagger_config(app)
-
-@app.get("/admin")
-def admin(user: OIDCUser = Depends(idp.get_current_user(required_roles=["admin"]))):
- return f'Hi premium user {user}'
-
-
-@app.get("/user/roles")
-def user_roles(user: OIDCUser = Depends(idp.get_current_user)):
- return f'{user.roles}'
-
-
-if __name__ == '__main__':
- uvicorn.run('app:app', host="127.0.0.1", port=8081)
+{!examples/introduction/app.py!}
```
diff --git a/documentation/docs/quick_start.md b/documentation/docs/quick_start.md
index ab34c1d..808d687 100644
--- a/documentation/docs/quick_start.md
+++ b/documentation/docs/quick_start.md
@@ -2,49 +2,18 @@
In order to just get started, we prepared some containers and configs for you.
+!!! info
+ If you have cloned the git repo, you can run this from the examples dir `fastapi-keycloak/documentation/docs/examples/quickstart`
+
## 1. Configure the Containers
**docker-compose.yaml**
```yaml hl_lines="16 18"
-version: '3'
-
-services:
- postgres:
- image: postgres
- environment:
- POSTGRES_DB: testkeycloakdb
- POSTGRES_USER: testkeycloakuser
- POSTGRES_PASSWORD: testkeycloakpassword
- restart:
- always
-
- keycloak:
- image: jboss/keycloak:16.1.0
- volumes:
- - ./realm-export.json:/opt/jboss/keycloak/imports/realm-export.json
- command:
- - "-b 0.0.0.0 -Dkeycloak.profile.feature.upload_scripts=enabled -Dkeycloak.import=/opt/jboss/keycloak/imports/realm-export.json"
- environment:
- DB_VENDOR: POSTGRES
- DB_ADDR: postgres
- DB_DATABASE: testkeycloakdb
- DB_USER: testkeycloakuser
- DB_SCHEMA: public
- DB_PASSWORD: testkeycloakpassword
- KEYCLOAK_USER: keycloakuser
- KEYCLOAK_PASSWORD: keycloakpassword
- PROXY_ADDRESS_FORWARDING: "true"
- KEYCLOAK_LOGLEVEL: DEBUG
- ports:
- - '8085:8080'
- depends_on:
- - postgres
- restart:
- always
+{!examples/quickstart/docker-compose.yaml!}
```
-This will create a Postgres and a Keycloak container ready to use. Make sure to download the [realm-export.json](./downloads/realm-export.json) and keep it in the same folder as
+This will create a Postgres and a Keycloak container ready to use. Make sure to download the [realm-export.json](./examples/quickstart/realm-export.json) and keep it in the same folder as
the docker compose file to bind the configuration.
!!! Caution
@@ -66,50 +35,7 @@ docker-compose up -d
You may use the code below without altering it, the imported config will match these values:
```python
-import uvicorn
-from fastapi import FastAPI, Depends
-from fastapi.responses import RedirectResponse
-from fastapi_keycloak import FastAPIKeycloak, OIDCUser
-
-app = FastAPI()
-idp = FastAPIKeycloak(
- server_url="http://localhost:8085/auth",
- client_id="test-client",
- client_secret="GzgACcJzhzQ4j8kWhmhazt7WSdxDVUyE",
- admin_client_secret="BIcczGsZ6I8W5zf0rZg5qSexlloQLPKB",
- realm="Test",
- callback_uri="http://localhost:8081/callback"
-)
-idp.add_swagger_config(app)
-
-
-@app.get("/") # Unprotected
-def root():
- return 'Hello World'
-
-
-@app.get("/user") # Requires logged in
-def current_users(user: OIDCUser = Depends(idp.get_current_user())):
- return user
-
-
-@app.get("/admin") # Requires the admin role
-def company_admin(user: OIDCUser = Depends(idp.get_current_user(required_roles=["admin"]))):
- return f'Hi admin {user}'
-
-
-@app.get("/login")
-def login_redirect():
- return RedirectResponse(idp.login_uri)
-
-
-@app.get("/callback")
-def callback(session_state: str, code: str):
- return idp.exchange_authorization_code(session_state=session_state, code=code) # This will return an access token
-
-
-if __name__ == '__main__':
- uvicorn.run('app:app', host="127.0.0.1", port=8081)
+{!examples/quickstart/app.py!}
```
## 4. Usage
@@ -119,4 +45,15 @@ You may now use any of the [APIs exposed endpoints](reference.md) as everything
After you call the `/login` endpoint of your app, you will be redirected to the login screen of Keycloak. You may open the Keycloak Frontend at [http://localhost:8085/auth](http://localhost:8085/auth) and create a user. To
log into your Keycloak instance, the username is `keycloakuser` and the password is `keycloakpassword` as described in the `docker-compose.yaml` above.
-To utilize this fully you need a way to store the Access-Token provided by the callback route and append it to the preceding requests as `Authorization` Bearer.
\ No newline at end of file
+To utilize this fully you need a way to store the Access-Token provided by the callback route and add it to any further requests as `Authorization` Bearer.
+
+You can test this with curl like so:
+
+```shell
+# TOKEN should be changed to the value of 'access_token'.
+# This can be aquired once you have visited http://localhost:8081/login
+
+TOKEN="eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJrbF9ITTQyMHVmcVVwYmhxcHJYVFBzelNlOWZocmdkamtZZF9EbmVhb0dVIn0.eyJleHAiOjE2NjAxNDUwOTAsImlhdCI6MTY2MDE0NDc5MCwiYXV0aF90aW1lIjoxNjYwMTQ0Nzc2LCJqdGkiOiI4YTI3MmEyYS1mMDMxLTQ0ZDctOWRkNy0zMTM4MDQ2ZWQyOTciLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODUvYXV0aC9yZWFsbXMvVGVzdCIsInN1YiI6ImUxZGEwZWYzLTVhMmQtNGMyYi05NGQ4LWQwN2E2Zjc3Y2JhMyIsInR5cCI6IkJlYXJlciIsImF6cCI6InRlc3QtY2xpZW50Iiwic2Vzc2lvbl9zdGF0ZSI6IjM5Mzc4ODVkLTk0Y2MtNDIyMy05YjczLWI2YmRiMGM1MzJlZiIsImFjciI6IjAiLCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJzaWQiOiIzOTM3ODg1ZC05NGNjLTQyMjMtOWI3My1iNmJkYjBjNTMyZWYiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiJNaWNoYWVsIFJvYmluc29uIiwicHJlZmVycmVkX3VzZXJuYW1lIjoibGF4ZG9nQGdtYWlsLmNvbSIsImdpdmVuX25hbWUiOiJNaWNoYWVsIiwiZmFtaWx5X25hbWUiOiJSb2JpbnNvbiIsImVtYWlsIjoibGF4ZG9nQGdtYWlsLmNvbSJ9.FQEtefB90W53L_MHXmhm15223zemd-eb-yMDNtup-lZ9-tEyW5FhE0ro-WzEVypAllQ3b1hH0mx_vZ_wxL00wTzXG_Vi_eMT5U5HTJA6UcwR-Ogv6B1BL42l6xwXQCVLTVgrIKBf1NcJbv0k0qD0Zt-VN1S32JPKr0lURdL99idnIOzWVWrS_urG_2R2RiIn-xTcqyGyxbHkBlPbnk55p9NKl_o1lsnBH-8bJme5c35tA6YTyd8Y2tI7zPHYHZ9s8mBlxrsVLubwAZj12L3cZuG1g_H9uASBOxYbfXwX8CR6lQJ2lTaYcfRriCBOMkTzGwb8VoIG8ti9dv9gJTSgSw"
+
+curl -H 'Accept: application/json' -H "Authorization: Bearer ${TOKEN}" http://localhost:8081/user
+```
diff --git a/documentation/mkdocs.yaml b/documentation/mkdocs.yaml
index d31f4e5..4d13c6f 100644
--- a/documentation/mkdocs.yaml
+++ b/documentation/mkdocs.yaml
@@ -48,6 +48,8 @@ markdown_extensions:
- attr_list
- def_list
- abbr
+ - markdown_include.include:
+ base_path: docs/
- pymdownx.snippets
extra_css:
@@ -78,4 +80,4 @@ extra:
link: https://github.com/code-specialist
name: Code Specialist on GitHub
-copyright: Copyright © 2021 Code Specialist | Legal Notice
\ No newline at end of file
+copyright: Copyright © 2021 Code Specialist | Legal Notice
diff --git a/requirements.txt b/requirements.txt
index bbc7997..ad62018 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -18,6 +18,7 @@ itsdangerous==2.0.1
Jinja2==3.0.3
Markdown==3.3.6
MarkupSafe==2.0.1
+markdown-include=0.7.0
mergedeep==1.3.4
mkdocs==1.2.3
mkdocs-autorefs==0.3.0
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..72e68ff
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,9 @@
+#!/usr/bin/env python
+
+# This has only been added to allow editable dev installs, pyproject.toml replaces setup.py
+# e.g. pip install -e .
+
+import setuptools
+
+if __name__ == "__main__":
+ setuptools.setup()