Source file SimpleClient.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
(**
* Copyright 2022 Ulrik Strid. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*)
type t =
{ client : Client.t
; provider_uri : Uri.t
; redirect_uri : Uri.t
}
let make
?secret
?(response_types = [ "code" ])
?(grant_types = [])
?(token_endpoint_auth_method = "client_secret_post")
~redirect_uri
~provider_uri
client_id
=
let client =
Client.make
?secret
~response_types
~grant_types
~redirect_uris:[ redirect_uri ]
~token_endpoint_auth_method
client_id
in
{ client; redirect_uri; provider_uri }
let discovery_uri t =
let base_path = Uri.path t.provider_uri in
Uri.with_path t.provider_uri (base_path ^ "/.well-known/openid-configuration")
type meth =
[ `POST
| `GET
| `CONNECT
| `DELETE
| `HEAD
| `PUT
| `TRACE
| `OPTIONS
| `Other of string
]
type request_descr =
{ body : string option
; headers : (string * string) list
; uri : Uri.t
; meth : meth
}
let make_token_request ~code ~discovery t =
let body =
Token.Request.make
t.client
~grant_type:"authorization_code"
~scope:[ `OpenID ]
~redirect_uri:t.redirect_uri
~code
|> Token.Request.to_body_string
in
let =
[ "Content-Type", "application/x-www-form-urlencoded"
; "Accept", "application/json"
]
in
let =
match t.client.token_endpoint_auth_method with
| "client_secret_basic" ->
Token.basic_auth
~client_id:t.client.id
~secret:(Option.value ~default:"" t.client.secret)
:: headers
| _ -> headers
in
let uri = discovery.Discover.token_endpoint in
{ body = Some body; headers; uri; meth = `POST }
let make_refresh_token_request ~refresh_token ~discovery t =
let body =
Token.RefreshTokenRequest.make
t.client
~grant_type:"authorization_code"
~scope:[ `OpenID ]
~redirect_uri:t.redirect_uri
~refresh_token
|> Token.RefreshTokenRequest.to_body_string
in
let =
[ "Content-Type", "application/x-www-form-urlencoded"
; "Accept", "application/json"
]
in
let =
match t.client.token_endpoint_auth_method with
| "client_secret_basic" ->
Token.basic_auth
~client_id:t.client.id
~secret:(Option.value ~default:"" t.client.secret)
:: headers
| _ -> headers
in
let uri = discovery.Discover.token_endpoint in
{ body = Some body; headers; uri; meth = `POST }
let make_userinfo_request ~(token : Token.Response.t) ~(discovery : Discover.t) =
match discovery.userinfo_endpoint, token with
| Some userinfo_endpoint, { access_token = Some access_token; _ } ->
let =
[ "Authorization", "Bearer " ^ access_token
; "Accept", "application/json"
]
in
let request_descr : request_descr =
{ headers; uri = userinfo_endpoint; body = None; meth = `GET }
in
(Ok request_descr : (request_descr, [> Error.t ]) result)
| Some _, { access_token = None; _ } -> Error `Missing_access_token
| None, _ -> Error `Missing_userinfo_endpoint
let get_auth_parameters ?scope ?claims ?nonce ~state t =
Parameters.make
?scope
?claims
?nonce
~state
~redirect_uri:t.redirect_uri
~client_id:t.client.id
()
let make_auth_uri ?scope ?claims ?nonce ~state ~discovery t =
let query =
get_auth_parameters ?scope ?claims ?nonce ~state t |> Parameters.to_query
in
Uri.add_query_params discovery.Discover.authorization_endpoint query
let valid_token_of_string ?clock_tolerance ?nonce ~jwks ~discovery t body =
let ret = Token.Response.of_string body in
match ret with
| Ok ret ->
Token.Response.validate
?clock_tolerance
?nonce
~jwks
~discovery
~client:t.client
ret
| e -> e
let valid_userinfo_of_string ~(token_response : Token.Response.t) userinfo =
match Jose.Jwt.unsafe_of_string (Option.get token_response.id_token) with
| Ok jwt -> Userinfo.validate ~jwt userinfo
| Error e -> Error e