I am trying to use Sendgrid's Python API to handle transactional emails in my Flask app.
When I fire up the localhost server, I am getting a 401 error when I complete the invite form and attempt to connect with the sendgrid API and I can't understand why.
I import my sendgrid API key from a sendgrid.env file.
The app's init:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import sendgrid
import os
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://unible:test#localhost/unible'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
app.config['SECRET_KEY'] = '\xea5\x1b.S3ME\x86\x8bP/\x86T\xca\x8f\xf4S\xe2h\x9c\xe5\xc6h'
app.config['WTF_CSRF_ENABLED'] = True
app.config['DAYS_TIL_INVITES_EXPIRE'] = 28
db = SQLAlchemy(app)
sendgrid = sendgrid.SendGridAPIClient(apikey=os.environ.get('SENDGRID_API_KEY'))
import unible.routes
The routing file:
Then import the sendgrid object into my routes file to create and send mail.
from unible import app, sendgrid
from flask import render_template, request, redirect, url_for, abort, flash
from unible.models.invite import Invite
from .forms.InviteForm import InviteForm
from sendgrid.helpers.mail import *
import urllib
#app.route('/account/invites/new', methods=['GET','POST'])
def new_invites():
form = InviteForm()
if request.method == 'GET':
return render_template('invite_form.html', form=form)
if request.method == 'POST':
invite_from = 1 # for testing only, will be replaced by session user id
if form.validate_on_submit():
invite_for = form.email.data.lower()
invite = Invite(invite_from, invite_for)
from_email = Email("test#example.com")
subject = "Hello World from the SendGrid Python Library!"
to_email = Email("test#example.com")
content = Content("text/plain", "Hello, Email!")
msg = Mail(from_email, subject, to_email, content)
try:
response = sendgrid.client.mail.send.post(request_body=msg.get())
print(response.status_code)
print(response.body)
print(response.headers)
except urllib.error.HTTPError as e:
print(e.read())
flash('An invite email was sent to %s' % invite_for)
return redirect(url_for('new_invites'))
else:
return redirect(url_for('new_invites'))
The error I get back is this:
b'{"errors":[{"message":"The provided authorization grant is invalid, expired, or revoked","field":null,"help":null}]}'
However when I run the same sample code from here outside of a server it runs fine and sends?
Related
I want to send emails from Yandex using Flask but I get "authentication failed: This user does not have access rights to this service".
I did this:
from flask import Flask, render_template, redirect, request
from flask_mail import Mail, Message
app=Flask(__name__)
mail = Mail(app)
app.config['MAIL_SERVER'] = 'smtp.yandex.com'
app.config['MAIL_PORT'] = 465
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USE_SSL'] = True
app.config['MAIL_USERNAME'] = 'my-username#yandex.com'
app.config['MAIL_PASSWORD'] = 'my-password'
mail = Mail(app)
#app.route('/send-email')
def send_mail():
msg = Message('This email was sent by me from Flask', sender='my-username#yandex.com',recipients=['rec#gmail.com'])
msg.body = "This is the email body, I just wanted to test the flask email option, and see how doest it work."
mail.send(msg)
return 'Email sent!'
I was expecting the message to be sent, but I got the authentification error.
PS: My email and password are correct.
I am using flask_jwt_extended for jwt authentication in my flask web application. After the user enters email and password, I make a token using create_access_token and then redirect to another link which can only be accessed with #jwt_required.
app.py file. Notice if the way of importing jwt from user.py file like this is correct.
from flask import Flask
from flask_restful import Api
from resources.organization import OrganizationResourceList, OrganizationResource
from resources.user import LoginResource, user_list, User, jwt
app = Flask(__name__)
app.config['SECRET_KEY'] = '_rV;He_7Bz8TVvA'
app.config['JWT_TOKEN_LOCATION'] = ['headers']
jwt.init_app(app)
api = Api(app)
user_list.append(User(name="Admin User", email="admin#test.com", password="12345", photo="", user_type="host"))
# Authorization
api.add_resource(LoginResource, '/login')
# Organization
api.add_resource(OrganizationResourceList, '/organizations')
if __name__ == '__main__':
app.run(port=5000, debug=True)
user.py file containing LoginResource This is where I am creating token.
from flask import request, Response, render_template, redirect
from flask_restful import Resource
from models.user import User, user_list
from passlib.hash import sha256_crypt
from flask_jwt_extended import create_access_token, create_refresh_token, JWTManager
jwt = JWTManager()
class LoginResource(Resource):
def post(self):
req = request.form
email = req.get("email")
user = [x for x in user_list if x.email == email]
if user:
user = user[0]
password = sha256_crypt.verify(req.get("password"), user.password)
if user and password:
access_token = create_access_token(identity=user.id)
refresh_token = create_refresh_token(user.id)
redir = redirect('/organizations')
redir.headers['Authorization'] = "Bearer %s" % access_token
return redir
return redirect("/login")
Interestingly, when I debug the app, I see the headers of redirect as shown.
organization.py file containing OrganizationResourceList class
from flask import request, Response, render_template
from flask_restful import Resource
from models.organization import Organization, organization_list
from flask_jwt_extended import jwt_required, get_jwt_identity
class OrganizationResourceList(Resource):
#jwt_required()
def get(self):
current_user = get_jwt_identity()
sample_org = Organization(
name='Bin Yousef',
description='My main working company in Qatar',
photo=''
)
data = []
for organization in organization_list:
data.append(organization.data)
return Response(response=render_template('organization/index.html', organizations=data))
After hours of searching, I am still not able to get rid of the error :( Please help
I am currently working with a flask application and am trying to send out emails once the user has registered with the site. I am having difficulties with circular imports between the main.py where the app is instantiated and the data_inserts.py where the data is committed to the db and a password is emailed back to the user. For the email functionality, I use Flask-mail extension. The error I get is given below:
ImportError: Cannot import name from 'DataInserts' from relevant_folder.data_inserts
Given below are the details:
main.py:
from relevant_folder.data_inserts import DataInserts
from flask import Flask
from flask_mail import Mail
from conf.mail_settings.py import mail_settings
app = Flask(__name__)
app.config.update[mail_settings]
mail = Mail(app)
#app.route("/register")
def register():
params = request.json
DataInserts.add_user(params)
relevant_folder.data_inserts.py:
from main import app
from main.app import mail
from flask_mail import message
class DataInserts():
def add_user(self, new_user_json):
''' add user name and email to db logic goes here'''
msg = Message(subject="Subject",
sender=app.config.get("MAIL_USERNAME"),
recipients=[new_user_json["email"]],
body="Hello " + new_user_json["name"] + ", your password is password")
mail.send(msg)
I feel I have not structured my application properly. Any help greatly appreciated
It should be enough to move the DataInserts import...
from flask import Flask
from flask_mail import Mail
from conf.mail_settings.py import mail_settings
app = Flask(__name__)
app.config.update[mail_settings]
mail = Mail(app)
from relevant_folder.data_inserts import DataInserts
#app.route("/register")
def register():
params = request.json
DataInserts.add_user(params)
Alternatively you could pass app and mail instances to the DataInsert class, instead of importing the globals...
Update: Another approach would be using "flask.current_app".
from relevant_folder.data_inserts import DataInserts
from flask import Flask
from flask_mail import Mail
from conf.mail_settings.py import mail_settings
app = Flask(__name__)
app.config.update[mail_settings]
mail = Mail(app)
app.mail = mail
#app.route("/register")
def register():
params = request.json
DataInserts.add_user(params)
Notice that I stored the mail instance in app.mail for easy access later.
relevant_folder.data_inserts.py:
from flask import current_app
from flask_mail import message
class DataInserts():
def add_user(self, new_user_json):
''' add user name and email to db logic goes here'''
msg = Message(subject="Subject",
sender=current_app.config.get("MAIL_USERNAME"),
recipients=[new_user_json["email"]],
body="Hello " + new_user_json["name"] + ", your password is password")
current_app.mail.send(msg)
But keep in mind that current_app needs an active application context.
when working on a request, the context should alway be there, otherwise you can manually create the context e.g. using with app.app_context():
For more on that topic, see the flask documentation:
http://flask.pocoo.org/docs/1.0/appcontext/
I come to you today with a problem that seems to be annoying and bother me for 1 week (maybe more).
I implemented Flask and Flask-JWT to create a Token and use it in different parts of my web-app.
The process is : The user puts is credentials on the form and when he's logged a token is created by making a POST request to the Flask-JWT /auth endpoint.
The problem is :
When the user has loged-in, the token creation part of the code (POST request to /auth endpoint) seems to be in an endless loop.
The code stop at : "r = requests.post(url, headers=headers, data=payload)"
The funny part is that if a user login and after go to the /auth endpoint, it has the token created.
Someone already had this problem ?
Thank you
project/config.py
JWT_EXPIRATION_DELTA = timedelta(seconds=900)
JWT_AUTH_URL_RULE = "/api/v1/auth"
SECURITY_PASSWORD_HASH = 'pbkdf2_sha512'
SECURITY_TRACKABLE = True
SECURITY_PASSWORD_SALT = "xxxx"
WTF_CSRF_ENABLED = True
WTF_CSRF_SECRET_KEY = "xxxx"
project/app/init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, login_required
from flask_bcrypt import Bcrypt
from flask_security import Security, SQLAlchemyUserDatastore, \
UserMixin, RoleMixin, login_required
from flask_jwt import JWT, jwt_required
import os
app = Flask(__name__)
app.config.from_object('config')
bcrypt = Bcrypt(app)
db = SQLAlchemy(app)
# To have random secret key
secret_key = os.urandom(24)
app.secret_key = secret_key
from textr import views, models
from search_number import views
from buy_number import views
from users import views, models
from sms_receiver import views, models
from sms_sender import views, models
from phone_number import models, views
from users.models import User, Role
# Setup Flask-Security
user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "login"
#login_manager.user_loader
def load_user(user_id):
return User.query.filter(User.id == int(user_id)).first()
from textr.tools import retreive_user, verify_password
def authenticate(username, password):
user = retreive_user(username)
if verify_password(username, password):
return user
def identity(payload):
return User.query.filter(User.id == payload['identity']).scalar()
jwt = JWT(app, authenticate, identity)
project/app/users/views.py
from flask_jwt import jwt_required, JWT
from models import User
from error_handling import error_400
from flask import Flask, request, render_template, jsonify, session, redirect, url_for, flash
import json
import requests
#app.route("/api/v1/login", methods=["GET", "POST"])
def login():
error = None
if request.method == 'POST':
json_data = request.json
if not json_data:
username = request.form['username']
password = request.form['password']
else:
username = json_data['username']
password = json_data['password']
if not check_user_present(username):
return error_400('Error', 'User not found', 404)
if verify_password(username, password):
flash('Logged in successfully.')
user = retreive_user(username)
login_user(user)
# Infos to create POST request for Token
payload = ({'username':username, 'password':password})
url = "http://127.0.0.1:8080/api/v1/auth"
headers = {'Content-Type': 'application/json', 'charset': 'utf-8'}
# Token creation
r = requests.post(url, headers=headers, data=payload)
response = r.json()
if (r.status_code is 200):
token = response['user']['authentication_token']
session['api_session_token'] = token)
else:
flash('Wrong credentials!')
return error_400('Error', 'Wrong credentials', 401)
return render_template('users/login.html')
I've just started working with Celery on my latest project with work; I'm having a bit of trouble with executing tasks asynchronously.
All the code is taken from Miguel Grinbergs 'Using Celery with Flask'
When sending the mail without executing a task, it sends perfectly fine. Though when I attempt to send the mail delayed, it fails out giving me an error as follows.
smtplib.SMTPSenderRefused: (530, b'5.5.1 Authentication Required. Learn more at\n5.5.1 https://support.google.com/mail/answer/14257 h19sm960819igq.6 - gsmtp', 'email-removed#gmail.com')
Here's the code I'm using, in my Flask app.
import os
import time
from flask import Flask, request, render_template, session, flash, redirect, url_for, jsonify
from flask.ext.mail import Mail, Message
from celery import Celery
app = Flask(__name__)
app.config['SECRET_KEY'] = 'super-duper-secret'
# Celery Configuration
app.config['CELERY_BROKER_URL'] = 'redis://localhost:6379/0'
app.config['CELERY_RESULT_BACKEND'] = 'redis://localhost:6379/0'
# Configuration for Flask-Mail
app.config['MAIL_SERVER'] = "smtp.gmail.com"
app.config['MAIL_PORT'] = 465
app.config['MAIL_USE_SSL'] = True
app.config['MAIL_USE_TLS'] = False
app.config['MAIL_USERNAME'] = 'email-removed#gmail.com'
app.config['MAIL_PASSWORD'] = 'password-removed'
app.config['MAIL_DEFAULT_SENDER'] = 'email-removed#gmail.com'
celery = Celery(app.name, broker=app.config['CELERY_BROKER_URL'])
celery.conf.update(app.config)
mail = Mail(app)
#app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'GET':
return render_template('index.html', email=session.get('email', ''))
email = request.form['email']
session['email'] = email
msg = Message("Hello from Flask", recipients=[email])
msg.body = "This is a test message from the flask application!"
if request.form['submit'] == "Send":
send_async_email(msg)
flash('Sending email to {0}'.format(email))
else:
send_async_email.apply_async(args=[msg], countdown=20)
flash('An email to {0} will be sent in a minute.'.format(email))
return redirect(url_for('index'))
#celery.task()
def send_async_email(msg):
with app.app_context():
mail.send(msg)
if __name__ == "__main__":
app.run(debug=True)
I'd appreciate some insight, and perhaps an explanation on how to make this work, and why it isn't working.
I've also looked upon other threads here, and turned on insecure-application access for my google accounts, along with nulling the captcha as suggested in the errors returned URL.