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
Finding text within a ticket: Expand All or Search this Ticket
The auto-collapse feature within a ticket is nice for screen scrolling, however it makes it difficult to find text within the ticket if the email is collapsed. In fact you cannot find text if it is collapsed. I would like to propose a feature that allows
Books & Desk. Client mapping
Hi, I’ve been using Zoho Books for several years and am now looking to improve my customer service. I'm experimenting with Zoho Desk and want to sync and map my client data from Zoho Books. However, it seems that mapping requires both contacts to have
Bulk update Profile Permissions
Dears, What should we do if we add new forms or reports and need to update more than 20 permissions? Updating them one by one feels pretty harsh, doesn’t it?
adding several team members to an Opportunity
How can we add several team members to one opportunity for collaboration? I have researched and only found something called Deal Team which I cannot find in my CRM to configure.
String handling
If I cut a currency string from a quote and try and paste it into the Deal "Amount", it will fail unless I manually delete any commas. Dollar signs are no problem, but comma's seem to fail. Please correct this Input Validation error.
text length in list report mobile/tablet
Is there a way to make the full text of a text field appear in the list report on mobile and tablet? With custom layouts, the text is always truncated after a certain number of characters.
email address somehow still not verified (?!)
L.S. After creating a new email template in CRM I was about to send a group email to my clients, then Zoho CRM announced that they would change the sender address to some kind of Zoho-e-ddress because my email address "has not been verified". Not only
What's new in Zoho Sheet: Simplify data entry and collaboration
Hello, Zoho Sheet community! Last year, our team was focused on research and development so we could deliver updates that enhance your spreadsheet experience. This year, we’re excited to deliver those enhancements—but we'll be rolling them out incrementally
WebDAV / FTP / SFTP protocols for syncing
I believe the Zoho for Desktop app is built using a proprietary protocol. For the growing number of people using services such as odrive to sync multiple accounts from various providers (Google, Dropbox, Box, OneDrive, etc.) it would be really helpful
Consolidated report for multi-organisation
I'm hoping to see this feature to be available but couldn't locate in anywhere in the trial version. Is this supported? The main aim to go to ERP is to have visibility of the multi-organisation in once place. I'm hopeful for this.
Using MPN across multiple SKUs and inventory tracking
I have several different SKU's that share a common MPN and would like to track inventory by MPN. SKU1 has MPN1 assigned SKU2 has MPN1 assigned Here is an example If I start with 5 of MPN 1 in stock I want each SKU1 and SKU2 to show as 5 in stock, If I
Unable to Access Application:
Whenever I try to access my application from the desktop, say I am editing it and want to test something in the desktop environment I get: An error has occurred. An internal error has occurred. Please check the URL , or try refreshing the page I can edit
Feature Request - Allow Customers To Pick Meeting Duration
Hi Bookings Team, It would be great if there was an option to allow customers to pick a duration based on a max and minimum amount of time defined by me and in increments defined by me. For example, I have some slots which are available for customers
Error Logs / Failure logs for Client Scripts Functions
Hi Team, While we are implementing client scripts for the automation, it is working fine in few accounts but not working for others. So, it would be great if we can have error Logs for client scripts also just like custom functions. Is there any way that
Welcome to the Zoho ERP Community Forum
Hello everyone, We are thrilled to launch Zoho ERP (India edition), a software to manage your business operations from end to end. We’ve created this community forum as a space for you to ask questions, comment answers, provide feedback, and share your
Google Fonts Integration in Pagesense Popup Editor
Hello Zoho Pagesense Team, We hope you're doing well. We’d like to submit a feature request to enhance Zoho Pagesense’s popup editor with Google Fonts support. Current Limitation: Currently, Pagesense offers a limited set of default fonts. Google Fonts
Add Popup Rejection Metrics to Reports
Hello Zoho PageSense Team, We would like to request improved reporting for popup interactions. Current Limitation: PageSense currently provides conversion data, but there is no clear visibility into: Popup rejections Popup closes (✕ button clicks) Dismissals
Ability to Reset / Reinitialize Popup Cookies
Hello Zoho PageSense Team, We would like to request the ability to manually reset popup cookies. Current Limitation: At the moment, it is not possible to initiate a new popup cookie from the our side. Visitors who rejected or closed a popup will not see
Control Popup Cookie Expiration Duration
Hello Zoho PageSense Team, We would like to request an enhancement related to popup cookie management. Current Limitation: Currently, PageSense popup cookies remain active for 365 days, and this duration cannot be modified by us. If a visitor closes or
Support for Custom Fonts in Zoho Recruit Career Site and Candidate Portal
Dear Zoho Recruit Team, I hope you're doing well. We would like to request the ability to use custom fonts in the Zoho Recruit Career Site and Candidate Portal. Currently only the default fonts (Roboto, Lato, and Montserrat) are available. While these
YouTube Live streaming? how to? Zoom has this feature, built-in. Can't find it on zoho meetings.
YouTube Live streaming? how to? Zoom has this feature, built-in. Can't find it on zoho meetings.
Feature Request - A Way To Search Item Groups
Hi Inventory Team, I can't find any way to filter or search by fields of Item Groups. It would be great to see that functionality added. I have a use case where a single product might come from 5 or more suppliers and each supplier's item is an Item in
Feature Reqeust - Include MPN In Selectable FIelds
I have noticed that the MPN is not available to show in the list view of Items. Please consider adding it as EAN, UPC and ISBN are all available, so it doesn't make much sense to exclude this similar option. Thanks for considering my feedback.
Advanced Payment for Inventory Items with serial numbers
Hello, We sell equipment that we track the unique serial numbers on using Sales Orders. We can charge the customers an advanced payment, then the balance on delivery. We cannot figure out a way to do this in Books/Inventory: - Cannot part invoice a SO
Feature Request - Option To Hide Default System Fields on Items
Hi Zoho Inventory Team, As far as I know it is not possible to hid some of the defult system fields on Items, such as UPC, MPN, EAN, ISBN. A good use case is that in many cases ISBN is not relevant and it would be an improved user experience if we could
Making an email campaign into a Template
I used a Zoho Campaign Template to create an email. Now I want to use this email and make it a new template, but this seems to be not possible. Am I missing something?
Campaigns does not work!
I am running into so many problems trying to use Zoho Campaigns, that I am seriously considering dropping the app from my (shrinking) list of Zoho applications I actually use. Apart from having to fight the software trying to create a design and email,
Feature Request - Make Available "Alias Name" Field In Item List View
Hi Zoho Inventory Team, I have noticed that the "Alias Name" field does not appear on the list of selectable columns in the Customise Columns feature in the Items module. This would be very useful to see for businesses who are using the Alias Name field
Marketing Automation
L.S. Marketing Automation is and has always been part of the Zoho One bundle - according to the information provided on the Zoho Website. Why when I open Marketing Automation do I get the following message?: "Your trial has expired. We hope you enjoyed
Cliq iOS can't see shared screen
Hello, I had this morning a video call with a colleague. She is using Cliq Desktop MacOS and wanted to share her screen with me. I'm on iPad. I noticed, while she shared her screen, I could only see her video, but not the shared screen... Does Cliq iOS is able to display shared screen, or is it somewhere else to be found ? Regards
Bigin, more powerful than ever on iOS 26, iPadOS 26, macOS Tahoe, and watchOS 26.
Hot on the heels of Apple’s latest OS updates, we’ve rolled out several enhancements and features designed to help you get the most from your Apple devices. Enjoy a refined user experience with smoother navigation and a more content-focused Liquid Glass
Notes badge as a quick action in the list view
Hello all, We are introducing the Notes badge in the list view of all modules as a quick action you can perform for each record, in addition to the existing Activity badge. With this enhancement, users will have quick visibility into the notes associated
Claude + MCP Server + Zoho CRM Integration – AI-Powered Sales Automation
Hello Zoho Community 👋 I’m excited to share a recent integration we’ve worked on at OfficehubTech: ✅ Claude + MCP Server + Zoho CRM This integration connects Zoho CRM with Claude AI through our custom MCP Server, enabling intelligent AI-driven responses
Automatic Matching from Bank Statements / Feeds
Is it possible to have transactions from a feed or bank statement automatically match when certain criteria are met? My use case, which is pretty broadly applicable, is e-commerce transactions for merchant services accounts (clearing accounts). In these
How to filter Packages in zoho inventory api
Hi Team, I want to perform some tasks in a schedular on the packages which are in "Shipped" state. I tried to use filter_by in my api call but in return I get response as {"code":-1,"message":"Given filter is not configured"} My Api request is as follows
CRM
Is anyone else experiencing this issue? Our company is not moving out of using Gmail's web app. It just has more features and is a better email program than Zoho Mail. Gmail has an extension (Zoho CRM for Gmail) that we're using but we've found some serious
Automate pushing Zoho CRM backups into Zoho WorkDrive
Through our Zoho One subscription we have both Zoho CRM and Zoho WorkDrive. We have regular backups setup in Zoho CRM. Once the backup is created, we are notified. Since we want to keep these backups for more than 7 days, we manually download them. They
Syncing with Google calendar, Tasks and Events
Is it possible to sync Zoho CRM calendar, task and events with Google Calendar's tasks and events. With the increasing adoption by many major tool suppliers to sync seamlessly with Google's offerings (for instance I use the excellent Any.do task planning
IMAP mail after specify date
Hi My customer's mail server is on premise and mail storage is very huge. So It never finish sync. and finally stop sync. Cloud CRM have a option like zoho mail sync mail after some date.
How can i view "Child" Accounts?
It can be very useful in our field of business to know the parent-child account relationship. However, there seems to be a shortcoming in the parent account view: no child account list. How can we view the child accounts per each account?
Next Page