Kdy OAuth a kdy API klíč
| API klíč | OAuth 2.0 | |
|---|---|---|
| Kdo ho vydá | vy ve své firmě | každý zákazník sám, ve své firmě |
| K čí data se dostanete | ke svým | k datům toho, kdo souhlas udělil |
| Odvolání | smazáním klíče | uživatel kdykoliv sám v Nastavení → API a integrace |
| Životnost | dokud klíč nesmažete | access token 1 hodinu, refresh token 30 dní |
Existující API klíče OAuth nijak neruší ani nemění — fungují přesně jako dosud, jen jim od této verze chodí navíc hlavičky s kvótou.
Registrace aplikace
Aplikaci zaregistrujete v aplikaci InvoiceHub: Nastavení → API a integrace → OAuth aplikace → Nová aplikace. Potřebujete k tomu právo zápisu do sekce Nastavení firmy.
Vyplňujete:
- Název a popis – uvidí je uživatel na consent obrazovce. Napište pravdivě, co s daty děláte; je to jediné, podle čeho se rozhoduje.
-
Návratové adresy (
redirect_uri) – jedna na řádek. Musí býthttps(výjimkou jelocalhostpro vývoj), nesmí obsahovat fragment#a porovnávají se na přesnou shodu celého řetězce. Adresa, která se liší jediným znakem nebo parametrem v query, projde jako neplatná. - Rozsah – scopes, o které aplikace vůbec smí požádat. Uživatel pak schvaluje jejich podmnožinu.
-
Důvěrný klient – zapnuto u serverových aplikací, které
client_secretuchrání. Aplikace běžící v prohlížeči nebo v mobilu ho uchránit nemůže: nechte volbu vypnutou a použijte PKCE.
Tajemství dostanete jen jednou
client_secret se zobrazí pouze v odpovědi na registraci.
V databázi z něj zůstane jen bcrypt otisk, takže ho znovu nezobrazí ani
podpora. Ztratíte-li ho, vydejte si tlačítkem Nové tajemství další — staré
tím okamžitě přestane platit. Vydané access tokeny to nezneplatní (klient se jimi
neautentizuje), jen přestane fungovat výměna kódu a obnova, dokud si nové
tajemství nedoplníte.
Scopes a oprávnění sekcí
Scope má tvar <sekce>:<read|write> a odpovídá jedna ku jedné
sekcím, po kterých se v InvoiceHub rozdávají oprávnění uživatelům.
write v sobě obsahuje i read — kdo smí doklad
založit, musí ho umět i zobrazit.
| Sekce | Scopes | Co pokrývá |
|---|---|---|
documents |
documents:read, documents:write |
faktury, zálohové faktury, nabídky, objednávky, dodací listy, dobropisy, ceník |
costs |
costs:read, costs:write |
přijaté náklady |
contacts |
contacts:read, contacts:write |
odběratelé a dodavatelé |
reports |
reports:read, reports:write |
reporty, statistiky, uložené pohledy a exporty |
company_settings |
company_settings:read, company_settings:write |
fakturační údaje, daně, číselné řady, vzhled dokladu, šablony |
payments |
payments:read, payments:write |
bankovní účty, pohyby, párování plateb, platební brána |
automations |
automations:read, automations:write |
automatizační pravidla a webhooky |
Aktuální katalog i s popisky, které uvidí uživatel, vrací
GET /api/v1/oauth/scopes (bez autentizace).
Scope není nová pravomoc, ale strop. Výsledný přístup tokenu je
průnik tří věcí: schválených scopes, oprávnění sekcí uživatele, který souhlas
udělil, a rozsahu jeho fakturačních oddělení. Požádáte-li o
documents:write a uživatel má doklady jen ke čtení, dostanete
čtení. Když mu firma oprávnění později odebere, přestane ho mít i váš token —
bez jakékoliv akce z vaší strany. Consent obrazovka tohle uživateli říká
dopředu: scope, na který sám nemá, ukáže jako nedostupný.
Authorization code flow
Průběh má tři kroky.
1. Pošlete uživatele na consent obrazovku
https://invoicehub-web.zeabur.app/oauth/souhlas
?client_id=ihc_...
&redirect_uri=https://partner.example.com/oauth/callback
&response_type=code
&scope=documents:read%20contacts:read
&state=nahodny-retezec
-
scope– mezerami oddělený seznam. Vynecháte-li ho, zeptáme se na celý rozsah, který má aplikace zaregistrovaný. -
state– náhodný řetězec, který si zapamatujete v session. Vrátíme vám ho beze změny; neshoduje-li se, požadavek zahoďte. Je to vaše jediná ochrana proti CSRF na návratové adrese.
Není-li uživatel přihlášený, aplikace ho nejdřív přihlásí a pak ho na souhlas vrátí.
2. Uživatel rozhodne, my ho vrátíme k vám
https://partner.example.com/oauth/callback?code=ihac_...&state=nahodny-retezec
Když přístup nepovolí:
https://partner.example.com/oauth/callback?error=access_denied&state=nahodny-retezec
Kód platí 10 minut a dá se uplatnit právě jednou. Druhý pokus o jeho výměnu bereme jako příznak, že kód někdo odposlechl: odvoláme celý souhlas včetně tokenů, které z prvního uplatnění vznikly.
3. Vyměňte kód za tokeny
POST /api/v1/oauth/token
Content-Type: application/json
{
"grant_type": "authorization_code",
"client_id": "ihc_...",
"client_secret": "ihs_...",
"code": "ihac_...",
"redirect_uri": "https://partner.example.com/oauth/callback"
}
Odpověď:
{
"access_token": "iho_...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "ihr_...",
"scope": "documents:read contacts:read"
}
redirect_uri musí být doslova ta samá, se kterou jste
uživatele posílali na souhlas. Tajemství můžete místo těla poslat i přes HTTP
Basic (Authorization: Basic base64(client_id:client_secret)).
Pole scope v odpovědi čtěte. Nemusí být stejné,
jako to, o co jste žádali — uživatel mohl schválit méně, nebo na část scopes
sám nemá právo. Podle něj si zapněte jen ty funkce, které opravdu půjdou.
PKCE pro veřejné klienty
Aplikace bez client_secret (jednostránková aplikace, mobil, desktop)
musí použít PKCE podle RFC 7636 — bez něj ji na consent
obrazovku nepustíme.
-
Vyrobte si náhodný
code_verifier(43–128 znaků) a spočítejtecode_challenge = base64url(sha256(code_verifier)). -
K odkazu na souhlas přidejte
code_challengeacode_challenge_method=S256. -
Při výměně kódu pošlete v těle
code_verifier. Neodpovídá-li, dostaneteinvalid_grant.
Metoda plain je podporovaná jen kvůli klientům bez SHA-256; používejte
S256.
Volání API tokenem
Access token se posílá úplně stejně jako API klíč — obyčejnou hlavičkou
Authorization. Hlavičku X-Organization-Id
neposílejte: firma je pevně dána souhlasem, ne požadavkem.
GET /api/v1/documents
Authorization: Bearer iho_...
Token platí jednu hodinu. Když vyprší, je odvolaný, je odvolaný
souhlas nebo je aplikace vypnutá, vrátí API 401 se stejnou hláškou
Invalid or expired token — rozlišit se to schválně nedá.
Obnova tokenu
POST /api/v1/oauth/token
Content-Type: application/json
{
"grant_type": "refresh_token",
"client_id": "ihc_...",
"client_secret": "ihs_...",
"refresh_token": "ihr_..."
}
Odpověď má stejný tvar jako výměna kódu. Refresh token se ale rotuje: starý pár okamžitě zneplatníme a vy si musíte uložit ten nový z odpovědi. Použití už rotovaného refresh tokenu je pro nás signál krádeže a odvolá celý souhlas — uživatel pak musí přístup povolit znovu.
Rozsah nového tokenu ořízneme aktuálním souhlasem: zúžil-li ho uživatel mezitím,
dostanete v odpovědi menší scope.
Odvolání přístupu
Odvolat se dá ze dvou stran.
Vy — když uživatel u vás integraci vypne, zahoďte token i u nás (RFC 7009):
POST /api/v1/oauth/revoke
Content-Type: application/json
{
"client_id": "ihc_...",
"client_secret": "ihs_...",
"token": "iho_..."
}
Přijímáme access i refresh token. Odpověď je vždycky 200, i pro
token, který jsme nenašli — z vašeho pohledu je výsledek stejný.
Uživatel — v Nastavení → API a integrace → OAuth aplikace → Povolené přístupy tlačítkem Odvolat přístup. Zneplatní se tím okamžitě všechny vydané tokeny, ne až vyprší poslední z nich.
Kvóty požadavků
Každé credentials — API klíč i OAuth aplikace — mají měsíční limit požadavků podle tarifu firmy. Limit je na jedny credentials, ne na celou firmu: dvě integrace se o něj nedělí.
| Tarif | Požadavků / měsíc / integrace |
|---|---|
| Free | 1 000 |
| Basic | 25 000 |
| Pro | 250 000 |
| AI | bez omezení |
Okno je kalendářní měsíc v UTC: počítadlo se vynuluje prvního dne v měsíci v 00:00 UTC. Aktuální stav vidí zákazník v aplikaci na obrazovce Nastavení → API a integrace, pruhem u každého klíče a každé aplikace zvlášť.
Do kvóty se počítají jen úspěšně autentizované požadavky. Volání s neplatným tokenem zákazníkovi z limitu neubere.
Hlavičky X-RateLimit-*
Na každé odpovědi autentizované klíčem nebo OAuth tokenem — ne až když je zle — chodí:
X-RateLimit-Limit– měsíční limit, nebounlimited.X-RateLimit-Remaining– kolik zbývá po započtení této odpovědi.X-RateLimit-Used– kolik se jich už spotřebovalo.X-RateLimit-Reset– unixový čas (v sekundách), kdy se počítadlo vynuluje.
HTTP/1.1 200 OK
X-RateLimit-Limit: 250000
X-RateLimit-Remaining: 249993
X-RateLimit-Used: 7
X-RateLimit-Reset: 1788220800
Po vyčerpání limitu vrací API až do konce období:
HTTP/1.1 429 Too Many Requests
Retry-After: 183600
{
"error": "Vyčerpali jste měsíční kvótu požadavků API pro váš tarif.",
"code": "RATE_LIMIT_EXCEEDED",
"limit": 250000,
"used": 250001,
"resetAt": 1788220800,
"upgrade": true
}
Nezkoušejte to znovu hned. Retry-After u měsíční
kvóty ukazuje na konec období, ne na pár sekund — opakování ve smyčce nepomůže.
Řešením je vyšší tarif, nebo méně volání (načítejte po stránkách a kešujte
číselníky).
Chybové kódy
Endpointy protokolu (/oauth/token, /oauth/revoke,
/oauth/authorize) vracejí tvar podle RFC 6749 — error
a error_description, ne obvyklý tvar chyby InvoiceHub.
| Kód | Co se stalo |
|---|---|
invalid_request | chybí povinný parametr, nesedí redirect_uri, nebo veřejný klient nepoužil PKCE |
invalid_client | neznámé client_id, špatné tajemství, nebo vypnutá aplikace |
invalid_grant | kód nebo refresh token neplatí, vypršel, už byl použit, nebo neodpovídá code_verifier |
invalid_scope | žádný z požadovaných scopes není pro aplikaci povolený |
unsupported_grant_type | jiný grant než authorization_code a refresh_token |
access_denied | uživatel přístup nepovolil, nebo na požadované scopes nemá právo |
Checklist integrace
- Návratová adresa na
https, zaregistrovaná doslova tak, jak ji používáte. stategenerovaný náhodně a ověřený po návratu.- Veřejný klient používá PKCE se
S256. client_secretjen na serveru, nikdy v kódu, který vidí prohlížeč.- Po každé obnově uložit nový refresh token — starý už neplatí.
- Řídit se polem
scopez odpovědi, ne tím, o co jste žádali. - Sledovat
X-RateLimit-Remaininga při429počkat doresetAt. - Při odpojení uživatele u vás zavolat
/oauth/revoke.