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
1010from .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
117139def 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
173195class 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