DEV Community

Naoto Shibata for Morph

Posted on • Originally published at Medium on

Tutorial: Building a custom AI chat with user authentication using LangChain, OpenAI API and Morph.

In this guide, we explain how to create an authenticated AI application using LangChain and Morph. With Morph’s RBAC (Role-based Access Control) system, you can securely deploy an AI application built with LangChain within your organization.

Morph is a framework that allows you to build and deploy AI apps using Python and Markdown. In the tutorial, we will use the Morph cloud dashboard (available for free), so please log in to morph-data.io in advance.

LangChain is an excellent framework for developing applications that utilize large language models (LLMs). It comes equipped with a variety of components and third-party integrations.

In this article, we detail the steps to build an AI workflow with LangChain and deploy it as an authenticated AI application using Morph, ready for your team to use.

The outcome of this tutorial is available as the RBAC LangChain App in the following repository:

https://github.com/morph-data/morph-sample-apps

Environment Setup

Start a new project using the morph new command. It is recommended to choose poetry as the package manager.

morph new rbac-langchain-app
Enter fullscreen mode Exit fullscreen mode

In your pyproject.toml, set the dependencies as shown below:

[tool.poetry.dependencies]
python = "<3.13,>=3.9"
morph-data = "0.1.3"
langchain = "0.3.16"
langchain-core = "0.3.32"
langchain-openai = "0.3.2"
Enter fullscreen mode Exit fullscreen mode

Since this tutorial uses the OpenAI API, be sure to set your API KEY in the .env file:

OPENAI_API_KEY=[Put your API KEY]
Enter fullscreen mode Exit fullscreen mode

Building the Frontend

With Morph, you can build your frontend by placing MDX files under the src/pages directory.

In this example, we use the component to create an interface where users can chat with the built AI. We also adjust the layout using the component and Tailwind CSS utility classes:

# Role-based access control with LangChain

This example shows how to use LangChain with role-based access control (RBAC).

<Grid cols={1} className="py-4">
    <div className="p-4 bg-gray-50 rounded-lg shadow-sm border border-gray-200 hover:shadow-md transition-shadow">
        <LLM postData="rbac_langchain" />
    </div>
</Grid>
Enter fullscreen mode Exit fullscreen mode

Now, run morph serve in your terminal and open http://localhost:8080 in your browser to see the interface.

Building the AI App with LangChain and Enforcing RBAC

We will build an AI workflow with LangChain that is accessible only to users with the ‘admin’ role.

From the MDX component, two parameters are automatically sent:

  • prompt: The message entered by the user.
  • thread_id: A unique ID that identifies the thread displayed in the Chat UI.

We use a global variable called history to store information about the most recent thread so that the system can respond to follow-up questions in context.

history = {}

@morph.func
def rbac_langchain(context: MorphGlobalContext):
    model = ChatOpenAI(model="gpt-4o-mini")
    # Initialize history when a new thread starts.
    if history.get("thread_id") != context.vars["thread_id"]:
        history["thread_id"] = context.vars["thread_id"]
        history["messages"] = [
            SystemMessage(content="Please translate the following text from English to French."),
        ]
    # Append the user's prompt.
    history["messages"].append(HumanMessage(content=context.vars["prompt"]))
    result = ""
    for token in model.stream(history["messages"]):
        result += token.content
        yield create_chunk(token.content)
    history["messages"].append(AIMessage(content=result))
Enter fullscreen mode Exit fullscreen mode

Next, we return an error message via chat if the user does not have the “admin” role:

# context.user_info comes from the user's authentication info.
if "admin" not in context.user_info["roles"]:
    yield stream_chat("You are not authorized to use this feature.")
    return
Enter fullscreen mode Exit fullscreen mode

Complete Python Code

import morph
from morph import MorphGlobalContext
from morph_lib.stream import create_chunk
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
from morph_lib.stream import stream_chat

history = {}

@morph.func
def rbac_langchain(context: MorphGlobalContext):
    # context.user_info comes from the user's authentication info.
    if "admin" not in context.user_info["roles"]:
        yield stream_chat("You are not authorized to use this feature. \\n\\n Please login as admin.")
        return
    model = ChatOpenAI(model="gpt-4o-mini")
    if history.get("thread_id") != context.vars["thread_id"]:
        history["thread_id"] = context.vars["thread_id"]
        history["messages"] = [
            SystemMessage(content="Please translate the following text from English to French."),
        ]
    history["messages"].append(HumanMessage(content=context.vars["prompt"]))
    result = ""
    for token in model.stream(history["messages"]):
        result += token.content
        yield create_chunk(token.content)
    history["messages"].append(AIMessage(content=result))
Enter fullscreen mode Exit fullscreen mode

Deploying and Sharing Your App

  1. Create a new project on the dashboard and issue an API Key.
  2. In your morph_project.yml, set the project_id you obtained from the project details page:
default_connection: DUCKDB
package_manager: pip
profile: default
project_id: [Insert your project ID]
result_cache_ttl: 0
source_paths:
- src
Enter fullscreen mode Exit fullscreen mode
  1. Next, bind your API Key to the CLI:
morph config
Enter fullscreen mode Exit fullscreen mode

With everything set up, deploy your app from the dashboard!

morph deploy
Enter fullscreen mode Exit fullscreen mode

For more details, see: https://docs.morph-data.io/docs/ja/deploy/deploy

After the deployment is complete, click “Open” from the dashboard to access your app. You can verify that access control is working as expected based on the Project Role set under Member Access in the dashboard.

chat-app-admin

chat-app-general


Top comments (0)