Skip to content

Commit 11cefe1

Browse files
committed
add apple sign in
1 parent caa1c5c commit 11cefe1

File tree

4 files changed

+216
-139
lines changed

4 files changed

+216
-139
lines changed

fasthtml/_modidx.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,12 @@
158158
'fasthtml.jupyter.wait_port_free': ('api/jupyter.html#wait_port_free', 'fasthtml/jupyter.py'),
159159
'fasthtml.jupyter.ws_client': ('api/jupyter.html#ws_client', 'fasthtml/jupyter.py')},
160160
'fasthtml.live_reload': {},
161-
'fasthtml.oauth': { 'fasthtml.oauth.Auth0AppClient': ('api/oauth.html#auth0appclient', 'fasthtml/oauth.py'),
161+
'fasthtml.oauth': { 'fasthtml.oauth.AppleAppClient': ('api/oauth.html#appleappclient', 'fasthtml/oauth.py'),
162+
'fasthtml.oauth.AppleAppClient.__init__': ('api/oauth.html#appleappclient.__init__', 'fasthtml/oauth.py'),
163+
'fasthtml.oauth.AppleAppClient.client_secret': ( 'api/oauth.html#appleappclient.client_secret',
164+
'fasthtml/oauth.py'),
165+
'fasthtml.oauth.AppleAppClient.get_info': ('api/oauth.html#appleappclient.get_info', 'fasthtml/oauth.py'),
166+
'fasthtml.oauth.Auth0AppClient': ('api/oauth.html#auth0appclient', 'fasthtml/oauth.py'),
162167
'fasthtml.oauth.Auth0AppClient.__init__': ('api/oauth.html#auth0appclient.__init__', 'fasthtml/oauth.py'),
163168
'fasthtml.oauth.Auth0AppClient._fetch_openid_config': ( 'api/oauth.html#auth0appclient._fetch_openid_config',
164169
'fasthtml/oauth.py'),

fasthtml/oauth.py

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

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

99
# %% ../nbs/api/08_oauth.ipynb
1010
from .common import *
@@ -112,6 +112,28 @@ def login_link(self, req):
112112
d = dict(response_type="code", client_id=self.client_id, scope=self.scope, redirect_uri=redir_url(req, self.redirect_uri))
113113
return f"{self.base_url}?{urlencode(d)}"
114114

115+
# %% ../nbs/api/08_oauth.ipynb
116+
class AppleAppClient(_AppClient):
117+
"A `WebApplicationClient` for Apple Sign In"
118+
base_url = "https://appleid.apple.com/auth/authorize"
119+
token_url = "https://appleid.apple.com/auth/token"
120+
121+
def __init__(self, client_id, key_id, team_id, private_key, code=None, scope=None, **kwargs):
122+
if not scope: scope = ["name", "email"]
123+
super().__init__(client_id, client_secret=None, code=code, scope=scope, **kwargs)
124+
self.key_id, self.team_id, self.private_key = key_id, team_id, private_key
125+
126+
@property
127+
def client_secret(self):
128+
now = int(time.time())
129+
payload = dict(iss=self.team_id, iat=now, exp=now + 86400 * 180, aud='https://appleid.apple.com', sub=self.client_id)
130+
return jwt.encode(payload, self.private_key, algorithm='ES256', headers={'kid': self.key_id})
131+
132+
def get_info(self, token=None):
133+
"Decode user info from the ID token"
134+
if token: self.token = token
135+
return jwt.decode(self.token.get('id_token'), options={"verify_signature": False})
136+
115137
# %% ../nbs/api/08_oauth.ipynb
116138
@patch
117139
def login_link(self:WebApplicationClient, redirect_uri, scope=None, state=None, **kwargs):
@@ -171,8 +193,9 @@ def url_match(request, patterns=http_patterns):
171193

172194
# %% ../nbs/api/08_oauth.ipynb
173195
class OAuth:
174-
def __init__(self, app, cli, skip=None, redir_path='/redirect', error_path='/error', logout_path='/logout', login_path='/login', https=True, http_patterns=http_patterns):
196+
def __init__(self, app, cli, skip=None, redir_path='/redirect', error_path='/error', logout_path='/logout', login_path='/login', https=True, http_patterns=http_patterns, redir_method='get'):
175197
if not skip: skip = [redir_path,error_path,login_path]
198+
redir_handler = app.post if redir_method == 'post' else app.get
176199
store_attr()
177200
def before(req, session):
178201
if 'auth' not in req.scope: req.scope['auth'] = session.get('auth')
@@ -182,7 +205,7 @@ def before(req, session):
182205
if res: return res
183206
app.before.append(Beforeware(before, skip=skip))
184207

185-
@app.get(redir_path)
208+
@redir_handler(redir_path)
186209
def redirect(req, session, code:str=None, error:str=None, state:str=None):
187210
if not code:
188211
session['oauth_error']=error

0 commit comments

Comments
 (0)