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 #198: Using Client Script for Custom Validation in Blueprint

      Nearing 200th Kaizen Post – 1 More to the Big Two-Oh-Oh! Do you have any questions, suggestions, or topics you would like us to cover in future posts? Your insights and suggestions help us shape future content and make this series better for everyone.
    • 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
    • Recent Topics

    • Celebrating our customers on International Client's Day

      Hi everyone, Every customer is part of our Zoho Desk family, and we wouldn’t want to miss celebrating you on this special day. This International Client's Day, we’d like to take a moment to appreciate every customer who has helped us grow and build meaningful
    • Faster Ticket Response with use of Snippets in comments

      Hello everyone, Responding to tickets often involves repeating the same messages, whether it is sharing updates with customers or leaving notes for internal collaboration. To make this easier and more consistent, we are allowing agents to use snippets
    • Improving Zoho Creator's email deliverability

      Hi all, We're pleased to announce updates to email handling in Zoho Creator to improve the deliverability of the emails sent from the Creator platform. These updates have been designed keeping in mind the fact that emails from domains with strong sender
    • What's New in Zoho Billing | February 2026

      February brings a powerful set of updates to Zoho Billing, from smarter subscription management and flexible payment options to better reporting and more control over your hosted pages. Here's everything that's new this February. Create Subscriptions
    • Displaying only unread tickets in ticket view

      Hello, I was wondering if someone might be able to help me with this one. We use filters to display our ticket list, typically using a saved filter which displays the tickets which are overdue or due today. What I'd really like is another filter that
    • Issue creating a YTD report with last years numbers

      I am looking to create a widget to compare the YTD leads we have in 2023 to the YTD leads we had in 2022. I'm having issues with the aggregate formula creation to gain access to the 2022 Leads YTD data point. Feeling like this is a common view of data
    • Brand Studio Projects in Analytics

      Hi All, Currently pulling my hair out over trying to link together some social media posts for a reporting dashboard in Analytics, so I thought I'd see if anyone on here had a solution. Our Marketing Team created a LinkedIn campaign in Zoho Brand Studio,
    • Manage Testing Process in Zoho Sprints

      Agile framework supports a robust quality assurance system that incorporates continuous testing throughout the development lifecycle of the product. The agile testing team focuses on planning the testing around the development, identifying defects early
    • Announcing the Zohotshots of the ZDC Hackathon 2025

      Hey everyone, The wait is finally over! After weeks of building, experimenting, and pushing creative boundaries, we’re excited to announce the winners of the second edition of the ZDC Hackathon. This edition brought together an incredible community of
    • ZOHO CRM Button Integration

      Hi Team, I’m currently working with Zoho CRM along with a custom application where I fetch deal details from the CRM and use them based on my requirements for each deal. Now, I want to enhance this setup. I plan to create a button on the Deal Detail page
    • Subtotals per Header

      Hey, we would like to display subtotals per header in our invoices:
    • Candidate Assessments, no workflows.

      I have an issue where I would like to trigger a workflow based on a candidate assessment being completed. Sometimes a returning candidate will complete a second assessment, on completion the status of the candidate will be updated to "Unqualified" if
    • Mail Merge in Zoho Desk

      Hello Team, Do we have an option of mail merge within Zoho Desk like we have in Zoho CRM? We have a requirement to generate some pdf file in the form of mail merge doc from the ticket.
    • Support Required for Integrating Zoho SalesIQ in Flutter Web

      Hi Team, I have developed a Flutter application and I am currently using the salesiq_mobilisten: ^6.5.4 package. As per the package documentation ( https://pub.dev/packages/salesiq_mobilisten ), it supports only Android and iOS, and it is working fine
    • Zoho vault filling in a form it's not supposed to fill in

      Hey there, I have a problem with ZOHO Vault autofill. In a software package, we have these options: For some reason zoho vault always replaces what's in the top option and fills a 2fa password in there, and then saves that value.. Meaning whatever was
    • Zoho Booking Integration with Desk

      Just wanting to share something in case it helps someone else who may run into a similar use case as we did. Issue: Currently, the Zoho Booking integration with desk will drop a comment into the ticket when a customer books a time, however it won't change
    • Let us view and export the full price books data from CRM

      I quote out of CRM, some of my clients have specialised pricing for specific products - therefore we use Price Books to manage these special prices. I can only see the breakdown of the products listed in the price book and the specialised pricing for
    • Syncing calendar with Google Calendar doesn't work when events are sent to auto repeat

      Hi... The ZOHO CRM -- GOOGLE CALENDAR sync is broken. If I create a single event on either side, sync works, but if I create an event with auto repeat on either side it doesn't work. Furthermore, events created before the sync don't show up in the calendar.
    • Organization wide Account and Contacts Visibility/Sharing Capabilities?

      Has anyone figured out a way to make visibility or sharing of Accounts and Contacts to be available across the entire organization without having to have every individual user edit their Sharing permissions? For our sales folks they need to be able to
    • Notification of Interaction Assignment

      If a user is assigned an interaction, they should be notified via email. These are almost useless because if I assign an interaction for someone to respond to, they dont know that I assigned it to them without me telling them or them logging in and discovering
    • Custom Display Field for Lookup Dropdowns in Zoho CRM

      Could Zoho CRM support changing the display field in lookup dropdowns, like Zoho Creator does? This would make it much easier to select the right record by showing a more useful field instead of only the default one. It would improve speed, clarity, and
    • A2P 10DLC Opt-in Rejection Issue with Zoho Creator Public Form

      Hi everyone, I’m working on an A2P 10DLC SMS campaign and running into repeated rejections due to opt-in issues. I’m using Zoho Creator for the registration flow. The form is public (no login required). Users enter their phone number and there is an unchecked
    • Trouble with using Apostrophe in Name of Customers and Vendors

      We have had an ongoing issue with how the system recognizes an apostrophe in the name of customers and vendors. The search will not return any results for a name that includes the mark; ie one of our vendors names is "L'Heritage" and when entering the
    • Books <-> CRM synchronisation with custom Fields

      Hello, We are synchronising Books Customers with CRM Accounts. In CRM Accounts I set up last year a "segments" multiselect field shown below In Books, I set up a custom multi-select field with the same value as in the CRM And set up the synchronisation inside Books. Want to synchronise the Books Segments with the CRM Segments, but the later doesn't exist, and another non-existing is there ?! First, I don't understand where the field Segmentation is coming from. Second, I set CRM Segmentation to sync
    • Recording overpayment?

      So a customer just overpaid me and how do I record this? I can't enter an amount that is higher than the invoice amount. Eg. Invoice is $195 and he sent $200. He's a reccuring customer so is there a way to record so that he has a $5 advance for future invoice?
    • Introducing the New Zoho Assist Quick Support Plugin

      We are thrilled to announce the new Zoho Assist Quick Support Plugin, the upgraded and enhanced version of the Zoho Assist Customer Plugin. This new plugin allows organizations and IT administrators to deploy it directly onto their customers’ devices,
    • Automate your signing workflows with Zoho Sign + n8n

      Hello! We're excited to announce that Zoho Sign is now available as a community node on n8n, a popular open-source workflow automation platform used by tens of thousands of teams worldwide. n8n lets you connect apps, APIs, and services through a visual
    • Retainer invoice in Zoho Finance modlue

      Hello, Is there a way of creating retainer invoices in the Zoho Finance module? If not can I request this is considered for future updates please.
    • Spotlight #27: Embed visual collaboration Spaces in your presentations using the Vani add-on

      Hello everyone! This month’s spotlight feature is the Vani add-on for Zoho Show. Every time you pause your presentation to open another tab or pull up supporting material, you lose a bit of momentum. At Zoho Show, we design features that keep everything
    • 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
    • Good news! Calendar in Zoho CRM gets a face lift

      Dear Customers, We are delighted to unveil the revamped calendar UI in Zoho CRM. With a complete visual overhaul aligned with CRM for Everyone, the calendar now offers a more intuitive and flexible scheduling experience. What’s new? Distinguish activities
    • Global Search / Command Palette in Live App

      Zoho Creator applications can contain many forms, reports, pages, and dashboards. While navigation inside the app is smooth, users still need to move through multiple menus or screens to find specific records or open particular modules. Currently, in
    • New 2026 Application Themes

      Love the new themes - shame you can't get a little more granular with the colours, ie 3 different colours so one for the dropdown menu background. Also, I did have our logo above the application name but it appears you can't change logo placement position
    • Smarter appointment allocation with round-robin distribution

      Greetings from the Zoho Bookings team! We’re excited to introduce the Appointment Distribution feature, a new way to decide how appointments are assigned among users. By default, appointments are distributed evenly across all event types, but this enhancement
    • Analytics & Reporting Improvements + Export Bug

      Hello, I would like to raise several important product suggestions and issues regarding Zoho Expense: 1. Filter in Admin View → Analytics → Expense Details → Receipt Is it possible to add a filter that allows us to prioritize or sort reports that have
    • Make Quick Edits to Images Before Attaching

      Hello everyone, We have enhanced how attachments are handled in tickets to help agents preview and share files more efficiently in Zoho Desk. Agents can preview image attachments before adding them to tickets and edit them using attachment annotator.
    • 3/18 オンライン勉強会のお知らせ Zoho ワークアウト (無料)

      ユーザーの皆さま、こんにちは。コミュニティチームの中野です。 3月開催のZoho ワークアウトの開催が決定しましたのでご案内します。 今回はZoomにて、オンライン開催します。 ▶︎参加登録はこちら(無料) https://us02web.zoom.us/meeting/register/BoNTN7zYR8OvOPGShqBY0A ━━━━━━━━━━━━━━━━━━━━━━━━ Zoho ワークアウトとは? Zoho ユーザー同士で交流しながら、サービスに関する疑問や不明点の解消を目指すイベントです。
    • Extend color coding to custom picklist fields

      Objectively, Projects has the best UI of any Zoho app — clean, intuitive, and never feels bloated. Big props to whoever owns the design. Feature request: color coding for custom picklist field values in field customization. You've already done it in two
    • New in Office Integrator: In-sheet text translation

      Hi users, We're pleased to introduce translation capability in the spreadsheet editor in Zoho Office Integrator. This allows you to translate the text in your spreadsheet's cells into 70+ languages from within your web app. Office Integrator's spreadsheet
    • Streamline email communication with Out of Office configuration

      Managing user communication effectively is the key to ensuring timely responses and consistent messaging. However, when users are unavailable, the absence of an Out of Office response can lead to delays and missed expectations. Managing these settings
    • Next Page