Skip to content

Commit 6d602ef

Browse files
committed
fixes #589
1 parent a6f92ad commit 6d602ef

File tree

3 files changed

+84
-27
lines changed

3 files changed

+84
-27
lines changed

fasthtml/_modidx.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,14 @@
131131
'fasthtml.jupyter.wait_port_free': ('api/jupyter.html#wait_port_free', 'fasthtml/jupyter.py'),
132132
'fasthtml.jupyter.ws_client': ('api/jupyter.html#ws_client', 'fasthtml/jupyter.py')},
133133
'fasthtml.live_reload': {},
134-
'fasthtml.oauth': { 'fasthtml.oauth.DiscordAppClient': ('api/oauth.html#discordappclient', 'fasthtml/oauth.py'),
134+
'fasthtml.oauth': { 'fasthtml.oauth.Auth0AppClient': ('api/oauth.html#auth0appclient', 'fasthtml/oauth.py'),
135+
'fasthtml.oauth.Auth0AppClient.__init__': ('api/oauth.html#auth0appclient.__init__', 'fasthtml/oauth.py'),
136+
'fasthtml.oauth.Auth0AppClient._fetch_openid_config': ( 'api/oauth.html#auth0appclient._fetch_openid_config',
137+
'fasthtml/oauth.py'),
138+
'fasthtml.oauth.Auth0AppClient.login_link': ( 'api/oauth.html#auth0appclient.login_link',
139+
'fasthtml/oauth.py'),
140+
'fasthtml.oauth.Auth0AppClient.redir_url': ('api/oauth.html#auth0appclient.redir_url', 'fasthtml/oauth.py'),
141+
'fasthtml.oauth.DiscordAppClient': ('api/oauth.html#discordappclient', 'fasthtml/oauth.py'),
135142
'fasthtml.oauth.DiscordAppClient.__init__': ( 'api/oauth.html#discordappclient.__init__',
136143
'fasthtml/oauth.py'),
137144
'fasthtml.oauth.DiscordAppClient.login_link': ( 'api/oauth.html#discordappclient.login_link',

fasthtml/oauth.py

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/api/08_oauth.ipynb.
44

55
# %% auto 0
6-
__all__ = ['http_patterns', 'GoogleAppClient', 'GitHubAppClient', 'HuggingFaceClient', 'DiscordAppClient', 'redir_url',
7-
'url_match', 'OAuth']
6+
__all__ = ['http_patterns', 'GoogleAppClient', 'GitHubAppClient', 'HuggingFaceClient', 'DiscordAppClient', 'Auth0AppClient',
7+
'redir_url', 'url_match', 'OAuth']
88

99
# %% ../nbs/api/08_oauth.ipynb
1010
from .common import *
@@ -14,6 +14,7 @@
1414

1515
# %% ../nbs/api/08_oauth.ipynb
1616
class _AppClient(WebApplicationClient):
17+
id_key = 'sub'
1718
def __init__(self, client_id, client_secret, code=None, scope=None, **kwargs):
1819
super().__init__(client_id, code=code, scope=scope, **kwargs)
1920
self.client_secret = client_secret
@@ -22,9 +23,8 @@ def __init__(self, client_id, client_secret, code=None, scope=None, **kwargs):
2223
class GoogleAppClient(_AppClient):
2324
"A `WebApplicationClient` for Google oauth2"
2425
base_url = "https://accounts.google.com/o/oauth2/v2/auth"
25-
token_url = "https://www.googleapis.com/oauth2/v4/token"
26-
info_url = "https://www.googleapis.com/oauth2/v3/userinfo"
27-
id_key = 'sub'
26+
token_url = "https://oauth2.googleapis.com/token"
27+
info_url = "https://openidconnect.googleapis.com/v1/userinfo"
2828

2929
def __init__(self, client_id, client_secret, code=None, scope=None, **kwargs):
3030
scope_pre = "https://www.googleapis.com/auth/userinfo"
@@ -39,8 +39,9 @@ def from_file(cls, fname, code=None, scope=None, **kwargs):
3939
# %% ../nbs/api/08_oauth.ipynb
4040
class GitHubAppClient(_AppClient):
4141
"A `WebApplicationClient` for GitHub oauth2"
42-
base_url = "https://github.com/login/oauth/authorize"
43-
token_url = "https://github.com/login/oauth/access_token"
42+
prefix = "https://github.com/login/oauth/"
43+
base_url = f"{prefix}authorize"
44+
token_url = f"{prefix}access_token"
4445
info_url = "https://api.github.com/user"
4546
id_key = 'id'
4647

@@ -50,11 +51,10 @@ def __init__(self, client_id, client_secret, code=None, scope=None, **kwargs):
5051
# %% ../nbs/api/08_oauth.ipynb
5152
class HuggingFaceClient(_AppClient):
5253
"A `WebApplicationClient` for HuggingFace oauth2"
53-
54-
base_url = "https://huggingface.co/oauth/authorize"
55-
token_url = "https://huggingface.co/oauth/token"
56-
info_url = "https://huggingface.co/oauth/userinfo"
57-
id_key = 'sub'
54+
prefix = "https://huggingface.co/oauth/"
55+
base_url = f"{prefix}authorize"
56+
token_url = f"{prefix}token"
57+
info_url = f"{prefix}userinfo"
5858

5959
def __init__(self, client_id, client_secret, code=None, scope=None, state=None, **kwargs):
6060
if not scope: scope=["openid","profile"]
@@ -87,6 +87,26 @@ def parse_response(self, code):
8787
r.raise_for_status()
8888
self.parse_request_body_response(r.text)
8989

90+
# %% ../nbs/api/08_oauth.ipynb
91+
class Auth0AppClient(_AppClient):
92+
"A `WebApplicationClient` for Auth0 OAuth2"
93+
def __init__(self, domain, client_id, client_secret, code=None, scope=None, https=True, redirect_uri="", **kwargs):
94+
self.redirect_uri,self.https,self.domain = redirect_uri,https,domain
95+
config = self._fetch_openid_config()
96+
self.base_url,self.token_url,self.info_url = config["authorization_endpoint"],config["token_endpoint"],config["userinfo_endpoint"]
97+
super().__init__(client_id, client_secret, code=code, scope=scope, https=https, redirect_uri=redirect_uri, **kwargs)
98+
99+
def _fetch_openid_config(self):
100+
r = httpx.get(f"https://{self.domain}/.well-known/openid-configuration")
101+
r.raise_for_status()
102+
return r.json()
103+
104+
def redir_url(self, req): return redir_url(req, self.redirect_uri, "https" if self.https else "http")
105+
106+
def login_link(self, req):
107+
d = dict(response_type="code", client_id=self.client_id, scope=self.scope, redirect_uri=self.redir_url(req))
108+
return f"{self.base_url}?{urlencode(d)}"
109+
90110
# %% ../nbs/api/08_oauth.ipynb
91111
@patch
92112
def login_link(self:WebApplicationClient, redirect_uri, scope=None, state=None):
@@ -96,8 +116,9 @@ def login_link(self:WebApplicationClient, redirect_uri, scope=None, state=None):
96116
return self.prepare_request_uri(self.base_url, redirect_uri, scope, state=state)
97117

98118
# %% ../nbs/api/08_oauth.ipynb
99-
def redir_url(request, redir_path, scheme='https'):
119+
def redir_url(request, redir_path, scheme=None):
100120
"Get the redir url for the host in `request`"
121+
scheme = 'http' if request.url.hostname == 'localhost' else 'https'
101122
return f"{scheme}://{request.url.netloc}{redir_path}"
102123

103124
# %% ../nbs/api/08_oauth.ipynb

nbs/api/08_oauth.ipynb

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"id": "507cd009",
2828
"metadata": {},
2929
"source": [
30-
"This is not yet thoroughly tested. See the [docs page](https://docs.fastht.ml/explains/oauth.html) for an explanation of how to use this."
30+
"See the [docs page](https://docs.fastht.ml/explains/oauth.html) for an explanation of how to use this."
3131
]
3232
},
3333
{
@@ -63,6 +63,7 @@
6363
"source": [
6464
"#| export\n",
6565
"class _AppClient(WebApplicationClient):\n",
66+
" id_key = 'sub'\n",
6667
" def __init__(self, client_id, client_secret, code=None, scope=None, **kwargs):\n",
6768
" super().__init__(client_id, code=code, scope=scope, **kwargs)\n",
6869
" self.client_secret = client_secret"
@@ -79,9 +80,8 @@
7980
"class GoogleAppClient(_AppClient):\n",
8081
" \"A `WebApplicationClient` for Google oauth2\"\n",
8182
" base_url = \"https://accounts.google.com/o/oauth2/v2/auth\"\n",
82-
" token_url = \"https://www.googleapis.com/oauth2/v4/token\"\n",
83-
" info_url = \"https://www.googleapis.com/oauth2/v3/userinfo\"\n",
84-
" id_key = 'sub'\n",
83+
" token_url = \"https://oauth2.googleapis.com/token\"\n",
84+
" info_url = \"https://openidconnect.googleapis.com/v1/userinfo\"\n",
8585
" \n",
8686
" def __init__(self, client_id, client_secret, code=None, scope=None, **kwargs):\n",
8787
" scope_pre = \"https://www.googleapis.com/auth/userinfo\"\n",
@@ -104,8 +104,9 @@
104104
"#| export\n",
105105
"class GitHubAppClient(_AppClient):\n",
106106
" \"A `WebApplicationClient` for GitHub oauth2\"\n",
107-
" base_url = \"https://github.com/login/oauth/authorize\"\n",
108-
" token_url = \"https://github.com/login/oauth/access_token\"\n",
107+
" prefix = \"https://github.com/login/oauth/\"\n",
108+
" base_url = f\"{prefix}authorize\"\n",
109+
" token_url = f\"{prefix}access_token\"\n",
109110
" info_url = \"https://api.github.com/user\"\n",
110111
" id_key = 'id'\n",
111112
"\n",
@@ -123,11 +124,10 @@
123124
"#| export\n",
124125
"class HuggingFaceClient(_AppClient):\n",
125126
" \"A `WebApplicationClient` for HuggingFace oauth2\"\n",
126-
"\n",
127-
" base_url = \"https://huggingface.co/oauth/authorize\"\n",
128-
" token_url = \"https://huggingface.co/oauth/token\"\n",
129-
" info_url = \"https://huggingface.co/oauth/userinfo\"\n",
130-
" id_key = 'sub'\n",
127+
" prefix = \"https://huggingface.co/oauth/\"\n",
128+
" base_url = f\"{prefix}authorize\"\n",
129+
" token_url = f\"{prefix}token\"\n",
130+
" info_url = f\"{prefix}userinfo\"\n",
131131
" \n",
132132
" def __init__(self, client_id, client_secret, code=None, scope=None, state=None, **kwargs):\n",
133133
" if not scope: scope=[\"openid\",\"profile\"]\n",
@@ -169,14 +169,42 @@
169169
" self.parse_request_body_response(r.text)"
170170
]
171171
},
172+
{
173+
"cell_type": "code",
174+
"execution_count": null,
175+
"id": "29521d68",
176+
"metadata": {},
177+
"outputs": [],
178+
"source": [
179+
"#| export\n",
180+
"class Auth0AppClient(_AppClient):\n",
181+
" \"A `WebApplicationClient` for Auth0 OAuth2\"\n",
182+
" def __init__(self, domain, client_id, client_secret, code=None, scope=None, https=True, redirect_uri=\"\", **kwargs):\n",
183+
" self.redirect_uri,self.https,self.domain = redirect_uri,https,domain\n",
184+
" config = self._fetch_openid_config()\n",
185+
" self.base_url,self.token_url,self.info_url = config[\"authorization_endpoint\"],config[\"token_endpoint\"],config[\"userinfo_endpoint\"]\n",
186+
" super().__init__(client_id, client_secret, code=code, scope=scope, https=https, redirect_uri=redirect_uri, **kwargs)\n",
187+
"\n",
188+
" def _fetch_openid_config(self):\n",
189+
" r = httpx.get(f\"https://{self.domain}/.well-known/openid-configuration\")\n",
190+
" r.raise_for_status()\n",
191+
" return r.json()\n",
192+
"\n",
193+
" def redir_url(self, req): return redir_url(req, self.redirect_uri, \"https\" if self.https else \"http\")\n",
194+
"\n",
195+
" def login_link(self, req):\n",
196+
" d = dict(response_type=\"code\", client_id=self.client_id, scope=self.scope, redirect_uri=self.redir_url(req))\n",
197+
" return f\"{self.base_url}?{urlencode(d)}\""
198+
]
199+
},
172200
{
173201
"cell_type": "code",
174202
"execution_count": null,
175203
"id": "109bc501",
176204
"metadata": {},
177205
"outputs": [],
178206
"source": [
179-
"cli = GoogleAppClient.from_file('/Users/jhoward/git/_nbs/oauth-test/client_secret.json')"
207+
"cli = GoogleAppClient.from_file('/Users/jhoward/subs_aai/_nbs/oauth-test/client_secret.json')"
180208
]
181209
},
182210
{
@@ -267,8 +295,9 @@
267295
"outputs": [],
268296
"source": [
269297
"#| export\n",
270-
"def redir_url(request, redir_path, scheme='https'):\n",
298+
"def redir_url(request, redir_path, scheme=None):\n",
271299
" \"Get the redir url for the host in `request`\"\n",
300+
" scheme = 'http' if request.url.hostname == 'localhost' else 'https'\n",
272301
" return f\"{scheme}://{request.url.netloc}{redir_path}\""
273302
]
274303
},

0 commit comments

Comments
 (0)