Guide
eSigKit accepts two authentication methods on every authenticated route: Cognito ID tokens (PKCE flow, browser apps) or per-tenant API keys (server-to-server). They’re interchangeable — pick whichever fits the calling context.
Decision matrix
| Use case | Method | Header |
|---|---|---|
| Browser app, Chrome extension, mobile | Cognito JWT (PKCE) | Authorization: Bearer <ID-token> |
| Server backend, CI, integration tests | API key | Authorization: Bearer esk_… or X-Api-Key: esk_… |
| Webhook receivers (e.g. Stripe → us) | HMAC signature | n/a — verified per-route |
Two Cognito app clients
eSigKit’s user pool ships two app clients with different security profiles. Customers should always use the first; the second is documented for completeness.
| Customer client | Admin client | |
|---|---|---|
| Name | esigkit-app-client-{stage} | esigkit-admin-client-{stage} |
| Used by | Dashboard at app.esigkit.com, Chrome extension, mobile | Staff admin app at admin.esigkit.com |
| Refresh-token TTL | 30 days | 7 days (smaller blast radius) |
| MFA enforced | Optional (TOTP available) | Required (Pre-Auth Lambda gate) |
| Scopes | openid email profile aws.cognito.signin.user.admin | Same + custom:role=super-admin |
| Callback URLs | app.esigkit.com, Chrome extension origin | admin.esigkit.com only |
eSigKit verifies ID tokens, not access tokens. The tenancy claims
(custom:orgId, custom:role) ride on the ID token only — sending an access
token returns 401.
PKCE flow (browser / extension)
Standard OAuth 2.0 Authorization Code with PKCE. The dashboard does this for
you via aws-amplify; if you’re building a custom client:
- Generate a random
code_verifier(43-128 chars URL-safe). - Compute
code_challenge = base64url(sha256(code_verifier)). - Redirect the user to:
https://auth.esigkit.com/oauth2/authorize
?client_id=YOUR_APP_CLIENT_ID
&response_type=code
&scope=openid+email+profile
&redirect_uri=YOUR_CALLBACK_URL
&code_challenge_method=S256
&code_challenge=<computed>
- Exchange the returned
codefor tokens:
curl -X POST "https://auth.esigkit.com/oauth2/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=authorization_code" \
-d "client_id=YOUR_APP_CLIENT_ID" \
-d "code=GOOGLE_AUTH_CODE" \
-d "redirect_uri=YOUR_CALLBACK_URL" \
-d "code_verifier=<from step 1>"
- Use
id_token(NOTaccess_token) as the bearer:
curl "https://api.esigkit.com/v1/orgs/$ESIGKIT_ORG_ID" \
-H "Authorization: Bearer $ID_TOKEN"
Token TTLs: ID + access tokens = 1 hour; refresh tokens = 30 days (customer) or 7 days (admin). Use the refresh token to mint new ID tokens silently.
API key flow (server-to-server)
Mint a key via the dashboard’s Settings → API Keys page (recommended) or
via POST /v1/orgs/{orgId}/api-keys. Both header
conventions work:
# Stripe-style — works because we accept Bearer tokens that start with `esk_`
curl "https://api.esigkit.com/v1/orgs/$ESIGKIT_ORG_ID/users" \
-H "Authorization: Bearer $ESIGKIT_TOKEN"
# Or the conventional X-Api-Key header
curl "https://api.esigkit.com/v1/orgs/$ESIGKIT_ORG_ID/users" \
-H "X-Api-Key: $ESIGKIT_TOKEN"
Key format: esk_<stage>_<random>. The stage prefix lets you tell at a glance
which environment the key belongs to (esk_prod_… vs esk_test_…). The
random portion is 256 bits of entropy, base64url-encoded.
API keys are bound to the admin role only — super-admin actions are
JWT-only and aren’t accessible via API key.
Token rotation
Recommended cadence: quarterly minimum. Rotation pattern that avoids downtime:
- Mint a new key (
POST /api-keys). - Roll the new key out to your callers (env var swap, secrets-manager update).
- Verify via dashboard’s API-keys list that the new key is being used (each
key tracks
lastUsedAt). - Revoke the old key (
DELETE /api-keys/{keyId}).
For JWTs, rotation is automatic via the refresh-token flow — your client silently mints fresh ID tokens every hour without user interaction.