From b088ecee9bf580709aa64ae828575765ead1fabc Mon Sep 17 00:00:00 2001 From: Michael Robinson Date: Wed, 10 Aug 2022 12:48:00 +0100 Subject: [PATCH 1/3] Add setup.py to allow editable installs, e.g. pip install -e . --- setup.py | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 setup.py 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() From 7ff3d94104cb24aacf9b5e79c22a68f1c6b64b10 Mon Sep 17 00:00:00 2001 From: Michael Robinson Date: Wed, 10 Aug 2022 13:54:15 +0100 Subject: [PATCH 2/3] Move all examples to examples folder and include them inline with markdown-include --- .../examples/full_example/full_example.py | 242 +++++++++++++++++ .../docs/examples/introduction/app.py | 28 ++ documentation/docs/examples/quickstart/app.py | 44 ++++ .../examples/quickstart/docker-compose.yaml | 35 +++ .../quickstart}/realm-export.json | 0 documentation/docs/full_example.md | 245 +----------------- documentation/docs/index.md | 29 +-- documentation/docs/quick_start.md | 85 +----- documentation/mkdocs.yaml | 4 +- requirements.txt | 1 + 10 files changed, 360 insertions(+), 353 deletions(-) create mode 100644 documentation/docs/examples/full_example/full_example.py create mode 100644 documentation/docs/examples/introduction/app.py create mode 100644 documentation/docs/examples/quickstart/app.py create mode 100644 documentation/docs/examples/quickstart/docker-compose.yaml rename documentation/docs/{downloads => examples/quickstart}/realm-export.json (100%) 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..ecd54f9 100644 --- a/documentation/docs/quick_start.md +++ b/documentation/docs/quick_start.md @@ -7,44 +7,10 @@ In order to just get started, we prepared some containers and configs for you. **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 +32,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 +42,4 @@ 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 append it to the preceding requests as `Authorization` Bearer. 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 From c5e9cd2948f12dcea5019428d90a1c46fdd39887 Mon Sep 17 00:00:00 2001 From: Michael Robinson Date: Wed, 10 Aug 2022 16:24:52 +0100 Subject: [PATCH 3/3] Update docs to refer to examples dir, add example curl command to test the app with a bearer token --- documentation/docs/quick_start.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/documentation/docs/quick_start.md b/documentation/docs/quick_start.md index ecd54f9..808d687 100644 --- a/documentation/docs/quick_start.md +++ b/documentation/docs/quick_start.md @@ -2,6 +2,9 @@ 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** @@ -42,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. +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 +```