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
Clients not receiving emails
I've been informed that my emails are not being received. Is there anything that I should look into to rectify this? Many thanks!
Free Plan mail accounts details
In the zoho mail pricing there's a free plan that includes: FREE PLAN Up to 25 Users 5GB* /User, 25MB Attachment Limit Webmail access only. Single domain hosting. I need to make sure that I'm able to create multiple email accounts in the form of: name@domain.com
No more IMAP/POP/SMTP on free plans even on referrals with NO NOTICE
Outraged. Just referred a colleague to use her domain (not posting it publicly here) to Zoho, just as I have other colleagues, clients, friends. Expected the exact same free plan features as I have and as everyone else I ever referred got. I was helping
Unable to receive email - "5.3.0 - Other mail system problem 554-'5.2.3 MailPolicy violation Error delivering to mailboxes'"
My users are unable to receive emails from one particular domain, apparently. The domain known to be kicked back is whitelisted in the spam control. I sent an email to support earlier this morning but I have not received a reply. The error in the title
Caixa de saída bloqueada. Como desbloquear?
Olá, meu e-mail isabela.celli@sivirino.com está com a caixa de saída bloqueada. Não consigo enviar e-mails. Acredito que tenha sido porque mandei o mesmo e-mail para várias pessoas, pedindo uma cotação de serviço. Vocês podem desbloquear para mim? Quantos
Zoho Forms - Improve the CRM integration field to query data from more than one module
Hi Forms team, Something I get stuck on regularly is pre-populating a form with data when that data is spread across 2 or 3 modules. For example Contacts, Accounts and Deals. I don't want to duplicate the information in CRM so I end up writing a function
desbloquear cuenta
Buenos dias Cordial saludo Tengo una cuenta libre en zoho mail asociado a un dominio, pero uno de los usuarios se bloquea el correo porque dice que ha excedido el límite de correo, por favor podrian desbloquearla y como hago para que esta persona debe enviar sus correos sin ningun probleama. Gracias de antemano
Not Receiving Incoming Mail
I can send emails from my account but I do not receive any. I originally set up forwarding and it worked for a while and then stopped. I turned off forwarding and now do not receive any emails. Could you please check what is causing this issue? Thank you
Making preview pane "stick"
Hello, Is it possible to fix/dock the preview pane so that it's always there? The modern monitors are all very wide so there's plenty of space horizontally. Having the preview pane disappearing and appearing again when you click on an email message in
Reason:554 5.1.8 Email Outgoing Blocked
I have been struggling to set up my email address for some time now; it's difficult to locate what I need. Additionally, I cannot send or receive any emails. I keep receiving the "Reason: 554 5.1.8 Email Outgoing Blocked" error. There doesn't seem to
Trouble Connecting Zoho Mail via IMAP in n8n – Need Help
Hi everyone 👋, I'm trying to connect my Zoho Mail account to n8n using the IMAP Email Trigger node, but I'm facing issues getting it to work fully. ✅ Here's what I’ve done so far: ✅ IMAP access is enabled in my Zoho Mail settings ✅ I’m using the correct
Unable to send message; Reason:554 5.1.8 Email Outgoing Blocked
Hi, I sent few emails and got this: Unable to send message; Reason:554 5.1.8 Email Outgoing Blocked And now I have few days since I cant send any email. Is there something wrong I did? Also can someone fix this please
Changes to the send mail Deluge task in Zoho CRM
Hello everyone, At Zoho, we continuously enhance our security measures to ensure a safer experience for all users. As part of our ongoing security enhancements, we're making an important update on using the send mail Deluge task in Zoho CRM. What's changing?
Page Rules in Forms
🚀 Dynamic Page Navigation Implementation I successfully implemented dynamic page navigation based on a user's radio button selection. The goal was to direct users to a specific, corresponding page while ensuring they only interact with the flow determined
Cancellation of written-off invoice
Hi, Can I know when we cancel the write off (write back), in which FY, the reversal is recorded. It doesn't ask as to when the write off should be cancelled to reflect!. It shouldn't reflect in the year in which the invoice was written off since the Year
Create Invoice automated with Package
Does anyone knows how to create an invoice from a SO when we have created the package? We do these manually. and validate that the product packed is the product invoiced (if the order is partially packed) Regards, JS
I want to create a mailing list, NOT a group.
Can I create a mailing list in Zoho mail? I just want to be able to make a list of email addresses and give the list a name. Then when I type the list name, the list of email addresses will be automatically listed. When I create a group it sends an email
ERROR CODE :554 - Your access to this mail system has been rejected due to poor reputation of a domain used in message transfer
This message was created automatically by mail delivery software. A message that you sent could not be delivered to one or more of its recipients. This is a permanent error. *****, ERROR CODE :554 - Your access to this mail system has been rejected due
no me llegan los correos a Zoho mail
No puedo recibir correos pero sí enviarlos, ya hice la modificación de MX y la verificación de teléfonos, qué es lo que ocurre? gracias
Group Calendar as Default for adding new events, etc?
Hi, I want to make the group calendar (that I created, if that makes a difference) the default for anything new I add to the calendar. How can I do that? thanks.
Bookmark Loading is Buffering
Hi, i clicked on the bookmark tab, around yesterday and since then it's been constantly buffering and doesn't allow me to access the mail's i have tried login in and out but of no help also trying to share a screenshot of the issue around 232 kb size,
Zoho Webinar custom registration fields into Zoho CRM
I am pushing webinar registrations into zoho crm as leads and this is working fine. I have added a few custom fields to my webinar registration and I wish for these fields values to get mapped into the resulting CRM lead record. I am not seeing anywhere
GitLab Extension for Zoho Desk: Connecting support and development for faster resolutions
Hello everyone! We’re excited to introduce the GitLab Extension for Zoho Desk, an integration that bridges the gap between support and development teams. This allows tickets to be converted into actionable GitLab issues for faster resolutions, better
Download Google Drive Shared Image/File Using Deluge – Complete Guide
Download Google Drive Shared Image/File Using Deluge – Complete Guide When working with Google Drive share links inside Deluge, many developers face a common challenge: 👉 How can we download a Google Drive file directly using Deluge? This article explains
How to update Acuity appointment fields from Zoho Flow?
Hi, I need help with Zoho Flow → Acuity Scheduling integration. What I’m doing Appointment created in Acuity Zoho Flow creates a Zoho Calendar event I want to send the Zoho Calendar Event UID back into Acuity (into a custom form field) This is so that
No horizontal scroll bar in reports
Hi support team, I created a pivot table report which contains many columns (in my application about 15). The problem is that there is no bottom scroll bar to display colums that does not fit within the browser page. I saw that it was possible to resize colums, but colums sizes are not saved within the session. So could you add scroll bar, and eventually make colums sizes stored in the user session ? Best regards,
Zoho Map integration tasks have changed - you need to "Locate all instances of Zoho Map integration tasks in your Deluge scripts by searching for the v1 marker... before 16 January 2026"
Details of an issue specific to Zoho CRM + Deluge Functions (originally posted in this forum) can be found here: https://help.zoho.com/portal/en/community/topic/zoho-map-integration-tasks-have-changed-you-need-to-locate-all-instances-of-zoho-map-integration-tasks-in-your-deluge-scripts-by-searching-for-the-v1-marker
filter broke my data
I uploaded a file recently from Sheets and it has top 2 rows frozen, with table headers in second row and each one is filterable. somehow my first 2 columns became unfiltered and no matter what I do I cannot reapply the filter?? also didn't realize they
Request to remove domain "greentechcoatings.vn" from previous Zoho account
Hello Zoho Support, I would like to register the domain "greentechcoatings.vn" in my new Zoho Mail account, but I receive the message "This domain is already associated with another account". Please remove the domain from any previous/unknown account
Trigger actions in third-party apps using Zoho Flow
Greetings, I hope you're all doing well. We're excited to share an enhancement to Bigin's workflow capabilities. Zoho Flow Actions are now available in Bigin, enabling you to automate tasks across third-party applications directly from your workflow and
Enroll, Script, Win: Hackathon 2025!
Hello CRM Developers! Are you ready to create some magic with Client Script, Widgets and Functions in Zoho CRM? Let’s make Hackathon 2025 an unforgettable adventure! The Zoho Developer Community Hackathon 2025 is here, and it’s your time to shine! REGISTER
Zoho Help Desk - Not receiving emails
Hello… I am on your trial version of help desk… and I am having trouble getting emails from Zoho Help Desk to my MS Outlook Exchange Server… So when a customer sends ticket request I am not getting an email with a notification? I have read your articles
Almost all Flows are being queued
A few days ago I saw one of my Flows status was Queued. This was the first time this had ever happened. Now, almost every Flow that triggers is Queued for at least a few minutes. If I re-trigger the Flow (which causes separate problems) they sometimes
Employment Contract / HRMS Documents - Zoho People
How do I create customized HR documents for disbursal from Zoho People ? Example, say offer letters ? appointment letters ? Memos ? We want to be able to preset these in one or ther other form and issue them to employees who are in Zoho People.
Live webinar: Craft and deliver impactful slides with Show’s desktop apps
If you love the comfort of working on your desktop and want a setup that keeps you focused and uninterrupted, this session is made for you. We’re excited to invite you to our upcoming live webinar: "Craft and deliver impactful slides with Show's desktop
Enhancements to Bigin's forms
Greetings, I hope all of you are doing well. We're happy to announce a few recent enhancements we've made to Bigin's forms. We'll go over each one in detail. Merge field support in auto-filled forms The auto-fill option in Bigin's forms lets you predefine
Table dimensions
I try changing the dimensions of the table on my computer but it doesn't change. Do I have to be a premium member or does it only work on the app?
Direct link to Record Summary
Hi everyone, In one of my reports, I have built a Record Summary template to display the details of one record. I would like to be able to link directly to this Record Summary once I submit a new record, without having to go to the list of records first and click on View. Is there a possibility to do so ? Should I use the URL by passing some parameters ? Thank you very much for your help ! Guillaume
Amendment effective date
Hi everyone, I noticed that the amendment effective date mentionned in my amendment is not right. Indeed, when a contract is amended several times, it states the previous amendment and their effective date. However, the effective date stated is always
Show both Vendor and Customers in contact statement
Dear Sir, some companies like us working with companies as Vendor and Customers too !!! it mean we send invoice and also receive bill from them , so we need our all amount in one place , but in contact statement , is separate it as Vendor and Customer,
Next Page