|
301 | 301 | "server = JupyUvi(app, port=port)"
|
302 | 302 | ]
|
303 | 303 | },
|
| 304 | + { |
| 305 | + "cell_type": "code", |
| 306 | + "execution_count": null, |
| 307 | + "id": "551454f9", |
| 308 | + "metadata": {}, |
| 309 | + "outputs": [], |
| 310 | + "source": [ |
| 311 | + "#|export\n", |
| 312 | + "def get_host(request):\n", |
| 313 | + " \"\"\"Get the host, preferring X-Forwarded-Host if available\"\"\"\n", |
| 314 | + " forwarded_host = request.headers.get('x-forwarded-host')\n", |
| 315 | + " return forwarded_host if forwarded_host else request.url.netloc" |
| 316 | + ] |
| 317 | + }, |
| 318 | + { |
| 319 | + "cell_type": "code", |
| 320 | + "execution_count": null, |
| 321 | + "id": "3cc9b978", |
| 322 | + "metadata": {}, |
| 323 | + "outputs": [ |
| 324 | + { |
| 325 | + "name": "stdout", |
| 326 | + "output_type": "stream", |
| 327 | + "text": [ |
| 328 | + "Without X-Forwarded-Host: localhost:8000\n", |
| 329 | + "With X-Forwarded-Host: example.com\n" |
| 330 | + ] |
| 331 | + } |
| 332 | + ], |
| 333 | + "source": [ |
| 334 | + "from types import SimpleNamespace\n", |
| 335 | + "from urllib.parse import urlparse\n", |
| 336 | + "\n", |
| 337 | + "mock_request_localhost = SimpleNamespace(headers={}, url=SimpleNamespace(netloc='localhost:8000'))\n", |
| 338 | + "mock_request_with_forward = SimpleNamespace(\n", |
| 339 | + " headers={'x-forwarded-host': 'example.com'}, \n", |
| 340 | + " url=SimpleNamespace(netloc='localhost:8000', hostname='localhost')\n", |
| 341 | + ")\n", |
| 342 | + "\n", |
| 343 | + "print(\"Without X-Forwarded-Host:\", get_host(mock_request_localhost))\n", |
| 344 | + "print(\"With X-Forwarded-Host:\", get_host(mock_request_with_forward))" |
| 345 | + ] |
| 346 | + }, |
304 | 347 | {
|
305 | 348 | "cell_type": "code",
|
306 | 349 | "execution_count": null,
|
|
309 | 352 | "outputs": [],
|
310 | 353 | "source": [
|
311 | 354 | "#| export\n",
|
312 |
| - "def redir_url(request, redir_path, scheme=None):\n", |
| 355 | + "def redir_url(req, redir_path, scheme=None):\n", |
313 | 356 | " \"Get the redir url for the host in `request`\"\n",
|
314 |
| - " scheme = 'http' if request.url.hostname in (\"localhost\", \"127.0.0.1\") else 'https'\n", |
315 |
| - " return f\"{scheme}://{request.url.netloc}{redir_path}\"" |
| 357 | + " host = get_host(req)\n", |
| 358 | + " scheme = 'http' if host.split(':')[0] in (\"localhost\", \"127.0.0.1\") else 'https'\n", |
| 359 | + " return f\"{scheme}://{host}{redir_path}\"" |
316 | 360 | ]
|
317 | 361 | },
|
318 | 362 | {
|
|
447 | 491 | "source": [
|
448 | 492 | "#| export\n",
|
449 | 493 | "http_patterns = (r'^(localhost|127\\.0\\.0\\.1)(:\\d+)?$',)\n",
|
450 |
| - "def url_match(url, patterns=http_patterns):\n", |
451 |
| - " return any(re.match(pattern, url.netloc.split(':')[0]) for pattern in patterns)" |
| 494 | + "def url_match(request, patterns=http_patterns):\n", |
| 495 | + " return any(re.match(pattern, get_host(request).split(':')[0]) for pattern in patterns)" |
| 496 | + ] |
| 497 | + }, |
| 498 | + { |
| 499 | + "cell_type": "code", |
| 500 | + "execution_count": null, |
| 501 | + "id": "fee2db6c", |
| 502 | + "metadata": {}, |
| 503 | + "outputs": [ |
| 504 | + { |
| 505 | + "name": "stdout", |
| 506 | + "output_type": "stream", |
| 507 | + "text": [ |
| 508 | + "Localhost: http://localhost:8000/redirect\n", |
| 509 | + "With X-Forwarded-Host: https://example.com/redirect\n", |
| 510 | + "Production: https://myapp.com/redirect\n" |
| 511 | + ] |
| 512 | + } |
| 513 | + ], |
| 514 | + "source": [ |
| 515 | + "from types import SimpleNamespace\n", |
| 516 | + "from urllib.parse import urlparse\n", |
| 517 | + "\n", |
| 518 | + "mock_request_prod = SimpleNamespace(headers={}, url=SimpleNamespace(netloc='myapp.com', hostname='myapp.com'))\n", |
| 519 | + "\n", |
| 520 | + "print(\"Localhost:\", redir_url(mock_request_localhost, '/redirect'))\n", |
| 521 | + "print(\"With X-Forwarded-Host:\", redir_url(mock_request_with_forward, '/redirect'))\n", |
| 522 | + "print(\"Production:\", redir_url(mock_request_prod, '/redirect'))" |
452 | 523 | ]
|
453 | 524 | },
|
454 | 525 | {
|
|
474 | 545 | " @app.get(redir_path)\n",
|
475 | 546 | " def redirect(req, session, code:str=None, error:str=None, state:str=None):\n",
|
476 | 547 | " if not code: session['oauth_error']=error; return RedirectResponse(self.error_path, status_code=303)\n",
|
477 |
| - " scheme = 'http' if url_match(req.url,self.http_patterns) or not self.https else 'https'\n", |
478 |
| - " base_url = f\"{scheme}://{req.url.netloc}\"\n", |
| 548 | + " scheme = 'http' if url_match(req,self.http_patterns) or not self.https else 'https'\n", |
| 549 | + " base_url = f\"{scheme}://{get_host(req)}\"\n", |
479 | 550 | " info = AttrDictDefault(cli.retr_info(code, base_url+redir_path))\n",
|
480 | 551 | " ident = info.get(self.cli.id_key)\n",
|
481 | 552 | " if not ident: return self.redir_login(session)\n",
|
|
551 | 622 | "text/markdown": [
|
552 | 623 | "---\n",
|
553 | 624 | "\n",
|
| 625 | + "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/oauth.py#L223){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 626 | + "\n", |
554 | 627 | "### GoogleAppClient.consent_url\n",
|
555 | 628 | "\n",
|
556 | 629 | "> GoogleAppClient.consent_url (proj=None)\n",
|
|
560 | 633 | "text/plain": [
|
561 | 634 | "---\n",
|
562 | 635 | "\n",
|
| 636 | + "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/oauth.py#L223){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 637 | + "\n", |
563 | 638 | "### GoogleAppClient.consent_url\n",
|
564 | 639 | "\n",
|
565 | 640 | "> GoogleAppClient.consent_url (proj=None)\n",
|
|
602 | 677 | "text/markdown": [
|
603 | 678 | "---\n",
|
604 | 679 | "\n",
|
| 680 | + "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/oauth.py#L231){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 681 | + "\n", |
605 | 682 | "### Credentials.update\n",
|
606 | 683 | "\n",
|
607 | 684 | "> Credentials.update ()\n",
|
|
611 | 688 | "text/plain": [
|
612 | 689 | "---\n",
|
613 | 690 | "\n",
|
| 691 | + "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/oauth.py#L231){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 692 | + "\n", |
614 | 693 | "### Credentials.update\n",
|
615 | 694 | "\n",
|
616 | 695 | "> Credentials.update ()\n",
|
|
652 | 731 | "text/markdown": [
|
653 | 732 | "---\n",
|
654 | 733 | "\n",
|
| 734 | + "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/oauth.py#L238){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 735 | + "\n", |
655 | 736 | "### Credentials.save\n",
|
656 | 737 | "\n",
|
657 | 738 | "> Credentials.save (fname)\n",
|
|
661 | 742 | "text/plain": [
|
662 | 743 | "---\n",
|
663 | 744 | "\n",
|
| 745 | + "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/oauth.py#L238){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 746 | + "\n", |
664 | 747 | "### Credentials.save\n",
|
665 | 748 | "\n",
|
666 | 749 | "> Credentials.save (fname)\n",
|
|
717 | 800 | "text/markdown": [
|
718 | 801 | "---\n",
|
719 | 802 | "\n",
|
| 803 | + "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/oauth.py#L249){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 804 | + "\n", |
720 | 805 | "### GoogleAppClient.creds\n",
|
721 | 806 | "\n",
|
722 | 807 | "> GoogleAppClient.creds ()\n",
|
|
726 | 811 | "text/plain": [
|
727 | 812 | "---\n",
|
728 | 813 | "\n",
|
| 814 | + "[source](https://github.com/AnswerDotAI/fasthtml/blob/main/fasthtml/oauth.py#L249){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 815 | + "\n", |
729 | 816 | "### GoogleAppClient.creds\n",
|
730 | 817 | "\n",
|
731 | 818 | "> GoogleAppClient.creds ()\n",
|
|
0 commit comments