tangled
alpha
login
or
join now
futur.blue
/
pegasus
57
fork
atom
objective categorical abstract machine language personal data server
57
fork
atom
overview
issues
2
pulls
pipelines
Use more accurate auth error names
futur.blue
2 months ago
c43ec295
7e1604c6
verified
This commit was signed with the committer's
known signature
.
futur.blue
SSH Key Fingerprint:
SHA256:QHGqHWNpqYyw9bt8KmPuJIyeZX9SZewBZ0PR1COtKQ0=
+77
-59
1 changed file
expand all
collapse all
unified
split
pegasus
lib
auth.ml
+77
-59
pegasus/lib/auth.ml
···
34
else if jwt.sub = "" then Lwt.return_error "missing sub"
35
else if now_s < jwt.iat then
36
Lwt.return_error "token issued in the future"
37
-
else if now_s > jwt.exp then Lwt.return_error "expired token"
38
else if jwt.scope <> expected_scope then
39
Lwt.return_error "invalid scope"
40
else if jwt.jti = "" then Lwt.return_error "missing jti"
···
42
let%lwt revoked_at =
43
Data_store.is_token_revoked t ~did:jwt.sub ~jti:jwt.jti
44
in
45
-
if revoked_at <> None then Lwt.return_error "token revoked"
46
else Lwt.return_ok jwt
47
with _ -> Lwt.return_error "invalid token format" )
48
···
219
| "admin", p when p = Env.admin_password ->
220
Lwt.return_ok Admin
221
| _ ->
222
-
Lwt.return_error @@ Errors.auth_required "invalid credentials" )
223
| Error _ ->
224
Lwt.return_error @@ Errors.auth_required "invalid authorization header"
225
···
234
Lwt.return_ok (Access {did})
235
| Some {deactivated_at= Some _; _} ->
236
Lwt.return_error
237
-
@@ Errors.auth_required ~name:"AccountDeactivated"
238
"account is deactivated"
239
| None ->
240
-
Lwt.return_error @@ Errors.auth_required "invalid credentials" )
0
0
0
0
241
| Error _ ->
242
-
Lwt.return_error @@ Errors.auth_required "invalid credentials" )
0
243
| Error _ ->
244
Lwt.return_error @@ Errors.auth_required "invalid authorization header"
245
···
297
let scopes = Oauth.Scopes.of_string scope_str in
298
let now = int_of_float (Unix.gettimeofday ()) in
299
if jkt_claim <> proof.jkt then
300
-
Lwt.return_error @@ Errors.auth_required "dpop key mismatch"
301
else if exp < now then
302
-
Lwt.return_error @@ Errors.auth_required "token expired"
0
303
else if not (Oauth.Scopes.has_atproto scopes) then
304
Lwt.return_error
305
-
@@ Errors.auth_required ~name:"InvalidToken"
306
"oauth token missing 'atproto' scope"
307
else
308
let%lwt session =
···
311
Lwt.return_ok sess
312
with _ ->
313
Lwt.return_error
314
-
@@ Errors.auth_required "invalid credentials"
315
in
316
match session with
317
| Ok {active= Some true; _} ->
318
Lwt.return_ok (OAuth {did; proof; scopes})
319
| Ok _ ->
320
Lwt.return_error
321
-
@@ Errors.auth_required ~name:"AccountDeactivated"
322
"account is deactivated"
323
| Error _ ->
324
Lwt.return_error
325
-
@@ Errors.auth_required "invalid credentials"
326
with _ ->
327
Lwt.return_error @@ Errors.auth_required "malformed JWT claims" )
328
) )
···
341
raise (Errors.Redirect "/account")
342
| Some {deactivated_at= Some _; _} ->
343
Lwt.return_error
344
-
@@ Errors.auth_required ~name:"AccountDeactivated"
345
"account is deactivated"
346
| None ->
347
let%lwt () = Session.Raw.clear_session req in
348
-
Lwt.return_error @@ Errors.auth_required "no active session" )
0
349
| None ->
350
Lwt.return_error @@ Errors.auth_required "no active session"
351
···
360
Lwt.return_ok (Refresh {did; jti})
361
| Some {deactivated_at= Some _; _} ->
362
Lwt.return_error
363
-
@@ Errors.auth_required ~name:"AccountDeactivated"
364
"account is deactivated"
365
| None ->
366
-
Lwt.return_error @@ Errors.auth_required "invalid credentials" )
367
-
| Error "" | Error _ ->
368
-
Lwt.return_error @@ Errors.auth_required "invalid credentials" )
0
0
0
0
0
369
| Error _ ->
370
Lwt.return_error @@ Errors.auth_required "invalid authorization header"
371
···
376
Lwt.return_ok (Access {did})
377
| Some {deactivated_at= Some _; _} ->
378
Lwt.return_error
379
-
@@ Errors.auth_required ~name:"AccountDeactivated"
380
"account is deactivated"
381
| None ->
382
-
Lwt.return_error @@ Errors.auth_required "invalid credentials"
0
383
in
384
let verify_with_key token pubkey_multibase did db =
385
-
let pubkey =
386
-
Kleidos.parse_multikey_str pubkey_multibase
387
-
in
388
match Jwt.verify_jwt token ~pubkey with
389
| Ok _ ->
390
check_actor_status did db
391
| Error e ->
392
Dream.debug (fun log -> log "service jwt verification failed: %s" e) ;
393
Lwt.return_error
394
-
@@ Errors.auth_required "jwt signature does not match jwt issuer"
0
395
in
396
fun {req; db} ->
397
match parse_bearer req with
···
410
let lxm = payload |> member "lxm" |> to_string_option in
411
let now = int_of_float (Unix.gettimeofday ()) in
412
if exp < now then
413
-
Lwt.return_error @@ Errors.auth_required "jwt expired"
0
414
else if aud <> Env.did then
415
Lwt.return_error
416
-
@@ Errors.auth_required "jwt audience does not match service did"
0
417
else
418
let nsid =
419
(Dream.path [@warning "-3"]) req |> List.rev |> List.hd
420
in
421
-
( match lxm with
422
| Some l when l <> nsid && l <> "*" ->
423
Lwt.return_error
424
-
@@ Errors.auth_required
425
("jwt lxm " ^ l ^ " does not match " ^ nsid)
426
-
| _ ->
427
let did =
428
match String.split_on_char '#' iss with
429
| did :: _ ->
···
431
| [] ->
432
iss
433
in
434
-
( match%lwt Id_resolver.Did.resolve did with
435
| Error e ->
436
Dream.debug (fun log ->
437
log "failed to resolve did %s: %s" did e ) ;
438
Lwt.return_error
439
-
@@ Errors.auth_required "could not resolve issuer did"
0
440
| Ok did_doc -> (
441
match
442
Id_resolver.Did.Document.get_verification_key did_doc
···
444
with
445
| None ->
446
Lwt.return_error
447
-
@@ Errors.auth_required
448
-
"missing or bad key in issuer did doc"
449
| Some pubkey_multibase -> (
450
-
match%lwt verify_with_key token pubkey_multibase did db with
0
0
451
| Ok creds ->
452
Lwt.return_ok creds
453
-
| Error _ ->
454
-
(* try again, skipping cache in case of key rotation *)
455
-
( match%lwt
456
-
Id_resolver.Did.resolve ~skip_cache:true did
457
-
with
458
-
| Error _ ->
0
0
0
0
0
0
0
0
0
459
Lwt.return_error
460
-
@@ Errors.auth_required
461
"jwt signature does not match jwt issuer"
462
-
| Ok fresh_doc -> (
463
-
match
464
-
Id_resolver.Did.Document.get_verification_key
465
-
fresh_doc "#atproto"
466
-
with
467
-
| None ->
468
-
Lwt.return_error
469
-
@@ Errors.auth_required
470
-
"jwt signature does not match jwt issuer"
471
-
| Some fresh_pubkey_multibase
472
-
when fresh_pubkey_multibase = pubkey_multibase ->
473
-
Lwt.return_error
474
-
@@ Errors.auth_required
475
-
"jwt signature does not match jwt issuer"
476
-
| Some fresh_pubkey_multibase ->
477
-
verify_with_key token fresh_pubkey_multibase did
478
-
db ) ) ) ) ) )
479
with _ ->
480
-
Lwt.return_error @@ Errors.auth_required "malformed service jwt" ) )
0
481
482
let authorization : verifier =
483
fun ctx ->
···
34
else if jwt.sub = "" then Lwt.return_error "missing sub"
35
else if now_s < jwt.iat then
36
Lwt.return_error "token issued in the future"
37
+
else if now_s > jwt.exp then Lwt.return_error "ExpiredToken"
38
else if jwt.scope <> expected_scope then
39
Lwt.return_error "invalid scope"
40
else if jwt.jti = "" then Lwt.return_error "missing jti"
···
42
let%lwt revoked_at =
43
Data_store.is_token_revoked t ~did:jwt.sub ~jti:jwt.jti
44
in
45
+
if revoked_at <> None then Lwt.return_error "ExpiredToken"
46
else Lwt.return_ok jwt
47
with _ -> Lwt.return_error "invalid token format" )
48
···
219
| "admin", p when p = Env.admin_password ->
220
Lwt.return_ok Admin
221
| _ ->
222
+
Lwt.return_error @@ Errors.invalid_request "invalid credentials" )
223
| Error _ ->
224
Lwt.return_error @@ Errors.auth_required "invalid authorization header"
225
···
234
Lwt.return_ok (Access {did})
235
| Some {deactivated_at= Some _; _} ->
236
Lwt.return_error
237
+
@@ Errors.invalid_request ~name:"AccountDeactivated"
238
"account is deactivated"
239
| None ->
240
+
Lwt.return_error
241
+
@@ Errors.internal_error ~msg:"invalid credentials" () )
242
+
| Error "ExpiredToken" ->
243
+
Lwt.return_error
244
+
@@ Errors.invalid_request ~name:"ExpiredToken" "token expired"
245
| Error _ ->
246
+
Lwt.return_error
247
+
@@ Errors.invalid_request ~name:"InvalidToken" "invalid credentials" )
248
| Error _ ->
249
Lwt.return_error @@ Errors.auth_required "invalid authorization header"
250
···
302
let scopes = Oauth.Scopes.of_string scope_str in
303
let now = int_of_float (Unix.gettimeofday ()) in
304
if jkt_claim <> proof.jkt then
305
+
Lwt.return_error @@ Errors.invalid_request "dpop key mismatch"
306
else if exp < now then
307
+
Lwt.return_error
308
+
@@ Errors.invalid_request ~name:"ExpiredToken" "token expired"
309
else if not (Oauth.Scopes.has_atproto scopes) then
310
Lwt.return_error
311
+
@@ Errors.invalid_request ~name:"InvalidToken"
312
"oauth token missing 'atproto' scope"
313
else
314
let%lwt session =
···
317
Lwt.return_ok sess
318
with _ ->
319
Lwt.return_error
320
+
@@ Errors.internal_error ~msg:"invalid credentials" ()
321
in
322
match session with
323
| Ok {active= Some true; _} ->
324
Lwt.return_ok (OAuth {did; proof; scopes})
325
| Ok _ ->
326
Lwt.return_error
327
+
@@ Errors.invalid_request ~name:"AccountDeactivated"
328
"account is deactivated"
329
| Error _ ->
330
Lwt.return_error
331
+
@@ Errors.internal_error ~msg:"invalid credentials" ()
332
with _ ->
333
Lwt.return_error @@ Errors.auth_required "malformed JWT claims" )
334
) )
···
347
raise (Errors.Redirect "/account")
348
| Some {deactivated_at= Some _; _} ->
349
Lwt.return_error
350
+
@@ Errors.invalid_request ~name:"AccountDeactivated"
351
"account is deactivated"
352
| None ->
353
let%lwt () = Session.Raw.clear_session req in
354
+
Lwt.return_error @@ Errors.internal_error ~msg:"no active session" ()
355
+
)
356
| None ->
357
Lwt.return_error @@ Errors.auth_required "no active session"
358
···
367
Lwt.return_ok (Refresh {did; jti})
368
| Some {deactivated_at= Some _; _} ->
369
Lwt.return_error
370
+
@@ Errors.invalid_request ~name:"AccountDeactivated"
371
"account is deactivated"
372
| None ->
373
+
Lwt.return_error
374
+
@@ Errors.internal_error ~msg:"invalid credentials" () )
375
+
| Error "ExpiredToken" ->
376
+
Lwt.return_error
377
+
@@ Errors.invalid_request ~name:"ExpiredToken" "token expired"
378
+
| Error _ ->
379
+
Lwt.return_error
380
+
@@ Errors.invalid_request ~name:"InvalidToken" "invalid credentials" )
381
| Error _ ->
382
Lwt.return_error @@ Errors.auth_required "invalid authorization header"
383
···
388
Lwt.return_ok (Access {did})
389
| Some {deactivated_at= Some _; _} ->
390
Lwt.return_error
391
+
@@ Errors.invalid_request ~name:"AccountDeactivated"
392
"account is deactivated"
393
| None ->
394
+
Lwt.return_error
395
+
@@ Errors.internal_error ~msg:"invalid credentials" ()
396
in
397
let verify_with_key token pubkey_multibase did db =
398
+
let pubkey = Kleidos.parse_multikey_str pubkey_multibase in
0
0
399
match Jwt.verify_jwt token ~pubkey with
400
| Ok _ ->
401
check_actor_status did db
402
| Error e ->
403
Dream.debug (fun log -> log "service jwt verification failed: %s" e) ;
404
Lwt.return_error
405
+
@@ Errors.invalid_request ~name:"InvalidToken"
406
+
"jwt signature does not match jwt issuer"
407
in
408
fun {req; db} ->
409
match parse_bearer req with
···
422
let lxm = payload |> member "lxm" |> to_string_option in
423
let now = int_of_float (Unix.gettimeofday ()) in
424
if exp < now then
425
+
Lwt.return_error
426
+
@@ Errors.invalid_request ~name:"ExpiredToken" "token expired"
427
else if aud <> Env.did then
428
Lwt.return_error
429
+
@@ Errors.invalid_request ~name:"InvalidToken"
430
+
"jwt audience does not match service did"
431
else
432
let nsid =
433
(Dream.path [@warning "-3"]) req |> List.rev |> List.hd
434
in
435
+
match lxm with
436
| Some l when l <> nsid && l <> "*" ->
437
Lwt.return_error
438
+
@@ Errors.invalid_request ~name:"InvalidToken"
439
("jwt lxm " ^ l ^ " does not match " ^ nsid)
440
+
| _ -> (
441
let did =
442
match String.split_on_char '#' iss with
443
| did :: _ ->
···
445
| [] ->
446
iss
447
in
448
+
match%lwt Id_resolver.Did.resolve did with
449
| Error e ->
450
Dream.debug (fun log ->
451
log "failed to resolve did %s: %s" did e ) ;
452
Lwt.return_error
453
+
@@ Errors.internal_error
454
+
~msg:"could not resolve jwt issuer did" ()
455
| Ok did_doc -> (
456
match
457
Id_resolver.Did.Document.get_verification_key did_doc
···
459
with
460
| None ->
461
Lwt.return_error
462
+
@@ Errors.internal_error
463
+
~msg:"missing or bad key in issuer did doc" ()
464
| Some pubkey_multibase -> (
465
+
match%lwt
466
+
verify_with_key token pubkey_multibase did db
467
+
with
468
| Ok creds ->
469
Lwt.return_ok creds
470
+
| Error _ -> (
471
+
(* try again, skipping cache in case of key rotation *)
472
+
match%lwt
473
+
Id_resolver.Did.resolve ~skip_cache:true did
474
+
with
475
+
| Error _ ->
476
+
Lwt.return_error
477
+
@@ Errors.invalid_request ~name:"InvalidToken"
478
+
"jwt signature does not match jwt issuer"
479
+
| Ok fresh_doc -> (
480
+
match
481
+
Id_resolver.Did.Document.get_verification_key
482
+
fresh_doc "#atproto"
483
+
with
484
+
| None ->
485
Lwt.return_error
486
+
@@ Errors.invalid_request ~name:"InvalidToken"
487
"jwt signature does not match jwt issuer"
488
+
| Some fresh_pubkey_multibase
489
+
when fresh_pubkey_multibase = pubkey_multibase ->
490
+
Lwt.return_error
491
+
@@ Errors.invalid_request ~name:"InvalidToken"
492
+
"jwt signature does not match jwt issuer"
493
+
| Some fresh_pubkey_multibase ->
494
+
verify_with_key token fresh_pubkey_multibase did
495
+
db ) ) ) ) )
0
0
0
0
0
0
0
0
0
496
with _ ->
497
+
Lwt.return_error @@ Errors.invalid_request "malformed service jwt" )
498
+
)
499
500
let authorization : verifier =
501
fun ctx ->