Kaizen #191: Implementing "Login with Zoho" using Python SDK

Kaizen #191: Implementing "Login with Zoho" using Python SDK

      
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:
  1. The app redirects the user to Zoho’s login page.
  2. The user logs in and approves the requested permissions (scopes).
  3. Zoho sends back an authorization code.
  4. The backend exchanges this code for access and refresh tokens.
  5. 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.
This server runs on http://127.0.0.1:8085/ in our example. 

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.

NotesNOTE: 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:
  1. <body onload="getRecords();">
In script.js, getRecords() calls the login() function:
  1. async function getRecords() {
  2.     login();
  3. }
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:
  1.    if parsed_url.path == '/login':
  2.             redirect_url = query_params.get('redirect_url', [''])[0]
  3.             scope = "ZohoCRM.settings.fields.ALL,ZohoCRM.modules.ALL,ZohoCRM.users.READ,ZohoCRM.org.READ"
  4.             url = "https://accounts.zoho.com/oauth/v2/auth?scope=" + scope + "&client_id=" + self.client_id + \
  5.                   "&redirect_uri=" + redirect_url + "&response_type=code&access_type=offline"
  6.             self._set_headers()
  7.             # Send response
  8.             response = {"url": url, "redirect_url": redirect_url}
  9.  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.
  1. const response = await fetch('http://127.0.0.1:8085/login?redirect_url=http://127.0.0.1:5501/redirect.html');
  2. const data = await response.json();
  3. 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:
  1. function setAccessToken() {
  2.     var hashProps = getPropertiesFromURL();
  3.     if (hashProps) {
  4.         for (var key in hashProps) {
  5.             if (hashProps.hasOwnProperty(key)) {
  6.                 localStorage.setItem(key, hashProps[key]);
  7.             }
  8.         }
  9.     }
  10.     setTimeout(function () { window.close(); }, 0);
  11. }

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:
  1. var code = localStorage.getItem("code");
  2. var location = localStorage.getItem("location");
  3. initialize(code, location, data.redirect_url);
  4. .
  5. .
  6. async function initialize(code, location, redirect_url) {
  7.     const response = await fetch('http://127.0.0.1:8085/initialize?code=' + code + '&location=' + location + '&redirect_url=' + redirect_url);
  8. }
In server.py, the /initialize endpoint handles SDK initialization:
  1. elif parsed_url.path == '/initialize':
  2.     code = query_params.get('code', [''])[0]
  3.     location = query_params.get('location', [''])[0]
  4.     redirect_url = query_params.get('redirect_url', [''])[0]
  5.     LeadsRecords().init(self.client_id, code, location, redirect_url)
In record.py, the SDK is initialized and tokens are stored.
  1. token = OAuthToken(client_id=client_id,
  2.                    client_secret=client_secret,
  3.                    grant_token=code,
  4.                    redirect_url=redirect_url)
  5. Initializer.initialize(environment=environment,
  6.                        token=token,
  7.                        logger=logger,
  8.                        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:
  1. 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).
  2. 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:

    • Sticky Posts

    • Kaizen #226: Using ZRC in Client Script

      Hello everyone! Welcome to another week of Kaizen. In today's post, lets see what is ZRC (Zoho Request Client) and how we can use ZRC methods in Client Script to get inputs from a Salesperson and update the Lead status with a single button click. In this
    • Kaizen #222 - Client Script Support for Notes Related List

      Hello everyone! Welcome to another week of Kaizen. The final Kaizen post of the year 2025 is here! With the new Client Script support for the Notes Related List, you can validate, enrich, and manage notes across modules. In this post, we’ll explore how
    • Kaizen #217 - Actions APIs : Tasks

      Welcome to another week of Kaizen! In last week's post we discussed Email Notifications APIs which act as the link between your Workflow automations and you. We have discussed how Zylker Cloud Services uses Email Notifications API in their custom dashboard.
    • Kaizen #216 - Actions APIs : Email Notifications

      Welcome to another week of Kaizen! For the last three weeks, we have been discussing Zylker's workflows. We successfully updated a dormant workflow, built a new one from the ground up and more. But our work is not finished—these automated processes are
    • Kaizen #152 - Client Script Support for the new Canvas Record Forms

      Hello everyone! Have you ever wanted to trigger actions on click of a canvas button, icon, or text mandatory forms in Create/Edit and Clone Pages? Have you ever wanted to control how elements behave on the new Canvas Record Forms? This can be achieved
    • Recent Topics

    • need to upload from airtable to google drive

      I have a zapier zap that automates between airtable and google drive. When a customer uploads a new file into airtable via a client portal interface, zapier uploads that file into a folder linked to that customer's project record. I need to replicate
    • Can't delete functions that are associated with deleted workflow rules

      We have a handful of functions that were once associated with a workflow rule, but the rule has been deleted. The function still thinks it is associated so I can't assign it to a new rule. It is starting to get really messy because we have a list of functions
    • Default Sorting on Related Lists

      Is it possible to set the default sorting options on the related lists. For example on the Contact Details view I have related lists for activities, emails, products cases, notes etc... currently: Activities 'created date' newest first Emails - 'created
    • Credit Management: #1 Credit You Owe vs Credits Owed to the Business

      Think about the last time you ordered food online. You might have paid in advance through your card, but you received a $20 refund because your order got delayed or cancelled. In most apps, refunds don't go into the bank account directly; instead, they're
    • Tip #46- Turn Every Session into an Insight with Zoho Assist survey report- 'Insider Insights'

      Delivering exceptional remote support isn’t just about resolving issues, it’s about understanding how both customers and technicians experience each session. That’s where Survey Report in Zoho Assist come in. You can configure and customize survey questions
    • CRM/Bookings integration edits Contact names

      Hi there, I've installed the extension that connects Zoho CRM and Zoho Bookings. When we get a new appointment from Bookings from an existing Contact, that Contact's record shows this: First Name was updated from asd to blank value Last Name was updated
    • Domain Change

      “Please update my Email-in domain from @biginmail.biginmail.in to @biginmail.zoho.com. Messages to the .in domain are bouncing.”
    • Webhooks Limit Exceeded

      Today, I received an error message saying, 'Total number of Webhook call exceeded', but when I look at Manage > Billing, it doesn't look like any of my invokeURL calls are being logged. Following the advice from this thread: https://help.zoho.com/portal/en/community/topic/webhooks-daily-limits-in-zoho-creator
    • Auto select option in CRM after Zoho Form merge

      Hi, I have a dropdown field in Zoho CRM that is filled with a Zoho Form. The data is filled but not automatically shown. After selecting the right value in the dropdown the information a second field is shown. So the question is; how can I make the dropdown
    • Bring your CRM data straight into your presentations in Zoho Show

      Let's say you are working on a presentation about your team's sales pipeline for an upcoming strategy meeting. All the information you need about clients and leads is in Zoho CRM, but you end up copying details from the CRM into your slides, adjusting
    • Zoho People 2.0 Help Manual

      Hello All, Zoho People Help Documentation is now available at this link. Please go through and get in touch with us at support@zohopeople.com in case of any questions. Regards, Hemalatha Chandrasekaran | Zoho People
    • Introducing New APIs in Zoho Contracts

      We are excited to announce the release of new APIs in Zoho Contracts to help you automate and manage every stage of your contract lifecycle more efficiently. Here’s a quick overview of what’s new: 1. Complete Contract Draft You can use this API to complete
    • Vimeo

      For me Vimeo is the most important video social channel for media and filmmakers. Would others agree and like it added to Zoho Social.
    • Delete a department or category

      How do I delete a Department? Also, how do I delete a Category? This is pretty basic stuff here and it's impossible to find.
    • Organization Emails in Email History

      How can I make received Org Emails to show up here?
    • How to setup pricing in Zoho

      Hi everyone, I am relatively new here and have just moved from my old inventory system to the Zoho one. I am trying to get my head around how it all works. I am mostly setup connected to a shopify store, but I do manual sales also For manual invoicing,
    • Prefilled Date fields auto-changed and then locked when using “Edit as new”

      If a document out for signature has date fields (not SignedDate fields) that were pre-filled before sending, and then you use “Edit as new” to create a new version of the same document, the value of those date fields gets automatically changed to today
    • Is there a way to update all the start and end dates of tasks of a project after a calendar change?

      Hi! Here's my situation. I've built a complete project planning. All its tasks have start dates and due dates. After completing the planning, I've realized that the project calendar was not the right one. So I changed the project calendar. I now have
    • Access Phone Field Components (Country Code) Directly

      Hello everyone, I'd like to propose an enhancement for the Phone field in Zoho Creator. The Problem: The Phone field captures the country code and local number separately, but stores them as a single string (e.g., +1234567890). To get the country code,
    • Send mass messages through WhatsApp from the Tickets module

      Hi Everyone! Effective communication is key to delivering prompt and reliable customer support. Because WhatsApp is one of the most widely used and familiar messaging platforms, it's an effective channel for agents to reach customers who have submitted
    • Lead Owner Signature Merge Field

      I want to automatically insert a signature (i.e. contact info usually found at the bottom of an email) into an email template, depending on who the lead owner is. What is the merge code for the Signature from a Users profile? CRM > Settings > Customization > Templates There is a popup near the bottom of the edit screen which says: "You can insert a Signature, which is available as a merge field in the users section." It is also referenced on this page: https://help.zoho.com/portal/en/kb/crm/customize-crm-account/customizing-templates/articles/template-builder#Merge_Fields
    • How do I create a time field?

      I want a field that only records time. I can only see how to create a date-time field. If I do that and enter a time, without a date, nothing is recorded. If I create a number or decimal field, I cannot use it in time calculations. All I want is a field
    • Alternating columns - How to reverse order on mobile - Responsive template

      Can the order of alternating columns be reversed on mobile so that image comes before the text? Example: Desktop Row 1 column Left (image) , column right (text) Row 2 column Left (text) , column right (image) Mobile Currently Row 1 Image over text Row
    • InvokeURL butchering JSON for OpenAI API calls

      My organization works with mostly educational institutions. We have a custom module called "Schools", which is the user-entered school name they put when using our service (which they enter along with their state and zip code). We want to map this to
    • Custom order for Current Stage (Blueprint field)

      Hi! I suggest adding the option to set a custom order in reports for the Blueprint field ‘Current Stage’. Currently, these fields can only be sorted in ascending or descending order. Thanks!
    • CRM E-mail Sync from Outlook

      We are exploring Zoho as a possible new solution for our company. We are trying to understand further on how e-mail sync works. We use outlook. Our current CRM logs anytime we e-mail a customer so we can see in the CRM the message sent to the customer.
    • Run your help desk on your schedule

      In business, time is of the essence. This is especially true in a function like customer service, where KPIs such as response time and agent availability are the measures of success. Perhaps the most crucial consideration one needs to make about time is your hours of operation. These set expectations for your customer and for employees. To make communicating this information easier, we've revamped the way business hours and holiday lists work in Zoho Desk.   In order to accommodate teams that work
    • The Customer Happiness REST API is broken

      1. We are unable to extract the customerHappiness ( https://desk.zoho.com/DeskAPIDocument#CustomerHappiness#CustomerHappiness_ListallcustomerHappiness) object using the ticketNumber criteria. We keep getting HTTP 429 even when we limit to 60 calls per
    • Zoho Expense and Chase Bank Visa credit cards - Direct Feed?

      Our company uses JP Morgan Chase Visa credit cards. We can't be the first to try and use a combination of Zoho Expense + Chase Bank + Visa Credit Cards --- anyone successful with this combination? 1. The direct feed automation isn't working. When I go
    • Impossibile inviare il messaggio;Motivo:554 5.1.8 Indirizzo del mittente bloccato

      " Impossibile inviare il messaggio;Motivo:554 5.1.8 Indirizzo del mittente bloccato" Hi need to send email, how can fix this? Thanks, Alice
    • Image inputed in Zoho Form were displayed to small in exported version

      Good evening, my name is athallah, i am a trainee in government office Right now, i use Zoho form to digitalize an airfield monitoring form. my problem is, the image were to small to displayed in exported version. it really bad for formal presentation
    • API. How do I get responses submitted to the form?

      Hi, I'm trying to retrieve a list of form responses via the API. I've created a Self Client application in API Console. What scoup should I enter? What are the endpoints? Is there documentation on this anywhere? I've wasted a lot of time searching. GPT
    • Question about Zoho CRM Professional plan users

      Hello! I have a question about the Zoho CRM Professional plan. Is the pricing charged per user or per organization? Are there any free users included in this plan, or do I need to pay for each additional user separately? Thank you!
    • Communicating with emojis

      On July 17, we celebrate World Emoji Day! We're a bit late 😐 sharing insights about this day. But we just couldn't let it pass without a mention 😊 because emojis have a meaningful connection with customer service 💬 🤝. We do not want to miss out on
    • Help integrating Aircall into Zoho CRM

      Hi all, We are need to get better AirCall integration into Zoho. We have configured in the Aircall dashboard but we have not done the Zoho side. We cannot see Zoho CRM information on incoming calls. And we calls and text's are not being logged. Specifically
    • Shopify Extension no longer working in CRM

      Zoho CRM and Extension are no longer working properly. We have used Shopify Basic for years. All of the sudden it stopped working, then started working again, and now no longer working again. There are several ways for Personal Information to sync from
    • How to keep track of bags, cans, drums of inventory?

      We buy and sell products that are packaged in bags 🛍️, cans🥫, drums🛢️, etc. with batch numbers. When we get a shipment of one of the products, how do we track we received (say) 10 cans each of 5L of a product and maybe we received 10 cans of another
    • Easy third party collaboration: Zoho Flow Integration

      Third-party integrations extend your project functionality with improved workflow, and data processing. With Zoho Flow integration you can include multiple third party actions into your project or task automation. Add Zoho Flow action in Workflow rules
    • Introducing Configure, Price, Quote (CPQ) in Zoho CRM | Public early access 2023

      Greetings Customers! We're excited to announce that the CPQ feature will soon be available in Zoho CRM and accessible to all accounts using the Professional, Enterprise, and Ultimate editions. We've opened this feature to select customers, and it'll be
    • how to edit the converted lead records?

      so I can fetch the converted leads records using API (COQL), using this endpoint https://www.zohoapis.com/crm/v5/coql and using COQL filter Converted__s=true for some reasons I need to change the value from a field in a converted lead record. When I try
    • Next Page