Related
I'm getting this error in my cloudwatch logs
botocore.exceptions.ClientError: An error occurred (UnrecognizedClientException) when calling the UpdateItem operation: The security token included in the request is invalid.
I'm authorizing an IAM User with full admin access to my boto3.client DynamoDB table and it's giving me that error. Here's my application code for context
from flask import Flask, Blueprint, render_template, request
import requests
import boto3
import os
AWS_ACCESS_KEY_ID = os.environ['AWS_ACCESS_KEY_ID']
AWS_SECRET_ACCESS_KEY = os.environ['AWS_SECRET_ACCESS_KEY']
pages = Blueprint('pages', __name__)
app = Flask(__name__)
table_name = "counterdb"
dynamodb = boto3.resource("dynamodb", region_name="us-west-2", aws_access_key_id=AWS_ACCESS_KEY_ID, aws_secret_access_key=AWS_SECRET_ACCESS_KEY)
db_client = boto3.client("dynamodb", region_name="us-west-2", aws_access_key_id=AWS_ACCESS_KEY_ID, aws_secret_access_key=AWS_SECRET_ACCESS_KEY)
table = dynamodb.Table(table_name)
def increment_visitor():
response = db_client.update_item(
TableName=table_name,
Key = {
'Primary Key': {
'N': "0"
}
},
ExpressionAttributeValues = { ":inc": {"N": "1"}},
UpdateExpression = "ADD visitor :inc"
)
def retrieve_visitor_count():
item = table.get_item(
Key = {
"Primary Key": 0
}
)
visitcount = (item["Item"])["visitor"]
return visitcount
def handler():
increment_visitor()
return retrieve_visitor_count()
# #app.before_first_request
# def before_first_request():
# dbcounter = handler()
# print(dbcounter)
#app.route('/')
def home():
return render_template("index.html")
if __name__ == "__main__":
handler()
app.run(host='0.0.0.0', port=80)
I originally didn't have the region specified and was told that could be it, so I added a region as well. I got the same error with and without specifying the region in my boto3.client. I triple checked and it's the correct IAM User that I'm using with full admin access, do I need more permissions or is this a different issue causing the error?
I am trying to implement SSO for one of my applications using flask-login and flask-dance. As a starting point I am using sample code given on Flask Dance website - https://flask-dance.readthedocs.io/en/v1.2.0/quickstarts/sqla-multiuser.html
Only change I did was - I replaced GitHub with my Azure AD credentials
Please find the code below:
import sys
from flask import Flask, redirect, url_for, flash, render_template
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm.exc import NoResultFound
from flask_dance.contrib.github import make_github_blueprint, github
from flask_dance.contrib.azure import make_azure_blueprint, azure
from flask_dance.consumer.storage.sqla import OAuthConsumerMixin, SQLAlchemyStorage
from flask_dance.consumer import oauth_authorized, oauth_error
from flask_login import (
LoginManager, UserMixin, current_user,
login_required, login_user, logout_user
)
# setup Flask application
app = Flask(__name__)
app.secret_key = "XXXXXXXXXXXXXX"
blueprint = make_azure_blueprint(
client_id="XXXXXXXXXXXXXXXXXXXXX",
client_secret="XXXXXXXXXXXXXXXXXXXXXXXX",
tenant="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
)
app.register_blueprint(blueprint, url_prefix="/login")
# setup database models
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///multi.db"
db = SQLAlchemy()
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
# Your User model can include whatever columns you want: Flask-Dance doesn't care.
# Here are a few columns you might find useful, but feel free to modify them
# as your application needs!
username = db.Column(db.String(1028), unique=True)
email = db.Column(db.String(1028), unique=True)
name = db.Column(db.String(1028))
class OAuth(OAuthConsumerMixin, db.Model):
provider_user_id = db.Column(db.String(1028), unique=True)
user_id = db.Column(db.Integer, db.ForeignKey(User.id))
user = db.relationship(User)
# setup login manager
login_manager = LoginManager()
login_manager.login_view = 'azure.login'
#login_manager.user_loader
def load_user(user_id):
#print(User.query.get(int(user_id)))
return User.query.get(int(user_id))
# setup SQLAlchemy backend
blueprint.storage = SQLAlchemyStorage(OAuth, db.session, user=current_user,user_required=False)
# create/login local user on successful OAuth login
#oauth_authorized.connect_via(blueprint)
def azure_logged_in(blueprint, token):
if not token:
#print(token)
flash("Failed to log in with azure.", category="error")
return False
resp = blueprint.session.get("/user")
if not resp.ok:
#print(resp)
msg = "Failed to fetch user info from Azure."
flash(msg, category="error")
return False
azure_info = resp.json()
azure_user_id = str(azure_info["id"])
#print(azure_user_id)
# Find this OAuth token in the database, or create it
query = OAuth.query.filter_by(
provider=blueprint.name,
provider_user_id=azure_user_id,
)
try:
oauth = query.one()
except NoResultFound:
oauth = OAuth(
provider=blueprint.name,
provider_user_id=azure_user_id,
token=token,
)
if oauth.user:
login_user(oauth.user)
flash("Successfully signed in with Azure.")
else:
# Create a new local user account for this user
user = User(
# Remember that `email` can be None, if the user declines
# to publish their email address on GitHub!
email=azure_info["email"],
name=azure_info["name"],
)
# Associate the new local user account with the OAuth token
oauth.user = user
# Save and commit our database models
db.session.add_all([user, oauth])
db.session.commit()
# Log in the new local user account
login_user(user)
flash("Successfully signed in with Azure.")
# Disable Flask-Dance's default behavior for saving the OAuth token
return False
# notify on OAuth provider error
#oauth_error.connect_via(blueprint)
def azure_error(blueprint, error, error_description=None, error_uri=None):
msg = (
"OAuth error from {name}! "
"error={error} description={description} uri={uri}"
).format(
name=blueprint.name,
error=error,
description=error_description,
uri=error_uri,
)
flash(msg, category="error")
#app.route("/logout")
#login_required
def logout():
logout_user()
flash("You have logged out")
return redirect(url_for("index"))
#app.route("/")
def index():
return render_template("home.html")
# hook up extensions to app
db.init_app(app)
login_manager.init_app(app)
if __name__ == "__main__":
if "--setup" in sys.argv:
with app.app_context():
db.create_all()
db.session.commit()
print("Database tables created")
else:
app.run(debug=True,port=5011)
I have also done appropriate changes in HTML file for 'azure.login'.
So after running it as python multi.py --setup database tables are getting created
and after I run python multi.py Oauth dance is actually starting but in the end I am getting error like below:
HTTP Response:
127.0.0.1 - - [28/Oct/2020 10:17:44] "?[32mGET /login/azure/authorized?code=0.<Token>HTTP/1.1?[0m" 302 -
127.0.0.1 - - [28/Oct/2020 10:17:44] "?[37mGET / HTTP/1.1?[0m" 200 -
Am I missing something? Is it a good idea to use Flask Dance and Flask Login to have SSO with Azure AD? Or I should go with MSAL only along with Flask Session?
Kindly give your valuable inputs..
Since you use Azure AD as the Flask dance provider, we need to use Microsoft Graph to get user's information. The URL should be https://graph.microsoft.com/v1.0/me. So please update the code resp = blueprint.session.get("/user") to resp = blueprint.session.get("/v1.0/me") in method azure_logged_in. Besides, please note that the azure ad user's information has different names. We also need to update the code about creating users.
For example
#oauth_authorized.connect_via(blueprint)
def azure_logged_in(blueprint, token):
if not token:
# print(token)
flash("Failed to log in with azure.", category="error")
return False
resp = blueprint.session.get("/v1.0/me")
# azure.get
if not resp.ok:
# print(resp)
msg = "Failed to fetch user info from Azure."
flash(msg, category="error")
return False
azure_info = resp.json()
azure_user_id = str(azure_info["id"])
# print(azure_user_id)
# Find this OAuth token in the database, or create it
query = OAuth.query.filter_by(
provider=blueprint.name,
provider_user_id=azure_user_id,
)
try:
oauth = query.one()
except NoResultFound:
oauth = OAuth(
provider=blueprint.name,
provider_user_id=azure_user_id,
token=token,
)
if oauth.user:
login_user(oauth.user)
flash("Successfully signed in with Azure.")
else:
# Create a new local user account for this user
user = User(
# create user with user information from Microsoft Graph
email=azure_info["mail"],
username=azure_info["displayName"],
name=azure_info["userPrincipalName"]
)
# Associate the new local user account with the OAuth token
oauth.user = user
# Save and commit our database models
db.session.add_all([user, oauth])
db.session.commit()
# Log in the new local user account
login_user(user)
flash("Successfully signed in with Azure.")
# Disable Flask-Dance's default behavior for saving the OAuth token
return False
For more details, please refer to here and here
I have a web application which first gets user authentication for an API token, then I want to run the latter part of the code every hour using the APScheduler module. I dont want to run the whole app from the start, because the first part requires user interaction to authorise the app again, which is unnecessary after the first run because we have the token, plus i obviously cant be there to click the authorise button every hour. WHere do i put the sched.start() part of the code? THe error i get is RuntimeError: Working outside of request context.
import requests
import json
from flask import Flask, render_template, request, redirect, session, url_for
from flask.json import jsonify
import os
from requests_oauthlib import OAuth2Session
from apscheduler.schedulers.background import BackgroundScheduler
import atexit
from datetime import datetime
app = Flask(__name__)
client_id = "x"
client_secret = "x"
scope = 'read_station'
password = 'x'
#grant_type = 'authorization_code'
grant_type = 'password'
username='x'
authurl = 'https://api.netatmo.com/oauth2/authorize?'
token_url = 'https://api.netatmo.com/oauth2/token'
redirect_uri = 'x'
response_type = 'code'
code = None
payload= {'grant_type':grant_type,'client_id':client_id,'client_secret':client_secret,
'username':username,'password':password,'scope':scope}
rip={}
CITIES = {'bolzano' : 'lat_ne=46.30&lon_ne=11.23&lat_sw=46.28&lon_sw=11.14',
'florence' : 'lat_ne=43.51&lon_ne=11.21&lat_sw=43.44&lon_sw=11.02',
'manchester' : 'lat_ne=53.35&lon_ne=-2.0011.21&lat_sw=53.21&lon_sw=-2.36',
}
dicty = {}
def dooby(CITIES, Header):
for city in CITIES.keys():
i = requests.get('https://api.netatmo.com/api/getpublicdata?'+CITIES[city]+'&filter=false', headers = Header).json()
dicty[str(city)]=i
return dicty
#app.route('/')
def auth():
redirect_uri = url_for('.redir', _external = True)
oauth = OAuth2Session(client_id, redirect_uri = redirect_uri,
scope = scope)
authorization_url, state = oauth.authorization_url(authurl)
session['oauth_state'] = state
return redirect(authorization_url)
#app.route('/redir', methods = ["GET","POST"])
def redir():
code = request.args.get('code')
payload['code']=code
rip = requests.post(token_url, data=payload)
rs = rip.content.decode()
response = json.loads(rs)
session['oauth_token'] = response['access_token']
session['expiry'] = response['expires_in']
session['refresh_token'] = response['refresh_token']
return redirect(url_for('.profile'))
#app.route('/profile', methods = ["GET","POST"])
def profile():
Header = {'Authorization':'Bearer '+session['oauth_token']}
def repeat():
return dooby(CITIES, Header)
i = repeat()
job = json.dumps(i)
dt = datetime.now().strftime("%Y_%m_%d %H_%M_%S")
f = open(r'C:\Users\freak\OneDrive\Documents\UHIpaper\{}.json'.format(dt),"w")
f.write(job)
f.close()
sched = BackgroundScheduler(daemon=True)
sched.add_job(func = profile,trigger='interval',minutes=2)
sched.start()
return jsonify(i)
if __name__ == "__main__":
os.environ['DEBUG'] = "1"
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = "1"
app.secret_key = os.urandom(24)
app.run(debug=True)
calling jsonify in your profile() func is causing the out of context error because you're calling a Flask function without the Flask app context.
Refer to this answer on how to add context or do not use jsonify in your profile() func but standard json lib instead.
I'm trying to add oauth2 (server) on my flask app and I have some issues with the /oauth/token endpoint with client_secret_post.
My app does POST the following to it as a form:
client_id=XXX
client_secret=YYY
grant_type=client_credentials
token_endpoint_auth_method=client_secret_post
redirect_uri=http://localhost:8081/oauth-callback
And I get in the logs:
DEBUG:authlib.oauth2.rfc6749.authenticate_client:Authenticate None via "client_secret_basic" failed
127.0.0.1 - - [23/Jun/2019 18:05:26] "POST /oauth/token HTTP/1.0" 401 -
The token_endpoint_auth_method doesn't seems to change anything and it always returns {"error": "invalid_client"}.
I have tried adding TOKEN_ENDPOINT_AUTH_METHODS = ['client_secret_post'] to my class AuthorizationCodeGrant(grants.AuthorizationCodeGrant): without effects (also none of the loggers print anything).
What did I missed there ?
I have implemented things in my app more or less like the oauth2 flask example, here is some extracts:
app.py:
from app_oauth import config_oauth
...
def create_app(...):
...
config_oauth(app)
...
app_oauth.py:
from authlib.flask.oauth2 import AuthorizationServer, ResourceProtector
from authlib.flask.oauth2.sqla import (
create_query_client_func,
create_save_token_func,
create_revocation_endpoint,
create_bearer_token_validator,
)
from authlib.oauth2.rfc6749 import grants
from werkzeug.security import gen_salt
from models import db, User
from models import OAuth2Client, OAuth2AuthorizationCode, OAuth2Token
from flask import current_app
class AuthorizationCodeGrant(grants.AuthorizationCodeGrant):
def create_authorization_code(self, client, user, request):
current_app.logger.debug("create auth code")
code = gen_salt(48)
item = OAuth2AuthorizationCode(
code=code,
client_id=client.client_id,
redirect_uri=request.redirect_uri,
scope=request.scope,
user_id=user.get_user_id(),
)
db.session.add(item)
db.session.commit()
return code
def parse_authorization_code(self, code, client):
current_app.logger.debug("parse auth code")
item = OAuth2AuthorizationCode.query.filter_by(
code=code, client_id=client.client_id).first()
if item and not item.is_expired():
return item
def delete_authorization_code(self, authorization_code):
current_app.logger.debug("delete auth code")
db.session.delete(authorization_code)
db.session.commit()
def authenticate_user(self, authorization_code):
current_app.logger.debug("auth user")
return User.query.get(authorization_code.user_id)
class PasswordGrant(grants.ResourceOwnerPasswordCredentialsGrant):
def authenticate_user(self, username, password):
current_app.logger.debug("password grant auth user")
user = User.query.filter_by(name=username).first()
if user.check_password(password):
return user
class RefreshTokenGrant(grants.RefreshTokenGrant):
def authenticate_refresh_token(self, refresh_token):
current_app.logger.debug("refresh token grant")
token = OAuth2Token.query.filter_by(refresh_token=refresh_token).first()
if token and not token.revoked and not token.is_refresh_token_expired():
return token
def authenticate_user(self, credential):
current_app.logger.debug("auth user grant user")
return User.query.get(credential.user_id)
query_client = create_query_client_func(db.session, OAuth2Client)
save_token = create_save_token_func(db.session, OAuth2Token)
authorization = AuthorizationServer(
query_client=query_client,
save_token=save_token,
)
require_oauth = ResourceProtector()
def config_oauth(app):
authorization.init_app(app)
# support all grants
authorization.register_grant(grants.ImplicitGrant)
authorization.register_grant(grants.ClientCredentialsGrant)
authorization.register_grant(AuthorizationCodeGrant)
authorization.register_grant(PasswordGrant)
authorization.register_grant(RefreshTokenGrant)
# support revocation
revocation_cls = create_revocation_endpoint(db.session, OAuth2Token)
authorization.register_endpoint(revocation_cls)
# protect resource
bearer_cls = create_bearer_token_validator(db.session, OAuth2Token)
require_oauth.register_token_validator(bearer_cls())
and my blueprint:
from app_oauth import authorization
...
#bp_api_v1_auth.route("/oauth/token", methods=["POST"])
def oauth_token():
return authorization.create_token_response()
Edit: after digging it looks like it is handled by ClientCredentialsGrant which does only client_secret_basic by default, I then added:
class ClientCredentialsGrant(grants.ClientCredentialsGrant):
TOKEN_ENDPOINT_AUTH_METHODS = [
'client_secret_basic', 'client_secret_post'
]
...
authorization.register_grant(ClientCredentialsGrant)
Which now validates but respond with {"error": "unauthorized_client"}
Finally nailed it: my OAuth2Client entry in database had only authorization_code and password, client_credentials was needed to validate.
Im trying to integrate the Zoho CRM v2 SDK with my Django app.
On the Django runserver, im able to get access tokens and using the refresh method and store them in the zcrm_oauthtokens.pkl file. The sdk then automatically refreshes the access token using the refresh token, so no problem here. However on my production server (heroku) im getting this error message:
2019-01-16T11:07:22.314759+00:00 app[web.1]: 2019-01-16 11:07:22,314 - Client_Library_OAUTH - ERROR - Exception occured while fetching oauthtoken from db; Exception Message::'NoneType' object has no attribute 'accessToken'
It seems to me that the tokens are being saved to file, but when the sdk try to access them it is looking for them in a DB and not the file specified in the token_persistence_path.
In my settings.py I have this:
ZOHO_CLIENT_ID = config('ZOHO_CLIENT_ID')
ZOHO_CLIENT_SECRET = config('ZOHO_CLIENT_SECRET')
ZOHO_REDIRECT_URI = config('ZOHO_REDIRECT_URI')
ZOHO_CURRENT_USER_EMAIL = 'jamesalexander#mylastwill.co.uk'
ZOHO_PATH = os.path.join(BASE_DIR, 'wills_online', 'zoho')
zoho_config = {'apiBaseUrl': "https://www.zohoapis.com",
'currentUserEmail': ZOHO_CURRENT_USER_EMAIL,
'client_id': ZOHO_CLIENT_ID,
'client_secret': ZOHO_CLIENT_SECRET,
'redirect_uri': ZOHO_REDIRECT_URI,
'token_persistence_path': ZOHO_PATH}
and in a views file I have this:
from zcrmsdk import *
import logging
from django.shortcuts import HttpResponse
from wills.models import PersonalDetails, ZoHoRecord, WillDocument
from wills_online.decorators import start_new_thread
from wills_online.settings import zoho_config
logger = logging.getLogger(__name__)
class ZohoRunOnce:
def __init__(self):
self.already_run = False
def run_once(self):
if not self.already_run:
print('zoho init run once')
ZCRMRestClient.initialize(zoho_config)
self.already_run = True
zoho_init = ZohoRunOnce()
zoho_init.run_once()
print(zoho_config['token_persistence_path'])
def zoho_callback():
return HttpResponse(200)
#start_new_thread
def zoho_personal_details(request):
""" updates or create a user account on zoho on profile completion """
personal_details_ob = PersonalDetails.objects.get(user=request.user)
zoho_ob = ZoHoRecord.objects.get(user=request.user)
try:
if zoho_ob.account:
record = ZCRMRecord.get_instance('Accounts', zoho_ob.account)
record.set_field_value('Account_Name', request.user.email)
record.set_field_value('Name', personal_details_ob.full_name)
record.set_field_value('Email', request.user.email)
record.set_field_value('Address_Line_1', personal_details_ob.address_line_1)
record.set_field_value('Address_Line_2', personal_details_ob.address_line_2)
record.set_field_value('Post_Town', personal_details_ob.post_town)
record.set_field_value('Post_Code', personal_details_ob.post_code)
record.set_field_value('Dob_Day', personal_details_ob.dob_day)
record.set_field_value('Dob_Month', personal_details_ob.dob_month)
record.set_field_value('Dob_Year', personal_details_ob.dob_year)
record.set_field_value('Gender', personal_details_ob.sex)
record.set_field_value('Marital_Status', personal_details_ob.marital_status)
record.set_field_value('Partner_Name', personal_details_ob.partner_full_name)
record.set_field_value('Partner_Gender', personal_details_ob.partner_gender)
record.set_field_value('Partner_Email', personal_details_ob.partner_email)
record.set_field_value('Children', personal_details_ob.children)
record.set_field_value('Pets', personal_details_ob.pets)
record.update()
else:
user = ZCRMUser.get_instance(name='James Alexander')
record = ZCRMRecord.get_instance('Accounts')
record.set_field_value('Account_Owner', user)
record.set_field_value('Account_Name', request.user.email)
record.set_field_value('Name', personal_details_ob.full_name)
record.set_field_value('Email', request.user.email)
record.set_field_value('Address_Line_1', personal_details_ob.address_line_1)
record.set_field_value('Address_Line_2', personal_details_ob.address_line_2)
record.set_field_value('Post_Town', personal_details_ob.post_town)
record.set_field_value('Post_Code', personal_details_ob.post_code)
record.set_field_value('Dob_Day', personal_details_ob.dob_day)
record.set_field_value('Dob_Month', personal_details_ob.dob_month)
record.set_field_value('Dob_Year', personal_details_ob.dob_year)
record.set_field_value('Gender', personal_details_ob.sex)
record.set_field_value('Marital_Status', personal_details_ob.marital_status)
record.set_field_value('Partner_Name', personal_details_ob.partner_full_name)
record.set_field_value('Partner_Gender', personal_details_ob.partner_gender)
record.set_field_value('Partner_Email', personal_details_ob.partner_email)
record.set_field_value('Children', personal_details_ob.children)
record.set_field_value('Pets', personal_details_ob.pets)
response = record.create()
# save account id to db for future updates
zoho_ob.account = response.details['id']
zoho_ob.save()
except ZCRMException as ex:
logger.log(1, ex.status_code)
logger.log(1, ex.error_message)
logger.log(1, ex.error_details)
logger.log(1, ex.error_content)
print(ex.status_code)
print(ex.error_message)
print(ex.error_content)
print(ex.error_details)
Ive tried running ZCRMRestClient.initialize(zoho_config) in settings.py, with no luck.
My method for getting the access token and refresh token, which seems to work is:
import os
import pprint
from sys import argv
import django
import requests
import zcrmsdk
from django.conf import settings
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'wills_online.settings')
django.setup()
def zoho_refresh_token(code):
""" supply a self client token from the zoho api credentials from web site """
zoho_config = {"apiBaseUrl": "https://www.zohoapis.com",
"currentUserEmail": settings.ZOHO_CURRENT_USER_EMAIL,
"client_id": settings.ZOHO_CLIENT_ID,
"client_secret": settings.ZOHO_CLIENT_SECRET,
"redirect_uri": settings.ZOHO_REDIRECT_URI,
"token_persistence_path": settings.ZOHO_PATH}
pprint.pprint(zoho_config)
print('working')
address = f'https://accounts.zoho.com/oauth/v2/token?code={code}&redirect_uri={settings.ZOHO_REDIRECT_URI}&client_id={settings.ZOHO_CLIENT_ID}&client_secret={settings.ZOHO_CLIENT_SECRET}&grant_type=authorization_code'
response = requests.post(address)
data = response.json()
pprint.pprint(data)
zcrmsdk.ZCRMRestClient.initialize(zoho_config)
oauth_client = zcrmsdk.ZohoOAuth.get_client_instance()
refresh_token = data['refresh_token']
print(type(refresh_token))
oauth_client.generate_access_token_from_refresh_token(refresh_token, settings.ZOHO_CURRENT_USER_EMAIL)
print(refresh_token)
print('finished')
if name == 'main':
zoho_refresh_token(argv[1])
This is driving me mad. Help would be greatly appreciated. This is my first post so go easy, lol.
For future reference, you will need to define persistence_handler_class and persistence_handler_path in your configuration dictionary. You will also need a handler class and a user-defined model to store the results. Sample code follows:
# settings.py
import zcrmsdk
configuration_dictionary = {
'apiBaseUrl': 'https://www.zohoapis.com',
'apiVersion': 'v2',
'currentUserEmail': ZOHO_CURRENT_USER_EMAIL,
'sandbox': 'False',
'applicationLogFilePath': '',
'client_id': ZOHO_CLIENT_ID,
'client_secret': ZOHO_CLIENT_SECRET,
'redirect_uri': ZOHO_REDIRECT_URI,
'accounts_url': 'https://accounts.zoho.com',
'access_type': 'online',
'persistence_handler_class': ZOHO_HANDLER_CLASS,
'persistence_handler_path': ZOHO_HANDLER_PATH,
}
zcrmsdk.ZCRMRestClient.initialize(configuration_dictionary)
# zoho.models.py
from django.db import models
from zcrmsdk.OAuthClient import ZohoOAuthTokens
class ZohoOAuthHandler:
#staticmethod
def get_oauthtokens(email_address):
oauth_model_instance = ZohoOAuth.objects.get(user_email=email_address)
return ZohoOAuthTokens(oauth_model_instance.refresh_token,
oauth_model_instance.access_token,
oauth_model_instance.expiry_time,
user_email=oauth_model_instance.user_email)
#staticmethod
def save_oauthtokens(oauth_token):
defaults = {
'refresh_token': oauth_token.refreshToken,
'access_token': oauth_token.accessToken,
'expiry_time': oauth_token.expiryTime,
}
ZohoOAuth.objects.update_or_create(user_email=oauth_token.userEmail, defaults=defaults)
class ZohoOAuth(models.Model):
refresh_token = models.CharField(max_length=250)
access_token = models.CharField(max_length=250)
expiry_time = models.BigIntegerField()
user_email = models.EmailField()
In this example ZOHO_HANDLER_CLASS = 'ZohoOAuthHandler' and ZOHO_HANDLER_PATH = 'zoho.models'
The first time you go to use this you will need a grant_token from https://accounts.zoho.com/developerconsole. For the scope use aaaserver.profile.READ,ZohoCRM.modules.ALL to start (see https://www.zoho.com/crm/developer/docs/api/oauth-overview.html#scopes)
Before you can use the api you'll need to run the code below in a django shell. This uses a grant token to generate your initial access and refresh tokens. Afterwards, the api should handle refreshing your access token.
grant_token = GRANT_TOKEN
import zcrmsdk
oauth_client = zcrmsdk.ZohoOAuth.get_client_instance()
oauth_tokens = oauth_client.generate_access_token(grant_token)