├── test ├── CMakeLists.txt └── main.c ├── src ├── CMakeLists.txt ├── curl_request.c └── oauth2.c ├── CMakeLists.txt ├── README └── include ├── curl_request.h └── oauth2.h /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCE_FILES main.c 2 | ) 3 | 4 | add_executable(oauth_test ${SOURCE_FILES}) 5 | target_link_libraries(oauth_test oauth) 6 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(SOURCE_FILES oauth2.c 2 | curl_request.c 3 | ) 4 | 5 | add_library(oauth SHARED ${SOURCE_FILES}) 6 | target_link_libraries(oauth curl) 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | 3 | project(OAuth2) 4 | 5 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 6 | include_directories("include") 7 | add_subdirectory(src) 8 | add_subdirectory(test) 9 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | A client library to implement OAuth 2.0 2 | 3 | Prerequisites: 4 | libcurl 5 | cmake 6 | 7 | Building: 8 | Create a new directory, then cd in and run cmake .. . This will generate build files for your platform. Then simply open the build 9 | files with the build system of your choice. -------------------------------------------------------------------------------- /test/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "oauth2.h" 3 | 4 | int main(int argc, char** argv) 5 | { 6 | oauth2_config* conf = oauth2_init("client_id", "client_secret"); 7 | oauth2_set_redirect_uri(conf, "http://notarealhosthonest.com/"); 8 | char* redir_uri = oauth2_request_auth_code(conf, "https://graph.facebook.com/oauth/authorize", "publish_stream", "LOL"); 9 | 10 | printf("Visit this url and hit authorize: %s\n", redir_uri); 11 | printf("Now put the auth token here: "); 12 | 13 | char code[255]; 14 | scanf("%s", code); 15 | 16 | //Now test token based auth 17 | char* rv = oauth2_access_auth_code(conf, "https://graph.facebook.com/oauth/access_token", code, NULL); 18 | 19 | oauth2_set_auth_code(conf, rv); 20 | printf("Access Token: %s\n", rv); 21 | free(rv); 22 | 23 | printf("Enter your Facebook status: "); 24 | char status[255]; 25 | scanf("%s", status); 26 | char status2[255]; 27 | sprintf(status2, "message=%s", status); 28 | 29 | rv = oauth2_request(conf, "https://graph.facebook.com/slugonamission/feed", status2); 30 | 31 | printf("%s\n", rv); 32 | 33 | oauth2_cleanup(conf); 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /include/curl_request.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010 Jamie Garside 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef __CURL_REQUEST_H__ 24 | #define __CURL_REQUEST_H__ 25 | 26 | #define MAX_BUFFER 2048 //2KB Buffers 27 | 28 | typedef struct _data { 29 | char d[MAX_BUFFER]; 30 | struct _data* next; 31 | int idx; 32 | } data; 33 | 34 | char* curl_make_request(char* url, char* params); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /include/oauth2.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010 Jamie Garside 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #ifndef __OAUTH2_H__ 24 | #define __OAUTH2_H__ 25 | 26 | //Constants 27 | #ifndef NULL 28 | #define NULL 0 29 | #endif 30 | 31 | typedef enum 32 | { 33 | OAUTH2_RESPONSE_CODE = 0, 34 | OAUTH2_RESPONSE_TOKEN, 35 | OAUTH2_RESPONSE_TOKEN_AND_CODE 36 | } oauth2_response_type; 37 | 38 | typedef enum 39 | { 40 | OAUTH2_ERROR_NO_ERROR = 0, 41 | OAUTH2_ERROR_INVALID_REQUEST, 42 | OAUTH2_ERROR_INVALID_CLIENT, 43 | OAUTH2_ERROR_UNAUTHORIZED_CLIENT, 44 | OAUTH2_ERROR_REDIRECT_URI_MISMATCH, 45 | OAUTH2_ERROR_ACCESS_DENIED, 46 | OAUTH2_ERROR_UNSUPPORTED_RESPONSE_TYPE, 47 | OAUTH2_ERROR_INVALID_SCOPE, 48 | OAUTH2_ERROR_INVALID_GRANT, 49 | OAUTH2_ERROR_UNSUPPORTED_GRANT_TYPE, 50 | } oauth2_error_type; 51 | 52 | //Internal structs 53 | typedef struct _oauth2_error { 54 | oauth2_error_type error; 55 | char* error_description; 56 | char* error_uri; 57 | char* state; 58 | } oauth2_error; 59 | 60 | typedef struct _oauth2_config 61 | { 62 | char* client_id; 63 | char* client_secret; 64 | char* redirect_uri; 65 | char* auth_code; 66 | oauth2_error last_error; 67 | } oauth2_config; 68 | 69 | //Methods 70 | 71 | //Initialiser 72 | oauth2_config* oauth2_init(char* client, char* secret); 73 | 74 | //Set the redirect URI for auth code authentication. This must be set before using oauth2_request_auth_code too. 75 | void oauth2_set_redirect_uri(oauth2_config* conf, char* redirect_uri); 76 | void oauth2_set_auth_code(oauth2_config* conf, char* auth_code); 77 | 78 | //Returns URL to redirect user to. 79 | char* oauth2_request_auth_code(oauth2_config* conf, char* auth_server, char* scope, char* state); 80 | char* oauth2_access_auth_code(oauth2_config* conf, char* auth_server, char* auth_code, char* scope); 81 | char* oauth2_access_resource_owner(oauth2_config* conf, char* auth_server, char* username, char* password); 82 | char* oauth2_access_refresh_token(oauth2_config* conf, char* refresh_token); 83 | char* oauth2_request(oauth2_config* conf, char* uri, char* params); 84 | void oauth2_cleanup(oauth2_config* conf); 85 | 86 | 87 | #endif 88 | -------------------------------------------------------------------------------- /src/curl_request.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010 Jamie Garside 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #include "curl_request.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | size_t curl_callback(void *ptr, size_t size, size_t nmemb, void *userdata) 30 | { 31 | size_t idx; 32 | size_t max; 33 | data* d; 34 | data* nd; 35 | 36 | d = (data*)userdata; 37 | 38 | idx = 0; 39 | max = nmemb * size; 40 | 41 | //Scan to the correct buffer 42 | while(d->next != NULL) 43 | d = d->next; 44 | 45 | //Store the data 46 | while(idx < max) 47 | { 48 | d->d[d->idx++] = ((char*)ptr)[idx++]; 49 | 50 | if(d->idx == MAX_BUFFER) 51 | { 52 | nd = malloc(sizeof(data)); 53 | nd->next = NULL; 54 | nd->idx = 0; 55 | d->next = nd; 56 | d = nd; 57 | } 58 | } 59 | 60 | return max; 61 | } 62 | 63 | void data_clean(data* d) 64 | { 65 | data* pd; 66 | while(d) 67 | { 68 | pd = d->next; 69 | free(d); 70 | d = pd; 71 | } 72 | } 73 | 74 | char* curl_make_request(char* url, char* params) 75 | { 76 | data* storage; 77 | data* curr_storage; 78 | CURL* handle; 79 | int data_len; 80 | char* retVal; 81 | 82 | assert(url != 0); 83 | assert(*url != 0); 84 | 85 | storage = malloc(sizeof(data)); 86 | storage->idx = 0; 87 | storage->next = 0; 88 | 89 | handle = curl_easy_init(); 90 | curl_easy_setopt(handle, CURLOPT_URL, url); 91 | curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, curl_callback); 92 | curl_easy_setopt(handle, CURLOPT_WRITEDATA, storage); 93 | 94 | //Do we need to add the POST parameters? 95 | if(params != NULL) 96 | { 97 | curl_easy_setopt(handle, CURLOPT_POST, 1); 98 | curl_easy_setopt(handle, CURLOPT_COPYPOSTFIELDS, params); //Copy them just incase 99 | //the user does something stupid 100 | } 101 | 102 | if(curl_easy_perform(handle) != 0) 103 | { 104 | //Error! 105 | curl_easy_cleanup(handle); 106 | data_clean(storage); 107 | return NULL; 108 | } 109 | 110 | //Everything went OK. 111 | //How long is the data? 112 | data_len = 0; 113 | curr_storage = storage; 114 | while(curr_storage) 115 | { 116 | data_len += curr_storage->idx; 117 | curr_storage = curr_storage->next; 118 | } 119 | 120 | //Allocate storage 121 | retVal = malloc(sizeof(char)*data_len); 122 | 123 | //Now copy in the data 124 | curr_storage = storage; 125 | data_len = 0; 126 | while(curr_storage) 127 | { 128 | memcpy(retVal+data_len, curr_storage->d, curr_storage->idx); 129 | curr_storage = curr_storage->next; 130 | } 131 | 132 | //Cleanup 133 | curl_easy_cleanup(handle); 134 | data_clean(storage); 135 | 136 | return retVal; 137 | } 138 | -------------------------------------------------------------------------------- /src/oauth2.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2010 Jamie Garside 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "oauth2.h" 29 | #include "curl_request.h" 30 | 31 | oauth2_config* oauth2_init(char* client, char* secret) 32 | { 33 | int input_strlen; 34 | oauth2_config* retVal = malloc(sizeof(oauth2_config)); 35 | 36 | if(retVal == NULL) 37 | return NULL; 38 | 39 | //Copy in the client id etc 40 | input_strlen = strlen(client)+1; 41 | retVal->client_id = malloc(input_strlen * sizeof(char)); 42 | strcpy(retVal->client_id, client); 43 | 44 | assert(retVal->client_id[input_strlen-1] == '\0'); 45 | 46 | input_strlen = strlen(secret)+1; 47 | retVal->client_secret = malloc(input_strlen); 48 | strcpy(retVal->client_secret, secret); 49 | 50 | assert(retVal->client_secret[input_strlen-1] == '\0'); 51 | 52 | retVal->redirect_uri = NULL; 53 | 54 | //Clear the error 55 | retVal->last_error.error = OAUTH2_ERROR_NO_ERROR; 56 | retVal->last_error.error_description = NULL; 57 | retVal->last_error.error_uri = NULL; 58 | retVal->last_error.state = NULL; 59 | retVal->auth_code = NULL; 60 | } 61 | 62 | void oauth2_set_redirect_uri(oauth2_config* conf, char* redirect_uri) 63 | { 64 | int input_strlen; 65 | 66 | assert(conf != NULL); 67 | 68 | input_strlen = strlen(redirect_uri)+1; 69 | conf->redirect_uri = malloc(sizeof(char) * input_strlen); 70 | strcpy(conf->redirect_uri, redirect_uri); 71 | } 72 | 73 | void oauth2_set_auth_code(oauth2_config* conf, char* auth_code) 74 | { 75 | int input_strlen; 76 | 77 | assert(conf != NULL); 78 | 79 | input_strlen = strlen(auth_code)+1; 80 | conf->auth_code = malloc(sizeof(char) * input_strlen); 81 | strcpy(conf->auth_code, auth_code); 82 | } 83 | 84 | char* oauth2_request_auth_code(oauth2_config* conf, char* auth_server, char* scope, char* state) 85 | { 86 | int core_len; 87 | int scope_len; 88 | int state_len; 89 | char* core_fmt; 90 | char* scope_fmt; 91 | char* state_fmt; 92 | char* final_str; 93 | 94 | scope_len = 1; 95 | state_len = 1; 96 | 97 | assert(conf != NULL); 98 | 99 | //We just need to build the request string, since we can't actually handle the callback ourselves 100 | //URL Format: ?response_type=code&client_id=&redirect_uri=&scope=&state= 101 | //Get the final length 102 | core_fmt = "%s?response_type=code&client_id=%s&redirect_uri=%s"; 103 | scope_fmt = "&scope=%s"; 104 | state_fmt = "&state=%s"; 105 | 106 | //Get the string lengths 107 | core_len = snprintf(NULL, 0, (const char*)core_fmt, auth_server, conf->client_id, conf->redirect_uri) + 1; 108 | if(scope != NULL) 109 | scope_len = snprintf(NULL, 0, (const char*)scope_fmt, scope) + 1; 110 | if(state != NULL) 111 | state_len = snprintf(NULL, 0, (const char*)state_fmt, state) + 1; 112 | 113 | //Actually build the string 114 | final_str = malloc(((core_len-1) 115 | +(scope_len-1) 116 | +(state_len-1)+1)*sizeof(char)); 117 | 118 | sprintf(final_str, (const char*)core_fmt, auth_server, conf->client_id, conf->redirect_uri); 119 | if(scope != NULL) 120 | sprintf((char*)(final_str+(core_len-1)), 121 | (const char*)scope_fmt, 122 | scope); 123 | 124 | if(state != NULL) 125 | sprintf((char*)(final_str+(core_len-1)+(scope_len-1)), 126 | (const char*)state_fmt, 127 | state); 128 | 129 | return final_str; 130 | } 131 | 132 | char* oauth2_access_auth_code(oauth2_config* conf, char* auth_server, char* auth_code, char* scope) 133 | { 134 | //Build up the request 135 | char* uri; 136 | char* query_fmt; 137 | char* output; 138 | char* acc_code; 139 | int query_len; 140 | char* acc_pos_s; 141 | char* acc_pos_e; 142 | int acc_pos_len; 143 | 144 | assert(conf != NULL); 145 | assert(auth_server != NULL); 146 | assert(auth_code != NULL); 147 | 148 | query_fmt = "grant_type=authorization_code&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s"; 149 | 150 | query_len = snprintf(NULL, 0, query_fmt, conf->client_id, conf->client_secret, auth_code, conf->redirect_uri); 151 | uri = malloc(sizeof(char)*query_len); 152 | sprintf(uri, query_fmt, conf->client_id, conf->client_secret, auth_code, conf->redirect_uri); 153 | output = curl_make_request(auth_server, uri); 154 | free(uri); 155 | 156 | //Strip out the access token 157 | acc_pos_s = strstr(output, "access_token="); 158 | if(acc_pos_s == NULL) 159 | { 160 | printf("%s\n", output); 161 | free(output); 162 | return NULL; 163 | } 164 | 165 | acc_pos_s += 13; //Skip past access_token 166 | //Find the end of the token 167 | acc_pos_e = acc_pos_s; 168 | while(*acc_pos_e != '&' && *acc_pos_e != '\0') ++acc_pos_e; 169 | 170 | //Now extract it 171 | acc_pos_len = acc_pos_e-acc_pos_s; 172 | acc_code = malloc(sizeof(char)*acc_pos_len); 173 | 174 | memcpy(acc_code, acc_pos_s, acc_pos_e-acc_pos_s); 175 | free(output); 176 | 177 | return acc_code; 178 | } 179 | 180 | char* oauth2_access_resource_owner(oauth2_config* conf, char* auth_server, char* username, char* password) 181 | { 182 | char* uri; 183 | char* query_fmt; 184 | char* output; 185 | int query_len; 186 | 187 | assert(conf != NULL); 188 | assert(auth_server != NULL); 189 | assert(username != NULL); 190 | assert(password != NULL); 191 | 192 | query_fmt = "grant_type=password&client_id=%s&username=%s&password=%s"; 193 | 194 | //Get the length of the query 195 | query_len = snprintf(NULL, 0, query_fmt, conf->client_id, username, password); 196 | 197 | //Allocate space for it and request 198 | uri = malloc(query_len+1); 199 | 200 | sprintf(uri, query_fmt, conf->client_id, username, password); 201 | 202 | //Now make the request! 203 | output = curl_make_request(auth_server, uri); 204 | 205 | //Cleanup 206 | free(uri); 207 | 208 | return output; 209 | } 210 | 211 | char* oauth2_access_refresh_token(oauth2_config* conf, char* refresh_token) 212 | { 213 | assert(0); 214 | return NULL; 215 | } 216 | 217 | char* oauth2_request(oauth2_config* conf, char* uri, char* params) 218 | { 219 | //For now, we'll just include the access code with the request vars 220 | //This is discouraged, but I don't know if most providers actually 221 | //support the header-field method (Facebook is still at draft 0...) 222 | 223 | char* retVal; 224 | char* uri2; 225 | int uri_len; 226 | 227 | //Sanity checks 228 | assert(conf != NULL); 229 | assert(conf->client_id != NULL); 230 | assert(conf->auth_code != NULL); 231 | assert(uri != NULL); 232 | 233 | //Are we POSTing? 234 | if(params != NULL) 235 | { 236 | //Attach the token to the params 237 | uri_len = snprintf(NULL, 0, "%s&access_token=%s", params, conf->auth_code); 238 | uri2 = malloc(sizeof(char)*uri_len); 239 | sprintf(uri2, "%s&access_token=%s", params, conf->auth_code); 240 | 241 | retVal = curl_make_request(uri, uri2); 242 | free(uri2); 243 | return retVal; 244 | } 245 | else 246 | { 247 | return NULL; //I'm not doing this now. 248 | } 249 | } 250 | 251 | void oauth2_cleanup(oauth2_config* conf) 252 | { 253 | if(conf == NULL) 254 | return; 255 | 256 | //Start freeing stuff up 257 | if(conf->client_id != NULL) 258 | free(conf->client_id); 259 | 260 | if(conf->client_secret != NULL) 261 | free(conf->client_secret); 262 | 263 | free(conf); 264 | } 265 | --------------------------------------------------------------------------------