2 * ========================================================================
3 * Copyright 2013-2020 Eduardo Chappa
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * ========================================================================
15 /* OAUTH2 support code goes here. This is necessary because
16 * 1. it helps to coordinate two different methods, such as XOAUTH2 and
17 * OAUTHBEARER, which use the same code, so it can all go in one place
19 * 2. It helps with coordinating with the client when the server requires
20 * the deviceinfo method.
25 #include "oauth2_aux.h"
27 /* we generate something like a guid, but not care about
28 * anything, but that it is really random.
30 char *oauth2_generate_state(void)
36 for(i
= 0; i
< 4; i
++)
37 sprintf(rv
+ strlen(rv
), "%x", (unsigned int) (random() % 256));
38 sprintf(rv
+ strlen(rv
), "%c", '-');
39 for(i
= 0; i
< 2; i
++)
40 sprintf(rv
+ strlen(rv
), "%x", (unsigned int) (random() % 256));
41 sprintf(rv
+ strlen(rv
), "%c", '-');
42 for(i
= 0; i
< 2; i
++)
43 sprintf(rv
+ strlen(rv
), "%x", (unsigned int) (random() % 256));
44 sprintf(rv
+ strlen(rv
), "%c", '-');
45 for(i
= 0; i
< 2; i
++)
46 sprintf(rv
+ strlen(rv
), "%x", (unsigned int) (random() % 256));
47 sprintf(rv
+ strlen(rv
), "%c", '-');
48 for(i
= 0; i
< 6; i
++)
49 sprintf(rv
+ strlen(rv
), "%x", (unsigned int) (random() % 256));
55 JSON_S
*oauth2_json_reply(OAUTH2_SERVER_METHOD_S
, OAUTH2_S
*, int *);
57 #define LOAD_HTTP_PARAMS(X, Y) { \
59 for(i = 0; (X).params[i] != OA2_End; i++){ \
60 OA2_type j = (X).params[i]; \
61 (Y)[i].name = oauth2->param[j].name; \
62 (Y)[i].value = oauth2->param[j].value; \
64 (Y)[i].name = (Y)[i].value = NULL; \
67 JSON_S
*oauth2_json_reply(OAUTH2_SERVER_METHOD_S RefreshMethod
, OAUTH2_S
*oauth2
, int *status
)
70 HTTP_PARAM_S params
[OAUTH2_PARAM_NUMBER
];
73 LOAD_HTTP_PARAMS(RefreshMethod
, params
);
75 if(strcmp(RefreshMethod
.name
, "POST") == 0
76 && ((s
= http_post_param(RefreshMethod
.urlserver
, params
, status
)) != NULL
)){
78 json
= json_parse(&u
);
79 fs_give((void **) &s
);
86 mm_login_oauth2_c_client_method (NETMBX
*mb
, char *user
, char *method
,
87 OAUTH2_S
*oauth2
, unsigned long trial
, int *tryanother
)
93 if(oauth2
->param
[OA2_Id
].value
== NULL
94 || (oauth2
->require_secret
&& oauth2
->param
[OA2_Secret
].value
== NULL
)){
95 oauth2clientinfo_t ogci
=
96 (oauth2clientinfo_t
) mail_parameters (NIL
, GET_OA2CLIENTINFO
, NIL
);
98 if(ogci
) (*ogci
)(oauth2
->name
, &oauth2
->param
[OA2_Id
].value
,
99 &oauth2
->param
[OA2_Secret
].value
);
102 if(oauth2
->param
[OA2_Id
].value
== NULL
103 || (oauth2
->require_secret
&& oauth2
->param
[OA2_Secret
].value
== NULL
))
106 /* Do we have a method to execute? */
107 if (oauth2
->first_time
&& oauth2
->server_mthd
[OA2_GetDeviceCode
].name
){
108 oauth2deviceinfo_t ogdi
;
110 json
= oauth2_json_reply(oauth2
->server_mthd
[OA2_GetDeviceCode
], oauth2
, &status
);
115 jx
= json_body_value(json
, "device_code");
116 if(jx
&& jx
->jtype
== JString
)
117 oauth2
->devicecode
.device_code
= cpystr((char *) jx
->value
);
119 jx
= json_body_value(json
, "user_code");
120 if(jx
&& jx
->jtype
== JString
)
121 oauth2
->devicecode
.user_code
= cpystr((char *) jx
->value
);
123 jx
= json_body_value(json
, "verification_uri");
124 if(jx
&& jx
->jtype
== JString
)
125 oauth2
->devicecode
.verification_uri
= cpystr((char *) jx
->value
);
127 if((jx
= json_body_value(json
, "expires_in")) != NULL
)
129 case JString
: oauth2
->devicecode
.expires_in
= atoi((char *) jx
->value
);
131 case JLong
: oauth2
->devicecode
.expires_in
= *(long *) jx
->value
;
135 if((jx
= json_body_value(json
, "interval")) != NULL
)
137 case JString
: oauth2
->devicecode
.interval
= atoi((char *) jx
->value
);
139 case JLong
: oauth2
->devicecode
.interval
= *(long *) jx
->value
;
143 jx
= json_body_value(json
, "message");
144 if(jx
&& jx
->jtype
== JString
)
145 oauth2
->devicecode
.message
= cpystr((char *) jx
->value
);
149 if(oauth2
->devicecode
.verification_uri
&& oauth2
->devicecode
.user_code
){
150 ogdi
= (oauth2deviceinfo_t
) mail_parameters (NIL
, GET_OA2DEVICEINFO
, NIL
);
151 if(ogdi
) (*ogdi
)(oauth2
, method
);
157 /* else check if we have a refresh token, and in that case use it */
159 if(oauth2
->param
[OA2_RefreshToken
].value
){
161 json
= oauth2_json_reply(oauth2
->server_mthd
[OA2_GetAccessTokenFromRefreshToken
], oauth2
, &status
);
166 jx
= json_body_value(json
, "access_token");
167 if(jx
&& jx
->jtype
== JString
)
168 oauth2
->access_token
= cpystr((char *) jx
->value
);
170 if((jx
= json_body_value(json
, "expires_in")) != NULL
)
172 case JString
: oauth2
->expiration
= time(0) + atol((char *) jx
->value
);
174 case JLong
: oauth2
->expiration
= time(0) + *(long *) jx
->value
;
183 * else, we do not have a refresh token, nor an access token.
184 * We need to start the process to get an access code. We use this
185 * to get an access token and refresh token.
187 { OAUTH2_SERVER_METHOD_S RefreshMethod
= oauth2
->server_mthd
[OA2_GetAccessCode
];
188 HTTP_PARAM_S params
[OAUTH2_PARAM_NUMBER
];
190 LOAD_HTTP_PARAMS(RefreshMethod
, params
);
192 if(strcmp(RefreshMethod
.name
, "GET") == 0){
193 char *url
= http_get_param_url(RefreshMethod
.urlserver
, params
);
194 oauth2getaccesscode_t ogac
=
195 (oauth2getaccesscode_t
) mail_parameters (NIL
, GET_OA2CLIENTGETACCESSCODE
, NIL
);
198 oauth2
->param
[OA2_Code
].value
= (*ogac
)(url
, method
, oauth2
, tryanother
);
201 if(oauth2
->param
[OA2_Code
].value
){
202 json
= oauth2_json_reply(oauth2
->server_mthd
[OA2_GetAccessTokenFromAccessCode
], oauth2
, &status
);
208 case HTTP_OK
: jx
= json_body_value(json
, "refresh_token");
209 if(jx
&& jx
->jtype
== JString
)
210 oauth2
->param
[OA2_RefreshToken
].value
= cpystr((char *) jx
->value
);
212 jx
= json_body_value(json
, "access_token");
213 if(jx
&& jx
->jtype
== JString
)
214 oauth2
->access_token
= cpystr((char *) jx
->value
);
216 if((jx
= json_body_value(json
, "expires_in")) != NULL
)
218 case JString
: oauth2
->expiration
= time(0) + atol((char *) jx
->value
);
220 case JLong
: oauth2
->expiration
= time(0) + *(long *) jx
->value
;
224 jx
= json_body_value(json
, "expires_in");
225 if(jx
&& jx
->jtype
== JString
)
226 oauth2
->expiration
= time(0) + atol((char *) jx
->value
);
230 case HTTP_BAD
: break;
232 default : { char tmp
[100];
233 sprintf(tmp
, "Oauth Client Received Code %d", status
);
244 /* Else, does this server use the /devicecode method? */
247 void oauth2deviceinfo_get_accesscode(void *inp
, void *outp
)
249 OAUTH2_DEVICEPROC_S
*oad
= (OAUTH2_DEVICEPROC_S
*) inp
;
250 OAUTH2_S
*oauth2
= oad
->xoauth2
;
251 OAUTH2_DEVICECODE_S
*dcode
= &oauth2
->devicecode
;
252 int done
= 0, status
, rv
;
253 HTTP_PARAM_S params
[OAUTH2_PARAM_NUMBER
];
256 if(dcode
->device_code
&& oauth2
->param
[OA2_DeviceCode
].value
== NULL
)
257 oauth2
->param
[OA2_DeviceCode
].value
= cpystr(dcode
->device_code
);
259 rv
= OA2_CODE_WAIT
; /* wait by default */
260 json
= oauth2_json_reply(oauth2
->server_mthd
[OA2_GetAccessTokenFromAccessCode
], oauth2
, &status
);
267 case HTTP_BAD
: jx
= json_body_value(json
, "error");
268 if(jx
&& jx
->jtype
== JString
)
269 error
= cpystr((char *) jx
->value
);
273 if(compare_cstring(error
, "authorization_pending") == 0)
275 else if(compare_cstring(error
, "authorization_declined") == 0)
277 else if(compare_cstring(error
, "bad_verification_code") == 0)
279 else if(compare_cstring(error
, "expired_token") == 0)
281 else /* keep waiting? */
286 case HTTP_OK
: jx
= json_body_value(json
, "refresh_token");
287 if(jx
&& jx
->jtype
== JString
)
288 oauth2
->param
[OA2_RefreshToken
].value
= cpystr((char *) jx
->value
);
290 jx
= json_body_value(json
, "access_token");
291 if(jx
&& jx
->jtype
== JString
)
292 oauth2
->access_token
= cpystr((char *) jx
->value
);
294 if((jx
= json_body_value(json
, "expires_in")) != NULL
)
296 case JString
: oauth2
->expiration
= time(0) + atol((char *) jx
->value
);
298 case JLong
: oauth2
->expiration
= time(0) + *(long *) jx
->value
;
302 rv
= OA2_CODE_SUCCESS
;
306 default : { char tmp
[100];
307 sprintf(tmp
, "Oauth device Received Code %d", status
);