Skip to content

Public vs Confidential Client

logi supports both client types from OAuth 2.0 RFC 6749 §2.1. The type you choose at RP registration determines both your security model and your implementation complexity.

One-line summary

QuestionAnswer
Do you have a backend server that can store a client_secret safely?Confidential
Static SPA + serverless functions (Vercel/Netlify/Cloudflare) doing the token exchange?Confidential (the secret lives in the function's env)
A pure mobile app / SPA / CLI tool that cannot store a secret?Public
Both? (mobile + backend)Confidential + BFF (recommended)

"SPA == Public" is not true

Even if your frontend is an SPA, you are Confidential if there is server-side code (including serverless functions) running the token exchange. The secret goes in the function's environment variables, not in the browser bundle. You are Public only when the browser calls /oauth/token directly (zero server code). For the static SPA + serverless pattern, see → SPA + serverless (Vercel).

Decision tree

mermaid
flowchart TD
    A[Start RP registration] --> B{Server-side backend<br>exists?}
    B -- Yes --> C{Also running a mobile/SPA<br>client?}
    B -- No<br>(pure client) --> D[Public client]
    C -- Yes --> E[Confidential + BFF<br>recommended]
    C -- No<br>(called only from the server) --> F[Confidential]
    D --> G[No client_secret issued<br>PKCE S256 enforced<br>refresh rotation required]
    E --> H[mobile → backend → logi<br>logi tokens stay on the backend only<br>client_secret kept on the backend]
    F --> I[server-to-server<br>HTTP Basic or form body]

Security model differences

Confidential client

  • ✅ Authenticates the client with a client_secret (RFC 6749 §2.3)
  • ✅ HTTP Basic or form body authentication
  • ✅ Can store logi tokens in the backend DB to rotate, revoke, and audit them
  • ⚠️ If the client_secret leaks, rotate it immediately (logi /developer/applications/:id/rotate_secret)

Public client

  • ✅ Cannot authenticate itself → mitigates authorization code interception with PKCE S256 (RFC 7636)
  • ✅ Automatic refresh token rotation + family detection (the whole family is revoked when reuse is detected)
  • ⚠️ logi tokens are exposed in client memory/storage → DPoP / keychain recommended
  • ⚠️ A request is rejected if HTTP Basic or a client_secret parameter is found (downgrade defense)
  • ⚠️ redirect_uri is allowed only for https / loopback / custom-scheme (RFC 8252 §8.5)

When to recommend a BFF (Backend-for-Frontend)

If you have a mobile app and also run your own backend, Confidential + BFF is almost always more secure.

BFF flow:

[Mobile]  Start PKCE → call the authorize URL → receive code at the callback


[Mobile]  POST /api/auth/logi/exchange { code, code_verifier }


[Backend] (holds client_secret) POST {logi}/oauth/token
   │      Receive logi tokens → verify id_token → match the User
   │      → Issue your own JWT

[Mobile]  Stores only your own JWT. It never sees logi tokens.

Security gains of a BFF:

  • The logi access_token / refresh_token never reaches the mobile device → even on device compromise, logi resources stay inaccessible
  • The backend independently manages the TTL/rotation/revocation of its own tokens
  • On token expiry, the backend silently refreshes with logi while the mobile app only renews its own JWT

When you don't need a BFF:

  • A pure SPA / mobile app with no backend of its own (e.g. a widget that is self-contained on the client)
  • Tokens used once on the device right after receipt (e.g. one-time pairing)

Security policies logi enforces on Public clients

ItemEnforcement
PKCES256 required, plain rejected
code_challenge / code_verifierRequired
HTTP Basic authRejected (invalid_client)
client_secret parameterRejected (invalid_client)
redirect_uri schemehttps / 127.0.0.1 / localhost / custom-scheme
External plaintext http redirect_uriRejected
Refresh token rotationA new refresh token is issued on every response
Refresh token reuse detectionThe whole family is revoked

Changing your choice later

Once decided, it cannot be changed — you must register a new RP. This is an intentional constraint. Because the token policies of Public and Confidential differ, changing the type midway can break the security model of tokens that were already issued.

Identity가 제품의 신뢰를 만듭니다.