Source file TokenResponse.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
type token_type = Bearer
type t =
{ token_type : token_type
; scope : Scopes.t list
; expires_in : int option
; access_token : string option
; refresh_token : string option
; id_token : string option
}
let make
?(token_type = Bearer)
?(scope = [])
?expires_in
?access_token
?refresh_token
?id_token
()
=
{ token_type; scope; expires_in; access_token; refresh_token; id_token }
let string_or_int_to_int_opt = function
| `String s -> (try Some (int_of_string s) with _ -> None)
| `Int i -> Some i
| `Null -> None
| _ -> None
let of_yojson json =
try
let module Json = Yojson.Safe.Util in
let scope =
match Json.member "scope" json with
| `Null -> []
| `String scope -> Scopes.of_scope_parameter scope
| `List json ->
List.map Json.to_string json |> List.map Scopes.of_string
| json ->
raise
(Json.Type_error
("scope: expected a string or an array of strings", json))
in
Ok
{ token_type = Bearer
;
scope
; expires_in =
json |> Json.member "expires_in" |> string_or_int_to_int_opt
; access_token =
json |> Json.member "access_token" |> Json.to_string_option
; refresh_token =
json |> Json.member "refresh_token" |> Json.to_string_option
; id_token = json |> Json.member "id_token" |> Json.to_string_option
}
with
| Yojson.Safe.Util.Type_error (str, _) -> Error (`Msg str)
let of_query query =
let scope =
let qp = Uri.get_query_param query "scope" in
Option.value ~default:[] (Option.map Scopes.of_scope_parameter qp)
in
{ token_type = Bearer
;
scope
; expires_in =
Uri.get_query_param query "expires_in" |> Option.map int_of_string
; access_token = Uri.get_query_param query "access_token"
; refresh_token = Uri.get_query_param query "refresh_token"
; id_token = Uri.get_query_param query "id_token"
}
let of_string str = Yojson.Safe.from_string str |> of_yojson
let validate
?clock_tolerance
?nonce
~jwks
~(client : Client.t)
~(discovery : Discover.t)
t
=
match Jose.Jwt.unsafe_of_string (Option.get t.id_token) with
| Ok jwt ->
if jwt.header.alg = `None
then
IDToken.validate
?clock_tolerance
?nonce
~client
~issuer:discovery.issuer
jwt
|> Result.map (fun _ -> t)
else (
match Jwks.find_jwk ~jwt jwks with
| Some jwk ->
IDToken.validate
?clock_tolerance
?nonce
~client
~issuer:discovery.issuer
~jwk
jwt
|> Result.map (fun _ -> t)
| None when List.length jwks.keys = 1 ->
let jwk = List.hd jwks.keys in
IDToken.validate
?clock_tolerance
?nonce
~client
~issuer:discovery.issuer
~jwk
jwt
|> Result.map (fun _ -> t)
| None -> Error (`Msg "Could not find JWK"))
| Error e -> Error e
let to_yojson { scope; expires_in; access_token; refresh_token; id_token; _ } =
let or_null = Option.value ~default:`Null in
let json_str = Option.map (fun x -> `String x) in
`Assoc
[ ( "scope"
, match scope with
| [] -> `Null
| _ -> `String (Scopes.to_scope_parameter scope) )
; "token_type", `String "Bearer"
; "expires_in", or_null (Option.map (fun x -> `Int x) expires_in)
; "access_token", or_null (json_str access_token)
; "refresh_token", or_null (json_str refresh_token)
; "id_token", or_null (json_str id_token)
]