How to send an email through gmail without enabling 'insecure access'?

后端 未结 6 672
你的背包
你的背包 2020-12-12 17:35

Google are pushing us to improve the security of script access to their gmail smtp servers. I have no problem with that. In fact I\'m happy to help.

But they\'re n

相关标签:
6条回答
  • 2020-12-12 17:47

    This was painful, but I seem to have something going now...

    Python3 is not supported (yet)

    I don't think it will be too hard to attain, as I was stumbling through converting packages without hitting anything massive: just the usual 2to3 stuff. Yet after a couple of hours I got tired of swimming upstream. At time of writing, I couldn't find a published package for public consumption for Python 3. The python 2 experience was straight-forward (in comparison).

    Navigating the Google website is half the battle

    No doubt, over time, this will change. Ultimately you need to download a client_secret.json file. You can only (probably) do this setting up stuff via a web browser:

    1. You need a google account - either google apps or gmail. So, if you haven't got one, go get one.
    2. Get yourself to the developers console
    3. Create a new project, and wait 4 or 400 seconds for that to complete.
    4. Navigate to API's and Auth -> Credentials
    5. Under OAuth select Create New Client ID
    6. Choose Installed Application as the application type and Other
    7. You should now have a button Download JSON. Do that. It's your client_secret.json—the passwords so to speak

    But wait that's not all!

    You have to give your application a "Product Name" to avoid some odd errors. (see how much I suffered to give you this ;-)

    1. Navigate to API's & auth -> Consent Screen
    2. Choose your email
    3. Enter a PRODUCT NAME. It doesn't matter what it is. "Foobar" will do fine.
    4. Save

    Newsflash! Whoa. Now there's even more!

    1. Navigate to API's & auth -> APIs -> Gmail API
    2. Click the button Enable API

    Yay. Now we can update the emailing script.

    Python 2

    You need to run the script interactively the first time. It will open a web browser on your machine and you'll grant permissions (hit a button). This exercise will save a file to your computer gmail.storage which contains a reusable token.

    [I had no luck transferring the token to a machine which has no graphical browser functionality—returns an HTTPError. I tried to get through it via the lynx graphical browser. That also failed because google have set the final "accept" button to "disabled"!? I'll raise another question to jump this hurdle (more grumbling)]

    First you need some libraries:

    pip install --upgrade google-api-python-client
    pip install --upgrade python-gflags
    
    • you need to change the to and from addresses
    • make sure you have the client_token.json file whereever the Storage instructions expect it
    • the directory needs to be writable so it can save the gmail.storage file

    Finally some code:

    import base64
    import httplib2
    
    from email.mime.text import MIMEText
    
    from apiclient.discovery import build
    from oauth2client.client import flow_from_clientsecrets
    from oauth2client.file import Storage
    from oauth2client.tools import run
    
    
    # Path to the client_secret.json file downloaded from the Developer Console
    CLIENT_SECRET_FILE = 'client_secret.json'
    
    # Check https://developers.google.com/gmail/api/auth/scopes for all available scopes
    OAUTH_SCOPE = 'https://www.googleapis.com/auth/gmail.compose'
    
    # Location of the credentials storage file
    STORAGE = Storage('gmail.storage')
    
    # Start the OAuth flow to retrieve credentials
    flow = flow_from_clientsecrets(CLIENT_SECRET_FILE, scope=OAUTH_SCOPE)
    http = httplib2.Http()
    
    # Try to retrieve credentials from storage or run the flow to generate them
    credentials = STORAGE.get()
    if credentials is None or credentials.invalid:
      credentials = run(flow, STORAGE, http=http)
    
    # Authorize the httplib2.Http object with our credentials
    http = credentials.authorize(http)
    
    # Build the Gmail service from discovery
    gmail_service = build('gmail', 'v1', http=http)
    
    # create a message to send
    message = MIMEText("Message goes here.")
    message['to'] = "yourvictim@goes.here"
    message['from'] = "you@go.here"
    message['subject'] = "your subject goes here"
    body = {'raw': base64.b64encode(message.as_string())}
    
    # send it
    try:
      message = (gmail_service.users().messages().send(userId="me", body=body).execute())
      print('Message Id: %s' % message['id'])
      print(message)
    except Exception as error:
      print('An error occurred: %s' % error)
    

    Hopefully that gets us all started. Not as simple as the old way, but does look a lot less complicated now I can see it in the flesh.

    0 讨论(0)
  • 2020-12-12 17:55

    I'm including some code that has been updated for python 3 usage - it seems to send emails once you get the necessary permissions and OAuth tokens working. It's mostly based off the google api website samples

        from __future__ import print_function
    
    import base64
    import os
    from email.mime.text import MIMEText
    
    import httplib2
    from apiclient import discovery
    from googleapiclient import errors
    from oauth2client import client
    from oauth2client import tools
    from oauth2client.file import Storage
    
    try:
        import argparse
    
        flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
    except ImportError:
        flags = None
    
    # If modifying these scopes, delete your previously saved credentials
    # at ~/.credentials/gmail-python-quickstart.json
    SCOPES = 'https://www.googleapis.com/auth/gmail.send'
    CLIENT_SECRET_FILE = 'client_secret.json'
    APPLICATION_NAME = 'Gmail API Python Quickstart'
    
    
    def get_credentials():
        """Gets valid user credentials from storage.
    
        If nothing has been stored, or if the stored credentials are invalid,
        the OAuth2 flow is completed to obtain the new credentials.
    
        Returns:
            Credentials, the obtained credential.
        """
        home_dir = os.path.expanduser('~')
        credential_dir = os.path.join(home_dir, '.credentials')
        if not os.path.exists(credential_dir):
            os.makedirs(credential_dir)
        credential_path = os.path.join(credential_dir,
                                       'gmail-python-quickstart.json')
    
        store = Storage(credential_path)
        credentials = store.get()
        if not credentials or credentials.invalid:
            flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
            flow.user_agent = APPLICATION_NAME
            if flags:
                credentials = tools.run_flow(flow, store, flags)
            else:  # Needed only for compatibility with Python 2.6
                credentials = tools.run(flow, store)
            print('Storing credentials to ' + credential_path)
        return credentials
    
    
    to = 'test@email.com'
    sender = 'test@email.com'
    subject = 'test emails'
    message_text = 'hello this is a text test message'
    user_id = 'me'
    
    def create_message(sender, to, subject, message_text):
        """Create a message for an email.
    
        Args:
          sender: Email address of the sender.
          to: Email address of the receiver.
          subject: The subject of the email message.
          message_text: The text of the email message.
    
        Returns:
          An object containing a base64url encoded email object.
        """
        message = MIMEText(message_text)
        message['to'] = to
        message['from'] = sender
        message['subject'] = subject
        return {'raw': (base64.urlsafe_b64encode(message.as_bytes()).decode())}
    
    
    def send_message(service, user_id, message):
        """Send an email message.
    
        Args:
          service: Authorized Gmail API service instance.
          user_id: User's email address. The special value "me"
          can be used to indicate the authenticated user.
          message: Message to be sent.
    
        Returns:
          Sent Message.
        """
        try:
            message = (service.users().messages().send(userId=user_id, body=message)
                       .execute())
            print('Message Id: {}'.format(message['id']))
            return message
        except errors.HttpError as error:
            print('An error occurred: {}'.format(error))
    
    
    def main():
        """Shows basic usage of the Gmail API.
    
        Creates a Gmail API service object and outputs a list of label names
        of the user's Gmail account.
        """
        credentials = get_credentials()
        http = credentials.authorize(httplib2.Http())
        service = discovery.build('gmail', 'v1', http=http)
    
        msg = create_message(sender,to,subject,message_text)
        message = (service.users().messages().send(userId=user_id, body=msg)
                   .execute())
        print('Message Id: {}'.format(message['id']))
        results = service.users().messages().list(userId='me').execute()
        labels = results.get('labels', [])
    
        if not labels:
            print('No labels found.')
        else:
            print('Labels:')
            for label in labels:
                print(label['name'])
    
    
    if __name__ == '__main__':
        main()
    
    0 讨论(0)
  • 2020-12-12 18:02

    It seems that John Mee's answer is out of date. It does not work in July, 2016. Maybe due to the update of Gmail's API. I update his code (python 2) as below:

        """Send an email message from the user's account.
    """
    
    import base64
    from email.mime.audio import MIMEAudio
    from email.mime.base import MIMEBase
    from email.mime.image import MIMEImage
    from email.mime.multipart import MIMEMultipart
    from email.mime.text import MIMEText
    import mimetypes
    import os
    
    #from __future__ import print_function
    import httplib2
    import os
    
    from apiclient import discovery
    import oauth2client
    from oauth2client import client
    from oauth2client import tools
    
    from apiclient import errors
    
    SCOPES = 'https://www.googleapis.com/auth/gmail.compose'
    CLIENT_SECRET_FILE = 'client_secret.json'
    APPLICATION_NAME = 'Gmail API Python Quickstart'
    
    try:
        import argparse
        flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
    except ImportError:
        flags = None
    
    def SendMessage(service, user_id, message):
      """Send an email message.
    
      Args:
        service: Authorized Gmail API service instance.
        user_id: User's email address. The special value "me"
        can be used to indicate the authenticated user.
        message: Message to be sent.
    
      Returns:
        Sent Message.
      """
      try:
        message = (service.users().messages().send(userId=user_id, body=message)
                   .execute())
        print 'Message Id: %s' % message['id']
        return message
      except errors.HttpError, error:
        print 'An error occurred: %s' % error
    
    
    def CreateMessage(sender, to, subject, message_text):
      """Create a message for an email.
    
      Args:
        sender: Email address of the sender.
        to: Email address of the receiver.
        subject: The subject of the email message.
        message_text: The text of the email message.
    
      Returns:
        An object containing a base64url encoded email object.
      """
      message = MIMEText(message_text)
      message['to'] = to
      message['from'] = sender
      message['subject'] = subject
      return {'raw': base64.urlsafe_b64encode(message.as_string())}
    
    
    def get_credentials():
        """Gets valid user credentials from storage.
    
        If nothing has been stored, or if the stored credentials are invalid,
        the OAuth2 flow is completed to obtain the new credentials.
    
        Returns:
            Credentials, the obtained credential.
        """
        home_dir = os.path.expanduser('~')
        credential_dir = os.path.join(home_dir, '.credentials')
        if not os.path.exists(credential_dir):
            os.makedirs(credential_dir)
        credential_path = os.path.join(credential_dir,
                                       'sendEmail.json')
    
        store = oauth2client.file.Storage(credential_path)
        credentials = store.get()
        if not credentials or credentials.invalid:
            flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
            flow.user_agent = APPLICATION_NAME
            if flags:
                credentials = tools.run_flow(flow, store, flags)
            else: # Needed only for compatibility with Python 2.6
                credentials = tools.run(flow, store)
            print('Storing credentials to ' + credential_path)
        return credentials
    
    if __name__ == "__main__":
    
        try:
            credentials = get_credentials()
            http = credentials.authorize(httplib2.Http())
            service = discovery.build('gmail', 'v1', http=http)
            SendMessage(service, "me", CreateMessage("send@gmail.com", "receive@gmail.com", "Test gmail automation", "Hello world"))
    
        except Exception, e:
            print e
            raise
    

    Note that if you encounter the error Insufficient Permission, one possible reason is that the scope in program is not set correctly. The other possible reason may be that you need to delete the storage json file ("sendEmail.json" in this program) and refresh your program. More details may be seen in this post.

    0 讨论(0)
  • 2020-12-12 18:03

    Updated sample for Python 3, and GMail's current API, below.

    Note that to get the credentials.json file below, you'll need to create an Oauth client ID credential here, after selecting the relevant GCP project. Once you've created it you'll be shown the client key and client secret. Close that prompt, and click the down arrow next to the account. This is the file you'll need.

    import base64
    import logging
    import mimetypes
    import os
    import os.path
    import pickle
    from email.mime.text import MIMEText
    from google_auth_oauthlib.flow import InstalledAppFlow
    from google.auth.transport.requests import Request
    from googleapiclient import errors
    from googleapiclient.discovery import build
    
    def get_service():
        """Gets an authorized Gmail API service instance.
    
        Returns:
            An authorized Gmail API service instance..
        """    
    
        # If modifying these scopes, delete the file token.pickle.
        SCOPES = [
            'https://www.googleapis.com/auth/gmail.readonly',
            'https://www.googleapis.com/auth/gmail.send',
        ]
    
        creds = None
        # The file token.pickle stores the user's access and refresh tokens, and is
        # created automatically when the authorization flow completes for the first
        # time.
        if os.path.exists('token.pickle'):
            with open('token.pickle', 'rb') as token:
                creds = pickle.load(token)
        # If there are no (valid) credentials available, let the user log in.
        if not creds or not creds.valid:
            if creds and creds.expired and creds.refresh_token:
                creds.refresh(Request())
            else:
                flow = InstalledAppFlow.from_client_secrets_file(
                    'credentials.json', SCOPES)
                creds = flow.run_local_server(port=0)
            # Save the credentials for the next run
            with open('token.pickle', 'wb') as token:
                pickle.dump(creds, token)
    
        service = build('gmail', 'v1', credentials=creds)
        return service
    
    def send_message(service, sender, message):
      """Send an email message.
    
      Args:
        service: Authorized Gmail API service instance.
        user_id: User's email address. The special value "me"
        can be used to indicate the authenticated user.
        message: Message to be sent.
    
      Returns:
        Sent Message.
      """
      try:
        sent_message = (service.users().messages().send(userId=sender, body=message)
                   .execute())
        logging.info('Message Id: %s', sent_message['id'])
        return sent_message
      except errors.HttpError as error:
        logging.error('An HTTP error occurred: %s', error)
    
    def create_message(sender, to, subject, message_text):
      """Create a message for an email.
    
      Args:
        sender: Email address of the sender.
        to: Email address of the receiver.
        subject: The subject of the email message.
        message_text: The text of the email message.
    
      Returns:
        An object containing a base64url encoded email object.
      """
      message = MIMEText(message_text)
      message['to'] = to
      message['from'] = sender
      message['subject'] = subject
      s = message.as_string()
      b = base64.urlsafe_b64encode(s.encode('utf-8'))
      return {'raw': b.decode('utf-8')}
    
    if __name__ == '__main__':
        logging.basicConfig(
            format="[%(levelname)s] %(message)s",
            level=logging.INFO
        )
    
        try:
            service = get_service()
            message = create_message("from@gmail.com", "to@gmail.com", "Test subject", "Test body")
            send_message(service, "from@gmail.com", message)
    
        except Exception as e:
            logging.error(e)
            raise
    
    0 讨论(0)
  • 2020-12-12 18:06
      protected string SendEmail(string toAddress, string subject, string body)
        {
            string result = "Message Sent Successfully..!!";
    
            string senderID = "...........";// use sender's email id here..
            const string senderPassword = "........."; // sender password here...
    
            try
            {
                SmtpClient smtp = new SmtpClient
                {
                    Host = "smtp.gmail.com", // smtp server address here...
                    Port = 587,
                    EnableSsl = true,
                    DeliveryMethod = SmtpDeliveryMethod.Network,
                    Credentials = new System.Net.NetworkCredential(senderID, senderPassword),
                    Timeout = 30000,
    
                };
    
                MailMessage message = new MailMessage(senderID, toAddress, subject, body);
    
                smtp.Send(message);
            }
            catch (Exception ex)
            {
                result = "Error sending email.!!!";
            }
    
            return result;
        }
    
    0 讨论(0)
  • 2020-12-12 18:10

    Have you considered using the Gmail API? The API has security features built in and is optimized specifically for Gmail. You can find the API documentation on http://developers.google.com - for example, here's the documentation for the Send API call:

    https://developers.google.com/gmail/api/v1/reference/users/messages/send

    0 讨论(0)
提交回复
热议问题