fix: add hooks to handle cors
This commit is contained in:
parent
e76c1830e1
commit
db4a7504e5
6 changed files with 101 additions and 14 deletions
|
|
@ -275,10 +275,12 @@ def process_response(response: Response):
|
|||
|
||||
|
||||
def set_cors_headers(response):
|
||||
allowed_origins = frappe.conf.allow_cors
|
||||
if hasattr(frappe.local, "allow_cors"):
|
||||
allowed_origins = frappe.local.allow_cors
|
||||
|
||||
if not (
|
||||
(allowed_origins := frappe.conf.allow_cors)
|
||||
and (request := frappe.local.request)
|
||||
and (origin := request.headers.get("Origin"))
|
||||
allowed_origins and (request := frappe.local.request) and (origin := request.headers.get("Origin"))
|
||||
):
|
||||
return
|
||||
|
||||
|
|
|
|||
|
|
@ -413,6 +413,7 @@ before_request = [
|
|||
"frappe.recorder.record",
|
||||
"frappe.monitor.start",
|
||||
"frappe.rate_limiter.apply",
|
||||
"frappe.integrations.oauth2.set_cors_for_privileged_requests",
|
||||
]
|
||||
|
||||
after_request = [
|
||||
|
|
|
|||
|
|
@ -6,15 +6,21 @@
|
|||
"doctype": "DocType",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"authorization_tab",
|
||||
"authorization_server_section",
|
||||
"show_auth_server_metadata",
|
||||
"enable_dynamic_client_registration",
|
||||
"skip_authorization",
|
||||
"column_break_ogmd",
|
||||
"enable_dynamic_client_registration",
|
||||
"allowed_origins_for_public_client_registration",
|
||||
"resource_tab",
|
||||
"config_section",
|
||||
"show_protected_resource_metadata",
|
||||
"column_break_wlfj",
|
||||
"show_social_login_key_as_authorization_server",
|
||||
"resource_server_section",
|
||||
"resource_name",
|
||||
"resource_policy_uri",
|
||||
"show_protected_resource_metadata",
|
||||
"show_social_login_key_as_authorization_server",
|
||||
"column_break_zyte",
|
||||
"resource_documentation",
|
||||
"resource_tos_uri",
|
||||
|
|
@ -22,10 +28,10 @@
|
|||
],
|
||||
"fields": [
|
||||
{
|
||||
"description": "These fields are used to provide resource server metadata to clients querying the \"/.well-known/oauth-protected-resource\" end point. For additional reference view: https://datatracker.ietf.org/doc/html/rfc9728",
|
||||
"description": "These fields are used to provide resource server metadata to clients querying the \"well known protected resource\" end point.",
|
||||
"fieldname": "resource_server_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Resource Server"
|
||||
"label": "Metadata"
|
||||
},
|
||||
{
|
||||
"default": "Frappe Framework Application",
|
||||
|
|
@ -59,8 +65,7 @@
|
|||
},
|
||||
{
|
||||
"fieldname": "authorization_server_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Authorization Server"
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
|
|
@ -102,13 +107,42 @@
|
|||
"fieldname": "show_social_login_key_as_authorization_server",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Social Login Key as Authorization Server"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_ogmd",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "authorization_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Authorization"
|
||||
},
|
||||
{
|
||||
"fieldname": "resource_tab",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Resource"
|
||||
},
|
||||
{
|
||||
"fieldname": "config_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Config"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_wlfj",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"description": "New line separated list of allowed public client URLs (eg <code>https://frappe.io</code>), or <code>*</code> to accept all.\n<br>\nPublic clients are restricted by default.",
|
||||
"fieldname": "allowed_origins_for_public_client_registration",
|
||||
"fieldtype": "Small Text",
|
||||
"label": "Allowed Origins for Public Client Registration"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2025-07-04 11:20:15.528611",
|
||||
"modified": "2025-07-04 12:05:50.723018",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Integrations",
|
||||
"name": "OAuth Settings",
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ class OAuthSettings(Document):
|
|||
if TYPE_CHECKING:
|
||||
from frappe.types import DF
|
||||
|
||||
allowed_origins_for_public_client_registration: DF.SmallText | None
|
||||
enable_dynamic_client_registration: DF.Check
|
||||
resource_documentation: DF.Data | None
|
||||
resource_name: DF.Data | None
|
||||
|
|
|
|||
|
|
@ -258,7 +258,7 @@ def introspect_token(token=None, token_type_hint=None):
|
|||
|
||||
|
||||
def handle_wellknown(path: str):
|
||||
"""Path handler for /.well-known/ endpoints. Invoked in app.py"""
|
||||
"""Path handler for GET requests to /.well-known/ endpoints. Invoked in app.py"""
|
||||
|
||||
if path.startswith("/.well-known/openid-configuration"):
|
||||
return get_openid_configuration()
|
||||
|
|
@ -284,6 +284,7 @@ def get_authorization_server_metadata():
|
|||
response = Response()
|
||||
response.mimetype = "application/json"
|
||||
response.data = frappe.as_json(_get_authorization_server_metadata())
|
||||
frappe.local.allow_cors = "*"
|
||||
return response
|
||||
|
||||
|
||||
|
|
@ -321,7 +322,7 @@ def _get_authorization_server_metadata():
|
|||
return metadata
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
@frappe.whitelist(allow_guest=True, methods=["POST"])
|
||||
def register_client():
|
||||
"""
|
||||
Registers an OAuth client.
|
||||
|
|
@ -473,3 +474,51 @@ def _del_none_values(d: dict):
|
|||
for k in list(d.keys()):
|
||||
if k in d and d[k] is None:
|
||||
del d[k]
|
||||
|
||||
|
||||
def set_cors_for_privileged_requests():
|
||||
"""
|
||||
Called in before_request hook, prevents failure of privileged requests,
|
||||
for OPTIONS and:
|
||||
1. GET requests on /.well-known/
|
||||
2. POST requests on /api/method/frappe.integrations.oauth2.register_client
|
||||
|
||||
Point 2. also depends on OAuth Settings for dynamic client registration.
|
||||
Without these, registration requests from public clients will fail due to
|
||||
preflight requests failing.
|
||||
"""
|
||||
if (
|
||||
frappe.conf.allow_cors == "*"
|
||||
or not frappe.local.request
|
||||
or not frappe.local.request.headers.get("Origin")
|
||||
):
|
||||
return
|
||||
|
||||
if frappe.request.path.startswith("/.well-known/") and frappe.request.method in ("GET", "OPTIONS"):
|
||||
frappe.local.allow_cors = "*"
|
||||
return
|
||||
|
||||
if (
|
||||
not frappe.request.path.startswith("/api/method/frappe.integrations.oauth2.register_client")
|
||||
or frappe.request.method not in ("POST", "OPTIONS")
|
||||
or not frappe.get_cached_value(
|
||||
"OAuth Settings",
|
||||
"OAuth Settings",
|
||||
"enable_dynamic_client_registration",
|
||||
)
|
||||
):
|
||||
return
|
||||
|
||||
allowed = frappe.get_cached_value(
|
||||
"OAuth Settings",
|
||||
"OAuth Settings",
|
||||
"allowed_origins_for_public_client_registration",
|
||||
)
|
||||
if not allowed:
|
||||
return
|
||||
|
||||
allowed = allowed.strip().splitlines()
|
||||
if "*" in allowed:
|
||||
frappe.local.allow_cors = "*"
|
||||
else:
|
||||
frappe.local.allow_cors = allowed
|
||||
|
|
|
|||
|
|
@ -252,7 +252,7 @@ def create_new_oauth_client(client: OAuth2DynamicClientMetadata):
|
|||
if client.software_version:
|
||||
doc.software_version = client.software_version
|
||||
|
||||
doc.save()
|
||||
doc.save(ignore_permissions=True)
|
||||
return doc
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue