#include "oauth.h" #include "http/server/request.h" #include "http/server/escape.h" #include "model/client_app.h" #include "model/account.h" #include "model/owner.h" #include "form.h" #include "json/json.h" #include "json/layout.h" #include #include #include static bool handle_oauth_authorize( struct http_request* req ) { char* client_id = NULL; char* redirect_uri = NULL; char* response_type = NULL; bool result = true; struct { bool read; bool write; bool follow; } scopes = { false, false, false }; // Parse query parameters { const char* key; while(( key = http_request_route_query_key(req) )) { if( 0 == strcmp(key,"client_id") ) { client_id = strdup(http_request_route_query_value(req)); } else if( 0 == strcmp(key,"redirect_uri") ) { redirect_uri = strdup(http_request_route_query_value(req)); // Validate redirection uri bool valid = false; if( 0 == strncmp("oauth2redirect://",redirect_uri,17) ) { valid = true; } if( !valid ) { result = false; goto cleanup; } } else if( 0 == strcmp(key,"response_type") ) { if( 0 != strcmp(http_request_route_query_value(req),"code") ) { result = false; goto cleanup; } } else if( 0 == strcmp(key,"scope") ) { char* scope = strdup(http_request_route_query_value(req)); char* resume = scope; char* item; while(( item = strtok_r( NULL, " ", &resume ) )) { if( 0 == strcmp(item,"read") ) { scopes.read = true; } else if( 0 == strcmp(item,"write") ) { scopes.write = true; } else if( 0 == strcmp(item,"follow") ) { scopes.follow = true; } else { free(scope); result = false; goto cleanup; } } free(scope); } else { printf( "key=%s\n", key ); printf( "value=%s\n\n", http_request_route_query_value(req) ); } } } struct client_app* app = client_app_from_id( client_id ); if( !app) { result =false; goto cleanup; } // Store the redirect URI for later use free(app->redirect_uri); app->redirect_uri = redirect_uri; client_app_save( app ); http_request_send_headers( req, 200, "text/html", true ); FILE* f = http_request_get_response_body( req ); #define RENDER #include "view/authorize.html.inc" #undef RENDER result = true; cleanup: free(client_id); free(redirect_uri); free(response_type); return result; } static bool handle_oauth_do_authorize( struct http_request* req ) { char* password = NULL; char* state = NULL; struct owner* o = owner_new(); struct client_app* app = NULL; FILE* body = http_request_get_request_data(req); struct form_parser* fp = form_pull_parser_new(body); if( !fp ) { goto invalid_request; } char* key; while(( key = form_pull_parser_read_key( fp ) )) { if( 0 == strcmp(key,"password") ) { password = strdup(form_pull_parser_read_value(fp)); } else if( 0 == strcmp(key,"state") ) { state = strdup(form_pull_parser_read_value(fp)); } else if( 0 == strcmp(key,"id") ) { const char* client_id = strdup(form_pull_parser_read_value(fp)); app = client_app_from_id( client_id ); if( !app ) { goto invalid_request; } } } form_pull_parser_release(fp); if( !app ) { goto invalid_request; } if( !password ) { goto invalid_request; } if( !owner_check_password( o, password ) ) { owner_free(o); free(password); goto access_denied; } client_app_gen_auth_code( app ); char location[512]; char workspace[1024]; const char* fmt = ( state ? "%s?code=%s&state=%s" : "%s?code=%s" ); snprintf( location, 512, fmt, http_escape( app->redirect_uri, workspace, 1024, ":/" ), app->auth_code, state ); printf( "redirecting to %s\n", location ); http_request_begin_send_headers( req, 302, false ); http_request_send_header( req, "Location", location ); http_request_end_send_headers( req, false ); free(password); return false; invalid_request: printf( "Invalid request\n" ); http_request_send_headers( req, 400, "text/plain", false ); fprintf( http_request_get_response_body(req), "invalid_request" ); return true; access_denied: printf( "Access denied\n" ); http_request_send_headers( req, 400, "text/plain", false ); fprintf( http_request_get_response_body(req), "access_denied" ); return true; } static bool handle_oauth_get_token( struct http_request* req ) { bool result = true; struct account* owner_account = NULL; struct owner* owner = NULL; struct client_app* app = NULL; struct data_t { char* code; char* redirect_uri; char* client_secret; char* client_id; char* grant_type; char* username; char* password; } data; memset(&data,0,sizeof(data)); FILE* post_data = http_request_get_request_data(req); const char* content_type = http_request_get_header( req, "Content-Type" ); if( 0 == strcasecmp(content_type,"application/json") ) { #define OBJ_TYPE struct data_t static struct json_object_field layout[] = { JSON_FIELD_STRING( code, false ), JSON_FIELD_STRING( redirect_uri, false ), JSON_FIELD_STRING( grant_type, false ), JSON_FIELD_STRING( client_secret, false ), JSON_FIELD_STRING( client_id, false ), JSON_FIELD_STRING( username, false ), JSON_FIELD_STRING( password, false ), JSON_FIELD_END, }; #undef OBJ_TYPE if( !json_read_object_layout_from_FILE( post_data, layout, &data ) ) { printf( "A" ); goto invalid_request; } } else { struct form_parser* fp = form_pull_parser_new(post_data); if( !fp ) { goto invalid_request; } char* key; // TODO: extend form parser to use a data layout while(( key = form_pull_parser_read_key( fp ) )) { if( 0 == strcmp(key,"grant_type") ) { data.grant_type = strdup(form_pull_parser_read_value(fp)); } else if( 0 == strcmp(key,"code") ) { data.code = strdup(form_pull_parser_read_value(fp)); } else if( 0 == strcmp(key,"client_id") ) { data.client_id = strdup(form_pull_parser_read_value(fp)); } else if( 0 == strcmp(key,"client_secret") ) { data.client_secret = strdup(form_pull_parser_read_value(fp)); } else if( 0 == strcmp(key,"redirect_uri") ) { data.redirect_uri = strdup(form_pull_parser_read_value(fp)); } else { printf( "%s=", key ); printf( "%s\n", form_pull_parser_read_value(fp) ); } } form_pull_parser_release(fp); } printf( "2" ); if( !data.redirect_uri ) { goto invalid_request; } printf( "3" ); if( !data.client_id ) { goto invalid_request; } printf( "4" ); app = client_app_from_id( data.client_id ); if( !app ) { goto invalid_request; } printf( "5" ); if( !app->redirect_uri ) { goto invalid_request; } if( 0 != strcmp( app->redirect_uri, data.redirect_uri ) ) { goto invalid_request; } printf( "6" ); if( 0 != strcmp( app->client.secret, data.client_secret ) ) { goto access_denied; } if( 0 == strcmp("authorization_code",data.grant_type) ) { if( !data.code ) { goto invalid_request; } if( 0 != strcmp( app->auth_code, data.code ) ) { goto access_denied; } } else if( 0 == strcmp("password", data.grant_type ) ) { owner_account = account_from_id(0); owner = owner_new(); if( 0 != strcmp( owner_account->handle, data.username ) ) { goto access_denied; } if( !owner_check_password( owner, data.password ) ) { goto access_denied; } } else { printf( "7" ); goto invalid_request; } // TODO: check code has not expired client_app_generate_access_token( app ); http_request_begin_send_headers( req, 200, false ); http_request_send_header( req, "Content-Type", "application/json" ); http_request_send_header( req, "Cache-Control", "no-store" ); http_request_end_send_headers( req, false ); FILE* f = http_request_get_response_body( req ); #define RENDER #include "view/token_grant.json.inc" #undef RENDER cleanup: client_app_free(app); free(data.code); free(data.redirect_uri); free(data.client_secret); free(data.client_id); free(data.grant_type); free(data.password); free(data.username); owner_free(owner); account_free(owner_account); return result; access_denied: printf( "Access denied\n" ); result = false; goto cleanup; invalid_request: printf( "Invalid request\n" ); result = false; goto cleanup; } bool route_oauth( struct http_request* req ) { if( http_request_route( req, "/authorize" ) ) { if( http_request_route_method(req,"POST") ) { return handle_oauth_do_authorize(req); } else if( http_request_route( req, "?" ) ) { return handle_oauth_authorize(req); } } else if( http_request_route( req, "/token" ) ) { if( http_request_route_method(req,"POST") ) { return handle_oauth_get_token(req); } } return false; }