OAuth2 גוגל - יצירת אישורי אימות ללא צורך בהרצה לוקאלית של האפליקציה
-
@צדיק-תמים
אני ניסיתי אפליקציה רק על מצב בדיקה כי אני עשיתי לשימוש אישי, ובמקרה כזה לא חייב בכלל לעבור דרך localhost, אלא אפשר להשים גם כתובות ודומיינים משלך, ולא חייב לעבור אימות, אלא אתה מגדיר בכתובות המורשות ל redirectUri את הכתובת המדוייקת של השירות שידע לקבל את הקוד ולשמור את האסימון המתקבל.
אתה מתכנן לעשות אפליקציה שתעבור בדיקת גוגל? -
@חוקר לא, כמו שאמרתי הרעיון הוא בעצם שכל אחד יאמת לעצמו, ללא צורך בפרסום, כיוון שהרעיון הוא שהוא נותן לאינסטנס האישי שלו גישה לחשבון, ולא נותן לי לגשת בשבילו.
@חוקר אמר בOAuth2 גוגל - יצירת אישורי אימות ללא צורך בהרצה לוקאלית של האפליקציה:
אני ניסיתי אפליקציה רק על מצב בדיקה כי אני עשיתי לשימוש אישי, ובמקרה כזה לא חייב בכלל לעבור דרך localhost, אלא אפשר להשים גם כתובות ודומיינים משלך,
הבעיה כל מי שמשתמש בסינון מבוסס רשימה לבנה כגון נטפרי, יצטרך לשלוח לבדיקה את הכתובת של האינסטנס שלו... אני רוצה לחסוך את זה
ולא חייב לעבור אימות, אלא אתה מגדיר בכתובות המורשות ל redirectUri את הכתובת המדוייקת של השירות שידע לקבל את הקוד ולשמור את האסימון המתקבל.
לא הבנתי, תוכל להרחיב?
-
@צדיק-תמים אמר בOAuth2 גוגל - יצירת אישורי אימות ללא צורך בהרצה לוקאלית של האפליקציה:
לא הבנתי, תוכל להרחיב?
מצורף דוגמת קוד ב PHP של לשיחת מיילים עם אימות המייל בפעם הראשונה.
ניתן להריץ משורת הפקודה או מהדפדפן, עיין בהערות בעברית בהמשך הקוד.
אם רוצים לאפשר הרצה בדפדפן צריך גם לטפל שה PHP ידע לטפל באימות לאחר אישור הגישה על ידי המשתמש.
ועל זה ציינתי שצריך לאפשר את ה redirectUri ביצירת האפליקציה.<?php $configGlobal = [ 'configFile' => __DIR__ . '/credentials.json', 'redirectUri' => '', // כתובת הURL לאן גוגל יחזיר את המשתמש לאחר האימות. הכתובת צריכה להיות מורשית הפרטי האפליקציה 'user' => '' ]; sendMessage(['gmail@gmail.com' => 'גוגל'], 'הי! שלחתי מייל', 'תוכן המייל') function sendMessage($ToMail, $Subject, $Content, $files = null, $FromEmail = null, $config = null) { if (!$config){ global $configGlobal; $config = $configGlobal; } require_once '../vendor/autoload.php';// Create the Transport $client = getGoogle_ServiceClient($config); $objGMail = new Google_Service_Gmail($client); $strRawMessage = ""; $boundary = uniqid(rand(), true); $subjectCharset = $charset = 'utf-8'; if (is_array($ToMail)) { if (isset($ToMail['name'])) { $strRawMessage .= 'To: ' . '=?utf-8?B?' . base64_encode($ToMail['name']) . '?=' . " <" . $ToMail['mail'] . ">" . "\r\n"; } else { $mail = array_keys($ToMail)[0]; $strRawMessage .= 'To: ' . '=?utf-8?B?' . base64_encode($ToMail[$mail]) . '?=' . " <" . $mail . ">" . "\r\n"; } } else { $strRawMessage .= 'To: ' . $ToMail . "\r\n"; } // $strRawMessage .= 'Bcc: ' . '=?utf-8?B?' . base64_encode( '' ) . '?=' . " <" . '@gmail.com' . ">" . "\r\n"; if (is_array($FromEmail)) { if (isset($FromEmail['name'])) { $strRawMessage .= 'From: ' . '=?utf-8?B?' . base64_encode($FromEmail['name']) . '?=' . " <" . $FromEmail['mail'] . ">" . "\r\n"; $strSesFromEmail = $FromEmail['mail']; } else { $mail = array_keys($FromEmail)[0]; $strRawMessage .= 'From: ' . '=?utf-8?B?' . base64_encode($FromEmail[$mail]) . '?=' . " <" . $mail . ">" . "\r\n"; $strSesFromEmail = $mail; } } else { $strRawMessage .= 'From: ' . $FromEmail . "\r\n"; $strSesFromEmail = $FromEmail; } $strRawMessage .= 'Subject: =?' . $subjectCharset . '?B?' . base64_encode($Subject) . "?=\r\n"; $strRawMessage .= 'MIME-Version: 1.0' . "\r\n"; $strRawMessage .= 'Content-type: Multipart/Mixed; boundary="' . $boundary . '"' . "\r\n"; $strRawMessage .= "\r\n--{$boundary}\r\n"; $strRawMessage .= 'Content-Type: text/html; charset=' . $charset . "\r\n"; $strRawMessage .= "Content-Transfer-Encoding: base64" . "\r\n\r\n"; $strRawMessage .= $Content . "\r\n"; $strRawMessage .= "--{$boundary}\r\n"; if ($files) { foreach ($files as $key => $file) { $filePath = $file[0]; if ($filePath) { $finfo = finfo_open(FILEINFO_MIME_TYPE); // return mime type ala mimetype extension $mimeType = finfo_file($finfo, $filePath); $fileName = $file[1]; $strRawMessage .= "\r\n--{$boundary}\r\n"; $strRawMessage .= 'Content-Type: ' . $mimeType . '; name="' . $fileName . '";' . "\r\n"; $strRawMessage .= 'Content-ID: <' . $strSesFromEmail . '>' . "\r\n"; $strRawMessage .= 'Content-Description: ' . $fileName . ';' . "\r\n"; $strRawMessage .= 'Content-Disposition: attachment; filename="' . $fileName . '"; size=' . filesize($filePath) . ';' . "\r\n"; $strRawMessage .= 'Content-Transfer-Encoding: base64' . "\r\n\r\n"; $strRawMessage .= chunk_split(base64_encode(file_get_contents($filePath)), 76, "\n") . "\r\n"; $strRawMessage .= "--{$boundary}\r\n"; } } } try { // The message needs to be encoded in Base64URL $mime = rtrim(strtr(base64_encode($strRawMessage), '+/', '-_'), '='); $msg = new Google_Service_Gmail_Message(); $msg->setRaw($mime); $objSentMsg = $objGMail->users_messages->send("me", $msg); // echo '<pre>'; print_r($objSentMsg); echo '</pre>'; } catch (Exception $e) { print($e->getMessage()); } } function getGoogle_ServiceClient($config) { require_once '../vendor/autoload.php';// Create the Transport $client = new Google_Client(); $client->setApplicationName('Gmail API PHP Quickstart'); $client->setScopes(Google_Service_Gmail::MAIL_GOOGLE_COM); $client->setAuthConfig($config['configFile']); $client->setAccessType('offline'); $client->setPrompt('select_account consent'); if (isset($config['user']) && $config['user']) { $client->setState($config['user']); } if (isset($config['redirectUri']) && $config['redirectUri']) { $client->setRedirectUri($config['redirectUri']); } // Load previously authorized token from a file, if it exists. // The file token.json stores the user's access and refresh tokens, and is // created automatically when the authorization flow completes for the first // time. $str = 'token' . str_replace('@', '.', $config['user']); $tokenPath = __DIR__ . '/' . $str . '.json'; if (file_exists($tokenPath)) { $accessToken = json_decode(file_get_contents($tokenPath), true); $client->setAccessToken($accessToken); } // If there is no previous token or it's expired. if ($client->isAccessTokenExpired()) { // Refresh the token if possible, else fetch a new one. if ($client->getRefreshToken()) { $client->fetchAccessTokenWithRefreshToken($client->getRefreshToken()); } else { // Request authorization from the user. if (php_sapi_name() != 'cli') { //אם מדובר בהרצת מהדפדפן זה יעביר את הגולש לדף האישור של גוגל ואח"כ יחזיר אותו redirectUri ויעביר לשם את הקוד, בקוד דלהלן הוא יודע לטפל בקוד שמתקבל מגוגל וליצור את האסימונים. //אם עדיין לא קיים קוד האימות הURL זה מפנה לגוגל if (!isset($_GET['code'])){ $authUrl = $client->createAuthUrl(); header('Location: ' . $authUrl); exit(); } else { //אם קיים קוד אימות ב URL הוא יאמת אותו בהמשך מול גוגל $authCode = $_GET['code']; } } else { //הקוד דלהלן כאשר מריצים את PHP משורת הפקודה, זה יתן לינק לפתיחה בדפדפן, וסיומו ניתן לגזור את הקוד מהURL ולהדביק לטרמינל $authUrl = $client->createAuthUrl(); printf("Open the following link in your browser:\n%s\n", $authUrl); print 'Enter verification code: '; $authCode = trim(fgets(STDIN)); } // Exchange authorization code for an access token. $accessToken = $client->fetchAccessTokenWithAuthCode($authCode); $client->setAccessToken($accessToken); // Check to see if there was an error. if (array_key_exists('error', $accessToken)) { throw new Exception(join(', ', $accessToken)); } } // Save the token to a file. if (!file_exists(dirname($tokenPath))) { mkdir(dirname($tokenPath), 0700, true); } file_put_contents($tokenPath, json_encode($client->getAccessToken())); } return $client; }
-
@צדיק-תמים אמר בOAuth2 גוגל - יצירת אישורי אימות ללא צורך בהרצה לוקאלית של האפליקציה:
אני מעוניין לעשות פרויקט בnodejs שיעבוד בצורה שמי שירצה להשתמש בו, יקים אינסטנס שלו, לדוגמה על heroku (באמצעות הapp.json Schema), יגש לגוגל קונסול, ינפיק אישורי אימות לגישה למיילים אנשי קשר וכו', ויכניס אותם במשתני סביבה, והיישום יקח אותם משם (ישירות או דרך buildpack כזה ששומר אותו כקובץ json באופן אוטומטי).
עד כאן הכל טוב.
אימות רגיל באמצעות OAuth2 על ידי פתיחת כתובת בlocalhost לא מתאים, כיוון שבשביל זה יהיה צורך להוריד את המאגר למחשב, ולהריץ אותו, וכו', דברים לא קלים למשתמש שאינו טכני (לדוגמה nodejs מסתמא לא יהיה מותקן לו...),
הבעיה לא מובנת לי. מה הקשר localhost? למה הציטוט הראשון מחייב פתיחה בLocalhost? למה אימות רגיל == פתיחה בlocalhost?
-
@צדיק-תמים
יש להכניס את הredirectUri מראש בעריכת ה credentials
כמו בתמונה
ואז בכניסה ל https://exmple.net/SendMail.php בפעם הראשונה בדפדפן זה יעביר לדף האימות של גוגל ואח"כ יחזיר אותו לאותו הדף https://exmple.net/SendMail.php יחד עם הcode ב URL והקוד הנ"ל שהבאתי מבצע כבר את האימות ויוצר אסימון -
@צדיק-תמים אמר בOAuth2 גוגל - יצירת אישורי אימות ללא צורך בהרצה לוקאלית של האפליקציה:
הבעיה כל מי שמשתמש בסינון מבוסס רשימה לבנה כגון נטפרי, יצטרך לשלוח לבדיקה את הכתובת של האינסטנס שלו... אני רוצה לחסוך את זה
אם הבנתי נכון, במה שהצעת הבעיה קיימת, כי צריך שהאתר יהיה פתוח? אני צודק? או שאפשר לפתוח את מסך האימות גם בלי זה (איך? בעצם אני צריך משהו כמו הoauthplayground, ורק בredirectUri להכניס את הכתובת של האינסטנס), ורק הredirect יהיה לכתובת שעדיין לא פתוחה - הכתובת של האינסטנס (וזה לא בעיה, כי הבקשה תגיע לשרת עם הקוד בurl, והוא ישמור אותו וכו', למרות שהתשובה תהיה חסומה לצפיה)
@dovid זה עונה גם על השאלה שלך - אם אני לא רוצה שיהיה צריך לשלוח כל אינסטנס לבדיקה, הדרך היחידה לכאורה היא localhost -
@צדיק-תמים רחמנות עלי!!
אני לא מבין כלום.
הבחור שמשתמש בפרוייקט שלך פותח חשבון בheruko וכמובן הוא צריך לפתוח את זה בנטפרי, מה זה קשור לכל השאלה הטכנולוגית אודות הauth??
לגוגל לא בוער שזה יהיה פתוח, וכל עוד זה חסום ממילא הוא לא יכול להיכנס לאפליקציה בין עם האימות ובין בלעדיו! -
@צדיק-תמים אמר בOAuth2 גוגל - יצירת אישורי אימות ללא צורך בהרצה לוקאלית של האפליקציה:
@צדיק-תמים אמר בOAuth2 גוגל - יצירת אישורי אימות ללא צורך בהרצה לוקאלית של האפליקציה:
הבעיה כל מי שמשתמש בסינון מבוסס רשימה לבנה כגון נטפרי, יצטרך לשלוח לבדיקה את הכתובת של האינסטנס שלו... אני רוצה לחסוך את זה
אם הבנתי נכון, במה שהצעת הבעיה קיימת, כי צריך שהאתר יהיה פתוח? אני צודק? או שאפשר לפתוח את מסך האימות גם בלי זה (איך? בעצם אני צריך משהו כמו הoauthplayground, ורק בredirectUri להכניס את הכתובת של האינסטנס), ורק הredirect יהיה לכתובת שעדיין לא פתוחה - הכתובת של האינסטנס (וזה לא בעיה, כי הבקשה תגיע לשרת עם הקוד בurl, והוא ישמור אותו וכו', למרות שהתשובה תהיה חסומה לצפיה)
@dovid זה עונה גם על השאלה שלך - אם אני לא רוצה שיהיה צריך לשלוח כל אינסטנס לבדיקה, הדרך היחידה לכאורה היא localhost- בנטפרי כשאתר לא נפתח והוא עושה רדיירקט לאתר פתוח, זה מעביר או לא?
- אתה יכול להכין מראש את הלינק לפתיחת הדף בגוגל, כי זה מורכב מפרטי הפרוייקט הנמצא בקובץ credentials.json ולפי ה redirectUri זה יעביר חזרה לאתר המבוקש, וכפי שכתבת הבקשה תבוצע למרות שזה סגור בנטפרי.
-
@dovid אמר בOAuth2 גוגל - יצירת אישורי אימות ללא צורך בהרצה לוקאלית של האפליקציה:
@חוקר המילה "מראש" לא מדוייקת, אפשר לשנות את זה בכל שלב.
אתה צודק, רק התכוונתי לפני ביצוע הבקשה עם ה redirectUri המבוקש.
אחרת גוגל יתן שגיאה לפני ביצוע האימות שזה מפנה ל redirectUri לא מוגדר.
אבל אכן ניתן תמיד להוסיף ולהסיר כתובות -
@צדיק-תמים אמר בOAuth2 גוגל - יצירת אישורי אימות ללא צורך בהרצה לוקאלית של האפליקציה:
@dovid מתנצל שלא הבהרתי את זה קודם, האפליקציה רצה ללא מגע יד אנוש או מול מערכת טלפונית, בשום אופן לא על ידי ממשק כלשהו בדפדפן וכדו'. וממילא לולי הצורך באימות, אין לי שום צורך לפתוח בנטפרי את האפליקציה.
כעת הבנתי.
תנחה את המשתמשים להזין כתובת אתר שלך שפתוח כבר, והוא יקבל את כל ההרשאות, ויקבל בstate שלו פרמטר למי לשלוח את טוקן הגישה. -
@חוקר אמר בOAuth2 גוגל - יצירת אישורי אימות ללא צורך בהרצה לוקאלית של האפליקציה:
בנטפרי כשאתר לא נפתח והוא עושה רדיירקט לאתר פתוח, זה מעביר או לא?
בדקתי, ומעביר! תודה, רעיון מעולה
@חוקר אמר בOAuth2 גוגל - יצירת אישורי אימות ללא צורך בהרצה לוקאלית של האפליקציה:
אתה יכול להכין מראש את הלינק לפתיחת הדף בגוגל, כי זה מורכב מפרטי הפרוייקט הנמצא בקובץ credentials.json ולפי ה redirectUri זה יעביר חזרה לאתר המבוקש, וכפי שכתבת הבקשה תבוצע למרות שזה סגור בנטפרי.
איך עושים את זה?
@dovid מה זה אומר state?