SPB Git forge

spb/fetcha

Public
11commits 1branches 0releases
1.5 MBsize
maindefault branch
16 days agolast push
TypeScript 97.5% SQL 1.4% Python 0.8%
8.0 KB · 173 lines tsx
Raw Blame History
1import type { Metadata } from "next";2import { CodeBlock } from "@/components/ui/code-block";3import { DocPage } from "@/components/docs/doc-page";4import { A, Code, H2, H3, Li, P, Strong, Table, TBody, Td, Th, THead, Tr, Ul } from "@/components/docs/prose";5import { Callout } from "@/components/docs/callout";6import { Endpoint } from "@/components/docs/endpoint";7import { CodeTabs } from "@/components/docs/code-tabs";8import { ResponseExample } from "@/components/docs/response-example";9import { apiTabs } from "@/components/docs/snippets";1011export const metadata: Metadata = {12  title: "Authentication",13  description: "API keys, Bearer and X-API-Key headers, scopes, rotation, revocation and the email verification requirement.",14};1516export default function AuthenticationPage() {17  return (18    <DocPage path="/docs/authentication" title="Authentication" description="Every request to the Fetcha API is authenticated with a project-scoped API key sent in a header. Keys carry scopes, a mode and an optional expiry." status="Stable">19      <H2>API keys</H2>20      <P>21        Keys are created in the dashboard under a project. A key belongs to exactly one project and one organization; the requests it makes are logged and counted against them (Fetcha is a private22        platform with a single unlimited plan, so nothing is billed). The plaintext has a recognisable prefix so you can tell keys apart in configuration:23      </P>24      <Table>25        <THead>26          <Tr>27            <Th>Prefix</Th>28            <Th>Mode</Th>29            <Th>Meaning</Th>30          </Tr>31        </THead>32        <TBody>33          <Tr>34            <Td mono>fch_live_…</Td>35            <Td>live</Td>36            <Td>Production key. Use it in deployed services.</Td>37          </Tr>38          <Tr>39            <Td mono>fch_test_…</Td>40            <Td>test</Td>41            <Td>42              Marked as a test key in logs and in <Code>GET /v1/me</Code>. Behaves like a live key today: requests are real and appear in your usage.43            </Td>44          </Tr>45        </TBody>46      </Table>47      <P>48        A key is between 20 and 128 characters. Anything else, or a token without one of the two prefixes, is rejected with <Code>401 INVALID_API_KEY</Code> before the database is consulted.49      </P>5051      <H3>Shown once, hashed at rest</H3>52      <P>53        The plaintext is displayed a single time when the key is created (or rotated). Fetcha persists only a SHA-256 hash together with the non-secret prefix and last four characters used to54        identify the key in the dashboard. There is no way to retrieve a lost key; rotate it instead.55      </P>5657      <H2>Sending the key</H2>58      <P>Use the standard Bearer scheme. This is what the SDKs send.</P>59      <CodeBlock lang="text" code={`Authorization: Bearer fch_live_…`} />60      <P>61        If your HTTP client cannot set <Code>Authorization</Code>, send the key in <Code>X-API-Key</Code> instead. When both headers are present, <Code>X-API-Key</Code> takes precedence.62      </P>63      <CodeBlock lang="text" code={`X-API-Key: fch_live_…`} />64      <Callout variant="warning" title="Server-side only">65        Never ship a key in a browser bundle or mobile app: anyone who can read it can fetch on your behalf, fill your request log and trip your spending limits. Call Fetcha from your backend. The API allows cross-origin requests so that the dashboard66        can talk to it, not so that keys can live in the browser.67      </Callout>6869      <H2>Scopes</H2>70      <P>71        Each key has a list of scopes chosen at creation. A request that needs a scope the key lacks is refused with <Code>403 FORBIDDEN</Code> and a message naming the missing scope.72      </P>73      <Table>74        <THead>75          <Tr>76            <Th>Scope</Th>77            <Th>Grants</Th>78            <Th>Status</Th>79          </Tr>80        </THead>81        <TBody>82          <Tr>83            <Td mono>fetch:execute</Td>84            <Td>85              <Code>POST /v1/fetch</Code>86            </Td>87            <Td>Live</Td>88          </Tr>89          <Tr>90            <Td mono>sessions:write</Td>91            <Td>92              <Code>POST /v1/sessions</Code>, <Code>DELETE /v1/sessions/:id</Code>. Listing and reading sessions requires no scope.93            </Td>94            <Td>Live</Td>95          </Tr>96          <Tr>97            <Td mono>usage:read</Td>98            <Td>99              <Code>GET /v1/usage</Code>100            </Td>101            <Td>Live</Td>102          </Tr>103          <Tr>104            <Td mono>browser:use</Td>105            <Td>106              Kept for compatibility. The <A href="/docs/browser">managed browser</A> is part of <Code>POST /v1/fetch</Code> and needs only <Code>fetch:execute</Code>.107            </Td>108            <Td>Compatibility</Td>109          </Tr>110        </TBody>111      </Table>112      <P>113        <Code>GET /v1/me</Code> and <Code>GET /v1/sessions</Code> only require a valid key.114      </P>115116      <H2>Accounts and email verification</H2>117      <P>118        Fetcha is invitation-only: an account can only be created with an email address that a Fetcha administrator placed on the access list. Once the account exists, API keys work only after119        that email address is verified. Until then every authenticated call, including <Code>/v1/me</Code>, fails with <Code>403 EMAIL_NOT_VERIFIED</Code>. Resend the verification email from the120        dashboard if you did not receive it. The dashboard Playground is exempt so you can try the product before verifying.121      </P>122      <ResponseExample123        status={403}124        statusText="Forbidden"125        body={{ error: { code: "EMAIL_NOT_VERIFIED", message: "Verify your email address before using the production API.", request_id: "req_7f2c1a9e0d4b6h3k" } }}126      />127128      <H2>Rotation and revocation</H2>129      <Ul>130        <Li>131          <Strong>Rotate</Strong> replaces the secret of an existing key and shows the new plaintext once. The old plaintext stops working immediately.132        </Li>133        <Li>134          <Strong>Revoke</Strong> disables a key permanently. Requests made with it return <Code>401 INVALID_API_KEY</Code> with the message <Code>This API key was revoked.</Code>135        </Li>136        <Li>137          <Strong>Expiry</Strong> is optional and set in days at creation. Expired keys return <Code>401 INVALID_API_KEY</Code> with <Code>This API key has expired.</Code>138        </Li>139      </Ul>140      <P>141        Authenticated principals are cached for up to 30 seconds per API process. The dashboard invalidates the cache when you rotate or revoke, so changes normally apply at once; in the rare case142        the invalidation is lost, a stale key stops working within 30 seconds.143      </P>144      <P>145        Other conditions that block a key even when it is valid: the project was archived (<Code>403 FORBIDDEN</Code>), the organization was suspended (<Code>403 FORBIDDEN</Code>, contact{" "}146        <A href="mailto:support@fetcha.co">support@fetcha.co</A>).147      </P>148149      <H2>Checking a key</H2>150      <P>151        <Code>GET /v1/me</Code> validates the key and returns the project, organization and key metadata it resolves to. It is the cheapest call you can make and a good health check for your152        configuration.153      </P>154      <Endpoint method="GET" path="/v1/me" scope={null} status="Live" />155      <CodeTabs tabs={apiTabs({ method: "GET", path: "/v1/me" })} />156      <ResponseExample157        status={200}158        body={{159          project: { id: "proj_2k8d1m3p9q4r7s6t", name: "Default" },160          organization: { id: "org_9a1b2c3d4e5f6g7h", name: "Acme Data", plan: "unlimited" },161          key: { id: "key_5t6y7u8i9o0p1a2s", name: "backend", mode: "live", scopes: ["fetch:execute", "sessions:write", "usage:read"] },162        }}163      />164165      <H2>Logging and redaction</H2>166      <P>167        Fetcha never logs your API key. On the request log, <Code>Authorization</Code>, <Code>Cookie</Code>, <Code>Set-Cookie</Code>, <Code>X-API-Key</Code> and similar headers that you send to168        targets are stored as <Code>[redacted]</Code> when your project log level includes headers. Response bodies are not stored by default.169      </P>170    </DocPage>171  );172}173