Skip to content

Commit

Permalink
Move Credentials to reddit_api_kernel
Browse files Browse the repository at this point in the history
  • Loading branch information
leviroth committed Nov 16, 2024
1 parent b7abe32 commit 9d0b0ba
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 156 deletions.
86 changes: 0 additions & 86 deletions reddit_api_async/connection.ml
Original file line number Diff line number Diff line change
@@ -1,92 +1,6 @@
open! Core
open! Async
open Reddit_api_kernel

module Credentials = struct
module Password = struct
type t =
{ client_id : string
; client_secret : string
; password : string
; username : string
}
[@@deriving sexp]
end

module Refresh_token = struct
type t =
{ client_id : string
; client_secret : string option
; refresh_token : string
}
[@@deriving sexp]
end

module Userless_confidential = struct
type t =
{ client_id : string
; client_secret : string
}
[@@deriving sexp]
end

module Userless_public = struct
type t =
{ client_id : string
; device_id : string option
}
[@@deriving sexp]

let device_id_or_default t =
Option.value t.device_id ~default:"DO_NOT_TRACK_THIS_DEVICE"
;;
end

type t =
| Password of Password.t
| Refresh_token of Refresh_token.t
| Userless_confidential of Userless_confidential.t
| Userless_public of Userless_public.t
[@@deriving sexp]

let client_id t =
match t with
| Password { client_id; _ }
| Refresh_token { client_id; _ }
| Userless_confidential { client_id; _ }
| Userless_public { client_id; _ } -> client_id
;;

let client_secret t =
match t with
| Refresh_token { client_secret = None; _ } | Userless_public _ -> None
| Password { client_secret; _ }
| Refresh_token { client_secret = Some client_secret; _ }
| Userless_confidential { client_secret; _ } -> Some client_secret
;;

let basic_auth_string t =
let client_id = client_id t in
let client_secret = Option.value (client_secret t) ~default:"" in
Cohttp.Auth.string_of_credential (`Basic (client_id, client_secret))
;;

let auth_header t = Cohttp.Header.init_with "Authorization" (basic_auth_string t)

let access_token_request_params t =
match t with
| Password { username; password; _ } ->
[ "grant_type", [ "password" ]; "username", [ username ]; "password", [ password ] ]
| Refresh_token { refresh_token; _ } ->
[ "grant_type", [ "refresh_token" ]; "refresh_token", [ refresh_token ] ]
| Userless_confidential _ -> [ "grant_type", [ "client_credentials" ] ]
| Userless_public public_credentials ->
[ "grant_type", [ "https://oauth.reddit.com/grants/installed_client" ]
; "device_id", [ Userless_public.device_id_or_default public_credentials ]
]
;;
end

module Sequencer_table = Sequencer_table.Make (Endpoint.Sequencer)

module Access_token_request_error = struct
Expand Down
67 changes: 0 additions & 67 deletions reddit_api_async/connection.mli
Original file line number Diff line number Diff line change
Expand Up @@ -41,73 +41,6 @@ open! Core
open! Async
open Reddit_api_kernel

module Credentials : sig
(** [Password] credentials correspond to Reddit's
{{:https://github.com/reddit-archive/reddit/wiki/oauth2-app-types#script} "script"}
app type.
@see < https://datatracker.ietf.org/doc/html/rfc6749#section-4.3.2 >
The
RFC 6749 section describing the corresponding access token request. *)
module Password : sig
type t =
{ client_id : string
; client_secret : string
; password : string
; username : string
}
[@@deriving sexp]
end

(** [Refresh_token] credentials correspond to Reddit's
{{:https://github.com/reddit-archive/reddit/wiki/oauth2-app-types#web-app} "web app"}
and
{{:https://github.com/reddit-archive/reddit/wiki/oauth2-app-types#installed-app} "installed-app"}
app types.
@see < https://praw.readthedocs.io/en/stable/tutorials/refresh_token.html
>
{{:https://praw.readthedocs.io/} PRAW}'s documentation on refresh tokens
for advice on obtaining a refresh token, which is currently outside the
scope of this project.
@see < https://datatracker.ietf.org/doc/html/rfc6749#section-6 >
The RFC
6749 section describing the corresponding access token request. *)
module Refresh_token : sig
type t =
{ client_id : string
; client_secret : string option
(** This field is present for web apps and absent for installed apps. *)
; refresh_token : string
}
[@@deriving sexp]
end

module Userless_confidential : sig
type t =
{ client_id : string
; client_secret : string
}
[@@deriving sexp]
end

module Userless_public : sig
type t =
{ client_id : string
; device_id : string option
}
[@@deriving sexp]
end

type t =
| Password of Password.t
| Refresh_token of Refresh_token.t
| Userless_confidential of Userless_confidential.t
| Userless_public of Userless_public.t
[@@deriving sexp]
end

type t [@@deriving sexp_of]

(** An [Access_token_request_error.t] represents an error encountered while
Expand Down
84 changes: 84 additions & 0 deletions reddit_api_kernel/credentials.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
open! Core

module Password = struct
type t =
{ client_id : string
; client_secret : string
; password : string
; username : string
}
[@@deriving sexp]
end

module Refresh_token = struct
type t =
{ client_id : string
; client_secret : string option
; refresh_token : string
}
[@@deriving sexp]
end

module Userless_confidential = struct
type t =
{ client_id : string
; client_secret : string
}
[@@deriving sexp]
end

module Userless_public = struct
type t =
{ client_id : string
; device_id : string option
}
[@@deriving sexp]

let device_id_or_default t =
Option.value t.device_id ~default:"DO_NOT_TRACK_THIS_DEVICE"
;;
end

type t =
| Password of Password.t
| Refresh_token of Refresh_token.t
| Userless_confidential of Userless_confidential.t
| Userless_public of Userless_public.t
[@@deriving sexp]

let client_id t =
match t with
| Password { client_id; _ }
| Refresh_token { client_id; _ }
| Userless_confidential { client_id; _ }
| Userless_public { client_id; _ } -> client_id
;;

let client_secret t =
match t with
| Refresh_token { client_secret = None; _ } | Userless_public _ -> None
| Password { client_secret; _ }
| Refresh_token { client_secret = Some client_secret; _ }
| Userless_confidential { client_secret; _ } -> Some client_secret
;;

let basic_auth_string t =
let client_id = client_id t in
let client_secret = Option.value (client_secret t) ~default:"" in
Cohttp.Auth.string_of_credential (`Basic (client_id, client_secret))
;;

let auth_header t = Cohttp.Header.init_with "Authorization" (basic_auth_string t)

let access_token_request_params t =
match t with
| Password { username; password; _ } ->
[ "grant_type", [ "password" ]; "username", [ username ]; "password", [ password ] ]
| Refresh_token { refresh_token; _ } ->
[ "grant_type", [ "refresh_token" ]; "refresh_token", [ refresh_token ] ]
| Userless_confidential _ -> [ "grant_type", [ "client_credentials" ] ]
| Userless_public public_credentials ->
[ "grant_type", [ "https://oauth.reddit.com/grants/installed_client" ]
; "device_id", [ Userless_public.device_id_or_default public_credentials ]
]
;;
70 changes: 70 additions & 0 deletions reddit_api_kernel/credentials.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
(** [Password] credentials correspond to Reddit's
{{:https://github.com/reddit-archive/reddit/wiki/oauth2-app-types#script} "script"}
app type.
@see < https://datatracker.ietf.org/doc/html/rfc6749#section-4.3.2 >
The
RFC 6749 section describing the corresponding access token request. *)
module Password : sig
type t =
{ client_id : string
; client_secret : string
; password : string
; username : string
}
[@@deriving sexp]
end

(** [Refresh_token] credentials correspond to Reddit's
{{:https://github.com/reddit-archive/reddit/wiki/oauth2-app-types#web-app} "web app"}
and
{{:https://github.com/reddit-archive/reddit/wiki/oauth2-app-types#installed-app} "installed-app"}
app types.
@see < https://praw.readthedocs.io/en/stable/tutorials/refresh_token.html
>
{{:https://praw.readthedocs.io/} PRAW}'s documentation on refresh tokens
for advice on obtaining a refresh token, which is currently outside the
scope of this project.
@see < https://datatracker.ietf.org/doc/html/rfc6749#section-6 >
The RFC
6749 section describing the corresponding access token request. *)
module Refresh_token : sig
type t =
{ client_id : string
; client_secret : string option
(** This field is present for web apps and absent for installed apps. *)
; refresh_token : string
}
[@@deriving sexp]
end

module Userless_confidential : sig
type t =
{ client_id : string
; client_secret : string
}
[@@deriving sexp]
end

module Userless_public : sig
type t =
{ client_id : string
; device_id : string option
}
[@@deriving sexp]

val device_id_or_default : t -> string
end

type t =
| Password of Password.t
| Refresh_token of Refresh_token.t
| Userless_confidential of Userless_confidential.t
| Userless_public of Userless_public.t
[@@deriving sexp]

val auth_header : t -> Cohttp.Header.t
val basic_auth_string : t -> string
val access_token_request_params : t -> (string * string list) list
1 change: 1 addition & 0 deletions reddit_api_kernel/reddit_api_kernel.ml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module Comment_response = Comment_response
module Credentials = Credentials
module Endpoint = Endpoint
module Id36 = Id36
module Inbox_item = Inbox_item
Expand Down
2 changes: 1 addition & 1 deletion test/import.ml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ let with_cassette cassette_name ~f =
let credentials =
match Sys.getenv "CREDENTIALS" with
| Some credential_path ->
Sexp.load_sexp_conv_exn credential_path [%of_sexp: Connection.Credentials.t]
Sexp.load_sexp_conv_exn credential_path [%of_sexp: Credentials.t]
| None ->
Password
{ username = "TEST_USERNAME"
Expand Down
2 changes: 1 addition & 1 deletion test/test_oauth2_refresh_token.ml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ let with_cassette cassette_name ~f =
let credentials =
match Sys.getenv "CREDENTIALS_REFRESH_TOKEN" with
| Some credential_path ->
Sexp.load_sexp_conv_exn credential_path [%of_sexp: Connection.Credentials.t]
Sexp.load_sexp_conv_exn credential_path [%of_sexp: Credentials.t]
| None ->
Refresh_token
{ client_id = "TEST_CLIENT_ID"
Expand Down
2 changes: 1 addition & 1 deletion test/test_oauth2_userless.ml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ let with_cassette cassette_name ~f ~is_confidential =
let credentials =
match Sys.getenv "CREDENTIALS_USERLESS" with
| Some credential_path ->
Sexp.load_sexp_conv_exn credential_path [%of_sexp: Connection.Credentials.t]
Sexp.load_sexp_conv_exn credential_path [%of_sexp: Credentials.t]
| None ->
(match is_confidential with
| true ->
Expand Down

0 comments on commit 9d0b0ba

Please sign in to comment.