LIME

Let an agent sign into your site

The agent opens your site in a browser, hits “Sign in with LIME”, and stays on the waiting screen — you create a login request and show login_request_id on that page. The agent reads the ID from the screen and approves.

  • 25 min
  • Intermediate

Done when

After approve, the browser has your normal site session for that agent_id.

  • Waiting UI showed login_request_id until approve finished
  • Passport JWT checked (aud=lime-site-login + matching request_id)
  • Your cookie is set; LIME_SITE_TOKEN never entered the browser

Run step 1

You need

  • “Sign in with LIME” button + waiting screen that shows login_request_id
  • Backend that can set a session cookie
  • LIME_SITE_TOKEN only on that backend

Steps

  1. Create the login request on button click

    When the browser hits Sign in with LIME, call create_login_request. Put login_request_id on the waiting screen — that screen is how the agent gets the ID.

    import asyncio
    from lime_sites import LimeSite
    
    LIME_SITE_TOKEN = "LIME_SITE_TOKEN_HERE"
    
    async def main() -> None:
        site = LimeSite(site_token=LIME_SITE_TOKEN)
        req = await site.create_login_request()
        print("login_request_id:", req.request_id)
        # Show req.request_id on the waiting screen in the browser.
        await site.aclose()
    
    asyncio.run(main())

    AssertResponse includes login*request_id (lr*…); waiting UI shows the ID.

  2. Wait for approval over SSE

    Keep GET …/events with X-Site-Token while the page waits. When the agent approves, the event carries the passport JWT for this request_id. Treat **login_request_id as the SSE idempotency key** — Redis delivery is at-least-once; duplicate APPROVED frames for the same id must not mint a second site session.

    import asyncio
    from lime_sites import LimeSite
    
    LIME_SITE_TOKEN = "LIME_SITE_TOKEN_HERE"
    
    async def main() -> None:
        received = asyncio.Event()
        box: dict[str, object] = {}
    
        site = LimeSite(site_token=LIME_SITE_TOKEN)
    
        @site.on_login
        async def handle_login(request_id: str, passport: str | None) -> None:
            box["request_id"] = request_id
            box["passport"] = passport
            received.set()
    
        await asyncio.wait_for(received.wait(), timeout=120)
        print(box.get("request_id"), bool(box.get("passport")))
        await site.aclose()
    
    asyncio.run(main())

    AssertSSE delivers passport JWT for the same login_request_id.

  3. Verify JWT and open the session

    verify_passport (signature, aud=lime-site-login, expected request_id). Read agent_id and set your normal site cookie — same as a human login.

    import asyncio
    from lime_sites import LimeSite
    
    LIME_SITE_TOKEN = "LIME_SITE_TOKEN_HERE"
    PASSPORT_JWT = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
    LOGIN_REQUEST_ID = "lr_xxxxxxxx"
    
    async def main() -> None:
        site = LimeSite(site_token=LIME_SITE_TOKEN)
        verified = await site.verify_passport(
            PASSPORT_JWT,
            expected_request_id=LOGIN_REQUEST_ID,
        )
        agent_id = verified.claims["agent_id"]
        # Set YOUR site session cookie for agent_id — LIME does not create it.
        print("signed_in_agent_id:", agent_id)
        await site.aclose()
    
    asyncio.run(main())

    AssertClaims.agent_id present; site session cookie issued for that agent_id.

Protocol

The agent reads the ID from the waiting screen

LIME does not push login_request_id to the agent. Show the ID on the waiting screen in the browser — the agent reads it there and calls approve. Site and agent must share one LIME portal account.

showWaitingScreen(login_request_id)

Verify

  • Assert: button → create → ID on waiting screen → agent approve → SSE JWT verifies with that request_id → cookie set.
  • Negative: foreign-owner or unknown login_request_id on approve → same 404 SITE_LOGIN_REQUEST_NOT_FOUND (no site_id in body).
  • Negative: verify_passport with a different request_id is rejected by the SDK.
  • Negative: duplicate SSE APPROVED for the same login_request_id → idempotent (one site session).

Wire failures

ScopeStatusCodeWhenWhat to do
LIME API404SITE_LOGIN_REQUEST_NOT_FOUNDlogin_request_id is unknown **or** approve lacks row authority (foreign owner / unknown site)Create a new request from the button and show the ID on the waiting screen again; use an agent under the same portal account as the site
LIME API409SITE_LOGIN_REQUEST_EXPIREDLogin request TTL elapsed before approveAsk the user to click Sign in with LIME again
SDK verify401passport_verify_failedSignature, aud, or request_id check fails in the SDKCall LimeSite.verify_passport with the same request_id from create

Related API

Let an agent sign into your site · LIME