@@ -125,22 +125,78 @@ async def oauth_authorization_server(request: web.Request):
125
125
# this is where a client will redirect to during the auth flow.
126
126
# they'll see a webpage asking them to login
127
127
@routes .get ("/oauth/authorize" )
128
- async def oauth_authorize (request : web .Request ):
129
- # TODO: extract request_uri
128
+ async def oauth_authorize_get (request : web .Request ):
129
+ now = int (time .time ())
130
+ db = get_db (request )
131
+
132
+ session_token = request .cookies .get ("millipds-oauth-session" )
133
+ row = db .con .execute (
134
+ """
135
+ SELECT user_id FROM oauth_session_cookie
136
+ WHERE token=? AND expires_at>?
137
+ """ ,
138
+ (session_token , now ),
139
+ ).fetchone ()
140
+ if row is None :
141
+ # no active oauth cookie session
142
+ return web .HTTPTemporaryRedirect ("/oauth/authenticate" )
143
+
144
+ # if we reached here, either there was an existing session, or the user
145
+ # just created a new one and got redirected back again
146
+
147
+ did , handle = db .con .execute (
148
+ "SELECT did, handle FROM user WHERE id=?" , row
149
+ ).fetchone ()
150
+ # TODO: check id hint in auth request matches!
151
+
152
+ # TODO: look at the requested scopes, see if the user already granted them already,
153
+ # display UI as appropriate
154
+
155
+ return web .Response (
156
+ text = html_templates .authz_page (handle = handle ),
157
+ content_type = "text/html" ,
158
+ headers = WEBUI_HEADERS ,
159
+ )
160
+
161
+
162
+ @routes .post ("/oauth/authorize" )
163
+ async def oauth_authorize_post (request : web .Request ):
164
+ now = int (time .time ())
165
+ db = get_db (request )
166
+
167
+ session_token = request .cookies .get ("millipds-oauth-session" )
168
+ row = db .con .execute (
169
+ """
170
+ SELECT user_id FROM oauth_session_cookie
171
+ WHERE token=? AND expires_at>?
172
+ """ ,
173
+ (session_token , now ),
174
+ ).fetchone ()
175
+ if row is None :
176
+ # no active oauth cookie session
177
+ return web .HTTPTemporaryRedirect ("/oauth/authenticate" )
178
+
179
+ # TODO: redirect back to app?
180
+ return web .Response (
181
+ text = "TODO" ,
182
+ content_type = "text/html" ,
183
+ headers = WEBUI_HEADERS ,
184
+ )
185
+
186
+
187
+ @routes .get ("/oauth/authenticate" )
188
+ async def oauth_authenticate_get (request : web .Request ):
130
189
return web .Response (
131
190
text = html_templates .authn_page (
132
191
identifier_hint = "todo.invalid"
133
- ), # this includes a login form that POSTs to /oauth/authorize (i.e. same endpoint)
192
+ ), # this includes a login form that POSTs to the same endpoint
134
193
content_type = "text/html" ,
135
194
headers = WEBUI_HEADERS ,
136
195
)
137
196
138
197
139
- # after login, assuming the creds were good, the user will be prompted to
140
- # authorize the client application to access certain scopes
141
- @routes .post ("/oauth/authorize" )
142
- async def oauth_authorize_handle_login (request : web .Request ):
143
- # TODO: CSRF tokens?
198
+ @routes .post ("/oauth/authenticate" )
199
+ async def oauth_authenticate_post (request : web .Request ):
144
200
form = await request .post ()
145
201
logging .info (form )
146
202
@@ -170,22 +226,23 @@ async def oauth_authorize_handle_login(request: web.Request):
170
226
now + static_config .OAUTH_COOKIE_EXP ,
171
227
),
172
228
)
229
+ # we can't use a 301/302 redirect because we need to produce a GET
173
230
res = web .Response (
174
- text = html_templates .authz_page ( handle = handle ),
231
+ text = html_templates .redirect ( "/oauth/authorize" ),
175
232
content_type = "text/html" ,
176
233
headers = WEBUI_HEADERS ,
177
234
)
178
235
res .set_cookie (
179
- name = "millipds-session" ,
236
+ name = "millipds-oauth- session" ,
180
237
value = session_token ,
181
238
max_age = static_config .OAUTH_COOKIE_EXP ,
239
+ path = "/oauth/authorize" , # the only page that needs to see it
182
240
secure = True , # prevents token from leaking over plaintext channels
183
241
httponly = True , # prevents XSS from being able to steal tokens
184
242
samesite = "Strict" , # mitigates CSRF
185
243
)
186
244
return res
187
245
except :
188
- # TODO: error
189
246
return web .Response (
190
247
text = html_templates .authn_page (
191
248
identifier_hint = form_identifier ,
@@ -271,7 +328,7 @@ async def oauth_pushed_authorization_request(request: web.Request):
271
328
272
329
# TODO: request client metadata???
273
330
274
- # TODO: we need to store the request somewhere, and associate it with the URI we return
331
+ # TODO: we need to store the request somewhere, and associate it with the URI we return (and also the DPoP key)
275
332
276
333
return web .json_response (
277
334
{
0 commit comments