mirror of
https://github.com/Nezumi-2711/google-drive-s3.git
synced 2026-09-22 13:38:30 +00:00
60 lines
4.6 KiB
Markdown
60 lines
4.6 KiB
Markdown
# Authentication and Signature V4
|
|
|
|
The Worker accepts AWS Signature Version 4 in either form:
|
|
|
|
- an `Authorization: AWS4-HMAC-SHA256 …` header with `x-amz-date`; or
|
|
- a presigned request with `X-Amz-Algorithm`, `X-Amz-Credential`, `X-Amz-Date`, `X-Amz-SignedHeaders`, and `X-Amz-Signature` query parameters.
|
|
|
|
The `Credential` access-key ID is looked up in the gateway's access-key store (managed from the dashboard under `/api/integration/keys`); the signing key is derived from that key's secret, `REGION`, service `s3`, and the `YYYYMMDD` request date. Unknown or expired key IDs are rejected with `403 AccessDenied`.
|
|
|
|
## Access keys
|
|
|
|
S3 credentials are named key pairs stored in `AUTH_KV` (`s3-credentials`), managed via the dashboard:
|
|
|
|
- Up to 5 live keys — one per integration, revocable independently.
|
|
- Rotation creates a replacement with the same label; the old key either dies immediately (`graceSeconds: 0`) or keeps authenticating for a grace period of 1 hour, 24 hours, or 7 days.
|
|
- The legacy `ACCESS_KEY`/`SECRET_KEY` secrets act as **bootstrap only**: on first use they are seeded into the store and are superseded by dashboard-managed keys afterwards.
|
|
|
|
Key changes are edge-cached for up to **60 seconds**. A rotated-away or revoked key may continue to authenticate for at most one minute after the change.
|
|
|
|
Secrets are stored readable in KV because SigV4 verification must derive the signing key from the raw secret — they cannot be hashed. Treat dashboard sessions as full credential access.
|
|
|
|
## Presigned URL expiry
|
|
|
|
Query-authenticated requests require `X-Amz-Expires` from 1 through 604800 seconds (seven days). The Worker returns `403 AccessDenied` with `Request has expired` when it is missing, invalid, out of range, or its signed timestamp plus expiry is in the past.
|
|
|
|
BFFs should normally use a much shorter period such as five minutes.
|
|
|
|
## Header-authenticated requests
|
|
|
|
The Worker requires a valid `x-amz-date` and rejects a request when the server time differs from it by more than 15 minutes. This returns `403 RequestTimeTooSkewed`.
|
|
|
|
## Canonical request behavior
|
|
|
|
The canonical request is built from:
|
|
|
|
1. method;
|
|
2. URL path;
|
|
3. sorted query string (excluding `X-Amz-Signature` for presigned URLs);
|
|
4. the signed-header list and normalized values;
|
|
5. the signed-header list; and
|
|
6. `x-amz-content-sha256`, defaulting to `UNSIGNED-PAYLOAD`.
|
|
|
|
A signed `accept-encoding` header gets special treatment. Cloudflare rewrites the received value at the edge (usually to `gzip, br`), so the delivered header cannot be compared against what the client signed, and clients disagree on the value anyway: aws-sdk-go-v2 (memos and most Go clients) signs `identity` on every operation, while rclone and AWS CLI v2 sign `gzip` for `GetObject` and `identity` elsewhere. The Worker therefore verifies against the pre-rewrite value in `request.cf.clientAcceptEncoding` when the edge supplies it, and otherwise retries the signature against each value a client plausibly signs (the delivered header, `identity`, `gzip`, empty). Only this header's canonical value varies — the signature must still be produced with the secret key. Clients that sign some other value can drop the header from the signature instead (rclone: `--s3-sign-accept-encoding=false`). Browser code must not sign `accept-encoding` because browser networking controls it.
|
|
|
|
Payload hashes are **not** verified. Browser and BFF clients should use `x-amz-content-sha256: UNSIGNED-PAYLOAD`; this is a deliberate streaming limitation, not an integrity guarantee.
|
|
|
|
## Public-read buckets
|
|
|
|
Buckets configured with `publicRead: true` (managed via the dashboard or API) permit unsigned `GET` and `HEAD` requests. All write operations still require valid Signature V4 authentication. Bucket metadata is tracked via Drive `appProperties`.
|
|
|
|
## Dashboard login API
|
|
|
|
The Worker provides management API endpoints under `/auth/*` for the management dashboard (`s3-drive-storage-manage`). These endpoints **do not** use AWS Signature V4:
|
|
|
|
- `POST /auth/login` — Verifies `{ passwordHash }` against `DASHBOARD_PASSWORD` (SHA-256 compared in constant time). Returns `{ token, expiresIn }` on success (opaque 256-bit base64url token with a 12-hour TTL stored in `AUTH_KV`). Rate-limited to 5 failed attempts per IP within 15 minutes.
|
|
- `GET /auth/session` — Validates the session token supplied in `Authorization: Bearer <token>`. Returns `{ valid: true }` if valid, or `401` if expired/invalid.
|
|
- `POST /auth/logout` — Revokes the session token supplied in `Authorization: Bearer <token>`. Returns `204 No Content`.
|
|
|
|
For a tested signing reference, see the `signed()` helper in [`test/s3.test.ts`](../test/s3.test.ts).
|