[GH-ISSUE #2685] How to use Better-Auth with FastAPI?(SQLModel, Alembic) #9302

Closed
opened 2026-04-13 04:43:48 -05:00 by GiteaMirror · 13 comments
Owner

Originally created by @1c7 on GitHub (May 17, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/2685

  • Frontend:Next.js
  • Backend:FastAPI(Web Framework), SQLModel(ORM Tool),Alembic(Database Migration)

Background

FastAPI doesn't have great auth solution, FastAPI Users is the "best" in Python ecosystem but not as good as better-auth

Question

How do I use Better Auth with FastAPI?use Better Auth login first, and then what?
get a JWT and sent that to Backend? and FastAPI verify that JWT?and use SQLModel to grab user info from
users table?

Thank you

Originally created by @1c7 on GitHub (May 17, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/2685 - Frontend:Next.js - Backend:FastAPI(Web Framework), SQLModel(ORM Tool),Alembic(Database Migration) ## Background FastAPI doesn't have great auth solution, [`FastAPI Users`](https://github.com/fastapi-users/fastapi-users) is the "best" in Python ecosystem but not as good as `better-auth` ## Question How do I use Better Auth with FastAPI?use Better Auth login first, and then what? get a JWT and sent that to Backend? and FastAPI verify that JWT?and use SQLModel to grab user info from `users` table? Thank you
GiteaMirror added the locked label 2026-04-13 04:43:48 -05:00
Author
Owner

@1c7 commented on GitHub (May 17, 2025):

https://www.better-auth.com/docs/plugins/bearer
this seem helpful,but I can't verify token on FastAPI server

https://www.better-auth.com/docs/plugins/jwt
this

<!-- gh-comment-id:2888349748 --> @1c7 commented on GitHub (May 17, 2025): https://www.better-auth.com/docs/plugins/bearer this seem helpful,but I can't verify token on FastAPI server https://www.better-auth.com/docs/plugins/jwt this
Author
Owner

@1c7 commented on GitHub (May 17, 2025):

Solution (am I right?)

  1. Assume we use single Linux server(CentOS or Ubuntu) to deploy our Frontend Next.js + Backend FastAPI web app,
    simplest possible architecture. server from AWS EC2 or other cloud provider

  2. Assume Next.js run on port 3000, FastAPI run on port 8000

  3. use Nginx or other load balancer (from cloud provider like AWS Application Load Blancer)

direct / to port 3000
direct /api to port 8000

  1. because they are the same domain, just different route, so cookies would be sent in every request by default

  2. FastAPI read the cookies, use session_token to query database table session

Image

seem like the better-auth.session_token only take first part split(".")[0] is enough

Image

if we found a match, that's the "current user"

<!-- gh-comment-id:2888392384 --> @1c7 commented on GitHub (May 17, 2025): ## Solution (am I right?) 1. Assume we use single Linux server(CentOS or Ubuntu) to deploy our Frontend `Next.js` + Backend `FastAPI` web app, simplest possible architecture. server from AWS EC2 or other cloud provider 2. Assume Next.js run on port 3000, FastAPI run on port 8000 3. use Nginx or other load balancer (from cloud provider like AWS Application Load Blancer) direct `/` to port 3000 direct `/api` to port 8000 4. because they are the same domain, just different route, so cookies would be sent in every request by default 5. FastAPI read the cookies, use session_token to query database table [`session`](https://www.better-auth.com/docs/concepts/database#session) <img width="1691" alt="Image" src="https://github.com/user-attachments/assets/c5b1759a-37ed-477b-b8f4-fb580a5b9c3d" /> seem like the `better-auth.session_token` only take first part `split(".")[0]` is enough <img width="1400" alt="Image" src="https://github.com/user-attachments/assets/0a807b09-9f56-495a-82c1-b30c1c67e819" /> if we found a match, that's the "current user"
Author
Owner

@1c7 commented on GitHub (May 17, 2025):

similar quesiton: https://github.com/better-auth/better-auth/issues/301#issuecomment-2517665417

<!-- gh-comment-id:2888444546 --> @1c7 commented on GitHub (May 17, 2025): similar quesiton: https://github.com/better-auth/better-auth/issues/301#issuecomment-2517665417
Author
Owner
<!-- gh-comment-id:2888471725 --> @1c7 commented on GitHub (May 17, 2025): https://github.com/better-auth/better-auth/blob/659f8391df0b4ac9a03f05a441a0497a71c52e73/packages/better-auth/src/plugins/bearer/bearer.test.ts#L56-L78
Author
Owner

@1c7 commented on GitHub (May 17, 2025):

search split(".")[0]

<!-- gh-comment-id:2888472629 --> @1c7 commented on GitHub (May 17, 2025): search `split(".")[0]`
Author
Owner
<!-- gh-comment-id:2888474294 --> @1c7 commented on GitHub (May 17, 2025): https://github.com/better-auth/better-auth/blob/659f8391df0b4ac9a03f05a441a0497a71c52e73/packages/better-auth/src/plugins/bearer/index.ts#L59
Author
Owner

@1c7 commented on GitHub (May 17, 2025):

659f8391df/packages/better-auth/src/api/routes/session.ts (L107)

<!-- gh-comment-id:2888475273 --> @1c7 commented on GitHub (May 17, 2025): https://github.com/better-auth/better-auth/blob/659f8391df0b4ac9a03f05a441a0497a71c52e73/packages/better-auth/src/api/routes/session.ts#L107
Author
Owner

@1c7 commented on GitHub (May 17, 2025):

659f8391df/packages/better-auth/src/cookies/index.ts (L64)

<!-- gh-comment-id:2888477465 --> @1c7 commented on GitHub (May 17, 2025): https://github.com/better-auth/better-auth/blob/659f8391df0b4ac9a03f05a441a0497a71c52e73/packages/better-auth/src/cookies/index.ts#L64
Author
Owner
<!-- gh-comment-id:2888492185 --> @1c7 commented on GitHub (May 17, 2025): https://github.com/better-auth/better-auth/blob/659f8391df0b4ac9a03f05a441a0497a71c52e73/packages/better-auth/src/plugins/bearer/index.ts#L52-L67 https://github.com/better-auth/utils/blob/main/src/hmac.ts
Author
Owner

@1c7 commented on GitHub (May 17, 2025):

Done, Here is a Python function to verify better-auth.session_token

import hashlib
import hmac
import base64
from urllib.parse import unquote

def verify_session_token(hmac_key: str, session_token: str) -> bool:
    """
    验证 session token 的签名

    Args:
        hmac_key: 用于签名的 HMAC 密钥。
        session_token: 待验证的 session token 字符串,格式为 "data.signature"。

    Returns:
        True 签名有效,False 签名无效。
    """
    try:
        parts = session_token.split('.')
        if len(parts) != 2:
            print("Error: Invalid session token format.")
            return False

        data_encoded = parts[0]
        signature_encoded = parts[1]

        # URL 解码 data 和 signature
        data = unquote(data_encoded)
        signature_expected_bytes = base64.urlsafe_b64decode(unquote(signature_encoded) + '=' * (4 - len(unquote(signature_encoded)) % 4))

        # 使用 HMAC-SHA256 计算 data 的签名
        hmac_obj = hmac.new(hmac_key.encode('utf-8'), data.encode('utf-8'), hashlib.sha256)
        signature_actual_bytes = hmac_obj.digest()

        # 对比计算出的签名和 token 中的签名
        return hmac.compare_digest(signature_actual_bytes, signature_expected_bytes)

    except Exception as e:
        print(f"An error occurred during verification: {e}")
        return False

def main():
    hmac_key = "LcWr9US2bCHDNckxuXKJRAXLOr0a2d6c"  # 请替换为你的实际密钥
    session_token = "6DLBcOCD6gxRQrKGHPZzShSNpXNyBPHX.BLH7eO3aRjS%2B3wWuoetnhkt6ix6jVQcn0A1aAYWcVTc%3D"

    is_valid = verify_session_token(hmac_key, session_token)

    if is_valid:
        print("Session token is valid.")
    else:
        print("Session token is invalid.")

if __name__ == "__main__":
    main()
<!-- gh-comment-id:2888495105 --> @1c7 commented on GitHub (May 17, 2025): ### Done, Here is a Python function to verify `better-auth.session_token` ```python import hashlib import hmac import base64 from urllib.parse import unquote def verify_session_token(hmac_key: str, session_token: str) -> bool: """ 验证 session token 的签名 Args: hmac_key: 用于签名的 HMAC 密钥。 session_token: 待验证的 session token 字符串,格式为 "data.signature"。 Returns: True 签名有效,False 签名无效。 """ try: parts = session_token.split('.') if len(parts) != 2: print("Error: Invalid session token format.") return False data_encoded = parts[0] signature_encoded = parts[1] # URL 解码 data 和 signature data = unquote(data_encoded) signature_expected_bytes = base64.urlsafe_b64decode(unquote(signature_encoded) + '=' * (4 - len(unquote(signature_encoded)) % 4)) # 使用 HMAC-SHA256 计算 data 的签名 hmac_obj = hmac.new(hmac_key.encode('utf-8'), data.encode('utf-8'), hashlib.sha256) signature_actual_bytes = hmac_obj.digest() # 对比计算出的签名和 token 中的签名 return hmac.compare_digest(signature_actual_bytes, signature_expected_bytes) except Exception as e: print(f"An error occurred during verification: {e}") return False def main(): hmac_key = "LcWr9US2bCHDNckxuXKJRAXLOr0a2d6c" # 请替换为你的实际密钥 session_token = "6DLBcOCD6gxRQrKGHPZzShSNpXNyBPHX.BLH7eO3aRjS%2B3wWuoetnhkt6ix6jVQcn0A1aAYWcVTc%3D" is_valid = verify_session_token(hmac_key, session_token) if is_valid: print("Session token is valid.") else: print("Session token is invalid.") if __name__ == "__main__": main() ```
Author
Owner

@1c7 commented on GitHub (May 17, 2025):

Conclusion (is this right?)

In order to use Better Auth with FastAPI, so FastAPI code can get current user by using session

  1. Assume use single Linux server(CentOS or Ubuntu) to deploy our Frontend Next.js + Backend FastAPI web app,
    simplest possible architecture. server from AWS EC2 or other cloud provider

  2. Assume Next.js run on port 3000, FastAPI run on port 8000

  3. use Nginx or other load balancer (from cloud provider like AWS Application Load Blancer)

direct / to port 3000
direct /backend to port 8000

config FastAPI like this:

app = FastAPI(root_path='/backend')
  1. because they are the same domain, just different route, so cookies would be sent in every request by default

  2. FastAPI read cookies better-auth.session_token

Image
6DLBcOCD6gxRQrKGHPZzShSNpXNyBPHX.BLH7eO3aRjS%2B3wWuoetnhkt6ix6jVQcn0A1aAYWcVTc%3D

we could split(".")[0] get the first part, in this case 6DLBcOCD6gxRQrKGHPZzShSNpXNyBPHX
use this to query database table session the tokenfield

Image

we could also use Python function def verify_session_token above, verify this token first, and then query database table.

<!-- gh-comment-id:2888497428 --> @1c7 commented on GitHub (May 17, 2025): ## Conclusion (is this right?) In order to use `Better Auth` with FastAPI, so FastAPI code can get `current user` by using session 1. Assume use single Linux server(CentOS or Ubuntu) to deploy our Frontend `Next.js` + Backend `FastAPI` web app, simplest possible architecture. server from AWS EC2 or other cloud provider 2. Assume Next.js run on port 3000, FastAPI run on port 8000 3. use Nginx or other load balancer (from cloud provider like AWS Application Load Blancer) direct `/` to port 3000 direct `/backend` to port 8000 config FastAPI like this: ``` app = FastAPI(root_path='/backend') ``` 4. because they are the same domain, just different route, **so cookies would be sent in every request by default** 5. FastAPI read cookies `better-auth.session_token` <img width="1691" alt="Image" src="https://github.com/user-attachments/assets/4996c6b8-bdf2-47a8-a9dc-28a0fcfe7320" /> ``` 6DLBcOCD6gxRQrKGHPZzShSNpXNyBPHX.BLH7eO3aRjS%2B3wWuoetnhkt6ix6jVQcn0A1aAYWcVTc%3D ``` we could `split(".")[0]` get the first part, in this case `6DLBcOCD6gxRQrKGHPZzShSNpXNyBPHX` use this to query database table `session` the `token`field <img width="1813" alt="Image" src="https://github.com/user-attachments/assets/d1b8ca7f-d080-4c8f-a878-5e37f41a6b18" /> we could also use Python function `def verify_session_token` above, verify this token first, and then query database table.
Author
Owner

@1c7 commented on GitHub (May 17, 2025):

in order to query session table using SQLModel,

Image

we can write code like this

from typing import Optional
from sqlmodel import Field, SQLModel, create_engine, Session
from datetime import datetime

class SessionModel(SQLModel, table=True):
    __tablename__ = "session"
    id: str | None = Field(default=None, primary_key=True)
    userId: str = Field(index=True)
    token: str = Field(index=True)
    expiresAt: datetime
    ipAddress: Optional[str] = None
    userAgent: Optional[str] = None
    createdAt: datetime
    updatedAt: datetime

and

    with Session(engine) as session:
        statement = select(SessionModel).where(SessionModel.token == token)
        result = session.exec(statement).first()
        return result
<!-- gh-comment-id:2888498858 --> @1c7 commented on GitHub (May 17, 2025): in order to query `session` table using SQLModel, <img width="1409" alt="Image" src="https://github.com/user-attachments/assets/c8cd615d-3207-4b83-8b93-518b7e02b882" /> we can write code like this ```python from typing import Optional from sqlmodel import Field, SQLModel, create_engine, Session from datetime import datetime class SessionModel(SQLModel, table=True): __tablename__ = "session" id: str | None = Field(default=None, primary_key=True) userId: str = Field(index=True) token: str = Field(index=True) expiresAt: datetime ipAddress: Optional[str] = None userAgent: Optional[str] = None createdAt: datetime updatedAt: datetime ``` and ```python with Session(engine) as session: statement = select(SessionModel).where(SessionModel.token == token) result = session.exec(statement).first() return result ```
Author
Owner
<!-- gh-comment-id:2888994545 --> @1c7 commented on GitHub (May 18, 2025): ## Demo: https://github.com/1c7/example_better_auth_with_nextjs_and_fastapi_as_backend
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#9302