iVendNextDevelopers Request a sandbox

"Sign in with iVendNext" — for a partner app

This recipe lets the users of a partner app outside the shop — a web page, a mobile app, a portal — sign in with their own iVendNext account. Every call the app then makes runs with that person's permissions and nothing more. It uses the standard authorisation code flow with PKCE (S256). The reference is tools/sign_in/signin_flow.py (Python, standard library only).

When to use it: your app acts for a named person in the shop, not for a shared integration user.

1. The shop registers your app (once)

The shop's administrator creates an OAuth Client for you, with:

They give you its client id. Optionally, Allowed Roles limits which of the shop's users may sign in to your app. A client secret is also made — see §4 for why you must not rely on it.

2. The flow

  1. Your app makes a random verifier (43–128 characters) and its challenge = base64url(SHA-256(verifier)), and a random state. Keep both on your side for this sign-in.
  2. Send the person's browser to https://<shop>/api/method/frappe.integrations.oauth2.authorize with client_id, response_type=code, redirect_uri, scope=openid all, state, code_challenge, code_challenge_method=S256.
  3. The person signs in to the shop with their own account. A person not yet signed in is sent to the shop's own sign-in page first. The shop then shows an Allow / Deny page naming your app.
  4. On Allow, the shop redirects to your redirect address with code and the same state. Refuse the callback if state is not the one you sent.
  5. Your server exchanges the code at …/frappe.integrations.oauth2.get_token (form fields: grant_type=authorization_code, code, redirect_uri, client_id, code_verifier). You get an access_token (and a refresh_token).
  6. Every call then carries Authorization: Bearer <access_token> and runs as that person. …/frappe.integrations.oauth2.openid_profile names them and their roles.

3. What is refused, and why

The shop refuses each of these, so a replayed code or a revoked token gets nothing (section 4: always use PKCE):

4. ⚠ Always use PKCE — the client secret is not checked

On this build the token endpoint does not check the client secret. So for a partner app, PKCE is the only thing that makes a stolen code useless. Use it for every sign-in, from every kind of app. Treat the client secret as an identifier, not a credential. Until the token endpoint checks it, rely on PKCE alone.

5. Your duties

Technical notes

Proven on our test bench on 2026-10-04 (run pp-20261004T111824Z-95458): 15 checks, every one passing, and one measurement (§4). The table groups the 15 checks into nine rows.

What was proven on the bench

Checks Result
A guest is sent to the shop's own sign-in page first, and signs in to their own account 2 ✅
The Allow/Deny page is shown; Allow returns a code and the same state to the registered address 2 ✅
Code + verifier → a token, no client secret sent 1 ✅
The token acts as that person; the profile names them and their roles (a Retail Cashier) 2 ✅
…reads what they may read (items) and is refused what they may not (the error log: 403) 2 ✅
A revoked token works no more 1 ✅
A code exchanged with a wrong verifier, with no verifier, or with no verifier and the right client secret, is refused 3 ✅
A code works once 1 ✅
A redirect address the app did not register gets no code 1 ✅

The measurement behind §4

On this build a code issued without PKCE was exchanged with a wrong secret and a token came back.

Bounds, stated

This page in the kit