diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index 13566b81..00000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
-# Datasource local storage ignored files
-/dataSources/
-/dataSources.local.xml
diff --git a/.idea/bootstrap-python-fastapi.iml b/.idea/bootstrap-python-fastapi.iml
index 4e1b42f2..6a0f2b0a 100644
--- a/.idea/bootstrap-python-fastapi.iml
+++ b/.idea/bootstrap-python-fastapi.iml
@@ -4,15 +4,16 @@
+
-
+
-
-
-
-
-
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
index d52b407b..db9e40be 100644
--- a/.idea/dataSources.xml
+++ b/.idea/dataSources.xml
@@ -20,7 +20,7 @@
sqlite.xerial
true
org.sqlite.JDBC
- jdbc:sqlite:$PROJECT_DIR$/auth/kratos/db.sqlite
+ jdbc:sqlite:$PROJECT_DIR$/auth_volumes/kratos/db.sqlite
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
deleted file mode 100644
index 0c3737ba..00000000
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 00000000..105ce2da
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index bb0acce9..a72c6a13 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,14 +1,7 @@
-
-
-
-
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Tests.xml b/.idea/runConfigurations/Tests.xml
index a41a4bbc..92e69331 100644
--- a/.idea/runConfigurations/Tests.xml
+++ b/.idea/runConfigurations/Tests.xml
@@ -14,7 +14,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 94a25f7f..35eb1ddf 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/auth_volumes/kratos/identity.schema.json b/auth_volumes/kratos/identity.schema.json
index 1a137875..d71cc3f1 100644
--- a/auth_volumes/kratos/identity.schema.json
+++ b/auth_volumes/kratos/identity.schema.json
@@ -1,5 +1,4 @@
{
- "$id": "https://schemas.ory.sh/presets/kratos/quickstart/email-password/identity.schema.json",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Person",
"type": "object",
diff --git a/auth_volumes/kratos/kratos.yml b/auth_volumes/kratos/kratos.yml
index ad9d1f04..61099bc6 100644
--- a/auth_volumes/kratos/kratos.yml
+++ b/auth_volumes/kratos/kratos.yml
@@ -81,11 +81,18 @@ selfservice:
registration:
lifespan: 10m
ui_url: http://127.0.0.1:8080/registration
+
after:
password:
hooks:
+ - hook: web_hook
+ config:
+ url: http://dev:8000/user_registered/
+ method: "POST"
+ body: file:///etc/config/kratos/user_registered.jsonnet
+ can_interrupt: true
+ emit_analytics_event: true
- hook: session
-# - hook: show_verification_ui
log:
level: info
diff --git a/auth_volumes/kratos/user_registered.jsonnet b/auth_volumes/kratos/user_registered.jsonnet
new file mode 100644
index 00000000..8751729f
--- /dev/null
+++ b/auth_volumes/kratos/user_registered.jsonnet
@@ -0,0 +1,4 @@
+function(ctx) {
+ user_id: ctx.identity.id,
+ email: ctx.identity.traits.email,
+}
diff --git a/auth_volumes/oathkeeper/access-rules.yml b/auth_volumes/oathkeeper/access-rules.yml
index 34748da7..b5f4ef39 100644
--- a/auth_volumes/oathkeeper/access-rules.yml
+++ b/auth_volumes/oathkeeper/access-rules.yml
@@ -73,13 +73,14 @@
config:
to: http://127.0.0.1:8080/login
-# Dev container access to protected /hello endpoint
+# Dev container access to protected /api/* endpoints, to the dev container
- id: "http_app:protected"
upstream:
preserve_host: true
url: "http://dev:8000"
+ strip_path: /api
match:
- url: "http://127.0.0.1:8080/hello<{,/,/**}>"
+ url: "http://127.0.0.1:8080/<{api/,api/**,openapi.json}>"
methods:
- GET
authenticators:
diff --git a/auth_volumes/oathkeeper/oathkeeper.yml b/auth_volumes/oathkeeper/oathkeeper.yml
index 26137b63..20666392 100644
--- a/auth_volumes/oathkeeper/oathkeeper.yml
+++ b/auth_volumes/oathkeeper/oathkeeper.yml
@@ -96,5 +96,8 @@ mutators:
jwks_url: file:///etc/config/oathkeeper/id_token.jwks.json
claims: |
{
+ {{ if .MatchContext.Header.Get "x-impersonate" }}
+ "impersonate": {{ .MatchContext.Header.Get "x-impersonate" | toJson }},
+ {{ end }}
"session": {{ .Extra | toJson }}
}
diff --git a/docs/zero_trust.md b/docs/zero_trust.md
index f7decaa9..ad57a1d8 100644
--- a/docs/zero_trust.md
+++ b/docs/zero_trust.md
@@ -50,7 +50,7 @@ subgraph dn["Internal Docker Network (intranet)"]
OO-->|"Proxies /auth/login, /auth/registration, /dashboard, ... to"|SA
SA-->|Talks to|OK
OO-->|Validates auth sessions using|OK
- OO-->|"Proxies /hello to"|DEV
+ OO-->|"Proxies /api/* requests (authenticated only)"|DEV
OK[Ory Kratos]
OO["Reverse Proxy (Ory Oathkeeper)"]
SA["SecureApp (Ory Kratos SelfService UI Node Example)"]
diff --git a/src/http_app/routes/__init__.py b/src/http_app/routes/__init__.py
index 0ca50d64..f914c039 100644
--- a/src/http_app/routes/__init__.py
+++ b/src/http_app/routes/__init__.py
@@ -1,6 +1,6 @@
from fastapi import FastAPI
-from http_app.routes import api, events, graphql, hello, ping
+from http_app.routes import api, events, graphql, hello, ping, user_registered_hook
def init_routes(app: FastAPI) -> None:
@@ -8,4 +8,5 @@ def init_routes(app: FastAPI) -> None:
app.include_router(ping.router)
app.include_router(hello.router)
app.include_router(events.router)
+ app.include_router(user_registered_hook.router)
app.include_router(graphql.router, prefix="/graphql")
diff --git a/src/http_app/routes/user_registered_hook.py b/src/http_app/routes/user_registered_hook.py
new file mode 100644
index 00000000..3299918e
--- /dev/null
+++ b/src/http_app/routes/user_registered_hook.py
@@ -0,0 +1,64 @@
+import logging
+
+from fastapi import APIRouter, status
+from fastapi.responses import JSONResponse, Response
+from pydantic import BaseModel
+
+router = APIRouter(prefix="/user_registered")
+
+
+class UserRegisteredWebhook(BaseModel):
+ user_id: str
+ email: str
+
+
+@router.post("/")
+async def user_registered(user: UserRegisteredWebhook): # pragma: no cover
+ """
+ Handles the user registration webhook.
+
+ This function is triggered when a user registration webhook is received.
+ It logs the event details, evaluates the email validity, and returns an
+ appropriate HTTP response based on the validation. If the user's email
+ is invalid, it returns an error response along with a structured error
+ message. Otherwise, it confirms successful processing with no additional
+ content.
+
+ Args:
+ user (UserRegisteredWebhook): The webhook payload received when a user
+ registers, containing user details such as email and traits.
+
+ Returns:
+ Response: An HTTP response with a 403 Forbidden status and structured
+ error message if the user email is invalid.
+ Otherwise, an HTTP 204 No Content response to confirm successful
+ processing.
+ """
+ logging.info("User registered", extra={"user": user.model_dump()})
+
+ error_message = {
+ "messages": [
+ {
+ "instance_ptr": "#/traits/email",
+ "messages": [
+ {
+ "id": 123, # Error id to be evaluated in frontend
+ "text": "You are not allowed to register.",
+ "type": "error",
+ "context": { # Additional context we can send to the Frontend
+ "value": "short value",
+ "any": "additional information",
+ },
+ }
+ ],
+ }
+ ]
+ }
+
+ if user.email == "invalid@test.com":
+ return JSONResponse(
+ error_message,
+ status.HTTP_403_FORBIDDEN,
+ )
+ else:
+ return Response(status_code=status.HTTP_204_NO_CONTENT)