mirror of
https://github.com/deltachat/deltachat-core.git
synced 2025-10-03 17:59:19 +02:00
145 lines
4.2 KiB
C
145 lines
4.2 KiB
C
#include "dc_context.h"
|
|
#include "dc_oauth2.h"
|
|
#include "../libs/jsmn/jsmn.h"
|
|
|
|
|
|
static int jsoneq(const char *json, jsmntok_t *tok, const char *s) {
|
|
// from the jsmn parser example
|
|
if (tok->type == JSMN_STRING && (int) strlen(s) == tok->end - tok->start &&
|
|
strncmp(json + tok->start, s, tok->end - tok->start) == 0) {
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
static char* jsondup(const char *json, jsmntok_t *tok) {
|
|
if (tok->type == JSMN_STRING) {
|
|
return strndup(json+tok->start, tok->end - tok->start);
|
|
}
|
|
return strdup("");
|
|
}
|
|
|
|
|
|
char* dc_oauth2_get_access_token(dc_context_t* context, const char* code, int flags)
|
|
{
|
|
char* access_token = NULL;
|
|
char* refresh_token = NULL;
|
|
char* auth_url = NULL;
|
|
char* expires_in_str = NULL;
|
|
char* error = NULL;
|
|
char* error_description = NULL;
|
|
char* json = NULL;
|
|
jsmn_parser parser;
|
|
jsmntok_t tok[128]; // we do not expect nor read more tokens
|
|
int tok_cnt = 0;
|
|
int locked = 0;
|
|
|
|
if (context==NULL || context->magic!=DC_CONTEXT_MAGIC
|
|
|| code==NULL || code[0]==0) {
|
|
dc_log_warning(context, 0, "Internal OAuth2 error");
|
|
goto cleanup;
|
|
}
|
|
|
|
pthread_mutex_lock(&context->oauth2_critical);
|
|
locked = 1;
|
|
|
|
// read generated token
|
|
if (!(flags&DC_REGENERATE)) {
|
|
access_token = dc_sqlite3_get_config(context->sql, "oauth2_access_token", NULL);
|
|
if (access_token!=NULL) {
|
|
goto cleanup; // success
|
|
}
|
|
}
|
|
|
|
// generate new token: build & call auth url
|
|
#define CLIENT_ID "959970109878-t6pl4k9fmsdvfnobae862urapdmhfvbe.apps.googleusercontent.com"
|
|
#define CLIENT_SECRET "g2f_Gc1YUJ-fWjnTkdsuk4Xo"
|
|
#define REDIRECT_URI "urn%3Aietf%3Awg%3Aoauth%3A2.0%3Aoob"
|
|
|
|
refresh_token = dc_sqlite3_get_config(context->sql, "oauth2_refresh_token", NULL);
|
|
if (refresh_token==NULL)
|
|
{
|
|
auth_url = dc_mprintf("https://accounts.google.com/o/oauth2/token"
|
|
"?client_id=%s"
|
|
"&client_secret=%s"
|
|
"&grant_type=authorization_code"
|
|
"&code=%s"
|
|
"&redirect_uri=%s",
|
|
CLIENT_ID, CLIENT_SECRET, code, REDIRECT_URI);
|
|
}
|
|
else
|
|
{
|
|
auth_url = dc_mprintf("https://accounts.google.com/o/oauth2/token"
|
|
"?client_id=%s"
|
|
"&client_secret=%s"
|
|
"&grant_type=refresh_token"
|
|
"&refresh_token=%s"
|
|
"&redirect_uri=%s",
|
|
CLIENT_ID, CLIENT_SECRET, refresh_token, REDIRECT_URI);
|
|
}
|
|
|
|
json = (char*)context->cb(context, DC_EVENT_HTTP_POST, (uintptr_t)auth_url, 0);
|
|
if (json==NULL) {
|
|
dc_log_warning(context, 0, "Error calling OAuth2 url");
|
|
goto cleanup;
|
|
}
|
|
|
|
// generate new token: parse returned json
|
|
jsmn_init(&parser);
|
|
tok_cnt = jsmn_parse(&parser, json, strlen(json), tok, sizeof(tok)/sizeof(tok[0]));
|
|
if (tok_cnt<2 || tok[0].type!=JSMN_OBJECT) {
|
|
dc_log_warning(context, 0, "Failed to parse OAuth2 json");
|
|
goto cleanup;
|
|
}
|
|
|
|
for (int i = 1; i < tok_cnt; i++) {
|
|
if (jsoneq(json, &tok[i], "access_token")==0) {
|
|
access_token = jsondup(json, &tok[i+1]);
|
|
}
|
|
else if (jsoneq(json, &tok[i], "refresh_token")==0) {
|
|
refresh_token = jsondup(json, &tok[i+1]);
|
|
}
|
|
else if (jsoneq(json, &tok[i], "expires_in")==0) {
|
|
expires_in_str = jsondup(json, &tok[i+1]);
|
|
}
|
|
else if (jsoneq(json, &tok[i], "error")==0) {
|
|
error = jsondup(json, &tok[i+1]);
|
|
}
|
|
else if (jsoneq(json, &tok[i], "error_description")==0) {
|
|
error_description = jsondup(json, &tok[i+1]);
|
|
}
|
|
}
|
|
|
|
if (error || error_description) {
|
|
dc_log_warning(context, 0, "OAuth error: %s: %s",
|
|
error? error : "unknown",
|
|
error_description? error_description : "no details");
|
|
dc_log_warning(context, 0, "FULL COMMAND WAS: %s", auth_url);
|
|
// continue, errors do not imply everything went wrong
|
|
}
|
|
|
|
if (access_token==NULL || access_token[0]==0) {
|
|
dc_log_warning(context, 0, "Failed to find OAuth2 access token");
|
|
goto cleanup;
|
|
}
|
|
|
|
dc_sqlite3_set_config(context->sql, "oauth2_access_token", access_token);
|
|
|
|
// update refresh_token if given,
|
|
// typically this is on the first round with `grant_type=authorization_code`
|
|
// but we update it later, too.
|
|
if (refresh_token && refresh_token[0]) {
|
|
dc_sqlite3_set_config(context->sql, "oauth2_refresh_token", refresh_token);
|
|
}
|
|
|
|
cleanup:
|
|
if (locked) { pthread_mutex_unlock(&context->oauth2_critical); }
|
|
free(refresh_token);
|
|
free(auth_url);
|
|
free(json);
|
|
free(expires_in_str);
|
|
free(error);
|
|
free(error_description);
|
|
return access_token? access_token : dc_strdup(NULL);
|
|
}
|