├── ESP8266-Gmail-Noot ├── ESP8266-Gmail-Noot.ino ├── README.md └── data │ └── config.json ├── ESP8266-OAUTH2 ├── ESP8266-OAUTH2.ino └── equifax.ino ├── LICENSE └── README.md /ESP8266-Gmail-Noot/ESP8266-Gmail-Noot.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | /* 7 | * ============ Variables ============ 8 | */ 9 | 10 | //#define DEBUG true 11 | #define FILE_NAME "/config.json" // config on SPIFFS 12 | #define LED_PIN 2 // built-in LED 13 | 14 | struct settings { 15 | char *wifi_ssid; 16 | char *wifi_pass; 17 | char *client_id; 18 | char *client_secret; 19 | char *refresh_token; 20 | } my_setup; 21 | 22 | // Poll settings 23 | const int POLL_INTERVAL = 10; 24 | unsigned long POLL_MILLIS = 0; 25 | 26 | /* 27 | * ============ OAUTH2 ============ 28 | */ 29 | 30 | // SSL Setup 31 | // http://askubuntu.com/questions/156620/how-to-verify-the-ssl-fingerprint-by-command-line-wget-curl/ 32 | // echo | openssl s_client -connect www.googleapis.com:443 | openssl x509 -fingerprint -noout 33 | const int httpsPort = 443; 34 | const char *host = "www.googleapis.com"; 35 | const char *fingerprint1 = "A6 7A 38 10 2C 29 27 9F F5 91 52 92 49 F2 2A E7 C0 B4 20 A8"; 36 | const char *fingerprint2 = "39 F2 B5 65 4F C9 E2 EF 46 F1 8E BC 66 15 E2 72 79 20 94 46"; 37 | const char *fingerprint3 = "73 56 DE 7F 17 31 42 9C E4 00 B6 5E E2 8F 59 6E 43 D5 1F 79"; 38 | 39 | // OAUTH2 Basics 40 | String access_type = "offline"; 41 | String redirect_uri = "urn:ietf:wg:oauth:2.0:oob"; 42 | String response_type = "code"; 43 | String auth_uri = "https://accounts.google.com/o/oauth2/auth"; 44 | String info_uri = "/oauth2/v3/tokeninfo"; 45 | String token_uri = "/oauth2/v4/token"; 46 | String authorization_code = ""; // leave empty 47 | String access_token = ""; // leave empty 48 | 49 | 50 | // Send messages only. No read or modify privileges on mailbox. 51 | String gmail_scope = "https://www.googleapis.com/auth/gmail.readonly"; 52 | // required to determine which user really authenticated 53 | String scope = "email " + gmail_scope; 54 | 55 | static const int ERROR_STATE = -1; 56 | static const int INITIAL_STATE = 0; 57 | static const int AWAIT_CHALLANGE = 1; 58 | static const int EXCHANGING = 2; 59 | static const int INFO = 3; 60 | static const int REFRESHING = 4; 61 | static const int DO_IT = 5; 62 | static const int END_STATE = 6; 63 | 64 | // Set global variable attributes. 65 | static int CURRENT_STATE = INITIAL_STATE; 66 | 67 | String parseResponse(String response, String key) { 68 | DynamicJsonBuffer jsonBuffer; 69 | JsonObject& root = jsonBuffer.parseObject(response); 70 | 71 | if (!root.success()) { 72 | Serial.println("parseObject() failed"); 73 | return ""; 74 | } else { 75 | return root[key]; 76 | String output; 77 | root.printTo(output); 78 | Serial.print("JSON: "); Serial.println(output); 79 | return output; 80 | } 81 | } 82 | 83 | String getRequest(const char* server, String request) { 84 | #ifdef DEBUG 85 | Serial.print("Function: "); Serial.println("getRequest()"); 86 | #endif 87 | 88 | String result = ""; 89 | 90 | // Use WiFiClientSecure class to create TLS connection 91 | WiFiClientSecure client; 92 | 93 | #ifdef DEBUG 94 | Serial.print("Connecting to: "); Serial.println(server); 95 | #endif 96 | 97 | if (!client.connect(server, httpsPort)) { 98 | Serial.println("connection failed"); 99 | return result; 100 | } 101 | 102 | if (client.verify(fingerprint1, server) || client.verify(fingerprint2, server) || client.verify(fingerprint3, server)) { 103 | #ifdef DEBUG 104 | Serial.println("certificate matches"); 105 | Serial.print("get: "); Serial.println(request); 106 | #endif 107 | 108 | client.print(request); 109 | 110 | #ifdef DEBUG 111 | Serial.println("request sent"); 112 | Serial.println("Receiving response"); 113 | #endif 114 | 115 | while (client.connected()) { 116 | if(client.find("HTTP/1.1 ")) { 117 | String status_code = client.readStringUntil('\r'); 118 | #ifdef DEBUG 119 | Serial.print("Status code: "); Serial.println(status_code); 120 | #endif 121 | if(status_code == "401 Unauthorized") { 122 | // lets see if access_token expired 123 | CURRENT_STATE = INFO; 124 | } else if(status_code != "200 OK") { 125 | Serial.println("There was an error"); 126 | break; 127 | } 128 | } 129 | if(client.find("\r\n\r\n")) { 130 | String line = client.readStringUntil('\r'); 131 | #ifdef DEBUG 132 | Serial.println("Data:"); 133 | Serial.println(line); 134 | #endif 135 | result += line; 136 | } 137 | } 138 | #ifdef DEBUG 139 | Serial.println("closing connection"); 140 | #endif 141 | return result; 142 | } else { 143 | Serial.println("certificate doesn't match"); 144 | } 145 | return result; 146 | } 147 | 148 | 149 | String postRequest(const char* server, String header, String data) { 150 | #ifdef DEBUG 151 | Serial.print("Function: "); Serial.println("postRequest()"); 152 | #endif 153 | 154 | String result = ""; 155 | 156 | // Use WiFiClientSecure class to create TLS connection 157 | WiFiClientSecure client; 158 | #ifdef DEBUG 159 | Serial.print("Connecting to: "); Serial.println(server); 160 | #endif 161 | 162 | if (!client.connect(server, httpsPort)) { 163 | Serial.println("connection failed"); 164 | return result; 165 | } 166 | 167 | if (client.verify(fingerprint1, server) || client.verify(fingerprint2, server) || client.verify(fingerprint3, server)) { 168 | #ifdef DEBUG 169 | Serial.println("certificate matches"); 170 | Serial.print("post: "); Serial.println(header + data); 171 | #endif 172 | 173 | client.print(header + data); 174 | 175 | #ifdef DEBUG 176 | Serial.println("request sent"); 177 | Serial.println("Receiving response"); 178 | #endif 179 | 180 | while (client.connected()) { 181 | if(client.find("HTTP/1.1 ")) { 182 | String status_code = client.readStringUntil('\r'); 183 | #ifdef DEBUG 184 | Serial.print("Status code: "); Serial.println(status_code); 185 | #endif 186 | if(status_code != "200 OK") { 187 | Serial.println("There was an error"); 188 | break; 189 | } 190 | } 191 | if(client.find("\r\n\r\n")) { 192 | String line = client.readStringUntil('\r'); 193 | #ifdef DEBUG 194 | Serial.println("Data:"); 195 | Serial.println(line); 196 | #endif 197 | result += line; 198 | } 199 | } 200 | 201 | #ifdef DEBUG 202 | Serial.println("closing connection"); 203 | #endif 204 | return result; 205 | } else { 206 | Serial.println("certificate doesn't match"); 207 | } 208 | return result; 209 | } 210 | 211 | // create URL 212 | void authorize() { 213 | #ifdef DEBUG 214 | Serial.print("Function: "); Serial.println("authorize()"); 215 | #endif 216 | if (isEqual(my_setup.refresh_token, "")) { 217 | String URL = auth_uri + "?"; 218 | URL += "scope=" + urlencode(scope); 219 | URL += "&redirect_uri=" + urlencode(redirect_uri); 220 | URL += "&response_type=" + urlencode(response_type); 221 | URL += "&client_id=" + urlencode(my_setup.client_id); 222 | URL += "&access_type=" + urlencode(access_type); 223 | Serial.println("Goto URL: "); 224 | Serial.println(URL); Serial.println(); 225 | Serial.print("Enter code: "); 226 | CURRENT_STATE = AWAIT_CHALLANGE; 227 | } else { 228 | CURRENT_STATE = INFO; 229 | } 230 | } 231 | 232 | bool exchange() { 233 | #ifdef DEBUG 234 | Serial.print("Function: "); Serial.println("exchange()"); 235 | #endif 236 | 237 | if (authorization_code != "") { 238 | 239 | String postData = ""; 240 | postData += "code=" + authorization_code; 241 | postData += "&client_id=" + (String)my_setup.client_id; 242 | postData += "&client_secret=" + (String)my_setup.client_secret; 243 | postData += "&redirect_uri=" + redirect_uri; 244 | postData += "&grant_type=" + String("authorization_code"); 245 | 246 | String postHeader = ""; 247 | postHeader += ("POST " + token_uri + " HTTP/1.1\r\n"); 248 | postHeader += ("Host: " + String(host) + ":" + String(httpsPort) + "\r\n"); 249 | postHeader += ("Connection: close\r\n"); 250 | postHeader += ("Content-Type: application/x-www-form-urlencoded\r\n"); 251 | postHeader += ("Content-Length: "); 252 | postHeader += (postData.length()); 253 | postHeader += ("\r\n\r\n"); 254 | 255 | String result = postRequest(host, postHeader, postData); 256 | String refresh_token = parseResponse(result, "refresh_token"); 257 | if(refresh_token != "") { 258 | // FIXME! 259 | Serial.print("refresh_token: "); Serial.println(refresh_token); 260 | char rt[refresh_token.length() + 1]; 261 | refresh_token.toCharArray(rt, refresh_token.length() + 1); 262 | my_setup.refresh_token = strdup(rt); 263 | CURRENT_STATE = END_STATE; 264 | return true; 265 | } else { 266 | return false; 267 | } 268 | } else { 269 | return false; 270 | } 271 | } 272 | 273 | void refresh() { 274 | #ifdef DEBUG 275 | Serial.print("Function: "); Serial.println("refresh()"); 276 | #endif 277 | if(!isEqual(my_setup.refresh_token, "")) { 278 | 279 | String postData = ""; 280 | postData += "refresh_token=" + (String)my_setup.refresh_token; 281 | postData += "&client_id=" + (String)my_setup.client_id; 282 | postData += "&client_secret=" + (String)my_setup.client_secret; 283 | postData += "&grant_type=" + String("refresh_token"); 284 | 285 | String postHeader = ""; 286 | postHeader += ("POST " + token_uri + " HTTP/1.1\r\n"); 287 | postHeader += ("Host: " + String(host) + ":" + String(httpsPort) + "\r\n"); 288 | postHeader += ("Connection: close\r\n"); 289 | postHeader += ("Content-Type: application/x-www-form-urlencoded\r\n"); 290 | postHeader += ("Content-Length: "); 291 | postHeader += (postData.length()); 292 | postHeader += ("\r\n\r\n"); 293 | 294 | String result = postRequest(host, postHeader, postData); 295 | access_token = parseResponse(result, String("access_token")); 296 | #ifdef DEBUG 297 | Serial.print("access_token: "); Serial.println(access_token); 298 | #endif 299 | if(access_token != "") { 300 | CURRENT_STATE = DO_IT; 301 | } else { 302 | CURRENT_STATE = END_STATE; 303 | } 304 | } 305 | } 306 | 307 | bool info() { 308 | #ifdef DEBUG 309 | Serial.print("Function: "); Serial.println("info()"); 310 | #endif 311 | if (access_token != "") { 312 | 313 | String reqHeader = ""; 314 | reqHeader += ("GET " + info_uri + "?access_token=" + access_token + " HTTP/1.1\r\n"); 315 | reqHeader += ("Host: " + String(host) + ":" + String(httpsPort) + "\r\n"); 316 | reqHeader += ("Connection: close\r\n"); 317 | reqHeader += ("\r\n\r\n"); 318 | String result = getRequest(host, reqHeader); 319 | // need to check for valid token here 320 | // Look for expires_in 321 | String expires_in = parseResponse(result, String("expires_in")); 322 | Serial.print("expires_in: "); Serial.println(expires_in); 323 | if(expires_in.toInt() >= 300) { // got 5 min.? 324 | CURRENT_STATE = DO_IT; 325 | } else { 326 | CURRENT_STATE = REFRESHING; 327 | } 328 | } else { 329 | CURRENT_STATE = REFRESHING; 330 | return false; 331 | } 332 | return true; 333 | } 334 | 335 | /* 336 | * ============ Filesystem ============ 337 | */ 338 | 339 | void cleanFS() { 340 | //clean FS, for testing 341 | Serial.println("formatting file system"); 342 | SPIFFS.format(); 343 | Serial.println("done."); 344 | } 345 | 346 | void printConfig() { 347 | Serial.print("wifi_ssid: "); Serial.println(my_setup.wifi_ssid); 348 | Serial.print("wifi_pass: "); Serial.println(my_setup.wifi_pass); 349 | Serial.print("client_id: "); Serial.println(my_setup.client_id); 350 | Serial.print("client_secret: "); Serial.println(my_setup.client_secret); 351 | Serial.print("refresh_token: "); Serial.println(my_setup.refresh_token); 352 | } 353 | 354 | bool writeConfig() { 355 | bool result = false; 356 | if (SPIFFS.begin()) { 357 | Serial.println("mounted file system"); 358 | 359 | Serial.println("open config file for writing"); 360 | File configFile = SPIFFS.open(FILE_NAME, "w+"); 361 | 362 | if (configFile) { 363 | Serial.println("opened config file"); 364 | DynamicJsonBuffer jsonBuffer; 365 | JsonObject& jsonConfig = jsonBuffer.createObject(); 366 | jsonConfig["wifi_ssid"] = my_setup.wifi_ssid; 367 | jsonConfig["wifi_pass"] = my_setup.wifi_pass; 368 | jsonConfig["client_id"] = my_setup.client_id; 369 | jsonConfig["client_secret"] = my_setup.client_secret; 370 | jsonConfig["refresh_token"] = my_setup.refresh_token; 371 | jsonConfig.prettyPrintTo(Serial); Serial.println(); 372 | jsonConfig.printTo(configFile); 373 | result = true; 374 | } 375 | 376 | Serial.println("closing config file"); 377 | configFile.close(); 378 | 379 | } else { 380 | Serial.println("failed to mount FS"); 381 | } 382 | Serial.println("unmounting file system"); 383 | SPIFFS.end(); 384 | return result; 385 | } 386 | 387 | bool readConfig() { 388 | 389 | bool result = false; 390 | 391 | if (SPIFFS.begin()) { 392 | Serial.println("mounted file system"); 393 | if (SPIFFS.exists(FILE_NAME)) { 394 | //file exists, reading and loading 395 | Serial.println("reading config file"); 396 | File configFile = SPIFFS.open(FILE_NAME, "r"); 397 | if (configFile) { 398 | Serial.println("opened config file"); 399 | size_t size = configFile.size(); 400 | 401 | // Allocate a buffer to store contents of the file. 402 | std::unique_ptr buf(new char[size]); 403 | configFile.readBytes(buf.get(), size); 404 | DynamicJsonBuffer jsonBuffer; 405 | JsonObject& jsonConfig = jsonBuffer.parseObject(buf.get()); 406 | 407 | if (jsonConfig.success()) { 408 | Serial.println("config parsed successfully"); 409 | jsonConfig.prettyPrintTo(Serial); Serial.println(); 410 | 411 | my_setup.wifi_ssid = (jsonConfig["wifi_ssid"] ? strdup(jsonConfig["wifi_ssid"]) : strdup("")); 412 | my_setup.wifi_pass = (jsonConfig["wifi_pass"] ? strdup(jsonConfig["wifi_pass"]) : strdup("")); 413 | my_setup.client_id = (jsonConfig["client_id"] ? strdup(jsonConfig["client_id"]) : strdup("")); 414 | my_setup.client_secret = (jsonConfig["client_secret"] ? strdup(jsonConfig["client_secret"]) : strdup("")); 415 | my_setup.refresh_token = (jsonConfig["refresh_token"] ? strdup(jsonConfig["refresh_token"]) : strdup("")); 416 | 417 | result = true; 418 | } else { 419 | Serial.println("failed to load json config"); 420 | } 421 | } 422 | Serial.println("closing config file"); 423 | configFile.close(); 424 | } else { 425 | Serial.println("No config file found."); 426 | } 427 | Serial.println("unmounting file system"); 428 | SPIFFS.end(); 429 | } else { 430 | Serial.println("failed to mount FS"); 431 | } 432 | return result; 433 | } 434 | 435 | /* 436 | * ============ Gmail Noot ============ 437 | */ 438 | 439 | bool getUnreadCount() { 440 | // GET https://www.googleapis.com/gmail/v1/users/userId/threads 441 | 442 | if(millis() - POLL_MILLIS >= POLL_INTERVAL * 1000UL || POLL_MILLIS == 0) { 443 | String reqHeader = ""; 444 | reqHeader += ("GET /gmail/v1/users/me/threads?"); 445 | reqHeader += ("access_token=" + access_token); 446 | reqHeader += ("&labelIds=INBOX"); 447 | reqHeader += ("&q=is:unread"); 448 | reqHeader += ("&fields=resultSizeEstimate"); 449 | reqHeader += (" HTTP/1.1\r\n"); 450 | reqHeader += ("Host: " + String(host) + ":" + String(httpsPort) + "\r\n"); 451 | reqHeader += ("Connection: close\r\n"); 452 | reqHeader += ("\r\n\r\n"); 453 | 454 | String result = getRequest(host, reqHeader); 455 | String unread = parseResponse(result, String("resultSizeEstimate")); 456 | 457 | #ifdef DEBUG 458 | Serial.print("unread: "); Serial.println(unread); 459 | #endif 460 | 461 | if(unread.toInt() > 0) { // you got mail. 462 | Serial.print("You got "); Serial.print(unread); Serial.println(" unread mail thread(s) in your inbox."); 463 | digitalWrite(LED_PIN, LOW); // noot noot 464 | } else { 465 | digitalWrite(LED_PIN, HIGH); 466 | } 467 | 468 | POLL_MILLIS = millis(); 469 | return true; 470 | } else { 471 | return false; 472 | } 473 | } 474 | 475 | /* 476 | * ============ General ============ 477 | */ 478 | 479 | bool isEqual(const char *stra, const char *strb) { 480 | if(strcasecmp(stra, strb) == 0) { 481 | return true; 482 | } else { 483 | return false; 484 | } 485 | } 486 | 487 | String urlencode(String str) 488 | { 489 | String encodedString = ""; 490 | char c; 491 | char code0; 492 | char code1; 493 | char code2; 494 | for (int i = 0; i < str.length(); i++) { 495 | c = str.charAt(i); 496 | if (c == ' ') { 497 | encodedString += '+'; 498 | } else if (isalnum(c)) { 499 | encodedString += c; 500 | } else { 501 | code1 = (c & 0xf) + '0'; 502 | if ((c & 0xf) > 9) { 503 | code1 = (c & 0xf) - 10 + 'A'; 504 | } 505 | c = (c >> 4) & 0xf; 506 | code0 = c + '0'; 507 | if (c > 9) { 508 | code0 = c - 10 + 'A'; 509 | } 510 | code2 = '\0'; 511 | encodedString += '%'; 512 | encodedString += code0; 513 | encodedString += code1; 514 | //encodedString+=code2; 515 | } 516 | yield(); 517 | } 518 | return encodedString; 519 | } 520 | 521 | // enable Serial communication 522 | String serialComm() { 523 | String result = ""; 524 | while (Serial.available()) { 525 | String inputString = Serial.readString(); 526 | inputString.trim(); 527 | if(inputString != ""){ 528 | if (inputString == "clean") { 529 | cleanFS(); 530 | } else if (inputString == "read") { 531 | readConfig(); 532 | } else if (inputString == "write") { 533 | writeConfig(); 534 | } else if (inputString == "print") { 535 | printConfig(); 536 | } else { 537 | result = inputString; 538 | } 539 | } 540 | } 541 | return result; 542 | } 543 | 544 | void setup() { 545 | Serial.begin(115200); Serial.println(); 546 | pinMode(LED_PIN, OUTPUT); 547 | if(readConfig() && strlen(my_setup.wifi_ssid) > 0) { 548 | Serial.print("Connecting to: "); Serial.println(my_setup.wifi_ssid); 549 | WiFi.begin(my_setup.wifi_ssid, my_setup.wifi_pass); 550 | while (WiFi.status() != WL_CONNECTED) { 551 | delay(500); 552 | Serial.print("."); 553 | } 554 | Serial.println(); 555 | Serial.print("WiFi connected. "); Serial.print("IP address: "); Serial.println(WiFi.localIP()); 556 | Serial.println(); 557 | } else { 558 | Serial.println("No Wireless config."); 559 | CURRENT_STATE = END_STATE; 560 | } 561 | } 562 | 563 | void loop() { 564 | serialComm(); 565 | switch (CURRENT_STATE) { 566 | case INITIAL_STATE: 567 | authorize(); 568 | break; 569 | case AWAIT_CHALLANGE: 570 | authorization_code = serialComm(); 571 | if (authorization_code != "") { 572 | Serial.println(authorization_code); 573 | CURRENT_STATE = EXCHANGING; 574 | } 575 | break; 576 | case EXCHANGING: 577 | exchange(); 578 | break; 579 | case INFO: 580 | info(); 581 | break; 582 | case REFRESHING: 583 | refresh(); 584 | break; 585 | case DO_IT: 586 | getUnreadCount(); 587 | break; 588 | case END_STATE: 589 | break; 590 | default: 591 | Serial.println("ERROR"); 592 | break; 593 | } 594 | } 595 | -------------------------------------------------------------------------------- /ESP8266-Gmail-Noot/README.md: -------------------------------------------------------------------------------- 1 | This is a PoC to demonstrate how to poll your Gmail Inbox for unread mail threads and make an LED light-up accordingly. 2 | -------------------------------------------------------------------------------- /ESP8266-Gmail-Noot/data/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "wifi_ssid": "", 3 | "wifi_pass": "", 4 | "client_id": "", 5 | "client_secret": "", 6 | "refresh_token": "" 7 | } 8 | -------------------------------------------------------------------------------- /ESP8266-OAUTH2/ESP8266-OAUTH2.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /* 8 | * ===================================== 9 | * Start editing your configuration here 10 | */ 11 | 12 | //#define DEBUG true 13 | 14 | // Configure TimeZone 15 | #define TZ 1 // UTC+1 16 | #define DST_MIN 0 // One hour for summer time 17 | #define TZ_SEC ((TZ)*3600) 18 | #define DST_SEC ((DST_MIN)*60) 19 | 20 | // WiFi Setup 21 | const char* ssid = ""; 22 | const char* pass = ""; 23 | 24 | // OAUTH2 Client credentials 25 | String client_id = ""; 26 | String client_secret = ""; 27 | 28 | // Tokens 29 | String access_token = ""; 30 | String refresh_token = ""; 31 | 32 | // Email Setup 33 | String email_from = ""; 34 | String email_to = ""; 35 | 36 | // Sheets Setup 37 | String sheet_id = ""; 38 | String sheet_range = "Sheet1!A:B"; 39 | 40 | /* ==================================== 41 | * Stop editing your configuration here 42 | */ 43 | 44 | // SSL Setup 45 | // http://askubuntu.com/questions/156620/how-to-verify-the-ssl-fingerprint-by-command-line-wget-curl/ 46 | // echo | openssl s_client -connect www.googleapis.com:443 | openssl x509 -fingerprint -noout 47 | const char* host = "www.googleapis.com"; 48 | const char* sheetsHost = "sheets.googleapis.com"; 49 | const int httpsPort = 443; 50 | const char* fingerprint = "A6 7A 38 10 2C 29 27 9F F5 91 52 92 49 F2 2A E7 C0 B4 20 A8"; 51 | extern const unsigned char data_equifax_der[] PROGMEM; 52 | extern const unsigned int data_equifax_der_len; 53 | 54 | // OAUTH2 Basics 55 | String access_type = "offline"; 56 | String redirect_uri = "urn:ietf:wg:oauth:2.0:oob"; 57 | String response_type = "code"; 58 | String auth_uri = "https://accounts.google.com/o/oauth2/auth"; 59 | String info_uri = "/oauth2/v3/tokeninfo"; 60 | String token_uri = "/oauth2/v4/token"; 61 | String authorization_code = ""; // leave empty 62 | 63 | // Send messages only. No read or modify privileges on mailbox. 64 | String gmail_scope = "https://www.googleapis.com/auth/gmail.send"; 65 | // Allows read/write access to the user's sheets and their properties. 66 | String sheet_scope = "https://www.googleapis.com/auth/spreadsheets"; 67 | // required to determine which user really authenticated 68 | String scope = "email " + gmail_scope + " " + sheet_scope; 69 | 70 | static const int ERROR_STATE = -1; 71 | static const int INITIAL_STATE = 0; 72 | static const int AWAIT_CHALLANGE = 1; 73 | static const int EXCHANGING = 2; 74 | static const int INFO = 3; 75 | static const int REFRESHING = 4; 76 | static const int DO_IT = 5; 77 | static const int END_STATE = 6; 78 | 79 | // Set global variable attributes. 80 | static int CURRENT_STATE = INITIAL_STATE; 81 | 82 | 83 | bool appendToSheet() { 84 | 85 | String postData = ""; 86 | postData += "{\n \"values\": [[\"brasel\",\"fink\"]]\n}"; 87 | 88 | String postHeader = ""; 89 | postHeader += ("POST /v4/spreadsheets/" + sheet_id + "/values/" + sheet_range + ":append" + "?valueInputOption=raw" + " HTTP/1.1\r\n"); 90 | postHeader += ("Host: " + String(sheetsHost) + ":" + String(httpsPort) + "\r\n"); 91 | postHeader += ("Connection: close\r\n"); 92 | postHeader += ("Authorization: Bearer " + access_token + "\r\n"); 93 | postHeader += ("Content-Type: application/json; charset=UTF-8\r\n"); 94 | postHeader += ("Content-Length: "); 95 | postHeader += (postData.length()); 96 | postHeader += ("\r\n\r\n"); 97 | 98 | String result = postRequest(sheetsHost, postHeader, postData); 99 | 100 | return true; 101 | } 102 | 103 | bool getSheetContent() { 104 | // GET https://sheets.googleapis.com/v4/spreadsheets/spreadsheetId/values/Sheet1!A1:D5 105 | if (sheet_id != "" && sheet_range != "") { 106 | 107 | String reqHeader = ""; 108 | reqHeader += ("GET /v4/spreadsheets/" + sheet_id + "/values/" + sheet_range + "?access_token=" + access_token + " HTTP/1.1\r\n"); 109 | reqHeader += ("Host: " + String(sheetsHost) + ":" + String(httpsPort) + "\r\n"); 110 | reqHeader += ("Connection: close\r\n"); 111 | reqHeader += ("\r\n\r\n"); 112 | 113 | String result = getRequest(sheetsHost, reqHeader); 114 | return true; 115 | } else { 116 | return false; 117 | } 118 | } 119 | 120 | bool sendEmail(String body) { 121 | if (body != "") { 122 | String userId = "me"; 123 | String headers = ""; 124 | headers += "From: " + email_from + "\n"; 125 | headers += "To: " + email_to + "\n"; 126 | headers += "Subject: I love you!\n\n"; 127 | 128 | String email = base64::encode(headers + body); 129 | email.replace("\n", ""); 130 | 131 | String postData = ""; 132 | postData += "{\n \"raw\": \"" + email + "\"\n}"; 133 | 134 | String postHeader = ""; 135 | postHeader += ("POST /gmail/v1/users/" + userId + "/messages/send" + " HTTP/1.1\r\n"); 136 | postHeader += ("Host: " + String(host) + ":" + String(httpsPort) + "\r\n"); 137 | postHeader += ("Connection: close\r\n"); 138 | postHeader += ("Authorization: Bearer " + access_token + "\r\n"); 139 | postHeader += ("Content-Type: application/json; charset=UTF-8\r\n"); 140 | postHeader += ("Content-Length: "); 141 | postHeader += (postData.length()); 142 | postHeader += ("\r\n\r\n"); 143 | 144 | String result = postRequest(host, postHeader, postData); 145 | 146 | return true; 147 | } else { 148 | return false; 149 | } 150 | } 151 | 152 | String parseResponse(String response) { 153 | DynamicJsonBuffer jsonBuffer; 154 | JsonObject& root = jsonBuffer.parseObject(response); 155 | 156 | if (!root.success()) { 157 | Serial.println("parseObject() failed"); 158 | return ""; 159 | } else { 160 | String output; 161 | root.printTo(output); 162 | Serial.print("JSON: "); Serial.println(output); 163 | return output; 164 | } 165 | } 166 | 167 | 168 | String getRequest(const char* server, String request) { 169 | #ifdef DEBUG 170 | Serial.print("Function: "); Serial.println("getRequest()"); 171 | #endif 172 | 173 | String result = ""; 174 | 175 | // Use WiFiClientSecure class to create TLS connection 176 | WiFiClientSecure client; 177 | Serial.print("Connecting to: "); Serial.println(server); 178 | 179 | if (!client.connect(server, httpsPort)) { 180 | Serial.println("connection failed"); 181 | return result; 182 | } 183 | 184 | if(!client.setCACert_P(data_equifax_der, data_equifax_der_len)) { 185 | Serial.println(" Failed!"); 186 | } 187 | 188 | if(client.verifyCertChain(server)) { 189 | 190 | // if (client.verify(fingerprint, server)) { 191 | #ifdef DEBUG 192 | Serial.println("certificate matches"); 193 | Serial.print("get: "); Serial.println(request); 194 | #endif 195 | 196 | client.print(request); 197 | 198 | #ifdef DEBUG 199 | Serial.println("request sent"); 200 | Serial.println("Receiving response"); 201 | #endif 202 | 203 | while (client.connected()) { 204 | if(client.find("HTTP/1.1 ")) { 205 | String status_code = client.readStringUntil('\r'); 206 | Serial.print("Status code: "); Serial.println(status_code); 207 | if(status_code != "200 OK") { 208 | Serial.println("There was an error"); 209 | break; 210 | } 211 | } 212 | if(client.find("\r\n\r\n")) { 213 | Serial.println("Data:"); 214 | } 215 | String line = client.readStringUntil('\r'); 216 | Serial.println(line); 217 | result += line; 218 | } 219 | 220 | Serial.println("closing connection"); 221 | return result; 222 | } else { 223 | Serial.println("certificate doesn't match"); 224 | } 225 | return result; 226 | } 227 | 228 | 229 | String postRequest(const char* server, String header, String data) { 230 | #ifdef DEBUG 231 | Serial.print("Function: "); Serial.println("postRequest()"); 232 | #endif 233 | 234 | String result = ""; 235 | 236 | // Use WiFiClientSecure class to create TLS connection 237 | WiFiClientSecure client; 238 | Serial.print("Connecting to: "); Serial.println(server); 239 | 240 | if (!client.connect(server, httpsPort)) { 241 | Serial.println("connection failed"); 242 | return result; 243 | } 244 | 245 | if(!client.setCACert_P(data_equifax_der, data_equifax_der_len)) { 246 | Serial.println(" Failed!"); 247 | } 248 | 249 | if(client.verifyCertChain(server)) { 250 | 251 | // if (client.verify(fingerprint, server)) { 252 | #ifdef DEBUG 253 | Serial.println("certificate matches"); 254 | Serial.print("post: "); Serial.println(header + data); 255 | #endif 256 | 257 | client.print(header + data); 258 | 259 | #ifdef DEBUG 260 | Serial.println("request sent"); 261 | Serial.println("Receiving response"); 262 | #endif 263 | 264 | while (client.connected()) { 265 | if(client.find("HTTP/1.1 ")) { 266 | String status_code = client.readStringUntil('\r'); 267 | Serial.print("Status code: "); Serial.println(status_code); 268 | if(status_code != "200 OK") { 269 | Serial.println("There was an error"); 270 | break; 271 | } 272 | } 273 | if(client.find("\r\n\r\n")) { 274 | Serial.println("Data:"); 275 | } 276 | String line = client.readStringUntil('\r'); 277 | Serial.println(line); 278 | result += line; 279 | } 280 | 281 | Serial.println("closing connection"); 282 | return result; 283 | } else { 284 | Serial.println("certificate doesn't match"); 285 | } 286 | return result; 287 | } 288 | 289 | // create URL 290 | void authorize() { 291 | #ifdef DEBUG 292 | Serial.print("Function: "); Serial.println("authorize()"); 293 | #endif 294 | if (refresh_token == "") { 295 | String URL = auth_uri + "?"; 296 | URL += "scope=" + urlencode(scope); 297 | URL += "&redirect_uri=" + urlencode(redirect_uri); 298 | URL += "&response_type=" + urlencode(response_type); 299 | URL += "&client_id=" + urlencode(client_id); 300 | URL += "&access_type=" + urlencode(access_type); 301 | Serial.println("Goto URL: "); 302 | Serial.println(URL); Serial.println(); 303 | Serial.print("Enter code: "); 304 | CURRENT_STATE = AWAIT_CHALLANGE; 305 | } else { 306 | CURRENT_STATE = INFO; 307 | } 308 | } 309 | 310 | bool exchange() { 311 | #ifdef DEBUG 312 | Serial.print("Function: "); Serial.println("exchange()"); 313 | #endif 314 | 315 | if (authorization_code != "") { 316 | 317 | String postData = ""; 318 | postData += "code=" + authorization_code; 319 | postData += "&client_id=" + client_id; 320 | postData += "&client_secret=" + client_secret; 321 | postData += "&redirect_uri=" + redirect_uri; 322 | postData += "&grant_type=" + String("authorization_code"); 323 | 324 | String postHeader = ""; 325 | postHeader += ("POST " + token_uri + " HTTP/1.1\r\n"); 326 | postHeader += ("Host: " + String(host) + ":" + String(httpsPort) + "\r\n"); 327 | postHeader += ("Connection: close\r\n"); 328 | postHeader += ("Content-Type: application/x-www-form-urlencoded\r\n"); 329 | postHeader += ("Content-Length: "); 330 | postHeader += (postData.length()); 331 | postHeader += ("\r\n\r\n"); 332 | 333 | String result = postRequest(host, postHeader, postData); 334 | 335 | CURRENT_STATE = END_STATE; 336 | return true; 337 | } else { 338 | return false; 339 | } 340 | } 341 | 342 | bool refresh() { 343 | #ifdef DEBUG 344 | Serial.print("Function: "); Serial.println("refresh()"); 345 | #endif 346 | if (refresh_token != "") { 347 | 348 | String postData = ""; 349 | postData += "refresh_token=" + refresh_token; 350 | postData += "&client_id=" + client_id; 351 | postData += "&client_secret=" + client_secret; 352 | postData += "&grant_type=" + String("refresh_token"); 353 | 354 | String postHeader = ""; 355 | postHeader += ("POST " + token_uri + " HTTP/1.1\r\n"); 356 | postHeader += ("Host: " + String(host) + ":" + String(httpsPort) + "\r\n"); 357 | postHeader += ("Connection: close\r\n"); 358 | postHeader += ("Content-Type: application/x-www-form-urlencoded\r\n"); 359 | postHeader += ("Content-Length: "); 360 | postHeader += (postData.length()); 361 | postHeader += ("\r\n\r\n"); 362 | 363 | String result = postRequest(host, postHeader, postData); 364 | 365 | CURRENT_STATE = END_STATE; 366 | return true; 367 | } else { 368 | return false; 369 | } 370 | } 371 | 372 | bool info() { 373 | #ifdef DEBUG 374 | Serial.print("Function: "); Serial.println("info()"); 375 | #endif 376 | if (access_token != "") { 377 | 378 | String reqHeader = ""; 379 | reqHeader += ("GET " + info_uri + "?access_token=" + access_token + " HTTP/1.1\r\n"); 380 | reqHeader += ("Host: " + String(host) + ":" + String(httpsPort) + "\r\n"); 381 | reqHeader += ("Connection: close\r\n"); 382 | reqHeader += ("\r\n\r\n"); 383 | String result = getRequest(host, reqHeader); 384 | 385 | // need to check for valid token here 386 | 387 | CURRENT_STATE = DO_IT; 388 | } else { 389 | CURRENT_STATE = REFRESHING; 390 | return false; 391 | } 392 | return true; 393 | } 394 | 395 | String urlencode(String str) 396 | { 397 | String encodedString = ""; 398 | char c; 399 | char code0; 400 | char code1; 401 | for (unsigned int i = 0; i < str.length(); i++) { 402 | c = str.charAt(i); 403 | if (c == ' ') { 404 | encodedString += '+'; 405 | } else if (isalnum(c)) { 406 | encodedString += c; 407 | } else { 408 | code1 = (c & 0xf) + '0'; 409 | if ((c & 0xf) > 9) { 410 | code1 = (c & 0xf) - 10 + 'A'; 411 | } 412 | c = (c >> 4) & 0xf; 413 | code0 = c + '0'; 414 | if (c > 9) { 415 | code0 = c - 10 + 'A'; 416 | } 417 | encodedString += '%'; 418 | encodedString += code0; 419 | encodedString += code1; 420 | } 421 | yield(); 422 | } 423 | return encodedString; 424 | 425 | } 426 | 427 | // enable Serial communication 428 | String serialComm() { 429 | String result = ""; 430 | while (Serial.available()) { 431 | String inputString = Serial.readString(); 432 | inputString.trim(); 433 | if (inputString != "") { 434 | return inputString; 435 | } 436 | } 437 | return result; 438 | } 439 | 440 | void setupWifi() { 441 | Serial.print("Connecting to: "); Serial.println(ssid); 442 | WiFi.mode(WIFI_STA); 443 | WiFi.begin(ssid, pass); 444 | while (WiFi.status() != WL_CONNECTED) { 445 | delay(500); 446 | Serial.print("."); 447 | } 448 | 449 | Serial.println(); 450 | Serial.print("WiFi connected. "); Serial.print("IP address: "); Serial.println(WiFi.localIP()); 451 | Serial.println(); 452 | } 453 | 454 | void setupSNTP() { 455 | // Synchronize time using SNTP. This is necessary to verify that 456 | // the TLS certificates offered by the server are currently valid. 457 | Serial.print("Setting time using SNTP"); Serial.println(""); 458 | configTime(TZ_SEC, DST_SEC, "pool.ntp.org", "time.nist.gov"); 459 | delay(500); 460 | time_t now = time(nullptr); 461 | 462 | // While it's before 2000, let's wait 463 | while (((2018 - 1970) * 365 * 24 * 3600) > time(&now)) { 464 | delay(500); 465 | Serial.print("."); 466 | } 467 | Serial.println(""); 468 | Serial.println(ctime(&now)); 469 | } 470 | 471 | void setup() { 472 | Serial.begin(115200); Serial.println(); 473 | setupWifi(); 474 | setupSNTP(); 475 | } 476 | 477 | void loop() { 478 | 479 | switch (CURRENT_STATE) { 480 | 481 | case INITIAL_STATE: 482 | authorize(); 483 | break; 484 | case AWAIT_CHALLANGE: 485 | authorization_code = serialComm(); 486 | if (authorization_code != "") { 487 | Serial.println("********"); 488 | CURRENT_STATE = EXCHANGING; 489 | } 490 | break; 491 | case EXCHANGING: 492 | exchange(); 493 | break; 494 | case INFO: 495 | info(); 496 | break; 497 | case REFRESHING: 498 | refresh(); 499 | break; 500 | case DO_IT: 501 | //sendEmail("<3"); 502 | appendToSheet(); 503 | //getSheetContent(); 504 | CURRENT_STATE = END_STATE; 505 | break; 506 | case END_STATE: 507 | break; 508 | default: 509 | Serial.println("ERROR"); 510 | break; 511 | } 512 | } 513 | -------------------------------------------------------------------------------- /ESP8266-OAUTH2/equifax.ino: -------------------------------------------------------------------------------- 1 | const unsigned char data_equifax_der[] PROGMEM = { 2 | 0x30, 0x82, 0x03, 0x20, 0x30, 0x82, 0x02, 0x89, 0xa0, 0x03, 0x02, 0x01, 3 | 0x02, 0x02, 0x04, 0x35, 0xde, 0xf4, 0xcf, 0x30, 0x0d, 0x06, 0x09, 0x2a, 4 | 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x4e, 5 | 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 6 | 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 7 | 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x31, 0x2d, 0x30, 0x2b, 0x06, 8 | 0x03, 0x55, 0x04, 0x0b, 0x13, 0x24, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 9 | 0x78, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 10 | 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 11 | 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x1e, 0x17, 0x0d, 0x39, 0x38, 12 | 0x30, 0x38, 0x32, 0x32, 0x31, 0x36, 0x34, 0x31, 0x35, 0x31, 0x5a, 0x17, 13 | 0x0d, 0x31, 0x38, 0x30, 0x38, 0x32, 0x32, 0x31, 0x36, 0x34, 0x31, 0x35, 14 | 0x31, 0x5a, 0x30, 0x4e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 15 | 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, 16 | 0x04, 0x0a, 0x13, 0x07, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x31, 17 | 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x24, 0x45, 0x71, 18 | 0x75, 0x69, 0x66, 0x61, 0x78, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 19 | 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 20 | 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x30, 0x81, 21 | 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 22 | 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 23 | 0x81, 0x81, 0x00, 0xc1, 0x5d, 0xb1, 0x58, 0x67, 0x08, 0x62, 0xee, 0xa0, 24 | 0x9a, 0x2d, 0x1f, 0x08, 0x6d, 0x91, 0x14, 0x68, 0x98, 0x0a, 0x1e, 0xfe, 25 | 0xda, 0x04, 0x6f, 0x13, 0x84, 0x62, 0x21, 0xc3, 0xd1, 0x7c, 0xce, 0x9f, 26 | 0x05, 0xe0, 0xb8, 0x01, 0xf0, 0x4e, 0x34, 0xec, 0xe2, 0x8a, 0x95, 0x04, 27 | 0x64, 0xac, 0xf1, 0x6b, 0x53, 0x5f, 0x05, 0xb3, 0xcb, 0x67, 0x80, 0xbf, 28 | 0x42, 0x02, 0x8e, 0xfe, 0xdd, 0x01, 0x09, 0xec, 0xe1, 0x00, 0x14, 0x4f, 29 | 0xfc, 0xfb, 0xf0, 0x0c, 0xdd, 0x43, 0xba, 0x5b, 0x2b, 0xe1, 0x1f, 0x80, 30 | 0x70, 0x99, 0x15, 0x57, 0x93, 0x16, 0xf1, 0x0f, 0x97, 0x6a, 0xb7, 0xc2, 31 | 0x68, 0x23, 0x1c, 0xcc, 0x4d, 0x59, 0x30, 0xac, 0x51, 0x1e, 0x3b, 0xaf, 32 | 0x2b, 0xd6, 0xee, 0x63, 0x45, 0x7b, 0xc5, 0xd9, 0x5f, 0x50, 0xd2, 0xe3, 33 | 0x50, 0x0f, 0x3a, 0x88, 0xe7, 0xbf, 0x14, 0xfd, 0xe0, 0xc7, 0xb9, 0x02, 34 | 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0x09, 0x30, 0x82, 0x01, 0x05, 35 | 0x30, 0x70, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x69, 0x30, 0x67, 0x30, 36 | 0x65, 0xa0, 0x63, 0xa0, 0x61, 0xa4, 0x5f, 0x30, 0x5d, 0x31, 0x0b, 0x30, 37 | 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 38 | 0x30, 0x0e, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x07, 0x45, 0x71, 0x75, 39 | 0x69, 0x66, 0x61, 0x78, 0x31, 0x2d, 0x30, 0x2b, 0x06, 0x03, 0x55, 0x04, 40 | 0x0b, 0x13, 0x24, 0x45, 0x71, 0x75, 0x69, 0x66, 0x61, 0x78, 0x20, 0x53, 41 | 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 42 | 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 43 | 0x69, 0x74, 0x79, 0x31, 0x0d, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x04, 0x03, 44 | 0x13, 0x04, 0x43, 0x52, 0x4c, 0x31, 0x30, 0x1a, 0x06, 0x03, 0x55, 0x1d, 45 | 0x10, 0x04, 0x13, 0x30, 0x11, 0x81, 0x0f, 0x32, 0x30, 0x31, 0x38, 0x30, 46 | 0x38, 0x32, 0x32, 0x31, 0x36, 0x34, 0x31, 0x35, 0x31, 0x5a, 0x30, 0x0b, 47 | 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x01, 0x06, 0x30, 48 | 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 49 | 0x48, 0xe6, 0x68, 0xf9, 0x2b, 0xd2, 0xb2, 0x95, 0xd7, 0x47, 0xd8, 0x23, 50 | 0x20, 0x10, 0x4f, 0x33, 0x98, 0x90, 0x9f, 0xd4, 0x30, 0x1d, 0x06, 0x03, 51 | 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x48, 0xe6, 0x68, 0xf9, 0x2b, 52 | 0xd2, 0xb2, 0x95, 0xd7, 0x47, 0xd8, 0x23, 0x20, 0x10, 0x4f, 0x33, 0x98, 53 | 0x90, 0x9f, 0xd4, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 54 | 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 55 | 0x86, 0xf6, 0x7d, 0x07, 0x41, 0x00, 0x04, 0x0d, 0x30, 0x0b, 0x1b, 0x05, 56 | 0x56, 0x33, 0x2e, 0x30, 0x63, 0x03, 0x02, 0x06, 0xc0, 0x30, 0x0d, 0x06, 57 | 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 58 | 0x03, 0x81, 0x81, 0x00, 0x58, 0xce, 0x29, 0xea, 0xfc, 0xf7, 0xde, 0xb5, 59 | 0xce, 0x02, 0xb9, 0x17, 0xb5, 0x85, 0xd1, 0xb9, 0xe3, 0xe0, 0x95, 0xcc, 60 | 0x25, 0x31, 0x0d, 0x00, 0xa6, 0x92, 0x6e, 0x7f, 0xb6, 0x92, 0x63, 0x9e, 61 | 0x50, 0x95, 0xd1, 0x9a, 0x6f, 0xe4, 0x11, 0xde, 0x63, 0x85, 0x6e, 0x98, 62 | 0xee, 0xa8, 0xff, 0x5a, 0xc8, 0xd3, 0x55, 0xb2, 0x66, 0x71, 0x57, 0xde, 63 | 0xc0, 0x21, 0xeb, 0x3d, 0x2a, 0xa7, 0x23, 0x49, 0x01, 0x04, 0x86, 0x42, 64 | 0x7b, 0xfc, 0xee, 0x7f, 0xa2, 0x16, 0x52, 0xb5, 0x67, 0x67, 0xd3, 0x40, 65 | 0xdb, 0x3b, 0x26, 0x58, 0xb2, 0x28, 0x77, 0x3d, 0xae, 0x14, 0x77, 0x61, 66 | 0xd6, 0xfa, 0x2a, 0x66, 0x27, 0xa0, 0x0d, 0xfa, 0xa7, 0x73, 0x5c, 0xea, 67 | 0x70, 0xf1, 0x94, 0x21, 0x65, 0x44, 0x5f, 0xfa, 0xfc, 0xef, 0x29, 0x68, 68 | 0xa9, 0xa2, 0x87, 0x79, 0xef, 0x79, 0xef, 0x4f, 0xac, 0x07, 0x77, 0x38 69 | }; 70 | const unsigned int data_equifax_der_len = 804; 71 | 72 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Jan Almeroth 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESP8266-OAUTH2 2 | WIP🔥 Implementation of OAUTH2 for ESP8266 3 | 4 | Send emails using Gmail API and push/fetch data from Spreadsheets. More to come… 5 | --------------------------------------------------------------------------------