OAuth 2.0 authorization and token exchange
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

ocaml-oauth: enable MDX on lib/oauth.mli

Run mdx on lib/oauth.mli so the two {[ ... ]} odoc blocks now
type-check.

The first block was the begin-redirect / on-callback OAuth flow with
several `(* placeholder *)` comments standing in for branch bodies
and prose comments instead of bindings. Restructured as
`begin_login ()` and `finish_login` functions that take the inputs
they actually need (callback_state, code, etc.).

The second block (Token wrapper) used `Result.get_ok` and trailing
`ignore access`. Replaced with a `acquire_token` helper that takes
real http/clock/credentials, pattern-matches the result via
`Fmt.failwith "%a" Oauth.pp_parse_token_error`, and prints the
access-token length so the value is consumed.

+34 -22
+4
lib/dune
··· 15 15 fmt 16 16 ohex 17 17 logs)) 18 + 19 + (mdx 20 + (files oauth.mli) 21 + (libraries oauth requests fmt))
+30 -22
lib/oauth.mli
··· 11 11 {2 Example} 12 12 13 13 {[ 14 - (* 1. Before redirect: generate state and PKCE, store in session *) 14 + (* 1. Before redirect: generate state and PKCE, store in session. *) 15 + let begin_login () = 15 16 let redirect_uri = 16 - Oauth.redirect_uri "https://app.com/callback" |> Result.get_ok 17 + match Oauth.redirect_uri "https://app.com/callback" with 18 + | Ok r -> r 19 + | Error (`Msg e) -> Fmt.failwith "redirect_uri: %s" e 17 20 in 18 21 let state = Oauth.generate_state () in 19 22 let verifier = Oauth.generate_code_verifier () in 20 23 let challenge = Oauth.code_challenge S256 verifier in 21 - (* store [state] and [verifier] in the user's session *) 22 24 let url = 23 - Oauth.authorization_url Github ~client_id:"xxx" 24 - ~redirect_uri ~state 25 + Oauth.authorization_url Github ~client_id:"xxx" ~redirect_uri ~state 25 26 ~scope:[ "user:email" ] ~code_challenge:challenge () 26 27 in 27 - (* redirect user to [url] *) 28 + (state, verifier, redirect_uri, url) 28 29 29 - (* 2. On callback: validate state, then exchange code *) 30 - let callback_state = (* [state] query param from callback URL *) in 30 + (* 2. On callback: validate state, then exchange the code. *) 31 + let finish_login http ~state ~verifier ~redirect_uri ~callback_state ~code = 31 32 if not (Oauth.validate_state ~expected:state ~actual:callback_state) then 32 33 failwith "CSRF state mismatch"; 33 34 let client_auth = 34 35 Oauth.Client_auth.basic ~client_id:"xxx" ~client_secret:"yyy" 35 36 in 36 37 match 37 - Oauth.exchange_code http Github ~client_auth 38 - ~code ~redirect_uri ~code_verifier:verifier () 38 + Oauth.exchange_code http Github ~client_auth ~code ~redirect_uri 39 + ~code_verifier:verifier () 39 40 with 40 - | Ok token -> (* use [token.access_token] *) 41 - | Error e -> (* handle error *) 41 + | Ok token -> Fmt.pr "access token: %s@." token.access_token 42 + | Error e -> Fmt.epr "exchange failed: %a@." Oauth.pp_parse_token_error e 42 43 ]} *) 43 44 44 45 (** {1:discovery Discovery metadata} ··· 704 705 705 706 {b Example} 706 707 {[ 707 - let client_auth = Oauth.Client_auth.basic ~client_id ~client_secret in 708 - let token = 709 - Oauth.exchange_code http Google ~client_auth ~code ~redirect_uri () 710 - |> Result.get_ok 711 - |> Oauth.Token.of_response http Google ~client_auth ~clock 712 - in 713 - (* Make many API calls; [access] refreshes if the access token is within 714 - 60 seconds of expiry. *) 715 - let access = Oauth.Token.access token in 716 - ignore access 708 + let acquire_token http clock ~client_id ~client_secret ~code 709 + ~redirect_uri = 710 + let client_auth = 711 + Oauth.Client_auth.basic ~client_id ~client_secret 712 + in 713 + match 714 + Oauth.exchange_code http Google ~client_auth ~code ~redirect_uri () 715 + with 716 + | Error e -> Fmt.failwith "exchange: %a" Oauth.pp_parse_token_error e 717 + | Ok response -> 718 + let token = 719 + Oauth.Token.of_response http Google ~client_auth ~clock response 720 + in 721 + (* Make many API calls; access refreshes if the access token is 722 + within 60 seconds of expiry. *) 723 + let access = Oauth.Token.access token in 724 + Fmt.pr "access token (%d chars)@." (String.length access) 717 725 ]} *) 718 726 module Token : sig 719 727 type t