https://github.com/glueops/qr-code-generator
A FastAPI endpoint that creates QR codes based off given URLs
https://github.com/glueops/qr-code-generator
allow-auto-merge glueops-platform
Last synced: 3 months ago
JSON representation
A FastAPI endpoint that creates QR codes based off given URLs
- Host: GitHub
- URL: https://github.com/glueops/qr-code-generator
- Owner: GlueOps
- Created: 2023-07-04T21:20:10.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2026-02-09T00:25:41.000Z (6 months ago)
- Last Synced: 2026-02-09T06:45:48.853Z (6 months ago)
- Topics: allow-auto-merge, glueops-platform
- Language: Python
- Homepage:
- Size: 155 KB
- Stars: 0
- Watchers: 2
- Forks: 1
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Signed QR Service (Go)
A tiny Go HTTP service that **mints signed, expiring QR codes** for approved target URLs.
It exposes two endpoints:
- `GET /v1/sign` — **protected** “mint” endpoint that creates a signed, expiring QR request URL
- `GET /v1/qr` — **public** endpoint that validates the signature + expiration and returns a **PNG QR code**
## Why
QR codes are easy to copy and reuse. This service lets you generate QR codes that:
- expire after a configurable TTL
- can only be minted by callers with a bearer token
- only encode valid `http/https` URLs (and rejects `localhost`)
## How it works
Signature format:
```
sig = base64url( HMAC-SHA256(secret, u + "\n" + exp) )
```
The QR endpoint requires:
- `u` = target URL to encode
- `exp` = unix timestamp (seconds) when the request expires
- `sig` = HMAC signature over `u` and `exp`
If the signature is valid and `exp` is in the future, the service returns a 256×256 PNG QR code encoding the **target URL**.
## Requirements
- Go 1.23+
- Environment variables:
- `QR_SIGNING_SECRET` — secret key used for HMAC signing
- `QR_MINT_TOKEN` — bearer token required to call `/v1/sign`
Dependencies used:
- `github.com/joho/godotenv` (optional `.env` loading)
- `github.com/skip2/go-qrcode` (PNG QR code generation)
## Setup
### 1) Install dependencies
```bash
go mod download
```
### 2) Configure environment
Create a .env file (or export env vars):
```bash
echo "QR_SIGNING_SECRET=$(openssl rand -hex 32)" > .env
echo "QR_MINT_TOKEN=$(openssl rand -hex 16)" >> .env
```
Then start the server:
```bash
go run .
```
The service listens on:
- 0.0.0.0:8000
## API
### GET /v1/sign (protected)
Mint a signed QR request path.
#### Auth
- Authorization: Bearer
#### Query params
- u (string, required): target URL to encode (must be http or https, not localhost)
- ttl (int, required): time-to-live in seconds (1..86400)
#### Response
- 200 text/plain: a relative path like:
```text
/v1/qr?u=&exp=&sig=
```
#### Example
```bash
curl -s \
-H "Authorization: Bearer $QR_MINT_TOKEN" \
"http://localhost:8000/v1/sign?u=https://example.com&ttl=300"
```
### GET /v1/qr (public)
Return a PNG QR code after validating signature + expiration.
#### Query params
- u (string, required): target URL to encode
- exp (int, required): unix timestamp when the request expires
- sig (string, required): base64url HMAC signature
#### Response
- 200 image/png: PNG QR code encoding the target URL (256×256)
- 401: expired or bad signature
- 400: missing/invalid params or invalid target URL
#### Example
First mint a signed path:
```bash
SIGNED_PATH=$(
curl -s \
-H "Authorization: Bearer $QR_MINT_TOKEN" \
"http://localhost:8000/v1/sign?u=https://example.com&ttl=300"
)
echo "$SIGNED_PATH"
```
Then fetch the QR image:
```bash
curl -s "http://localhost:8000$SIGNED_PATH" -o qr.png
open qr.png # macOS; use your OS viewer
```
### Validation rules for u
The target URL must:
- parse successfully
- use scheme http or https
- include a host
- not have hostname localhost
(Other private/internal hostnames are not blocked by default—see “Hardening” below.)
### Security notes
- /v1/sign is protected via a constant-time compare of the Authorization header.
- /v1/qr uses constant-time comparison on decoded HMAC bytes.
- Responses for QR images include Cache-Control: no-store.
### Project layout
Currently everything lives in main.go.