Welcome back to another week of Kaizen!!
This week, we are diving into how to implement secure user authentication using
Login with Zoho and integrate it with
Zoho CRM through our
Python SDK.
To ground this in a real-world scenario, we will look at how Zylker Academy, a training institute offering web design and development courses, uses an internal portal that connects directly to Zoho CRM. This setup allows course coordinators to manage student data without maintaining a separate backend database.
Zylker receives frequent student enquiries and uses Zoho CRM to manage all related information. Every course coordinator, academic advisor, and support staff member who needs access to student information is added as a user in Zoho CRM, with access permissions aligned to their role. Instead of using Zoho’s interface directly, Zylker’s team works through a custom internal web portal, tailored to their workflow. This portal connects directly to Zoho CRM, reading from and writing to it, but does not have its own database.
But before this portal can access any CRM data, it must authenticate itself securely. Every time a user opens the portal, they must log in with their Zoho account. Once authenticated, they will be granted access to the CRM modules and records they are authorized to work with. That is where Login with Zoho comes in.
What is "Login with Zoho"?
Login with Zoho is Zoho’s implementation of the OAuth 2.0 Authorization Code flow. It allows applications to authenticate users and access their Zoho CRM data without ever handling their passwords.
Instead of asking users for their Zoho credentials directly, the app redirects them to Zoho’s login screen. Here is how it works:
- The app redirects the user to Zoho’s login page.
- The user logs in and approves the requested permissions (scopes).
- Zoho sends back an authorization code.
- The backend exchanges this code for access and refresh tokens.
- These tokens are used to make authenticated API calls.
This flow ensures that users maintain full control over their data. They can revoke access at any time, and your application never handles or stores passwords.
In Zylker’s case, every time a coordinator opens the portal, they are prompted to log in with their Zoho account. Once authenticated, they can immediately begin working with student records—all backed by Zoho CRM.
Use Case Implementation: Zylker’s Student Management Portal
To demonstrate how this login flow works, we have built a stripped-down version of Zylker's portal:
- A front-end form to enter and view student data
- A backend server that interacts with Zoho CRM via the Zoho CRM Python SDK
The application includes a simple form for capturing student details—name, college, course, email, and phone number. Submitted data is treated as a Lead in Zoho CRM.
The app allows users to:
- Add new leads
- View a list of all registered leads
- Edit an existing lead’s information
- Delete records if necessary
All actions go straight to Zoho CRM using its Python SDK. But before any of this can happen, the user must complete the login flow.
Sample Project Structure
Before going into the implementation details, let us briefly define the components of the project.
Frontend
The frontend is a simple static web interface built with HTML, CSS, and JavaScript. It runs in the browser and handles user interactions and triggers backend API calls. These are the main files:
- index.html : Main UI for login, data entry, and record viewing.
- script.js : Contains the client-side logic to trigger login, submit data, and render records.
- redirect.html : A minimal page used to capture the authorization code returned by Zoho after login.
The frontend is served using any static server (e.g., Live Server in VS Code) and runs on
http://localhost:5501/ in our example.
Download the files from
here.
Configuration Notes:
- In script.js, update the redirect_url value in the login request to match your actual domain or port if you’re not using localhost:5501.
- Ensure the URL in the Zoho API Console matches this redirect URI and port.
Backend
The backend is a Python server that handles all interactions with Zoho CRM via the Python SDK. It includes:
- server.py : A custom HTTP server that:
- Generates the Zoho login URL
- Exchanges the authorization code for tokens
- Initializes the SDK
- Exposes endpoints like /create, /get_records, /update, and /delete
- record.py : Contains functions to create, fetch, update, and delete records in CRM modules like Leads. Each function uses the Zoho Python SDK methods to perform a specific operation.
Download the files from
here.
Configuration Notes:
- In server.py, replace the client_id with your actual client ID from Zoho's API Console.
- In record.py, replace the client_secret with your actual client secret.
- If required, change the front-end server’s host and port in the run() function at the bottom of server.py:
def run(server_class=HTTPServer, handler_class=SDKInitialize, port=xxxx):
Sample project flow
Step 1: Register the application with Zoho API console
To initiate the login process, you need to register your application on the
Zoho API Console. This is a one-time setup that provides your app with a
Client ID and
Client Secret, both of which are required to authenticate users and exchange authorization codes for tokens.
To register your application:
We will be using these values in the backend script (server.py) that handles token exchange.
NOTE: To support users from multiple data centres, make sure to enable multi-DC support for your application. You can do this by going to your app’s settings in the Zoho API Console and turning on the Multi-DC option.Step 2: Implementing the login flow
Here is a walkthrough of the flow implemented in the project:
1. Page loads and triggers login
When a user opens the portal, the frontend automatically initiates the login sequence. It first makes a call to the backend to retrieve the Zoho authorization URL.
In index.html, this triggers getRecords() on page load:
- <body onload="getRecords();">
In script.js, getRecords() calls the login() function:
- async function getRecords() {
- login();
- }
The login() function sends a request to the backend to get the Zoho OAuth authorization URL.
2. Backend builds login URL
The backend responds with an OAuth URL that includes:
- Your client ID
- Scopes like ZohoCRM.modules.ALL
- The redirect URI
In server.py, under do_GET, the /login endpoint generates the OAuth URL:
- if parsed_url.path == '/login':
- redirect_url = query_params.get('redirect_url', [''])[0]
- scope = "ZohoCRM.settings.fields.ALL,ZohoCRM.modules.ALL,ZohoCRM.users.READ,ZohoCRM.org.READ"
- url = "https://accounts.zoho.com/oauth/v2/auth?scope=" + scope + "&client_id=" + self.client_id + \
- "&redirect_uri=" + redirect_url + "&response_type=code&access_type=offline"
- self._set_headers()
- # Send response
- response = {"url": url, "redirect_url": redirect_url}
- self.wfile.write(json.dumps(response).encode('utf-8'))
Once the frontend (script.js) receives the login URL, it opens it in a popup window.
- const response = await fetch('http://127.0.0.1:8085/login?redirect_url=http://127.0.0.1:5501/redirect.html');
- const data = await response.json();
- const popup = openCenteredPopup(data.url, "PopupWindow", 600, 400);
Here's an example of the Zoho OAuth authorization URL format:
scope=ZohoCRM.modules.ALL&
client_id=YOUR_CLIENT_ID&
response_type=code&
access_type=offline&
redirect_uri=YOUR_REDIRECT_URI
3. User logs in on Zoho
The user logs in with their Zoho credentials and is prompted to approve the app's access. Once they approve, Zoho redirects them to the specified redirect URI along with an authorization code and location parameter. The location parameter indicates which data centre the user belongs to.
4. Frontend captures the authorization code
The redirect page, a minimal HTML file (redirect.html), reads the URL parameters and stores them in localStorage, then closes the popup:
- function setAccessToken() {
- var hashProps = getPropertiesFromURL();
- if (hashProps) {
- for (var key in hashProps) {
- if (hashProps.hasOwnProperty(key)) {
- localStorage.setItem(key, hashProps[key]);
- }
- }
- }
- setTimeout(function () { window.close(); }, 0);
- }
5. Token exchange and SDK initialization
Once the popup window is closed, the main window retrieves the authorization code and location and sends them to the backend’s /initialize endpoint.
In script.js:
- var code = localStorage.getItem("code");
- var location = localStorage.getItem("location");
- initialize(code, location, data.redirect_url);
- .
- .
- async function initialize(code, location, redirect_url) {
- const response = await fetch('http://127.0.0.1:8085/initialize?code=' + code + '&location=' + location + '&redirect_url=' + redirect_url);
- }
In server.py, the /initialize endpoint handles SDK initialization:
- elif parsed_url.path == '/initialize':
- code = query_params.get('code', [''])[0]
- location = query_params.get('location', [''])[0]
- redirect_url = query_params.get('redirect_url', [''])[0]
- LeadsRecords().init(self.client_id, code, location, redirect_url)
In record.py, the SDK is initialized and tokens are stored.
- token = OAuthToken(client_id=client_id,
- client_secret=client_secret,
- grant_token=code,
- redirect_url=redirect_url)
- Initializer.initialize(environment=environment,
- token=token,
- logger=logger,
- store=store) # FilePersistence or custom store
This exchanges the authorization code for:
- An access token (valid for one hour)
- A refresh token (used to get new access tokens)
These tokens are saved in a local file (sdk_tokens.json). This is configured using Zoho’s FilePersistence class during SDK initialization
How are tokens linked to users?
The SDK maps each access and refresh token pair to a unique user-organization combination. This means tokens generated for different organizations by the same user are stored separately. Likewise, if a user generates new tokens for the same organization, the SDK updates the existing tokens instead of creating duplicates. This ensures that API calls always use the correct tokens tied to the authenticated user and their organization.
To enable this mapping, the SDK retrieves the user and organization information in the background. This requires the appropriate scopes to be included during authentication, ZohoCRM.users.READ and ZohoCRM.org.READ. Without these scopes, the SDK cannot identify the user-org combination correctly, which can lead to multiple token entries for the same user. That is why, in our sample project, we have included these scopes explicitly in the server.py file during the SDK initialization.
Once the SDK is initialized, the user is logged in, and the app can begin making CRM API calls on their behalf.
Step 3: Accessing Zoho CRM
Once the user is authenticated and the Zoho SDK is initialized on the backend, the frontend can call custom backend endpoints like /create or /get_records. These endpoints use the authenticated SDK instance to make CRM API calls on behalf of the user.
- GET /get_records?module=Leads : View all students
- POST /create?module=Leads : Add new student
- PUT /update?module=Leads&id=... : Edit existing entry
- DELETE /delete?module=Leads&id=... : Remove existing entry
Deploying the sample project
To run this application, you will need two components:
- A frontend server to serve your HTML files (index.html, script.js, redirect.html). This can be done using any static web server (e.g., Live Server in VS Code).
- A Python backend server that handles login, token storage, and CRM API communication. You can run it using:
python server.py
In the given example, both servers communicate over localhost. You should set your redirect URI accordingly when registering your app in the Zoho console.
Conclusion
Login with Zoho is a secure, OAuth-based mechanism that allows users to authorize your application to access their Zoho CRM data. In this example, we built a real-world use case, a student portal for Zylker Academy, that authenticates users and interacts with CRM directly using the Zoho CRM Python SDK.
By walking through the entire flow, you now understand:
- Why OAuth is essential for secure CRM access
- How to register an application in Zoho
- What the login and token exchange flow looks like
- How to implement "Login with Zoho" in your applications
What is next?
In this project, we have used a simple file persistence method to store the token files. But in a real world scenario, this may not always meet your business requirements. In next week's Kaizen, we will implement custom token persistence instead of file persistence in the current project. We will explain how to implement this using SQLite, In-Memory and List DBs. With that, you will be equipped to implement a persistence method that fits your application architecture and deployment environment.
We hope that you found this useful. If you have any queries, let us know the comments below, or send an email to
support@zohocrm.com. As always, we would love to hear from you!!
Stay tuned for next week's Kaizen : Implementing Custom Token Persistence
Download Links:
Further Reading:
Recent Topics
POP3 authentication error - SOLVED
Just in case others are as forgetful as me ... As Zoho has changed the POP server for personal and free organisational users, I needed to change the POP server on my email client. This failed persistently but eventually I remembered that I had chosen
Add Zoho One Groups/Departments to Shared Mailbox Access
Hi, I hope you're doing well. Currently, in Zoho Mail, I can manually add specific users or the entire organization to a shared mailbox. However, there is no option to add Zoho One groups or departments. Feature Request: We would like the ability to assign
Allow Filters with Only Special Characters in Zoho Mail
Hi Zoho Mail Team, I hope you're doing well. We have noticed that currently, Zoho Mail does not allow creating filter criteria using only special characters, such as = or #. However, there are scenarios where such a filter is necessary. For example: Filtering
How to save email as PDF?
I saw 2 previous threads about this. One is from 14 years ago. The other was closed as "answered" a year ago but the feature was never implemented: https://help.zoho.com/portal/en/community/topic/how-to-download-save-emails-as-pdf Is the "save as PDF"
Flexible plans
Hi, I have a Workplace Standard subscription. On Zoho's website, it mentions that with the annual plan it's possible to have multiple plans under the same organization—for example, Workplace Standard and Mail Lite. However, I can’t find a way to do this
Weekly Tips : Teamwork made easy with Multiple Assignees
Let's say you are working on a big project where different parts of a single task need attention from several people at the same time—like reviewing a proposal that requires input from sales, legal, and finance teams. Instead of sending separate reminders
Cannot give public access to Html Snippet in Zoho Creator Page
Hi, I created a form in Zoho Creator and published it. The permalink works but I want to override the css of the form. (style based URL parameters is not good enough) So I created a page and added an Html snippet. I can now override the css, which is
Weekly Tips : Customize your Compose for a smoother workflow
You are someone who sends a lot of emails, but half the sections in the composer just get in your way — like fields you never use or sections that clutter the space. You find yourself always hunting for the same few formatting tools, and the layout just
Accounting on the Go Series-43:Enhancing Your Reporting Efficiency with Dashboard Filter State Retention
Hello everyone! Welcome back to our series on Zoho Books mobile app features. Today, we will talk about a feature that yet again helps you focus on work that really matters-Dashboard Filter State Retention. Imagine you're working on your Zoho Books dashboard,
Zoho books/payroll tax payment
I accidentally made a second payment to my taxes for $300 which is reflected in my bank account and therefore on Zoho books but I can not match it to any transactions because its not reflected in payroll as a tax payment. Is there a way to add an extra
I can't renew the Finance Plus subscription
I tried to renew the Finance Plus subscription but it keeps reloading the same page over and over when ever I click on "Renew Subscription" button
Estimate vs Quote
they are different. Quote is for 1 piece price + other charges. Estimate is for total quantity to be ordered. The gross total amount of the Estimate is the amount payable. Replacing Estimate as Quote is not understandable because they are different. In
Accounting on the Go Series-47: Effortless GSTIN Management- Auto Populate TaxPayer Details in Zoho Books Mobile App
Hello everyone, Welcome back! Today, we're focusing on a feature specifically designed for our Indian users in the Zoho Books-Indian edition, particularly those who deal with GST compliance regularly. We understand the importance of accurate and efficient
Accounting on the Go Series-48: Enhance Accuracy with Custom Work Week Start Days in Zoho Books iOS app
Hello everyone, Welcome back! We’re here with another feature spotlight that might seem small but can have a big impact on your daily routine: setting the first day of the work week in the Zoho Books iOS app. Imagine this: You’re a business owner who
Time to Get Paid Report in ZBooks
Hello, One of our customers who has 25 different companies around the world gets 60 days to make payments. Unfortunately, the subject report does not report an average time to get paid (in days) or the ability to look at a custom period of time. Currently
How to prepare a balance sheet for a company that has no operations yet?
.
Project Billing Method from Zoho People
Normaly our customers use Zoho Projects to manage projects and timesheet that are being charge to the customer. Using the integration from Zoho Project we can have projects base on different billing method. For example most of our customer use Hourly
Zoho Books-Accounting on the Go Series!
Dear users, Continuing in the spirit of our 'Function Fridays' series, where we've been sharing custom function scripts to automate your back office operations, we're thrilled to introduce our latest initiative – the 'Zoho Books-Accounting on the Go Series'.
Zoho Books | Product updates | July 2025
Hello users, We’ve rolled out new features and enhancements in Zoho Books. From plan-based trials to the option to mark PDF templates as inactive, explore the updates designed to enhance your bookkeeping experience. Introducing Plan Based Trials in Zoho
Zoho Books | Product updates | August 2025
Hello users, We’ve rolled out new features and enhancements in Zoho Books. From the right sidebar where you can manage all your widgets, to integrating Zoho Payments feeds in Zoho Books, explore the updates designed to enhance your bookkeeping experience.
Update or Upsert Records Using Unique Custom Fields
Hello customers, We've enhanced the process of updating records via API. You can now: Update records using unique custom fields Upsert records using unique custom fields Note: Both the features are available in the Zoho Books and Zoho Inventory apps.
[Webinar] Understanding the New Invoice Management Systems
Join industry expert CA Pritam Mahure as he discusses the importance of the new Invoice Management System (IMS) and its impact on taxpayers. Topics Covered: - Concept of IMS and pre-requisites - Applicability and Restrictions on Invoices/Records for IMS
Accounting on the Go Series-51: Effortless Transactions: Create and Manage Directly from Uploaded Documents
Hello everyone, We’re back with another useful feature that makes working with Zoho Books even easier! This time, we’re simplifying the process of creating transactions directly from uploaded documents. Imagine you’re out meeting clients, and you receive
[WEBINAR] Effortless migration to the VAT-ready South Africa edition of Zoho Books
Hello there, If you are one of our early users from South Africa using the older edition of Zoho Books, this webinar is for you! We have now launched a new South Africa edition with VAT support, to which you can migrate your existing data. Join this session,
Accounting on the Go Series-52: Capture Receipts on the Go, Right from Your Lock Screen.
Hello everyone, We’re excited to introduce a feature that will make your life so much easier—especially for those constantly on the move! With iOS 18, Zoho Books now allows you to capture receipts directly from your lock screen using Locked Camera Capture.
AT - Austrian Accountant familiar with ZOHO
Hello, as I have just migrated to ZOHO and unfortunately have to change my accountant next year, I am looking for an accountant (for Austria) who already has experience with Zoho or who already manages clients using Zoho books. Is there anyone here from
Zoho Workflow issue
Dear Zoho Users, I have Zoho Books premium subscription, I have created a email alerts workflow for Invoices and following are the configurations for the same: 1. Workflow for Invoice module 2. Choose when to tigger - created or edited 3. Filter the triggers
Accounting on the Go Series-53: Swift Transactions at Your Fingertips-Zoho Books in Your Control Center
Hello again, We’ve all had those moments where you need to get something done quickly, but the process of unlocking your phone, finding the app, and navigating through menus slows you down. Now, with Zoho Books integrated into the iOS 18 Control Center,
Update on PDF Download Issues Caused by Antivirus Software
Hello users, Antivirus software is designed to protect your computer from malicious software and threats. However, they may mistakenly identify legitimate files and software as threats at times. This is known as a false positive. We have received reports
Accounting on the Go Series-54: Effortlessly Find Your Zoho Books Customers in Your Phone’s Contact List
Hello Zoho Books users! How are you all ? We’re happy to share a handy new feature in Zoho Books mobile app. Now, you can access your Zoho Books customers right from your phone's contact list, making it easier to stay connected on the go. How this can
Integration of Phase 2 -e-invoicing- KSA
Zoho Team, I want to get a training of integration of phase 2 invoicing KSA in zoho step by step.
I can not save new invoice
Hello Now I am trying to save a new invoice but I can not save it because showing unpaid invoice warning which are not overdue. Please let me know how to skip unpaid invoice warning letter and save new invoice. Thank you
ADD CONDITIONS FOR FIELD IN ZOHO BOOKS TEMPLATE CUSTOMAZATION
HELLO I WANT TO ADD CONDITIONS FOR FIELD IN ZOHO BOOKS TEMPLATE CUSTOMAZATION FOR SALES ORDER . I HAVE SET %StatusStamp% IF APPROVAL I WANT THAT TEXT TO BE DISPLAYED WITH GREEN COLOR OTHERWISE OF OTHERR STATUS RED COLOR AS SHOWN IN CODE BELOW . BUT IT
Accounting on the Go Series-55: Seamlessly Add New Vendors While Creating Transactions from Scanned Documents
Hi there! We’ve made handling documents and vendors in Zoho Books even simpler. Now, when you upload a document and scan it, if the app detects a vendor that isn’t already in your organization, you don’t have to leave the page to add them manually. With
Accounting on the Go Series-57: Effortlessly Add and Manage Bank Accounts from the Zoho Books Mobile App
Hi all, Great news for our users in the US and Canada! Managing your finances just got a whole lot easier. Zoho Books has always made it seamless to integrate bank accounts and fetch feeds automatically. Now, we’re taking it up a notch—directly from the
Accounting on the Go Series:58-Effortless Compliance: Download XML for Invoices & Credit Notes on Mobile
Hi there! In Mexico, XML files are crucial for electronic invoicing and fulfilling SAT (Tax Administration Service) requirements. These files ensure your transactions are accurately recorded and tax-compliant. Now, you don’t need to rely on the web app
Auto Generated Invoice number YEAR
Auto Generated Invoice number shows transaction year as 25 even though it's 24 still.
Books generiert keine valide XRechnung
Hallo zusammen, ich möchte hier ein Problem ansprechen, das mir aktuell bei der Nutzung von Books erhebliche Schwierigkeiten bereitet, und hoffe auf Austausch oder Lösungsansätze von anderen Nutzern. Bei der Erstellung von XRechnungen mit Books treten
WHEN UPDATE ORGANIZATION INFO OLD INVOICES ALSO CHANGE INVOICING DATA
Hi We have updated our tax information because we have become a company, up until now we were an individual. The problem is that when updating the data in the zoho books profile all the old invoices change their tax information as well. Is there a way
Invoice import error - duplicate customer name column - there are no duplicates
It is not allowing the importing of any rows because of a duplicate customer name problem, but there are no duplicates in the custoemr name row. Has anyone dealt with this issue before?
Next Page