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 #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
    • Kaizen #142: How to Navigate to Another Page in Zoho CRM using Client Script

      Hello everyone! Welcome back to another exciting Kaizen post. In this post, let us see how you can you navigate to different Pages using Client Script. In this Kaizen post, Need to Navigate to different Pages Client Script ZDKs related to navigation A.
    • Recent Topics

    • Automatic Portal invite

      We have numerous customers we move through a blueprint in deals, when they get to a certain point we need to give them portal access, how can this be done through deluge or a workflow? Latest Update (December 2025): The option to automate portal invitations
    • Marketer's Space: Why mobile optimization deserves a place in your email strategy

      Hello Marketers, Welcome back to Marketer's Space! Today, we'll talk about the importance of creating mobile-friendly email designs. While mobile phones were once used only to make phone calls, today they're used for almost everything, including texting,
    • Enhancements in Canvas

      Dear All, Greetings! Canvas lets you design the record details page to suit your brand or business preferences. We are glad to introduce the following enhancements to uplift your design experience. Reusable Components Style Presets Let's go! Reusable
    • Introducing Dark Mode / Light Mode : A New Look For Your CRM

      Hello Users, We are excited to announce a highly anticipated feature - the launch of Day, Night and Auto Mode implementation in Zoho CRM's NextGen user interface! This feature is designed to provide a visually appealing and comfortable experience for
    • ABN with Legal Entity Name

      Hi, How can I execute this? The ABN number is entered in Accounts Module and ideally, it should display or suggest the correct Legal Entity Name based on the ABN lookup. ex. Account Name: JPG Resources ABN Number: 65 067 761 871 Legal Entity Name: (auto
    • Unify Overlapping Functionalities Across Zoho Products

      Hi Zoho One Team, We would like to raise a concern about the current overlap of core functionalities across various Zoho applications. While Zoho offers a rich suite of tools, many applications include similar or identical features—such as shift management,
    • Issue: Ticket Export Does Not Include Ticket Threads

      Dear Zoho Desk Support Team, I hope you’re doing well. I wanted to bring to your attention that the current ticket export feature in Zoho Desk does not seem to include the ticket threads or conversation history. When exporting tickets, only the summary
    • Pushover Notification Module

      Hello, it would be good if there would be a "Pushover" (https://pushover.net/) module besides the standard SMS module. Pushover is now very well known, especially in IT, and is becoming more and more popular. The biggest advantage are the customizable
    • Forward - no Ticket Number

      Hello, when I send an email to Zoho Desk via Reply or Reply All, the ticket number is in the subject line. But if I use forward then not. Is there an option to change this? We often forward e-mails and a reply to this will of course create a new ticket
    • Checkout: Adding Images

      Hello everyone, I’m trying to add a small GoGreen logo in the shipping section of the checkout to promote our sustainable shipping. While I can insert text without any issues, it seems that adding images is not supported in this area. Is there currently
    • [solved] #original_sender trick not working for us

      Hello community ! We really like DESK.. it's working like a charm for us but I have a request. Some users are sending emails personnaly to me (instead of using the support adress).. and I saw there was a possibilty to use : #original_sender {mail@mail.com}
    • SalesIQ Chat Notifications

      I am the admin of our salesIQ implementation. About two weeks ago, I started hearing/seeing notification for ALL chats messages from monitored agents/chat participants. I don't need to see these, we have a manager who deals with this. I can't stop the
    • Correlated subqueries not supported in Zoho Analytics. This creates huge limitations

      Running into a major limitation in Zoho Analytics: correlated subqueries simply don’t work, even in completely standard SQL patterns inside a JOIN. Example: LEFT JOIN "Bills" b ON d."Id" = b."Deal ID" AND EXISTS ( SELECT 1 FROM "Bill
    • Batch Emails in CRM Plus without using a template?

      Hi guys. Is it possible to send the same email to multiple people at the same time within CRM Plus without using an email template? At the moment we create a custom view in Contacts to display the people that we need. We then click the boxes on the left, and click 'Send Mail', but always need to select a template. Sometimes its better to write a quick one off message, without needing to set up a template first. Thanks.
    • bank charge

      a charge to my account was made that I did not authorize, of $16.46, for something that looks like "computer maintenance or something to that matter". please refund.
    • [Free Webinar] Product Updates: Quick Catch-Up Session - Part II - Creator Tech Connect

      Hello Everyone! We welcome you all to the upcoming free webinar on the Creator Tech Connect Series. The Creator Tech Connect series is a free monthly webinar featuring deep-dive technical sessions designed for developers, administrators, and app builders.
    • Introducing Liquid Glass UI on the Zoho Mail iOS app

      Hello everyone! We're excited to announce that the Zoho Mail app is now fully optimised for iOS 26's Liquid Glass design on both iPhone and iPad! The updated interface features transparent layers, smooth animations, and refined visual elements that enhance
    • Plan change from Zoho One to Zoho Workplace

      Hello Zoho, Following the recent pricing update for Zoho One, we are interested in transitioning to Zoho Workplace products. Please inform us about the necessary steps for this process so we can proceed promptly. Kind regards,
    • Partner with HDFC And Sbi Bank.

      Hdfc and sbi both are very popular bank if zoho books become partner with this banks then many of the zoho books users will benefit premium features of partnered banks.
    • Links in Instagram

      Hi there, I have been using Later for a while now but keen to come back to Zoho Social as Later doesn't offer tagging of pages on Facebook but they offer something Zoho doesn't. You can add a link to your bio which opens up your profile feed where images
    • Spotlight #7 - Automatic Transitions in Blueprint

      Previously, records could not be moved from one state to another without user intervention. With automatic transitions, move records from one state to another automatically, when it elapses its pre-defined time. This Spotlight discusses how automatic
    • Sub Folders

      It would be great if there could be sub-folders in reports. We have a ton of individual reports and folders that would be easier to navigate this way 
    • Zoho CRM Community Digest - November 2025 | Part 2

      Hello Everyone! Second half of November brought several exciting updates, especially around Zia, making AI assistance smarter, faster, and more context-aware. We also feature engaging community snippets highlighting members who went the extra mile to
    • Zoho CRM - Writing Assistant Tone

      Hi Zoho CRM Team, Text in my emails often gets underlined in yellow because I tend to use a more informal tone with my client's, like using "I'm" instead of "I am". Is there some way for me to tell the system that this is my preferred writing tone, so
    • Hide Contact Number for Field Users & Agents

      Is it possible to hide the contact number in both the Service Appointment and Work Order modules for field users and agents? The agents are using the Zoho FSM mobile app, and we want to restrict them from viewing the contact number.
    • Form Submission Emails

      Is there a current delay with submission emails being sent? We've had 10-20 forms completed today but only a handful of emails.
    • Subform edits don't appear in parent record timeline?

      Is it possible to have subform edits (like add row/delete row) appear in the Timeline for parent records? A user can edit a record, only edit the subform, and it doesn't appear in the timeline. Is there a workaround or way that we can show when a user
    • 【Zoho CRM for Everyone】設定画面のアップデート

      ユーザーの皆さま、こんにちは。コミュニティチームの藤澤です。 今回は「Zoho CRM アップデート情報」の中から、Zoho CRM for Everyone の設定画面のアップデート情報をご紹介します。 目次 設定ホーム画面の追加 設定画面での検索結果の表示形式の変更 設定画面でのよく使用する機能の表示 設定メニューの展開/折りたたみ状態の保持 1. 設定ホーム画面の追加 ホーム画面の設定項目は、直感的に分類できるように"カテゴリごと"で分けられています。 このような一元化された表示により、各ツールが1つの画面に集約され、必要な機能をより簡単に見つけられるようになりました。
    • What's New - November 2025 | Zoho Backstage

      A new month, a new set of updates. With a mix of new features and thoughtful improvements, you get smoother workflows and better control across registrations, exhibitors, and communications. Let's take a look at what's new and enhanced in Zoho Backstage
    • Zoho Checkout - Duplicate Customer record created for each payment received

      Hi All. We are using Zoho Checkout to capture online payments for a club membership form (in Zoho Forms). We've noticed that each new payment seems to create a new customer record, regardless of whether the customer already exists in the Zoho Finance
    • Templates for Zoho Desk - Knowledge Base

      We are looking at migrating our Knowledge base into Zoho Desk. Is there a way of creating templates for article in Knowledge base. We want to be able to set templates for certain types of content. Is this possible
    • Import KB template OR Export template for zoho desk?

      Greetings. Can you tell me if there is a way to get an EXPORT of my KB articles? OR is there a template you supply for importing KB articles into my zoho desk? I am looking for a method of understanding what fields can be imported, and what their possible
    • What KPIs Do You Use to Measure Your Support Team’s Performance in Zoho Desk?

      Hi everyone, We hope you’re all doing well. We are trying to improve how we measure the performance and effectiveness of our support department, and we would really appreciate learning from the community. We assume there are several common and widely-used
    • Backorder quantity change

      New Purchase Order × Almost there, but... Quantity of items in this purchase order is greater than the quantity that can be backordered from that sales order. Why can't I change the quantity of a backorder purchase? It looks like a normal order form and I would like to use it as one because I increased the amount and added some items. 
    • How to Associate multiple contacts with deal in Sales Inbox

      Hello, I have many deals that have multiple potential customers associated with a single deal, for example an engineer and a manager. The manager is the Deal's primary contact in CRM and the Engineer is added to the deal in the "Contact Roles" Associated
    • "code": "500","description": "Account not exists", mail api

      I have been through all the steps and have a functioning Oath access_token etc etc... I then GET https://mail.zoho.com/api/organization And get my zoid then GET http://mail.zoho.com/api/organization/<hidden>/accounts and get the account details, which
    • RTL Support for Webforms in Zoho CRM

      Dear Zoho CRM Support Team, We are writing to request an enhancement to the webform builder functionality within Zoho CRM. Currently, to create a webform in a right-to-left (RTL) language, the entire CRM instance must be set to RTL, which can be inconvenient
    • Imported contacts succesful but contacts do not show up in Contacts

      I imported +300 contacts. I can see them in import history. When clicking on a contact in import history I see all the imported contact details for any given contact. So import seems to be successful.   However the imported contacts do not show up in the contacts tab. Not in All contacts, and not in a filtered view where they absolutely should show up. The search function also does not find any imported contact.   Help?
    • Rich Text For Notes in Zoho CRM

      Hello everyone, As you know, notes are essential for recording information and ensuring smooth communication across your records. With our latest update, you can now use Rich Text formatting to organize and structure your notes more efficiently. By using
    • Saving sent email campaign as PDF

      I'm looking to add all campaigns sent to an archive folder in sharepoint. Is there anyway to accomplish this in Zoho Flow ? I'm falling at the first hurdle ... can I automatically save a sent campaign as a PDF to a folder location ?
    • Next Page