Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions src/auth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# STAC Asset Auth Demo

Eksempel på autentisering av STAC assets ved bruk av pre-signed-urls.
Denne demoen tar noen snarveier for å eksemplifisere en flyt hvor
STAC API peker på en auth server som genererer og redirecter til
en pre-singed-url.


## Getting started

```bash
# Start STAC API and storage
# pwd: src/auth/
cd api/
docker compose up -d
./init_db.sh
```

```bash
# Start Auth server
# pwd src/auth/
pip install -r requrements.txt
fastapi dev
```

```bash
# Test connections

# Authenticated directly to storage
curl -i http://localhost:8080/test.txt?token=demo123
# Not authenticated directly to storage
curl -i http://localhost:8080/test.txt

# Authenticated to auth server (redirect)
curl -H "Authorization: bearer mysecrettoken123" http://localhost:8000/test.txt -L
# Not authenticated to auth server
curl http://localhost:8000/test.txt -L

# Find the item using STAC search
curl -X 'GET' \
'http://localhost:8082/search?collections=auth-test&ids=the-message&limit=10&filter-lang=cql2-text' \
-H 'accept: application/geo+json'
```



55 changes: 55 additions & 0 deletions src/auth/api/compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
services:
app:
image: stac-utils/stac-fastapi-pgstac
build: .
environment:
- APP_HOST=0.0.0.0
- APP_PORT=8082
- RELOAD=true
- ENVIRONMENT=local
- PGUSER=username
- PGPASSWORD=password
- PGDATABASE=postgis
- PGHOST=database
- PGPORT=5432
- WEB_CONCURRENCY=10
- VSI_CACHE=TRUE
- GDAL_HTTP_MERGE_CONSECUTIVE_RANGES=YES
- GDAL_DISABLE_READDIR_ON_OPEN=EMPTY_DIR
- DB_MIN_CONN_SIZE=1
- DB_MAX_CONN_SIZE=1
- USE_API_HYDRATE=${USE_API_HYDRATE:-false}
- ENABLE_TRANSACTIONS_EXTENSIONS=true
- ENABLE_CATALOGS_EXTENSION=true
ports:
- "8082:8082"
volumes:
- ./stac_fastapi:/app/stac_fastapi
depends_on:
- database
command: bash -c "scripts/wait-for-it.sh database:5432 && python -m stac_fastapi.pgstac.app"

database:
image: ghcr.io/stac-utils/pgstac:v0.9.8
environment:
- POSTGRES_USER=username
- POSTGRES_PASSWORD=password
- POSTGRES_DB=postgis
- PGUSER=username
- PGPASSWORD=password
- PGDATABASE=postgis
ports:
- "5439:5432"
command: postgres -N 500

storage:
image: nginx:alpine
ports:
- 8080:80
volumes:
- ./data/cog/:/usr/share/nginx/html:ro
- ./data/nginx.conf:/etc/nginx/nginx.conf:ro

networks:
default:
name: stac-fastapi-network
1 change: 1 addition & 0 deletions src/auth/api/data/cog/test.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
I am super secret!
48 changes: 48 additions & 0 deletions src/auth/api/data/collection.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"id": "auth-test",
"description": "for testing stac auth",
"stac_version": "1.0.0",
"links": [
],
"stac_extensions": [],
"title": "Auth Test",
"type": "Collection",
"assets": {
},
"license": "proprietary",
"extent": {
"spatial": {
"bbox": [
[
-180,
-90,
180,
90
]
]
},
"temporal": {
"interval": [
[
"2026-06-15T10:41:07.993Z",
null
]
]
}
},
"keywords": [
"auth", "test"
],
"providers": [
{
"name": "Skygeo",
"description": "For the cloud",
"roles": [
"publisher"
],
"url": "https://github.com/kartAI/skygeo"
}
],
"summaries": {
}
}
69 changes: 69 additions & 0 deletions src/auth/api/data/item.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"bbox": [
-180,
-90,
180,
90
],
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
10.443471396313623,
60.20395514925783
],
[
10.443471396313623,
60.209497078619115
],
[
10.428807703585424,
60.209497078619115
],
[
10.428807703585424,
60.20395514925783
],
[
10.443471396313623,
60.20395514925783
]
]
]
},
"properties": {
"title": "secret message",
"description": "this is the protected message",
"datetime": "2026-06-15T11:25:59.408Z",
"created": "2026-06-15T11:25:59.408Z",
"updated": "2026-06-15T11:25:59.408Z",
"start_datetime": "2026-06-15T11:25:59.408Z",
"end_datetime": "2026-06-15T11:25:59.408Z",
"license": "proprietary",
"providers": [
{
"name": "Kartverket",
"roles": [
"owner"
],
"url": "https://www.kartverket.no"
}
]
},
"id": "the-message",
"stac_version": "1.0.0",
"assets": {
"cog": {
"href": "http://localhost:8000/test.txt",
"type": "txt",
"title": "Message",
"description": "A secret message"
}
},
"links": [
],
"stac_extensions": [],
"collection": "auth-test"
}
14 changes: 14 additions & 0 deletions src/auth/api/data/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
events {
}

http {
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
location / {
if ($arg_token != "demo123") { return 403; }
try_files $uri $uri/ =404;
}
}
}
20 changes: 20 additions & 0 deletions src/auth/api/init_db.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash

set -e

# 1. Get the absolute path to the directory where THIS script is located
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd)

# Create collection
curl -X 'POST' \
'http://localhost:8082/collections' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d @"$SCRIPT_DIR/data/collection.json"

# Add an item
curl -X 'POST' \
'http://localhost:8082/collections/auth-test/items' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d @"$SCRIPT_DIR/data/item.json"
39 changes: 39 additions & 0 deletions src/auth/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi.responses import RedirectResponse
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

app = FastAPI()

# Static token for demo purposes
API_TOKEN = "mysecrettoken123"
STORAGE_HOST = "http://localhost:8080"
# The auth api server manages the credentials to storage.
# this is comparable to having the server issue pre-signed urls.
STORAGE_TOKEN = "demo123"

security = HTTPBearer()

def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
if credentials.scheme.lower() != "bearer":
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication scheme"
)
if credentials.credentials != API_TOKEN:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or missing token"
)
return credentials.credentials

app = FastAPI(dependencies=[Depends(verify_token)])

def get_presigned_url(obj: str):
# Dummy function to simulate creating a presinged url
return f"{STORAGE_HOST}/{obj}?token={STORAGE_TOKEN}"

@app.get("/{obj}")
def read_item(obj: str):
return RedirectResponse(
url=get_presigned_url(obj)
)
1 change: 1 addition & 0 deletions src/auth/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
fastapi[standard]==0.137.0